這是分布式網絡中隨機數生成的解決方案。
在開發我們的遊戲過程中,我們需要解決一個問題,在分布式網絡中生成隨機數很複雜,現幾乎所有區塊鏈都面臨這個問題。 因此,在任何人都無法相互信任的網際網路之間,創建隨機數可以解決許多問題。
在本文中,我們將講解我們是如何在我們的遊戲中解決這個問題的。 首當其衝的便是Waves Xmas Tree.
起初,我們計劃使用區塊鏈信息來生成一個數字,但在進一步的研究中我們發現創建這個數字的過程可以被操縱幹預,因此我們不得不放棄這個方案。
此後我們提出了一個新的方案, 使用「Commit-Reveal」方案。首先伺服器生成一個1到5之間的數字,然後往其中添加「鹽」並且使用Keccak function進行哈希值處理。伺服器使用已儲存的數字對智能合約進行預調試, 結論是遊戲被有效的簡化,為用戶猜測哈希值下所隱藏的數字。
玩家下注後伺服器將發送一個隱藏的數字並且將其「鹽」帶到一個智能合約中。換句話說,信息已被揭露,之後伺服器也會驗證這個數字並判斷用戶的輸贏。
如果伺服器未發送該數字和「鹽」進行驗證,則該用戶贏了。 在這種情況下,必須提前部署智能合約,並為每場比賽安排潛在的獲勝機會。 這是非常不方便,成本高且消耗大量時間。 但那時還沒有提出其它安全的解決方案。
此後不久,Tradisys團隊提議向Waves協議添加rsaVerify()函數。 這將根據公鑰和私鑰檢查RSA籤名的有效性。 根據我們的建議,該功能已被添加。
我們開發了三款新遊戲:《搖骰子》,《擲硬幣》和《乘浪行》。 在每個遊戲中,每款遊戲都部署了新的隨機數方法,讓我們仔細觀察它是如何工作的
讓我們先看看生成的隨機數,你可以在此處查看智能合約。
切換到腳本選項卡並選擇反編譯,您即可看到智能合約的代碼(或腳本)。
智能合約的代碼由一系列的函數組成。其中的@Callable 可以通過調用事務選項運行。 我們對其中兩個很感興趣:下注和撤回:
1.用戶選擇範圍和下注大小
2.客戶安排下注功能。 對於上面的圖像,即為bet(50)
3. 客戶端將調用事務發送到智能合約地址(播報 InvocationTx)。 作為Call參數的交易依據並包含bet函數。 這意味著調用事務開始在智能合約上執行下注函數(選擇:字符串)。
4.讓我們看一看bet函數
@Callable(i)
func bet (playerChoice) = {
let newGameNum = IncrementGameNum()
let gameId = toBase58String(i.transactionId)
let pmt = extract(i.payment)
let betNotInWaves = isDefined(pmt.assetId)
let feeNotInWaves = isDefined(pmt.assetId)
let winAmt = ValidateBetAndDefineWinAmt(pmt.amount, playerChoice)
let txIdUsed = isDefined(getString(this, gameId))
if (betNotInWaves)
then throw ("Bet amount must be in Waves")
else if (feeNotInWaves)
then throw ("Transaction's fee must be in Waves")
else if (txIdUsed)
then throw ("Passed txId had been used before. Game aborted.")
else {
let playerPubKey58 = toBase58String(i.callerPublicKey)
let gameDataStr = FormatGameDataStr(STATESUBMITTED, playerChoice, playerPubKey58, height, winAmt, "")
ScriptResult(WriteSet(cons(DataEntry(RESERVATIONKEY, ValidateAndIncreaseReservedAmt(winAmt)),cons(DataEntry(GAMESCOUNTERKEY, newGameNum), cons(DataEntry(gameId, gameDataStr), nil)))), TransferSet(cons(ScriptTransfer(SERVER, COMMISSION, unit), nil)))
}
該函數在智能合約中記錄了一場新的遊戲。
l 遊戲的ID = game id
l 遊戲狀態 = 已提交
l 玩家選擇(選擇範圍為50)
l 公鑰
l 潛在獎勵(取決於玩家的下注)
這是鍵值資料庫在區塊鏈上的呈現方式
{
"type": "string",
"value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
"key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx" }
「Key」是新遊戲的game id,其他數據都包括在了value屬性部分。
這是數據都儲存在智能合約的數據一欄中。
5. 伺服器通過區塊鏈API接口發送新的事務(新的遊戲),遊戲ID已被區塊鏈記錄,因此不可能去刪除或者篡改該數據。
6. 伺服器生成撤回函數(gameId,rsaSign)例如:
7.伺服器發送一個調用事務到智能合約中(broadcast InvocationTX)。該事務包含了一個調用以生成撤回函數。(gameId, rasSign):
該函數是含有 game id 以及RSA籤名的唯一id,因此籤名保證了其不可篡改性。
這意味著什麼?
我們使用相同的值並且將RSA籤名應用在其中,這就是RSA算法的工作原理。 由於game id和RSA算法的結果是未知的,因此不可能操控其最終數值,並且猜測最終數值也變得毫無意義。
8.區塊連結收到一個運行撤回函數的事務(gameId,rsaSign)
9.撤回函數內包含一個調用GenerateRandIn函數。
# @return 1 ... 100
func GenerateRandInt (gameId,rsaSign) = {
# verify RSA signature to proof random
let rsaSigValid = rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)
if (rsaSigValid)
then {
let rand = (toInt(sha256(rsaSign)) % 100)
if ((0 > rand))
then ((-1 * rand) + 1)
else (rand + 1)
}
else throw ("Invalid RSA signature")
rand 即為一個隨機數
首先,RSA籤名結果的字符串已被使用。其次,其已通過SHA-256哈希處理。
我們無法預測籤名結果以及後續的哈希處理,因此生成結果不可能被幹預。為了獲取指定範圍內的一個數字(例如從1到100),轉換函數toInt 以及%100(mod模擬)也被應用在其中。
在本文開頭,我們提到了rsaVerify()函數,該函數允許通過私鑰針對公鑰來檢查RSA籤名的有效性。這裡是部分GenerateRandInt(gameId,rsaSign):
rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC
以此開始, RSAPUBLIC公鑰以及rsaSign字符串已被使用,該籤名用以驗證其有效性。 如果驗證通過,那麼數字將會生成。 否則系統將會判定該籤名無效(Invalid RSA signature)
伺服器需要使用私鑰註冊遊戲ID並且在2880個區塊內發送有效的RSA籤名。在部署智能合約的同時該選項已被管理。如果規定時間內沒有其他情況則玩家獲勝。在此基礎上,獎勵會被用戶單獨發送。 就此而言,作弊對伺服器將毫無益處,因為作弊會導致輸局。 如下示例:
玩家參與搖色子遊戲。玩家從6個面中選擇2個,並下注14個WAVES。 如果伺服器未在設定的時間(2,880區塊)內向智能合約發送有效的RSA籤名,則用戶將收到34.44 WAVES。
就數字生成而言,我們使用了預言機,一個非區塊鏈的外部系統。伺服器應用一個RSA籤名到遊戲ID中,智能合約將會檢查籤名的有效性並且判斷贏家。如果伺服器未發送結果,那麼玩家則會自動勝出。
這種方法保證了人為操縱是不可能的,所有Tradisys遊戲都基於以上闡述的算法以保證我們遊戲的公平性和透明性。 所有內容都可以公開審核以確保其公正。
本文來源: 金色財經 /