Neoverse NFT设计逻辑与规则详解|Neo专栏
共 3481字,需浏览 7分钟
·
2021-09-10 20:37
「Neoverse NFT收集活动」刚刚官宣
通过打开盲盒、合成9款不同的N3元素碎片
即可获得由国际知名NFT艺术家打造的
【N3典藏版NFT】一枚🎁
那么,这项活动的具体玩法是如何设计的?
共9款N3典藏版NFT的生成规则又是怎样的呢?🤔
本篇Neo专栏,我们特别邀请了
参与设计Neoverse的开发者😎
与大家分享「Neoverse NFT收集活动」
在算法设计和合约开发等方面
遇到了哪些困难
又想出了哪些有趣的解决方案💡
快来一起看看吧👇
算法设计
合约开发
盲盒游戏最重要的部分是随机性。既然涉及到随机性,一定有人问你们的随机性是怎么实现的,是否公平,能不能被预测,会不会被黑客利用等。
我们通过以下几个版本的合约来逐步分析,最终找到了最佳的盲盒玩法设计方案。
01 - 青铜版本🥉
在开盲盒的时候,取当前区块的Nonce(在N3的区块中,有一个字段叫Nonce,它是随机且固定不变的)作为随机数种子,再对3000取模+1,获得1~3000的随机数。
这样操作比较简单直接,但是存在很多问题:
✔️ 同一个区块所开的盲盒都是相同的结果;
✔️ 取出的随机数可能有重复。
02 - 白银版本🥈
在这个版本中,针对上个版本的1号问题进行了修复。我们首先想到的是将区块的Nonce和交易ID进行异或操作,获得随机数的种子,但这样在一笔交易中开出的盲盒又是相同的结果,显然也是不行的。然后,我们又想对盲盒的TokenId进行哈希运算,将其转为大整数,并与当前区块进行异或操作,获得随机数的种子。因为每个盲盒的Token ID是不同的,哈希自然也是不同的,与Nonce进行异或操作,可避免每次都产生同一个随机数。但是NGD工程师黎工表示直接使用当前区块的Nonce会有被人利用的风险:
✔️ 黑客可以发布一个合约A,在合约A中调用开盲盒的方法,并且对开盲盒的结果进行判断,如果发现开盲盒的结果不满意则抛出异常,中断合约执行;
✔️ 黑客可以通过在开盲盒的脚本后面追加精心构造的OpCode,完成上面的步骤。
这两点在前一版本中也同样存在。
03 - 黄金版本🏅️
在这个版本里,我们将随机数的生成放在买盲盒之后,开盲盒之前。这样当你买盲盒的时候无法预测将来的区块Nonce,当你开盲盒的时候,结果是确定的。
具体操作如下: 购买盲盒时,对下一个区块的索引进行记录。当你开盲盒时,取那个区块的Nonce并和Token ID的哈希进行异或。
这样就避免了被黑客攻击,但是青铜版本的第二个问题仍没有解决。两个盲盒可以开出同样的碎片,比如 Fragment A #33。
04 - 铂金版本🏆
在这个版本里,我们主要解决随机数重复的问题,有以下两种解决方案:
存储区中存储所有未被使用的随机数(最多27000个),每用到一个,就将其删除。下个人取随机数时,读取存储区中的随机数数组,从中取随机数。
存储区中存储所有已使用的随机数(最多27000个),每取一个随机数,就将其存到存储区里。下个人取随机数时,读取存储区中的随机数数组,找到未使用的随机数,从中取随机数。
以上两种方案其实是等价的,无论存储区中存储已使用的,还是未使用的随机数,都要大量使用存储区。按每个随机数2字节计算,一共54KB的数据,写入一次约13.5GAS,手续费是相当高昂的。
那么如果用位(Bit)存储呢,一个长度为3375的字节数组,每一位的下标表示随机数本身,每一位的值(0或1)表示该随机数是否使用。再结合分片操作,将9种碎片的随机数分开存储。这样读取写入的费用会大大降低,但考虑到将位还原为数组,会有大量计算,手续费仍然不很乐观。
NGD工程师印工提出了一种新的解决方案:
针对每种碎片的1~3000的随机数,存储区中存储最后一个随机数的下标,和抽取替换的过程。具体来说,假设用户抽取了下标为500的随机数,则把500给他,并在存储区中记录k:500,v:3000,表示下标500的位置存放的是随机数3000。然后将随机数下标的最大值3000更改为2999。第二个用户又抽取到下标为500的随机数,检查存储区,将下标500位置的随机数3000给用户,并更新存储区k:500,v:2999。然后将随机数下标的最大值2999更改为2998。依此类推。
即随机数的生成可以描述为:第一个人,Nonce模3000+1;第二个人,Nonce模2999+1; 第三个人,Nonce模2998+1......
细心的用户又会发现,在黄金版本中,开盲盒的结果是确定的,不随着你开的先后顺序而变化,但在这个版本中,你先开和后开,结果可能会变化。这又暴露了另一个问题:
● 黑客可以在本地对合约进行预执行,如果结果满意,则广播交易,如果不满意,则等待其它人开出该类碎片后再进行预执行操作,直到它满意了,发送交易。
●白银版本中的两个问题再次出现了。
05 - 星钻版本💎
这里我就不得不吹一下Neo底层的随机数生成算法了。其实这个随机数算法是在N3上线前加入的,我在写前几个版本的合约的时候还不能使用。NGD工程师刘工给我介绍了这个随机数算法。它能保证真随机、不可预测(预执行和链上执行获得的随机数不同)、每使用一次就会更改。在合约中也可以很方便地用互操作服务(Runtime.GetRandom())来使用这个随机数。结合铂金版本中印工的方案,即可每次获得1-3000中不重复的随机数。到这里,随机数部分已经近乎完美了。
但是白银版本中提到的两个问题其实是在链上执行,只是增加了对结果的判断,这是随机数算法所不能干预的,要通过其它安全限制来规避。我计算了一下,黑客通过这种方式开盲盒的成本比较高,初始成本为10.1GAS,之后每尝试开一次盲盒成本为1.05GAS。尝试两次都不满意的话,相当于直接浪费了一个盲盒(1个盲盒的价格为2GAS)。当然,我们更希望黑客即使亏本攻击,也无从下手。因此,有了最终的皇冠版本。
06 - 皇冠版本👑
我和Neo社区工程师廖工在星钻版本上进行了改进:
在这个版本中添加了对合约调用的限制,只允许通过交易来调用Neoverse合约进行开盲盒操作,不允许通过其它合约进行调用。
针对开单个盲盒以及批量开盲盒的方法,根据实参长度精确计算所需脚本长度。不允许在标准调用脚本中附加任何一个字节的脚本。
到此合约近乎完美。
Neoverse官网将于9月中下旬上线,届时可在官网购买N3元素碎片盲盒🎁
更多空投活动信息敬请关注官方通知🪂