Neoverse NFT設(shè)計邏輯與規(guī)則詳解|Neo專欄

「Neoverse NFT收集活動」剛剛官宣
通過打開盲盒、合成9款不同的N3元素碎片
即可獲得由國際知名NFT藝術(shù)家打造的
【N3典藏版NFT】一枚??
那么,這項活動的具體玩法是如何設(shè)計的?
共9款N3典藏版NFT的生成規(guī)則又是怎樣的呢???
本篇Neo專欄,我們特別邀請了
參與設(shè)計Neoverse的開發(fā)者??
與大家分享「Neoverse NFT收集活動」
在算法設(shè)計和合約開發(fā)等方面
遇到了哪些困難
又想出了哪些有趣的解決方案??
快來一起看看吧??
算法設(shè)計
合約開發(fā)
盲盒游戲最重要的部分是隨機性。既然涉及到隨機性,一定有人問你們的隨機性是怎么實現(xiàn)的,是否公平,能不能被預(yù)測,會不會被黑客利用等。
我們通過以下幾個版本的合約來逐步分析,最終找到了最佳的盲盒玩法設(shè)計方案。
01 - 青銅版本??
在開盲盒的時候,取當(dāng)前區(qū)塊的Nonce(在N3的區(qū)塊中,有一個字段叫Nonce,它是隨機且固定不變的)作為隨機數(shù)種子,再對3000取模+1,獲得1~3000的隨機數(shù)。
這樣操作比較簡單直接,但是存在很多問題:
?? 同一個區(qū)塊所開的盲盒都是相同的結(jié)果;
?? 取出的隨機數(shù)可能有重復(fù)。
02 - 白銀版本??
在這個版本中,針對上個版本的1號問題進行了修復(fù)。我們首先想到的是將區(qū)塊的Nonce和交易ID進行異或操作,獲得隨機數(shù)的種子,但這樣在一筆交易中開出的盲盒又是相同的結(jié)果,顯然也是不行的。然后,我們又想對盲盒的TokenId進行哈希運算,將其轉(zhuǎn)為大整數(shù),并與當(dāng)前區(qū)塊進行異或操作,獲得隨機數(shù)的種子。因為每個盲盒的Token ID是不同的,哈希自然也是不同的,與Nonce進行異或操作,可避免每次都產(chǎn)生同一個隨機數(shù)。但是NGD工程師黎工表示直接使用當(dāng)前區(qū)塊的Nonce會有被人利用的風(fēng)險:
?? 黑客可以發(fā)布一個合約A,在合約A中調(diào)用開盲盒的方法,并且對開盲盒的結(jié)果進行判斷,如果發(fā)現(xiàn)開盲盒的結(jié)果不滿意則拋出異常,中斷合約執(zhí)行;
?? 黑客可以通過在開盲盒的腳本后面追加精心構(gòu)造的OpCode,完成上面的步驟。
這兩點在前一版本中也同樣存在。
03 - 黃金版本???
在這個版本里,我們將隨機數(shù)的生成放在買盲盒之后,開盲盒之前。這樣當(dāng)你買盲盒的時候無法預(yù)測將來的區(qū)塊Nonce,當(dāng)你開盲盒的時候,結(jié)果是確定的。
具體操作如下: 購買盲盒時,對下一個區(qū)塊的索引進行記錄。當(dāng)你開盲盒時,取那個區(qū)塊的Nonce并和Token ID的哈希進行異或。
這樣就避免了被黑客攻擊,但是青銅版本的第二個問題仍沒有解決。兩個盲盒可以開出同樣的碎片,比如 Fragment A #33。
04 - 鉑金版本??
在這個版本里,我們主要解決隨機數(shù)重復(fù)的問題,有以下兩種解決方案:
存儲區(qū)中存儲所有未被使用的隨機數(shù)(最多27000個),每用到一個,就將其刪除。下個人取隨機數(shù)時,讀取存儲區(qū)中的隨機數(shù)數(shù)組,從中取隨機數(shù)。
存儲區(qū)中存儲所有已使用的隨機數(shù)(最多27000個),每取一個隨機數(shù),就將其存到存儲區(qū)里。下個人取隨機數(shù)時,讀取存儲區(qū)中的隨機數(shù)數(shù)組,找到未使用的隨機數(shù),從中取隨機數(shù)。
以上兩種方案其實是等價的,無論存儲區(qū)中存儲已使用的,還是未使用的隨機數(shù),都要大量使用存儲區(qū)。按每個隨機數(shù)2字節(jié)計算,一共54KB的數(shù)據(jù),寫入一次約13.5GAS,手續(xù)費是相當(dāng)高昂的。
那么如果用位(Bit)存儲呢,一個長度為3375的字節(jié)數(shù)組,每一位的下標(biāo)表示隨機數(shù)本身,每一位的值(0或1)表示該隨機數(shù)是否使用。再結(jié)合分片操作,將9種碎片的隨機數(shù)分開存儲。這樣讀取寫入的費用會大大降低,但考慮到將位還原為數(shù)組,會有大量計算,手續(xù)費仍然不很樂觀。
NGD工程師印工提出了一種新的解決方案:
針對每種碎片的1~3000的隨機數(shù),存儲區(qū)中存儲最后一個隨機數(shù)的下標(biāo),和抽取替換的過程。具體來說,假設(shè)用戶抽取了下標(biāo)為500的隨機數(shù),則把500給他,并在存儲區(qū)中記錄k:500,v:3000,表示下標(biāo)500的位置存放的是隨機數(shù)3000。然后將隨機數(shù)下標(biāo)的最大值3000更改為2999。第二個用戶又抽取到下標(biāo)為500的隨機數(shù),檢查存儲區(qū),將下標(biāo)500位置的隨機數(shù)3000給用戶,并更新存儲區(qū)k:500,v:2999。然后將隨機數(shù)下標(biāo)的最大值2999更改為2998。依此類推。
即隨機數(shù)的生成可以描述為:第一個人,Nonce模3000+1;第二個人,Nonce模2999+1; 第三個人,Nonce模2998+1......
細心的用戶又會發(fā)現(xiàn),在黃金版本中,開盲盒的結(jié)果是確定的,不隨著你開的先后順序而變化,但在這個版本中,你先開和后開,結(jié)果可能會變化。這又暴露了另一個問題:
● 黑客可以在本地對合約進行預(yù)執(zhí)行,如果結(jié)果滿意,則廣播交易,如果不滿意,則等待其它人開出該類碎片后再進行預(yù)執(zhí)行操作,直到它滿意了,發(fā)送交易。
●白銀版本中的兩個問題再次出現(xiàn)了。
05 - 星鉆版本??
這里我就不得不吹一下Neo底層的隨機數(shù)生成算法了。其實這個隨機數(shù)算法是在N3上線前加入的,我在寫前幾個版本的合約的時候還不能使用。NGD工程師劉工給我介紹了這個隨機數(shù)算法。它能保證真隨機、不可預(yù)測(預(yù)執(zhí)行和鏈上執(zhí)行獲得的隨機數(shù)不同)、每使用一次就會更改。在合約中也可以很方便地用互操作服務(wù)(Runtime.GetRandom())來使用這個隨機數(shù)。結(jié)合鉑金版本中印工的方案,即可每次獲得1-3000中不重復(fù)的隨機數(shù)。到這里,隨機數(shù)部分已經(jīng)近乎完美了。
但是白銀版本中提到的兩個問題其實是在鏈上執(zhí)行,只是增加了對結(jié)果的判斷,這是隨機數(shù)算法所不能干預(yù)的,要通過其它安全限制來規(guī)避。我計算了一下,黑客通過這種方式開盲盒的成本比較高,初始成本為10.1GAS,之后每嘗試開一次盲盒成本為1.05GAS。嘗試兩次都不滿意的話,相當(dāng)于直接浪費了一個盲盒(1個盲盒的價格為2GAS)。當(dāng)然,我們更希望黑客即使虧本攻擊,也無從下手。因此,有了最終的皇冠版本。
06 - 皇冠版本??
我和Neo社區(qū)工程師廖工在星鉆版本上進行了改進:
在這個版本中添加了對合約調(diào)用的限制,只允許通過交易來調(diào)用Neoverse合約進行開盲盒操作,不允許通過其它合約進行調(diào)用。
針對開單個盲盒以及批量開盲盒的方法,根據(jù)實參長度精確計算所需腳本長度。不允許在標(biāo)準(zhǔn)調(diào)用腳本中附加任何一個字節(jié)的腳本。
到此合約近乎完美。
Neoverse官網(wǎng)將于9月中下旬上線,屆時可在官網(wǎng)購買N3元素碎片盲盒??
更多空投活動信息敬請關(guān)注官方通知??
