【CryptoZombies|編寫(xiě)區(qū)塊鏈游戲?qū)W智能合約】Lesson4: 僵尸作戰(zhàn)系統(tǒng)


CryptoZombies是個(gè)在編游戲的過(guò)程中學(xué)習(xí)Solidity智能合約語(yǔ)言的互動(dòng)教程诵次。本教程是為了Solidity初學(xué)者而設(shè)計(jì)的察藐,會(huì)從最基礎(chǔ)開(kāi)始教起歪脏,即便你從來(lái)沒(méi)有接觸過(guò)Solidity也可以學(xué),CryptoZombies會(huì)手把手地教你技扼。


在這一課伍玖,將綜合利用在前面課程中學(xué)到的許多知識(shí),創(chuàng)建一個(gè)僵尸作戰(zhàn)系統(tǒng)剿吻。 我們也將學(xué)習(xí)payable函數(shù)窍箍,學(xué)習(xí)如何開(kāi)發(fā)可以接收其他玩家付款的DApp。


1.可支付

我們先復(fù)習(xí)一下函數(shù)修飾符丽旅,我們有如下決定函數(shù)何時(shí)和被誰(shuí)調(diào)用的可見(jiàn)性修飾符

  • private意味著它只能被合約內(nèi)部調(diào)用椰棘;
  • internal就像private但是也能被繼承的合約調(diào)用券膀;
  • external只能從合約外部調(diào)用最易;
  • public可以在任何地方調(diào)用守伸,不管是內(nèi)部還是外部漓摩。

我們也有狀態(tài)修飾符,告訴我們函數(shù)如何和區(qū)塊鏈交互:

  • view告訴我們運(yùn)行這個(gè)函數(shù)不會(huì)更改和保存任何數(shù)據(jù)沐批;
  • pure告訴我們這個(gè)函數(shù)不但不會(huì)往區(qū)塊鏈寫(xiě)數(shù)據(jù)痢毒,它甚至不從區(qū)塊鏈讀取數(shù)據(jù)冠王。

這兩種在被從合約外部調(diào)用的時(shí)候都不花費(fèi)任何gas(但是它們?cè)诒粌?nèi)部其他函數(shù)調(diào)用的時(shí)候?qū)?huì)耗費(fèi)gas)米丘。

然后我們有了自定義的modifiers剑令,例如在第三課學(xué)習(xí)的:onlyOwner和aboveLevel。 對(duì)于這些修飾符我們可以自定義其對(duì)函數(shù)的約束邏輯拄查。這些修飾符可以同時(shí)作用于一個(gè)函數(shù)定義上:

function test() external view onlyOwner anotherModifier { /* ... */ }

在這一章吁津,我們來(lái)學(xué)習(xí)一個(gè)新的修飾符payable。

payable修飾符

payable方法是讓Solidity和以太坊變得如此酷的一部分——它們是一種可以接收以太的特殊函數(shù)堕扶。當(dāng)你在調(diào)用一個(gè)普通網(wǎng)站服務(wù)器上的API函數(shù)的時(shí)候碍脏,你無(wú)法用你的函數(shù)傳送美元——你也不能傳送比特幣。但是在以太坊中稍算, 因?yàn)殄X(qián) (以太)潮酒,數(shù)據(jù) (事務(wù)負(fù)載),以及合約代碼本身都存在于以太坊邪蛔。你可以在同時(shí)調(diào)用函數(shù)并付錢(qián)給另外一個(gè)合約。

這就允許出現(xiàn)很多有趣的邏輯扎狱, 比如向一個(gè)合約要求支付一定的錢(qián)來(lái)運(yùn)行一個(gè)函數(shù)侧到。來(lái)看個(gè)例子:

contract OnlineStore {
  function buySomething() external payable {
    // 檢查以確定0.001以太發(fā)送出去來(lái)運(yùn)行函數(shù):
    require(msg.value == 0.001 ether);
    // 如果為真,一些用來(lái)向函數(shù)調(diào)用者發(fā)送數(shù)字內(nèi)容的邏輯
    transferThing(msg.sender);
  }
}

在這里淤击,msg.value是一種可以查看向合約發(fā)送了多少以太的方法匠抗,另外 ether是一個(gè)內(nèi)建單元。這里發(fā)生的事是污抬,一些人會(huì)從web3.js調(diào)用這個(gè)函數(shù) (從DApp的前端)汞贸, 像這樣 :

// 假設(shè) `OnlineStore` 在以太坊上指向你的合約:
OnlineStore.buySomething().send(from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001))

注意這個(gè)value字段绳军, JavaScript 調(diào)用來(lái)指定發(fā)送多少(0.001)以太。如果把事務(wù)想象成一個(gè)信封矢腻,你發(fā)送到函數(shù)的參數(shù)就是信的內(nèi)容门驾。 添加一個(gè) value很像在信封里面放錢(qián) —— 信件內(nèi)容和錢(qián)同時(shí)發(fā)送給了接收者。

注意: 如果一個(gè)函數(shù)沒(méi)標(biāo)記為payable多柑, 而你嘗試?yán)蒙厦娴姆椒òl(fā)送以太奶是,函數(shù)將拒絕你的事務(wù)。


2.提現(xiàn)

在你發(fā)送以太之后竣灌,它將被存儲(chǔ)進(jìn)以合約的以太坊賬戶(hù)中聂沙,并凍結(jié)在那里—— 除非你添加一個(gè)函數(shù)來(lái)從合約中把以太提現(xiàn)。你可以寫(xiě)一個(gè)函數(shù)來(lái)從合約中提現(xiàn)以太初嘹,類(lèi)似這樣:

contract GetPaid is Ownable {
  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }
}

注意我們使用Ownable合約中的owner和onlyOwner及汉,假定它已經(jīng)被引入了。你可以通過(guò) transfer 函數(shù)向一個(gè)地址發(fā)送以太屯烦, 然后this.balance 將返回當(dāng)前合約存儲(chǔ)了多少以太坷随。 所以如果100個(gè)用戶(hù)每人向我們支付1以太, this.balance將是100以太漫贞。你可以通過(guò) transfer 向任何以太坊地址付錢(qián)甸箱。 比如,你可以有一個(gè)函數(shù)在 msg.sender超額付款的時(shí)候給他們退錢(qián):

uint itemFee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee);

或者在一個(gè)有買(mǎi)家和賣(mài)家的合約中迅脐, 你可以把賣(mài)家的地址存儲(chǔ)起來(lái)芍殖, 當(dāng)有人買(mǎi)了它的東西的時(shí)候,把買(mǎi)家支付的錢(qián)發(fā)送給它seller.transfer(msg.value)谴蔑。


3.隨機(jī)數(shù)

keccak256來(lái)制造隨機(jī)數(shù)豌骏。

Solidity 中最好的隨機(jī)數(shù)生成器是 keccak256 哈希函數(shù),我們可以這樣來(lái)生成一些隨機(jī)數(shù):

// 生成一個(gè)0到100的隨機(jī)數(shù):
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;

這個(gè)方法首先拿到now的時(shí)間戳隐锭、 msg.sender窃躲、 以及一個(gè)自增數(shù)nonce(一個(gè)僅會(huì)被使用一次的數(shù),這樣我們就不會(huì)對(duì)相同的輸入值調(diào)用一次以上哈希函數(shù)了)钦睡。然后利用 keccak 把輸入的值轉(zhuǎn)變?yōu)橐粋€(gè)哈希值, 再將哈希值轉(zhuǎn)換為uint蒂窒,然后利用% 100來(lái)取最后兩位, 就生成了一個(gè)0到100之間隨機(jī)數(shù)了。

這個(gè)方法很容易被不誠(chéng)實(shí)的節(jié)點(diǎn)攻擊

在以太坊上, 當(dāng)你在和一個(gè)合約上調(diào)用函數(shù)的時(shí)候, 你會(huì)把它廣播給一個(gè)節(jié)點(diǎn)或者在網(wǎng)絡(luò)上的 transaction 節(jié)點(diǎn)們荞怒。 網(wǎng)絡(luò)上的節(jié)點(diǎn)將收集很多事務(wù)洒琢,試著成為第一個(gè)解決計(jì)算密集型數(shù)學(xué)問(wèn)題的人,作為“工作證明”褐桌,然后將“工作證明”(Proof of Work, PoW)和事務(wù)一起作為一個(gè) block 發(fā)布在網(wǎng)絡(luò)上衰抑。一旦一個(gè)節(jié)點(diǎn)解決了一個(gè)PoW, 其他節(jié)點(diǎn)就會(huì)停止嘗試解決這個(gè) PoW,并驗(yàn)證其他節(jié)點(diǎn)的事務(wù)列表是有效的荧嵌,然后接受這個(gè)節(jié)點(diǎn)轉(zhuǎn)而嘗試解決下一個(gè)節(jié)點(diǎn)呛踊。

這就讓我們的隨機(jī)數(shù)函數(shù)變得可利用了砾淌。

假設(shè)有一個(gè)硬幣翻轉(zhuǎn)合約——正面你贏雙倍錢(qián),反面你輸?shù)羲械腻X(qián)谭网。假如它使用上面的方法來(lái)決定是正面還是反面 (random >= 50 算正面, random < 50 算反面)汪厨。

如果我正運(yùn)行一個(gè)節(jié)點(diǎn),我可以只對(duì)我自己的節(jié)點(diǎn)發(fā)布一個(gè)事務(wù)蜻底,且不分享它骄崩。 我可以運(yùn)行硬幣翻轉(zhuǎn)方法來(lái)偷窺我的輸贏——如果我輸了,我就不把這個(gè)事務(wù)包含進(jìn)我要解決的下一個(gè)區(qū)塊中去薄辅。我可以一直運(yùn)行這個(gè)方法要拂,直到我贏得了硬幣翻轉(zhuǎn)并解決了下一個(gè)區(qū)塊,然后獲利站楚。

所以我們?cè)撊绾卧谝蕴簧习踩厣呻S機(jī)數(shù)呢脱惰?

因?yàn)閰^(qū)塊鏈的全部?jī)?nèi)容對(duì)所有參與者來(lái)說(shuō)是透明的, 這就讓這個(gè)問(wèn)題變得很難窿春,它的解決方法不在本課程討論范圍拉一,你可以閱讀StackOverflow上的討論 來(lái)獲得一些靈感。 一個(gè)方法是利用 oracle 來(lái)訪問(wèn)以太坊區(qū)塊鏈之外的隨機(jī)數(shù)函數(shù)旧乞。

當(dāng)然蔚润, 因?yàn)榫W(wǎng)絡(luò)上成千上萬(wàn)的以太坊節(jié)點(diǎn)都在競(jìng)爭(zhēng)解決下一個(gè)區(qū)塊,我能成功解決下一個(gè)區(qū)塊的幾率非常之低尺栖。 這將花費(fèi)我們巨大的計(jì)算資源來(lái)開(kāi)發(fā)這個(gè)獲利方法——但是如果獎(jiǎng)勵(lì)異常地高(比如我可以在硬幣翻轉(zhuǎn)函數(shù)中贏得 1個(gè)億)嫡纠, 那就很值得去攻擊了。

所以盡管這個(gè)方法在以太坊上不安全延赌,在實(shí)際中除盏,除非我們的隨機(jī)函數(shù)有一大筆錢(qián)在上面,你游戲的用戶(hù)一般是沒(méi)有足夠的資源去攻擊的挫以。


4.總結(jié)

在本課者蠕,我們重新復(fù)習(xí)了可見(jiàn)性修飾符、狀態(tài)修飾符和自定義修飾符掐松,學(xué)習(xí)了payable修飾符踱侣,它是可以接收以太的特殊函數(shù),從而通過(guò)這個(gè)修飾符使區(qū)塊鏈上付費(fèi)變得簡(jiǎn)單大磺。有付費(fèi)就有提現(xiàn)(可能轉(zhuǎn)移更準(zhǔn)確)泻仙,提現(xiàn)是通過(guò)普通的函數(shù)完成,它除了自身的含義外量没,還包括找零和代付。隨機(jī)數(shù)在智能合約中的使用頻率很高突想,Solidity中最好的隨機(jī)數(shù)生成器是keccak256哈希函數(shù)殴蹄,雖然這個(gè)函數(shù)很容易被攻擊究抓,但要攻擊的代價(jià)也挺高。

結(jié)合前面幾節(jié)課所學(xué)袭灯,使用本課的學(xué)到的付費(fèi)刺下、體現(xiàn)和隨機(jī)數(shù),一個(gè)僵尸作戰(zhàn)系統(tǒng)的邏輯就出來(lái)了(代碼就不貼了稽荧,有興趣的可以去學(xué)習(xí))橘茉。


【CryptoZombies|編寫(xiě)區(qū)塊鏈游戲?qū)W智能合約】Lesson1: 搭建僵尸工廠
【CryptoZombies|編寫(xiě)區(qū)塊鏈游戲?qū)W智能合約】Lesson2: 僵尸攻擊人類(lèi)
【CryptoZombies|編寫(xiě)區(qū)塊鏈游戲?qū)W智能合約】Lesson3: 搭建僵尸工廠
【CryptoZombies|編寫(xiě)區(qū)塊鏈游戲?qū)W智能合約】Lesson4: 僵尸作戰(zhàn)系統(tǒng)
【CryptoZombies|編寫(xiě)區(qū)塊鏈游戲?qū)W智能合約】Lesson5: ERC721標(biāo)準(zhǔn)和加密收藏品

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市姨丈,隨后出現(xiàn)的幾起案子畅卓,更是在濱河造成了極大的恐慌,老刑警劉巖蟋恬,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翁潘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡歼争,警方通過(guò)查閱死者的電腦和手機(jī)拜马,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沐绒,“玉大人俩莽,你說(shuō)我怎么就攤上這事∏钦冢” “怎么了扮超?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)申眼。 經(jīng)常有香客問(wèn)我瞒津,道長(zhǎng),這世上最難降的妖魔是什么括尸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任巷蚪,我火速辦了婚禮,結(jié)果婚禮上濒翻,老公的妹妹穿的比我還像新娘屁柏。我一直安慰自己,他們只是感情好有送,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布淌喻。 她就那樣靜靜地躺著,像睡著了一般雀摘。 火紅的嫁衣襯著肌膚如雪裸删。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天阵赠,我揣著相機(jī)與錄音涯塔,去河邊找鬼肌稻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匕荸,可吹牛的內(nèi)容都是我干的爹谭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼榛搔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诺凡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起践惑,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腹泌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后童本,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體真屯,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年穷娱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绑蔫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泵额,死狀恐怖配深,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嫁盲,我是刑警寧澤篓叶,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站羞秤,受9級(jí)特大地震影響缸托,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘾蛋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一俐镐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哺哼,春花似錦佩抹、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至茵汰,卻和暖如春枢里,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工坡垫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梭灿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓冰悠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親配乱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子溉卓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容