使用 sCrypt 实现一个简单的 NFT 合约
Posted freedomhero
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 sCrypt 实现一个简单的 NFT 合约相关的知识,希望对你有一定的参考价值。
我们之前的token方案针对的是可替换(fungible)的 token。这里来看看另一种方案如何实现 NFT(non-fungible token)合约。这类 token 可以代表独一无二的和不可分割的资产,比如房地产和收藏品。
概览
与可替换 token 类似,在每个 UTXO 中,token 合约的数据部分也有两个部分与 NFT 有关:
- 一个公钥:用于控制 token 的发行或转让。
- 一个整数:唯一标识一个 token。
同样,通过使用与公钥对应的私钥进行签名来对 UTXO 中的 token 进行转账。不同之处在于,在 NFT 的方案中每个UTXO只存储一个 token,而不象可替换 token 方案那样可以存储多个 token。
发行
token 发行 UTXO 是一个特殊的 UTXO 合约,只有发行者才可以运行该合约。发行者通过把该 UTXO 拆分成多个 UTXO 的方式来发行新的 token。下图是把 token 发行 UTXO 拆分成两个,这样就发行了一个新的 token i 给Alice。
下面是相关的代码:
public function issue(Sig issuerSig, PubKey receiver, int satoshiAmount0, int satoshiAmount1, SigHashPreimage txPreimage)
// this ensures the preimage is for the current tx
require(Tx.checkPreimage(txPreimage));
// read previous locking script: codePart + OP_RETURN + currTokenId + issuer + matchAction
bytes lockingScript = SigHash.scriptCode(txPreimage);
int scriptLen = len(lockingScript);
// constant part of locking script: upto op_return
int constStart = scriptLen - DataLen - Constants.PubKeyLen - 1;
bytes constPart = lockingScript[: constStart];
bytes matchAction = lockingScript[scriptLen - 1 :];
// action must be issue
require(matchAction == NonFungibleToken.ISSUE);
PubKey issuer = PubKey(lockingScript[constStart + DataLen : scriptLen - 1]);
// authorize: only the issuer can mint new tokens
require(checkSig(issuerSig, issuer));
int currTokenId = unpack(lockingScript[constStart : constStart + DataLen]);
// increment token ID to mint a new token
bytes outputScript0 = constPart + num2bin(currTokenId + 1, DataLen) + issuer + NonFungibleToken.ISSUE;
bytes output0 = Utils.buildOutput(outputScript0, satoshiAmount0);
// transfer previous token to another receiver
bytes outputScript1 = constPart + num2bin(currTokenId, DataLen) + receiver + NonFungibleToken.TRANSFER;
bytes output1 = Utils.buildOutput(outputScript1, satoshiAmount1);
// check outputs
Sha256 hashOutputs = hash256(output0 + output1);
require(hashOutputs == SigHash.hashOutputs(txPreimage));
转移
UTXO 中的 token 可以如下图所示进行转移:
如下是相关代码:
public function transfer(Sig senderSig, PubKey receiver, int satoshiAmount, SigHashPreimage txPreimage)
require(Tx.checkPreimage(txPreimage));
// read previous locking script: codePart + OP_RETURN + tokenID + ownerPublicKey + matchAction
bytes lockingScript = SigHash.scriptCode(txPreimage);
int scriptLen = len(lockingScript);
// constant part of locking script: upto tokenID
int constStart = scriptLen - Constants.PubKeyLen - 1;
bytes constPart = lockingScript[: constStart];
bytes matchAction = lockingScript[scriptLen - 1 :];
// action must be transfer
require(matchAction == NonFungibleToken.TRANSFER);
PubKey sender = PubKey(lockingScript[constStart : scriptLen - 1]);
// authorize
require(checkSig(senderSig, sender));
// transfer
bytes outputScript = constPart + receiver + NonFungibleToken.TRANSFER;
bytes output = Utils.buildOutput(outputScript, satoshiAmount);
require(hash256(output) == SigHash.hashOutputs(txPreimage));
组合
下图展示了 NFT 的发行及转移的操作流程。
讨论
我们只展示了 NFT 的基本方案。它扩展起来也很简单,例如:
- 在一个 Tx 中发行多个新 token
- 在一个 Tx 中转移多个 token
- 限制 token总量
- 销毁 token
以上是关于使用 sCrypt 实现一个简单的 NFT 合约的主要内容,如果未能解决你的问题,请参考以下文章