使用 sCrypt 实现简单的 Token 智能合约

很早之前就有人开始研究在比特币网络上实现外部资产的 Token 化,也产生了很多相关的协议。总体来说,这些协议大部分是通过将代表资产的原信息通过诸如 OP_RETURN 之类的操作码放入脚本后进行上链,从而实现了“资产上链”的过程。但是因为用于执行和验证这些元数据的逻辑并不是由比特币矿工完成的,故而这些方案被称作第二层(Layer 2)解决方案。

与之前这些工作不同,我们在这里即将介绍的是一种实现第一层 Token 智能合约的机制。这种方案的核心是依靠比特币网络的矿工在共识层完成合约的相关逻辑和验证,从而进一步改善了 Token 合约的安全性和可靠性。我们使用 sCrypt 语言实现一个简单的例子(只有2位 Token 的持有人)。

Token 智能合约

Token 的状态表

使用之前我们介绍过的支持有状态的比特币智能合约技术,我们可以将整个 Token 的持有人信息表存储在合约的状态中。实际上,这个信息表是一个 map 结构,保存每个持有人的公钥地址和其对应的持有份额。如下图所示:

tokenTable

具体的 Token 合约实现可以在这里找到:

import "util.scrypt";

/**
 * A toy token example between two holders
 */
contract Token {
    
    
    public function transfer(PubKey sender, Sig senderSig, PubKey receiver, 
	 int value /* amount to be transferred */, bytes txPreimage, int amount) {
    
    
        
        // this ensures the preimage is for the current tx
	 require(Tx.checkPreimage(txPreimage));

        // authorize
        require(checkSig(senderSig, sender));

        // read previous locking script
        bytes lockingScript = Util.scriptCode(txPreimage);
        int scriptLen = length(lockingScript);

        PubKey pk0 = PubKey(lockingScript[scriptLen - 68 : scriptLen - 35]);
        int balance0 = unpack(lockingScript[scriptLen - 35 : scriptLen - 34]);
        PubKey pk1 = PubKey(lockingScript[scriptLen - 34 : scriptLen - 1]);
        int balance1 = unpack(lockingScript[scriptLen - 1 : ]);

        // only between two holders
        require(sender == pk0 && receiver == pk1 || sender == pk1  && receiver == pk0);

        // transfer
        if (sender == pk0) {
    
    
            require(balance0 >= value);
            balance0 = balance0 - value;
            balance1 = balance1 + value;
        } else {
    
    
            require(balance1 >= value);
            balance1 = balance1 - value;
            balance0 = balance0 + value;
        }

        // write new locking script
        bytes lockingScript_ = lockingScript[: scriptLen - 68] + pk0 + num2bin(balance0, 1) + pk1 + num2bin(balance1, 1);
        bytes hashOutputs = Util.hashOutputs(txPreimage);
        Sha256 hashOutputs_ = hash256(num2bin(amount, 8) + Util.writeVarint(lockingScript_));
		require(hashOutputs == hashOutputs_);
    }
}

正如上篇文章里介绍的,首先我们需要验证 preimage 参数确实是当前 transaction:

require(Tx.checkPreimage(txPreimage));

下面这行代码保证了只有所有者才能转移 Token:

require(checkSig(senderSig, sender));

剩下的代码部分就是验证和维护合约的内部状态了,这里的过程与上篇文章中的也是类似。

Token 发行

这里是部署和转移 Token 的 Javascript 示例代码。合约的部署交易为 Tx1。我们可以看到 pk0 的值为 024c79d694ef7dfd53217de55f7fbf63d2381b18e31afc408b226bc88a6a3cb4f0, 它的 token 初始余额为 100;pk1 的值为 038ad6e71978a3bcc2974b7106f91a61cf03b184189c67ceb1e34e4e7cc898c2aa,它的 token 初始余额为 0。如下图中红色标注的部分即为他们各自的余额:
Tx1

Token 转移

接下来 pk0 转移 40 个 token 给 pk1,对应的交易为 Tx2。那么 pk0 的余额将变为 60(16进制的表示为0x3c), 而 pk1 的余额将变成 40(16进制的表示为0x28)。
Tx2

最后,pk1 又将 10 个 token 转回给 pk0,对应交易为 Tx3。最终他们的余额分别为 pk0 70(0x46) 和 pk1 30(0x1e)。
Tx3

结论

这里我们只展示了在两个所有者之间如何转移 token。实际上真正可以使用的合约会比这个复杂很多,比如说加上转移前检查余额的逻辑。当然,类似的扩展应该都是可行的。多亏了 Bitcoin SV 网络的可扩展性,任何可扩展了此合约的第一层 Token 合约都能以很低的成本运行。再加上之前的诸多第二层 Token 化协议,为我们这些区块链世界的探索者打开了未来无限的可能。

猜你喜欢

转载自blog.csdn.net/freedomhero/article/details/107308691