契機
一年前朋友在玩一個NFT社群,項目方透過盲盒發售NFT,有不少稀有的NFT還未開出。因我之前在前公司有做過區塊鏈的樂透機制Demo,那時的研究了解到單憑區塊鏈是無法做到真正的隨機,一定要仰賴第三方做隨機數。所以朋友就邀我一起研究一下他們的NFT開盲盒機制,看能不能有甚麼方法提高中獎機率。
智能合約研究(ERC721)
找到該項目NFT的智能合約,為ERC721的應用,其中解盲時產生NFT token_id隨機數的程式碼如下
function XXXXXXX(uint256 tokenId_) internal returns (uint256) {
require(_remaining > 0, "there are no available token id");
uint256 tempRemaining = _remaining;
// PRG:
uint256 value = uint256(
keccak256(
abi.encodePacked(
tokenId_,
block.difficulty,
block.timestamp,
msg.sender
)
)
) % tempRemaining;
// Durstenfeld-Fisher–Yates shuffle
uint256 tokenId = value;
uint256 tempRecordValue = _cache[value];
if (tempRecordValue != 0) {
tokenId = tempRecordValue;
}
uint256 remainingIndex = tempRemaining - 1;
uint256 tempLastValue = _cache[remainingIndex];
if (tempLastValue == 0) {
tempLastValue = remainingIndex;
}
_cache[value] = tempLastValue;
_remaining = remainingIndex;
return tokenId + _startingIndex;
}
最主要是中間那段程式碼在計算盲盒的隨機數
uint256 value = uint256(
keccak256(
abi.encodePacked(
tokenId_,
block.difficulty,
block.timestamp,
msg.sender
)
)
) % tempRemaining;
- tokenId_ : 盲盒ID
- block.difficulty : 區塊鏈難度
- block.timestamp : 區塊時間戳
- sender : 交易發送者
由以上4者算Hash值後再對剩餘NFT發行量取Mod。
至於Funtion底下的「//Durstenfeld-Fisher–Yates shuffle…」,這段是假如開出同樣號碼的處理,不在我們要鎖定中獎的目標中,畢竟稀有NFT是還沒被開出的,不會重複。
解析與初步設想
tokenId_在購買盲盒後就已知,Sender也是已知,剩下的就是開盲盒當下的區塊鏈難度與區塊時間戳。朋友寫了一個預測「難度 + 時間戳」對應所開出NFT的程式,以此模擬。只要接下來一段時間內開出稀有的NFT機率大於80%,就發送交易開盲盒。但實際跑了一段時間後,開出稀有NFT機率大於80%的時間段比預期的少很多,難度變化頻繁更增加失敗機率。但在這段嘗試中,又發現了可以更準確開出盲盒的變數。
交易流程中的可控因子validBefore(timestamp)
開啟盲盒流程中,會需要給使用者簽署交易,最後確認簽署交易後才會給出對應的NFT。這部分的智能合約設計有給使用者一個緩衝時間,也就是validBefore,一旦開啟盲盒的流程超過某個時間就無法再簽署完成交易。有點像是我們平常登入銀行的自動登出機制,一旦超過validBefore這個時間,則操作就會無效。這本來是一個安全機制,但在此時卻成為可以更準確控制時間戳的一個方法。
實際解盲流程操作
- 程式預測出某難度在未來固定時間(validBefore)有大獎
- 開始解盲盒流程到簽署前
- 監控難度是否與預測一致
- 若在validBefore前幾秒難度一致,則簽署交易發送
- 若在validBefore前幾秒難度不對,則不簽署交易,讓交易超出時間
以上方法的成本為交易失敗的手續費,基本上成功率可以接近100%
發行方如何避免此提高機率的方法
- 提高手續費,增加試誤的成本
- 不以Token_id為參數,而是以像是Chainlink VRF產生的隨機數當參數
- 以區塊上不可預測的參數增加輸入變數,如區塊頭Hash值。但這樣可能會變成讓礦工可以操控結果
結語
與隨機相關的智能合約開發需要多花時間研究撰寫,最簡單的方法可能是串接Chainlink VRF,但這也可能是成本最高的方法,畢竟每次隨機數產生都要一筆手續費。其餘方法都有不是真隨機的缺點,可能會被花時間研究破解或提高預測的機率。