打开sCrypt的盒子(4)OP_PUSH_TX

sCrypt是比特币合约走出来的第一步。我们看看sCrypt创造的新技术:OP_PUSH_TX

OP_PUSH_TXOP开始,但它不是比特币脚本的操作符,是一个将交易原像放入解锁脚本,从而判断新的输出是否符合规定的技术。

假设已经有了一个交易TX1的输出out_1,其中的锁定脚本要求:解锁它的解锁脚本必须拥有一样代码逻辑,变化的只是最后包括的数字。这个TX2应该怎么做呢?TX1的锁定脚本是如何实现的呢?

在这里插入图片描述 实现的技术就是OP_PUSH_TX, 它要求解锁参数是新交易TX2的原像

OP_PUSH_TX的官方文档在sCrypt语言参考 (opens new window)

官方Counter代码例子

contract Counter {
  public function increment(bytes txPreimage, int amount) {
    require(Tx.checkPreimage(txPreimage));

    bytes scriptCode = Util.scriptCode(txPreimage);
    int scriptLen = length(scriptCode);

    // state (i.e., counter value) is at the end
    int counter = unpack(scriptCode[scriptLen - Util.DataLen : ]);
    // increment counter
    bytes scriptCode_ = scriptCode[ : scriptLen - Util.DataLen] + num2bin(counter + 1, Util.DataLen);
    // output: amount + scriptlen + script
    Sha256 hashOutputs = hash256(num2bin(amount, Util.OutputValueLen) + Util.writeVarint(scriptCode_));
    // ensure output is expected: amount is same with specified
    // also output script is the same with scriptCode except counter incremented
    require(hashOutputs == Util.hashOutputs(txPreimage));
  }
}

Tx.checkPreimage是名叫Tx合约中的静态函数,它检查原像是否同当前交易一致。调用increment函数时,原像中包括了前一个交易锁定脚本。Tx.checkPreimage函数内部使用了OP_CHECKSIG,进行签名检查。签名检查用随机生成的公钥私钥。从而确认解锁参数提供的原像就是TX2的原像。

这就像DNA遗传,新诞生的小鸡必须同母鸡拥有一致的DNA,但在某些地方可以有变化。必须一致的DNA代码就写在锁定脚本中。每个新的生命,都有必须一致的那部分。

可以改变的部分,也必须符合规定。继续看一下上面的例子

bytes scriptCode = Util.scriptCode(txPreimage)这一句获取了原像TX1_OUT_1中的锁定脚本。它的最后包含数据OP_RETURN 00(不同的脚本也可能不包含数据)

有趣的是scriptCode如何写入原像的(虽然同本文无关)。scriptCode用Varint格式保存锁定脚本。Varint是一个字符串的表现形式,基本上是一个header表明数据字节长度站几个字节,是1,2,4还是8,然后获取长度len,剩下的其余部分是数据。 一个很好的资料可以看 https://learnmeabitcoin.com/technical/varint (opens new window)

在这里插入图片描述

然后从原像最后一个字节取出 counter 00, 加一,置换原像最后一个字节为 01

counter就是可变部分,他要求新的输出TX2中的锁定脚本的最后一个字节必须是01。如果再有新的交易TX3,TX4,每一次counter的数字都加一。

判断的办法是检查原像中的hashOutputs, 在签名类型为ALL的时候,会对TX2的所有输出,采用 satoshis(8个字节, UInt64LE类型) + 锁定脚本(Varint格式)串接成字节数组,最后sha256(sha256(b)) 。

在脚本中计算的hashOutputs同原像中的hashOutputs一致,可以说明新的TX2输出符合要求。

注意⚠️计算hashOutputs是TX2的所有输出,这里是可能遇到的难点。在官方例子中严格假设只有一个输入和一个输出。在别的例子中要求一个输入两个输出,或者两个输入一个输出。

另外一个问题就是OP_RETURN后面的数据格式,目前采用字节拼接的方式 counter.dataLoad = num2bin(0, DataLen) 虽然可行,但也许有更加好的方案。

想要比特币上的Token吗?敬请期待...

享受比特币带来的安全自由, 关注使用NoteSV (opens new window)