随着 NFT 概念的爆火,绝大部分公链都设计了自己的 NFT 协议,就今天讨论的两条公链来说,Nervos 有 mNFT 协议,以太坊有 ERC721 和 ERC1155 协议。
这些协议在创建、使用和展示 NFT 的时候没有任何问题,但当要基于这些协议设计更复杂、更通用和更多样的功能的时候就都会遇到一个问题:实现起来有点困难。 这里我们列举三个比较典型的功能:
-
自定义 NFT 交易分成
-
用 sUDT 或 ERC20 代币交易 NFT
-
集齐某些 NFT 合成特定 NFT
实现不了的原因是这些协议一开始并没有朝着强可扩展性的方向去设计,当协议所能表述的功能范围无法覆盖项目方所需要的使用场景时,上述问题就会出现。虽然这些无法实现的功能可由项目方独立完成制作,但这也意味着不同项目之间就这些功能而言很难做到兼容。
那有没有更加通用的 NFT 协议去囊括以上这些(甚至更多)的功能呢?首先可以明确的是:这样的协议受限于 Nervos 和以太坊本身能支持的在合约设计上的自由度,于是这又引申出另外一个更 General 的问题:拥有不同编程模型的公链在设计 NFT 协议的时候是否有它们的理论极限?
就 Nervos 和以太坊而言,它们能支持的 NFT 协议设计的极限一时半会儿很难想明白,不过我们可以先从扩展现有的 NFT 协议(mNFT 和 ERC721)出发,在实现上述三个典型问题后,再用归纳法总结出一条规律,最后基于这条规律用演绎法推导出我们需要的理论设计极限。
在 Nervos 上如何设计
在 CKB 网络中一个 Cell 可通过 TypeScript 来表示一个 NFT,那么更多的功能扩展就主要放在 LockScript 上。除了表明 NFT 所有权之外 LockScript 还将承载更多关于 NFT 交易相关的功能,当然不管是 LockScript 还是 TypeScript 都能通过 Witness 和 CellDep 再做一次扩展,只不过放在 Witness 中的数据只能在解锁阶段使用而无法被存储下来。
基于上述理论扩展 mNFT 协议以实现以下三个功能:
1. 自定义 NFT 交易分成
通过 LockScript 实现,假设 NFT 是用 CKB 来交易,那可以在 LockScript_Args 中设定针对创作者、买方和卖方三者的分成比例,买方在获得 NFT 所有权的交易中可以设定新的分成比例,对于修改分成比例的交易场景是需要买卖双方多签参与还是直接沿用 ACP 的逻辑,则完全由 LockScript 代码自由设定。
2.用 sUDT 代币交易 NFT
仍然通过 LockScript 实现,在交易中引入 sUDT 所属的 Cell 即可,然后其余的功能逻辑和《自定义 NFT 交易分成》基本完全相同。
3.用多个 NFT 合成特定 NFT
多个 NFT 合成一个 NFT 本质上是一个交易里有多个 NFT 在输入端,而只有一个 NFT 在输出端,对于如何配置这样的输入输出结构,就需要一个专门的规则来限制和说明:
-
这一规则说明可以通过 LockScript 来实现,即在 LockScript 代码中明确表明 TypeScript 中表示的 NFT 能和哪些 NFT 一同放在输入端,然后输出端需要放入怎样的 NFT;
-
规则也可以通过引入规则 Cell 来说明,这个 Cell 可以同时放在交易的输入端和输出端,然后通过 LockScript 或TypeScript 来验证规则逻辑,这样 NFT-Cell 的 LockScript 就可以只是普通的 Sighash 脚本即可;
-
最后,规则 Cell 也可以通过 CellDep 的形式引入,具体的规则验证逻辑则存储在规则 Cell 的 Data 或 ScriptArgs 中,由 NFT-Cell 的 LockScript 去读取。
我们发现,在设计这三个功能的时候完全没有修改 mNFT 的协议逻辑,通过为 Cell 编写特定的 LockScript 或组合特定功能的 Cell 或 CellDep 就可以做到在不修改原有协议的情况下实现功能扩展。
我们还能发现,针对上述三个功能而言,即使所有 NFT 的 LockScript 都为标准的 Sighash 合约,也可以通过直接在交易输入输出端引入规则 Cell 来实现这些功能,甚至还能实现更多功能,只要交易双方都对此交易进行了见证,那规则 Cell 对他们来说就是合法的。
在以太坊上如何设计
在以太坊上修改现有协议有个现实问题,那就是新的协议在原有协议已经完全普及的情况下很难传播开来以替换原有协议,所以如果我们假定在不修改现有协议的情况下就做到功能扩展,那只能是用新的合约来组合现有的 ERC721 或 ERC1155 合约,而这个新的合约通常都和项目本身的逻辑强相关,这也是现在大多数项目所采用的设计方式。
不过这种方式有个明显的弊端,那就是不同项目之间就同一类型的功能很难做到兼容,比如都是实现同样的自定义交易分成功能,可能每个项目都有一套自己的实现逻辑,而这些逻辑通常都无法兼容,所以如果要做到不同项目之间都兼容同一套功能逻辑的话,那只能是将需要扩展的功能集成进现有的 NFT 协议中。
基于上述理论,在不考虑实际操作难度而只考虑理论设计难度的情况下,我们在现有 ERC721 的协议框架下实现以下三个功能:
1.自定义 NFT 交易分成
在协议的 NFT 结构上增加一个属性,这个属性是一个合约地址,在某个 NFT 被铸造的时候填入这个合约地址,该外部合约描述了传入的 ETH 该如何分割给在合约中设定的或通过接口传入的受益人,这些受益人通常是 NFT 交易的买方、卖方、NFT 的创作者或项目方。在 ERC721 协议的 transferFrom 函数中完成了对 NFT 的所有权转移后调用与该NFT关联的外部合约接口以实现本次交易的交易分成逻辑。
2. 用 ERC20 代币交易 NFT
同样在 NFT 结构上增加一个属性,这个属性是一个 ERC20 合约地址列表,表明这个 NFT 支持用哪些 ERC20 代币交易,然后在 ERC721 协议中新增一个 transferFromErc20 函数用于支持用 ERC20 代币交易 NFT 的功能。
3. 用多个 NFT 合成特定 NFT
在 ERC721 协议中新增一个 transferFromCompose 函数用于接收一个 NFT 数组,然后通过销毁数组中的 NFT 来直接为调用者铸造一个新的 NFT,而验证这个铸造过程仍然需要调用一个外部规则合约来完成,这个规则合约的地址可以直接写进在 ERC721 协议里新增的某个变量中,与具体的 NFT 实例无关。
我们发现,在上述三个功能中,每实现一个功能都要对 ERC721 协议做一次修改,而且这些修改几乎很难归纳成某种特定的形式,也就是说每新增一个功能就需要对协议进行一次功能集成,而每一次功能集成也意味着要催生出一个新的协议。
理论边界推导
虽然到目前为止我们只用了三个功能来扩展 Nervos 和以太坊上现有的 NFT 协议,但就 Nervos 和以太坊在设计上的哲学和范式来看,两条链的差别其实已经很明显了:Nervos 明显更偏向组合型,而以太坊则更偏向集成型。
组合型意味着你可以将功能设计得足够碎片化和细粒度化,通过组合的方式达到功能效果的多样性,而集成型则意味着功能设计很难碎片化,在设计功能的时候就要考虑到足够多的因素并提前设计进协议里,不然在后期扩展更多多样性的时候就会比较棘手。我想,这很大程度上也反映出 UTXO 模型和 Account 模型的本质差别。
用归纳法我们可以知道,在经过了实现上述三个典型功能之后,基本可以归纳出一个规律:Nervos 和以太坊在 NFT 协议设计上的理论边界完全受制于两条链的编程模型,即 UTXO 编程模型和 Account 编程模型。
再用演绎法我们可以知道:
-
在基于 UTXO 编程模型设计协议的时候,考虑的更多的是如何将协议流程拆分成较为独立的或不可分割的功能单元,通过不同功能单元之间的组合实现具体的协议表达,如果后期需要引入新的功能需求,那再根据这些需求设计功能单元即可,这种设计方式更加面向未来的不确定性
-
而在基于 Account 编程模型设计协议的时候,考虑的更多的是如何尽可能多的考虑到已有的和潜在的功能需求,然后将这些需求转化为具体的功能并集成在一起以实现最终的协议表达,如果后期需要引入新的功能需求,则可能会发生重构现有架构抽象度以兼容更多需求的情况,而通常这种情况都会比较棘手,所以这种设计方式会更加面向过去和现在的确定性
所以,Nervos 的协议理论边界主要受限于协议单元的拆分粒度和组合逻辑,而以太坊的协议理论边界则主要受限于协议架构的抽象程度。两者对比而言:Nervos 的理论边界明显大于以太坊的理论边界,因为单元设计的难度明显小于架构设计的难度。
最大通用性协议设计
既然已经知道了 Nervos 和以太坊在设计 NFT 协议上的理论边界,那我们试图分别在两条链上都设计一种能尽可能触达它们理论边界的协议,看看这样的协议到底会是什么样的。
在 Nervos 上,拥有最大通用性的 NFT 协议可以有以下两种设计思路:
-
让 LockScript 和 TypeScript 都保持最小化的原则。LockScript 只是简单的处理 NFT 的解锁功能,而 TypeScript 则单纯的处理 NFT 的展示功能,将所有的流通功能交由独立的规则 Cell 来实现,通过在一个交易的 Input 端和 Output 端放入一个相同的规则 Cell 来为这个交易中的所有 NFT 赋予这个 Cell 所能表达的流通规则。优点是 NFT 的展示属性和流通属性完全分离,并且流通属性可随意切换,缺点是存在规则 Cell 的竞争问题。
-
让 LockScript 和 TypeScript 都具备最大程度的可扩展性。LockScript 和 TypeScript 所指向的合约脚本需要内嵌一个 JS 或 Lua 虚拟机,然后将虚拟机所需要执行的代码逻辑放入对应的规则 Cell 中,通过在一个交易的 CellDep 中引入这些规则 Cell 就可以实现 LockScript 所对应的NFT流通属性和TypeScript 所对应的 NFT 展示属性的完全定制化效果。优点是 NFT 的展示属性和流通属性仍然完全分离并且还都可随意组合,而且还不存在规则 Cell 的竞争问题,缺点是协议设计较为复杂。
在以太坊上,拥有最大通用性的 NFT 协议基本上只有一种设计思路:
- 设计一个尽可能囊括各种情况的 NFT 协议流程,然后在最有必要进行定制化的地方埋下钩子,协议允许铸造者或项目方在钩子里设定不同的合约回调函数来实例化具备不同展示和流通逻辑的 NFT 实例,并且这些 NFT 的展示和流通行为与项目本身无关,在不同项目之间都可自由流转。优点是相比于现有的NFT协议更加具备脱离于具体项目的定制化功能,缺点仍然是设计边界明显,可能在未来无法满足某些特定的功能需求。
总结
总的来说,UTXO 模型赋予了 Nervos 组合式的编程模型,在逻辑单元的拆分密度合适的情况下,理论上可以组合出无限种 Cell 验证逻辑。 而 Nervos 网络中的验证网络 CKB 和计算网络 Layer 2 的分离也正好体现了这一重要的组合式分离思想。而 Account 模型赋予了以太坊集成式的编程模型,虽然这种模型更加贴近传统的编程习惯,上手门槛低,但自然也无法突破传统编程范式所能触及的边界。
综上,相对于以太坊而言,拥有组合式分离思想的 Nervos 将会更加面向不可预期的未来。