关于Nervos与以太坊就NFT协议设计极限的思考

一、这篇文章从何而起

随着NFT概念的爆火,绝大部分公链都设计了自己的NFT协议,就今天讨论的两条公链来说,Nervos有mNFT协议,以太坊有ERC721和ERC1155协议,这些协议在创建、使用和展示NFT的时候没有任何问题,但当要基于这些协议设计更复杂、更通用和更多样的功能的时侯就都会遇到一个问题:实现起来有点困难。这里我们列举三个比较典型的功能:

  1. 自定义NFT交易分成
  2. 用sUDT或ERC20代币交易NFT
  3. 集齐某些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在输出端,对于如何配置这样的输入输出结构,就需要一个专门的规则来限制和说明:

  1. 这一规则说明可以通过LockScript来实现,即在LockScript代码中明确表明TypeScript中表示的NFT能和哪些NFT一同放在输入端,然后输出端需要放入怎样的NFT;
  2. 规则也可以通过引入规则Cell来说明,这个Cell可以同时放在交易的输入端和输出端,然后通过LockScript或TypeScript来验证规则逻辑,这样NFT-Cell的LockScript就可以只是普通的Sighash脚本即可;
  3. 最后,规则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编程模型。

再用演绎法我们可以知道:

  1. 在基于UTXO编程模型设计协议的时候,考虑的更多的是如何将协议流程拆分成较为独立的或不可分割的功能单元,通过不同功能单元之间的组合实现具体的协议表达,如果后期需要引入新的功能需求,那再根据这些需求设计功能单元即可,这种设计方式更加面向未来的不确定性
  2. 而在基于Account编程模型设计协议的时候,考虑的更多的是如何尽可能多的考虑到已有的和潜在的功能需求,然后将这些需求转化为具体的功能并集成在一起以实现最终的协议表达,如果后期需要引入新的功能需求,则可能会发生重构现有架构抽象度以兼容更多需求的情况,而通常这种情况都会比较棘手,所以这种设计方式会更加面向过去和现在的确定性

所以,Nervos的协议理论边界主要受限于协议单元的拆分粒度和组合逻辑,而以太坊的协议理论边界则主要受限于协议架构的抽象程度。两者对比而言:Nervos的理论边界明显大于以太坊的理论边界,因为单元设计的难度明显小于架构设计的难度。

四、最大通用性协议设计

既然已经知道了Nervos和以太坊在设计NFT协议上的理论边界,那我们试图分别在两条链上都设计一种能尽可能触达它们理论边界的协议,看看这样的协议到底会是什么样的。

在Nervos上,拥有最大通用性的NFT协议可以有以下两种设计思路:

  1. 让LockScript和TypeScript都保持最小化的原则。LockScript只是简单的处理NFT的解锁功能,而TypeScript则单纯的处理NFT的展示功能,将所有的流通功能交由独立的规则Cell来实现,通过在一个交易的Input端和Output端放入一个相同的规则Cell来为这个交易中的所有NFT赋予这个Cell所能表达的流通规则。优点是NFT的展示属性和流通属性完全分离,并且流通属性可随意切换,缺点是存在规则Cell的竞争问题。
  2. 让LockScript和TypeScript都具备最大程度的可扩展性。LockScript和TypeScript所指向的合约脚本需要内嵌一个JS或Lua虚拟机,然后将虚拟机所需要执行的代码逻辑放入对应的规则Cell中,通过在一个交易的CellDep中引入这些规则Cell就可以实现LockScript所对应的NFT流通属性和TypeScript所对应的NFT展示属性的完全定制化效果。优点是NFT的展示属性和流通属性仍然完全分离并且还都可随意组合,而且还不存在规则Cell的竞争问题,缺点是协议设计较为复杂。

在以太坊上,拥有最大通用性的NFT协议基本上只有一种设计思路:

  1. 设计一个尽可能囊括各种情况的NFT协议流程,然后在最有必要进行定制化的地方埋下钩子,协议允许铸造者或项目方在钩子里设定不同的合约回调函数来实例化具备不同展示和流通逻辑的NFT实例,并且这些NFT的展示和流通行为与项目本身无关,在不同项目之间都可自由流转。优点是相比于现有的NFT协议更加具备脱离于具体项目的定制化功能,缺点仍然是设计边界明显,可能在未来无法满足某些特定的功能需求。

五、总结

总的来说,UTXO模型赋予了Nervos组合式的编程模型,在逻辑单元的拆分密度合适的情况下,理论上可以组合出无限种Cell验证逻辑,而Nervos网络中的验证网络CKB和计算网络Layer2的分离也正好体现了这一重要的组合式分离思想,而Account模型赋予了以太坊集成式的编程模型,虽然这种模型更加贴近传统的编程习惯,上手门槛低,但自然也无法突破传统编程范式所能触及的边界。

综上,相对于以太坊而言,拥有组合式分离思想的Nervos将会更加面向不可预期的未来。

10 Likes

透过这篇文章学习了不少,想要请教下,如果 Lock script 没有规范明确的实现方式,是不是会产生出各种不同的 Lock 引发实现上的困难或者不好的体验?

不好意思,才看到消息,理论上是这样的,但实际上一般只会有一些典型的设计会流行起来,可以理解成某种“潜规则”,不同lock之间的流转引入的问题其实可以理解成某种反向激励,促使形成“只有某些主流的脚本被大众采纳”这一局面,举个例子,比特币上本来脚本也是可以自定义的,但实际使用起来就那么几个,因为长时间的使用证明了这些脚本没有问题,然后主要就是方便、够用、不用自己去写了。

1 Like

是的 我一开始就是在担心不同的 lock 对用户来说就是不同的地址、解锁规则
但是最后看起来 Chained_locks 应该可以解决这个问题