层级化NFT标准诞生:EIP-6150

互联网 阅读 667 2023-01-16 23:10:00

前言

因为一些机缘,我最近和几个同行朋友一起提交了一个新的 EIP 协议标准,EIP-6150,这是一个支持层级结构的 NFT 协议标准,撰写此文时处在 Review 状态,改为 Last Call 状态的 PR 还在等待通过。

该协议标准有 4 位作者:Keegan Lee、msfew、Kartin 和 qizhou。Keegan Lee 就是我,主要负责了接口的定义和实现代码的编写。Kartin 是这个 EIP 的发起人,也是 Hyper Oracle 的创始人。msfew 则是 Hyper Oracle 的研究员,主要帮忙做一些辅助性的工作,包括完善文档、提交 PR、跟进讨论区的 QA 等。qizhou 是 EthStorage 的创始人,之前就提交过其他 EIP,熟悉申请 EIP 的流程,也对以太坊基金会的人比较熟悉,为这个协议提供了很多指导。以下是该 EIP-6150 的 github 地址:

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6150.md

Hierarchical NFTs

目前的 NFT 几乎都是扁平化的,不同 NFT 之间不存在关联关系。而层级化的 NFT,则可以将所有 NFT 串联起来组成树状结构,就像文件系统一样。

NFT

如上图所示,可以想象成每个文件夹都是一个单独的 NFT,不同文件夹之间的层级关系也就是 NFT 之间的层级关系。

层级化的 NFT 可用于多种不同的应用场景,比如:

  • 组织架构
  • 社交关系图谱
  • 电商商品类目结构
  • 层级评论系统

可以说,任何具有层级结构的场景都可以适用这个 EIP-6150 协议标准。层级结构的 NFT 在去中心化社交、去中心化电商等领域都将可能产生广泛应用。

接口定义

EIP-6150 总共定义了 5 个接口文件:

  • IERC6150
  • IERC6150Enumerable
  • IERC6150Burnable
  • IERC6150ParentTransferable
  • IERC6150AccessControl

IERC6150

IERC6150是规定必须实现的接口,最小化定义了一个事件和四个函数,且要求继承 IERC165 和 IERC721 接口,接口定义如下:

NFT

Minted事件需在铸造一个新的 NFT 时发出,记录了新 NFT 的铸造者(minter)、接收者(to)、父节点 NFT 的 ID(parentId)、新 NFT ID(tokenId)。当铸造一个根节点 NFT 时,那 parentId 则记为 0,即 0 表示一个无效的空节点,因此,有效的节点 NFT 的 tokenId 就不可以为 0。

parentOf函数用于查询指定 tokenId 的 NFT 的父节点 NFT。

childrenOf函数则查询出指定 tokenId 的 NFT 的所有子节点 NFTs。

isRootisLeaf函数则分别可查询指定 tokenId 在整个 NFT 层级树中是不是根节点或叶子节点。

IERC6150Enumerable

IERC6150Enumerable是可选的扩展接口,主要补充了几个跟层级相关的 Enumerable 的查询接口,接口定义如下:

NFT

继承IERC721Enumerable也是可选的,但为了更好地兼容 ERC721,最好可以继承。childrenCountOf函数用于查询指定节点下有多少个子节点,如果参数 parentId 为 0,则表示查询根节点的数量。childOfParentByIndex函数则是从指定的父节点下的所有子节点数组中找出指定索引位置的子节点的 tokenId,比如指定父节点 parentId = 110,其下有 10 个子节点,找出索引位置为 5 的子节点 tokenId = 1105,则查询结果返回 1105。indexInChildrenEnumeration函数则是查询指定的 tokenId 在指定父节点下的子节点数组中所在的索引位置,比如指定 tokenId = 1105,父节点 parentId = 110,1105 在子节点数组中的索引位置为 5,则查询结果返回 5。如果指定 tokenId 并不在指定父节点 parentId 下面,则需要抛出错误。

IERC6150Burnable

IERC6150Burnable也是可选的扩展接口,定义了销毁节点的操作,接口定义如下:

NFT

只定义了两个函数,safeBurn用于安全销毁单个节点,但要求只有指定节点为叶子节点时才允许销毁。就和 Linux 的文件系统一样,如果某目录下存在其他文件或文件夹,是不允许直接删除的。若强制删除,则目录下的所有文件和文件夹都会被级联式全部删除。当前协议没有定义级联式删除的函数,若有这个需求,可以自己再额外去添加函数实现。safeBatchBrun函数则是用于批量销毁多个叶子节点。

IERC6150ParentTransferable

IERC6150ParentTransferable也是一个可选的扩展接口,支持层级关系的转移,就和文件夹可以从一个目录移动到另一个目录一样,接口定义如下:

NFT

接口定义了两个函数和一个事件,支持单节点的转移,也支持多节点的批量转移。每个节点发生层级关系转移时,需要抛出ParentTransferred事件,记录下所转移的 tokenId、旧的父节点 ID 和新的父节点 ID。transferParent将指定的 tokenId 转移到指定的父节点下,若指定的父节点为 0,则表示指定节点改为了根节点。batchTransferParent则可以指定多个 tokenId,批量将这些节点都转移到指定的父节点下。

IERC6150AccessControl

最后一个接口IERC6150AccessControl也是可选的,提供了几个权限控制的函数,接口定义如下:

NFT

总共定义了三个函数,isAdminOf用于查询指定的 account 对指定的 tokenId 是否有管理员权限。在 ERC721 中,每个 NFT 都只有唯一的 owner 并拥有管理权限。但在层级式的结构中,一个 NFT 是可以有多个管理员的,就和文件系统中可以有多个管理员一样。这个扩展接口就提供了支持多管理员的模式,但对于怎么设置多个管理员,则难以定义通用函数,所以就没做标准化的定义。不过,需要保证,NFT 的 owner 同时也是管理员。

canMintChildren则用来判定某个 account 对指定的 parentId 是否具有铸造子节点的权限。

canBurnTokenByAccount则用来检查某个 account 对指定的 tokenId 是否具有销毁的权限。

参考实现

EIP 的 github 上,我对每个接口都提供了对应的参考实现代码,代码地址如下:

https://github.com/ethereum/EIPs/tree/master/assets/eip-6150/contracts

但这里我不打算对每个实现代码都一一讲解,我只讲最核心的ERC6150.sol的实现。因为代码相对有点长,就不贴代码出来了,大家可以点击链接进去看代码。我主要讲讲实现的一些逻辑和思路。

存储上,用了三个 mapping:_parentOf、_childrenOf、_indexInChildrenArray,分别用来存储指定节点的:父节点、子节点数组、所在子节点数组里的索引位置。有了这三个 mapping 之后,几个查询函数的实现就非常简单了,我就不细说了。核心是_safeMintWithParent_safeBurn函数,分别是铸造 NFT 和销毁 NFT 的内部函数。

铸造函数的代码如下:

NFT

实现逻辑其实也很简单,有两个校验需要注意下,一是要铸造的新 NFT 的 tokenId 需要大于 0,正如前面所说的,0 为无效节点;二是当 parentId 不为 0 时,需保证 parentId 是存在的,当 parentId 为 0 时,则表示铸造的是根节点 NFT。_beforeMintWithParent_afterMintWithParent是为了增加扩展性而增加的,可由继承此合约的上层合约根据需求再去实现。中间代码就是对几个 mapping 进行赋值了,然后调用了 ERC721 的_safeMint函数实现底层的铸造,接着就发送 Minted 事件了。

这个铸造函数是 internal virtual 的,上层合约可以重载该函数,且上层的实现合约需要再根据具体需求自己添加开放的铸造函数。

接着看看销毁函数,代码如下:

NFT

销毁时,要求 tokenId 是存在的且需是叶子节点才允许销毁。另外,销毁时,需要从子节点数组中移除,而为了节省 gas,同时把子节点数组中的最后一个元素移到了销毁的索引位置。

另外,实现代码中,也封装了批量铸造的内部函数,方便扩展支持批量铸造多个子节点的需求。

其实,整个协议并不复杂,但已经足以覆盖到很多应用场景,后续我会结合一些具体的应用场景,再增加示例代码作为案例,以促进该协议的落地应用。

免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
上一篇:以太坊KZG的初始可信仪式 下一篇:SocialFi 1.0 到 2.0 的现状、观察、思考与畅想

相关资讯

  • 盲化的两方 Musig2 签名
    盲化的两方 Musig2 签名

    本文是一种 Schnorr 多签名协议 Musig2 的一种实现提议,在这种实现中,两方参与签名,但任何一方都无法知晓:(1)完整的共有公钥;以及(2)最终生成的签名。 在下面的描述中,私钥(域元素)以小写字母来表示,而椭圆曲线点用大写字母来表示。G 是椭圆曲线上的生成器点。点乘法使用 X = xG 来表示,点加法使用 A = G + G 来表示。H() 是一种哈希函数。

    比特币知识 2024-04-29 16:10 374
  • 比特币签名体积的变化
    比特币签名体积的变化

    数字签名是比特币协议的根本模块;并且区块链上存储的大部分数据都是数字签名。我们详细介绍了比特币区块链上编码的 ECDSA 签名的体积是如何逐渐减小的,以及还在提议阶段的 Schnorr 签名与当前使用的 ECDSA 签名的体积对比。 在花费非 SegWit 输出时,比特币交易中的数字签名会放在输入的 SigScript 字段;而在花费 SegWit 输出时,签名会放在 Witness 字段。无论哪一种情形,签名都由编码的 r 值和 s 值以及一个叫做 SigHash 的标签组成;SigHash 标签指明

    比特币知识 2024-04-29 15:50 453
  • 比特币更多密钥一定更安全吗?
    比特币更多密钥一定更安全吗?

    多签名是一种比特币自带的功能,让你可以在由多个私钥(而非单个私钥)控制的钱包中保管比特币。你可以将花费这些比特币所需的几个私钥存放在不同的地方,从而提高财产的安全性。

    比特币知识 2024-04-29 15:37 308
  • 多签和拆分备份:让你的比特币更安全
    多签和拆分备份:让你的比特币更安全

    随着比特币价格回暖,高水准的安全保管也变得愈发重要。多签地址和拆分备份(例如,Shamir 的密钥分割方案)是两种有效的安全存储方式,可用来长期保管密码学货币。

    比特币知识 2024-04-29 15:23 502
  • Schnorr 签名如何提升比特币
    Schnorr 签名如何提升比特币

    当前比特币的所有权体系用的是 ECDSA(椭圆曲线签名算法)。在签名一条消息 m时,我们先哈希这条消息,得出一个哈希值,即 z=hash(m) 。我们也需要一个随机数(或者至少看似随机的数)k。在这里,我们不希望信任随机数生成器(有太多的错误和漏洞都与不合格的随机数生成器有关),所以我们通常使用 RFC6979,基于我们所知的一个秘密值和我们要签名的消息,计算出一个确定性的 k。

    比特币知识 2024-04-29 15:20 140
  • Schnorr 签名系列:简介
    Schnorr 签名系列:简介

    本文中我会解释 Schnorr 签名是什么、直观上它是如何工作的。到了下一篇文章,我会给出证据,证明这种方案是安全而且正确的。除了这两部分,本系列博客剩下的文章会介绍 Schnorr 签名能够支持的多种签名方案以及它们的应用场景。

    比特币知识 2024-04-29 15:15 589
  • 比特币钱包丢失怎么办?比特币钱包找回指南
    比特币钱包丢失怎么办?比特币钱包找回指南

    什么!找回比特币钱包竟然也需要一份指南?难道不是将助记词随便导入一个钱包软件,比特币就回来了吗?先别急。看了这篇指南,你将了解助记词和钱包背后的基本原理。以及,如何找回你的比特币钱包。

    比特币知识 2024-04-29 15:08 220
  • Schnorr 签名系列:从 ID 到签名
    Schnorr 签名系列:从 ID 到签名

    在本文中,我们会将身份证明方案转化为一套签名方案,并论证我们的转换的结果会是一个正确的签名协议,从而完成对 Schnorr 签名安全性的论证。 完成论证之后,Schnorr 签名系列的剩余部分,我们会探究 Schnorr 签名的多种调整方案,它们能用来为比特币开发许多很酷的协议和应用场景!

    比特币知识 2024-04-29 15:05 313