事实证明, BSV 上的智能合约比以前想象的更强大、更通用。然而,仍然有两个严重的限制:
-
合约一旦部署就不能支持新功能。例如,我们可以部署一个代币合约,但后来发现由于缺乏某些功能,它无法集成到一些新的交易所或投票应用程序中。这极大地阻碍了它与第三方应用程序的互操作性,从而阻碍了它的广泛采用。 -
第一个限制也暗示所有受支持的功能都必须包含在代币合约中。即使我们可以预见到令牌在最初构思时需要支持的所有功能,也只有少数功能是常用的,而大多数功能很少使用。
支付到合约哈希 (Pay to Contract Hash)
我们提出了一种新颖的方法来同时解决这两个限制,称为支付到合约哈希 (P2CH)。我们以 NFT 为例来演示它是如何工作的。它也适用于其他加密资产,例如原生 BSV 和同质化代币。在我们之前的 NFT 合约中,我们维护了一个表,将每个代币映射到其所有者的地址。所有者拥有可以授权转移令牌的私钥。
图 1: NFT 3 由合约拥有
在 P2CH 下,所有者也可以是合约的地址,定义为其脚本的哈希值。哈希值作为合约的唯一 ID,用于引用合约。
具体来说,使用了 hash160,与用于将 BSV 公钥散列到地址中的散列算法相同。合约哈希看起来与普通 BSV 地址相同,因为它们都是 20 字节。新的 NFT 合约如下所示:
contract Pay2ContractHash {
@state
HashedMap<int, PubKeyHash> owners;
public function transferFrom(PubKeyHash from, PubKeyHash to,
Sig fromSig, PubKey fromPubKey,
bytes prevouts, bytes contractTx, int contractInputIndex,
int tokenId, int keyIndex, SigHashPreimage preimage) {
require(this.owners.canGet(tokenId, from, keyIndex));
if (hash160(fromPubKey) == from) {
require(checkSig(fromSig, fromPubKey));
} else {
require(TxUtil.verifyContractByHash(
prevouts,
contractInputIndex,
contractTx,
from,
preimage));
}
require(this.owners.set(tokenId, to, keyIndex));
require(this.propagateState(preimage));
}
}
Pay2ContractHash 合约
除了 else 分支, 这与之前的 NFT 合约相同。else 分支负责处理当代币由合约而不是用户的私钥控制时的代币传输。
我们需要确保合约在同一交易的另一个输入中被解锁,就像我们在合约间调用中所做的那样。
在图 2 所示的示例中,输入 0 中引用的令牌需要验证同一交易 tx2 中的输入 1 是否引用了预期合约。
- 第
20 -22 行获取包含合约的交易的 id (图中的 tx1)和它包含的输出(tx1 中的输出 0 ) - 第
24 行验证完整交易是否与 txid 匹配。 - 第
26 行解析合约交易以获取合约的完整脚本并将其与第 27 行的所有者“地址”进行哈希匹配。
图 2: 转让合约拥有的代币
代币销售合约示例
一个示例合同可能如下:
contract TokenSale {
PubKeyHash recvAddr;
int recvAmount;
public function unlock(SigHashPreimage txPreimage) {
SigHashType sigHashType = SigHash.SINGLE | SigHash.FORKID;
require(Util.checkPreimageSigHashType(txPreimage, sigHashType));
bytes outputScript = Util.buildPublicKeyHashScript(this.recvAddr);
bytes output = Util.buildOutput(outputScript, this.recvAmount);
require(hash256(output) == Util.hashOutputs(txPreimage));
}
}
TokenSale 合约
当且仅当给定地址支付特定金额时,才能使用它。使用这个合约,我们可以通过三个步骤以给定的价格原子地出售代币:
- 部署 TokenSale 合约。
- 将代币转入上述合约的哈希,即其锁定脚本的
hash160 。现在代币受合约控制,而不是用户的私钥。这类似于以太坊中的合约账户,没有对应的私钥,以太坊中用户常用的是EOA账户。 - 在一次交易中同时解锁代币和
TokenSale 合约,如图 2 所示。买方将把代币从合约中转移给自己。
可以在此处找到完整的代码示例。
总结
值得注意的是,合约可以是任意的,可以由第三方独立开发,也不必事先知道。这意味着令牌是无限可扩展的(限制 1 )。此外,它结构紧凑,仅处理适用于任何合约的 P2CH(限制 2 )。此外,还可以使用模式匹配合约的替代方法。上面例子我们使用完整合约脚本的哈希匹配,也可以使用部分脚本的哈希进行匹配,例如有状态合约的代码部分。
致谢
这个想法来自Sensible Contract的陈诚。
|