成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

以太坊開(kāi)發(fā)實(shí)戰(zhàn)學(xué)習(xí)-solidity語(yǔ)法(二)

wemall / 3950人閱讀

摘要:以太坊開(kāi)發(fā)高級(jí)語(yǔ)言學(xué)習(xí)。地址以太坊區(qū)塊鏈由賬戶組成,你可以把它想象成銀行賬戶。使用很安全,因?yàn)樗哂幸蕴粎^(qū)塊鏈的安全保障除非竊取與以太坊地址相關(guān)聯(lián)的私鑰,否則是沒(méi)有辦法修改其他人的數(shù)據(jù)的。

以太坊開(kāi)發(fā)高級(jí)語(yǔ)言學(xué)習(xí)。
一、映射(Mapping)和地址(Address)

我們通過(guò)給數(shù)據(jù)庫(kù)中的僵尸指定“主人”, 來(lái)支持“多玩家”模式。

如此一來(lái),我們需要引入2個(gè)新的數(shù)據(jù)類(lèi)型:mapping(映射) 和 address(地址)。

Addresses(地址)

以太坊區(qū)塊鏈由 account (賬戶)組成,你可以把它想象成銀行賬戶。一個(gè)帳戶的余額是 以太 (在以太坊區(qū)塊鏈上使用的幣種),你可以和其他帳戶之間支付和接受以太幣,就像你的銀行帳戶可以電匯資金到其他銀行帳戶一樣。

每個(gè)帳戶都有一個(gè)“地址”,你可以把它想象成銀行賬號(hào)。這是賬戶唯一的標(biāo)識(shí)符,它看起來(lái)長(zhǎng)這樣:

0x0cE446255506E92DF41614C46F1d6df9Cc969183

我們將在后面的課程中介紹地址的細(xì)節(jié),現(xiàn)在你只需要了解地址屬于特定用戶(或智能合約)的。

所以我們可以指定“地址”作為僵尸主人的 ID。當(dāng)用戶通過(guò)與我們的應(yīng)用程序交互來(lái)創(chuàng)建新的僵尸時(shí),新僵尸的所有權(quán)被設(shè)置到調(diào)用者的以太坊地址下。

Mapping(映射)

在上一篇博文中,我們看到了 結(jié)構(gòu)體數(shù)組映射 是另一種在 Solidity 中存儲(chǔ)有組織數(shù)據(jù)的方法。

映射是這樣定義的:

//對(duì)于金融應(yīng)用程序,將用戶的余額保存在一個(gè) uint類(lèi)型的變量中:
mapping (address => uint) public accountBalance;

//或者可以用來(lái)通過(guò)userId 存儲(chǔ)/查找的用戶名
mapping (uint => string) userIdToName;

映射本質(zhì)上是存儲(chǔ)和查找數(shù)據(jù)所用的鍵-值對(duì)。在第一個(gè)例子中,鍵是一個(gè) address,值是一個(gè) uint,在第二個(gè)例子中,鍵是一個(gè)uint,值是一個(gè) string。

實(shí)戰(zhàn)演練

為了存儲(chǔ)僵尸的所有權(quán),我們會(huì)使用到兩個(gè)映射:一個(gè)記錄僵尸擁有者的地址,另一個(gè)記錄某地址所擁有僵尸的數(shù)量。

1.創(chuàng)建一個(gè)叫做 zombieToOwner 的映射。其鍵是一個(gè)uint(我們將根據(jù)它的 id 存儲(chǔ)和查找僵尸),值為 address。映射屬性為public。

2.創(chuàng)建一個(gè)名為 ownerZombieCount 的映射,其中鍵是 address,值是 uint。

Contract.sol

// 1. 這里寫(xiě)版本指令
pragma solidity ^0.4.19; 

// 2. 這里建立智能合約
contract ZombieFactory {

  // 12.這里建立事件
  event NewZombie(uint zombieId, string name, uint dna);

  // 3. 定義 dnaDigits 為 uint 數(shù)據(jù)類(lèi)型, 并賦值 16
  uint dnaDigits = 16;

  // 4. 10 的 dnaDigits 次方
  uint dnaModulus = 10 ** dnaDigits;

   // 5.結(jié)構(gòu)體定義
   struct Zombie {
        string name;
        uint dna;

    }
    
    // 6.數(shù)組類(lèi)型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;
    
    // 13.在這里定義映射
    mapping(uint => address) public zombieToOwner;
    mapping(address => uint) ownerZombieCount;
    
    /*
    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局?jǐn)?shù)組)
        zombies.push(Zombie(_name, _dna));
    }
    */
    
     // 7.創(chuàng)建函數(shù)(改為私有方法)
    function _createZombie(string _name, uint _dna) private {
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局?jǐn)?shù)組)
         // zombies.push(Zombie(_name, _dna));
         
        // 12、數(shù)組長(zhǎng)度減一就是當(dāng)前的數(shù)組ID
        uint id = zombies.push(Zombie(_name, _dna)) - 1;

        // 12、這里觸發(fā)事件
        NewZombie(id, _name, _dna);
    }
    
    // 9.函數(shù)修飾符 private, view, returns 返回值
    function _generateRandomDna(string _str) private view returns (uint){
        // 10.散列并取模
        uint rand = uint(keccak256(_str));  // 注意這里需要將string類(lèi)型轉(zhuǎn)為uint類(lèi)型
        return rand % dnaModulus;
    }
        
     
     // 11、綜合函數(shù)
    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
二、Msg.sender

現(xiàn)在有了一套映射來(lái)記錄僵尸的所有權(quán)了,我們可以修改 _createZombie 方法來(lái)運(yùn)用它們。

為了做到這一點(diǎn),我們要用到 msg.sender。

msg.sender

在 Solidity 中,有一些全局變量可以被所有函數(shù)調(diào)用。 其中一個(gè)就是 msg.sender,它指的是當(dāng)前調(diào)用者(或智能合約)address。

注意:在 Solidity 中,功能執(zhí)行始終需要從外部調(diào)用者開(kāi)始。 一個(gè)合約只會(huì)在區(qū)塊鏈上什么也不做,除非有人調(diào)用其中的函數(shù)。所以 msg.sender 總是存在的。

以下是使用 msg.sender 來(lái)更新 mapping 的例子:

mapping (address => uint) favoriteNumber;

function setMyNumber(uint _myNumber) public {
  // 更新我們的 `favoriteNumber` 映射來(lái)將 `_myNumber`存儲(chǔ)在 `msg.sender`名下
  favoriteNumber[msg.sender] = _myNumber;
  // 存儲(chǔ)數(shù)據(jù)至映射的方法和將數(shù)據(jù)存儲(chǔ)在數(shù)組相似
}

function whatIsMyNumber() public view returns (uint) {
  // 拿到存儲(chǔ)在調(diào)用者地址名下的值
  // 若調(diào)用者還沒(méi)調(diào)用 setMyNumber, 則值為 `0`
  return favoriteNumber[msg.sender];
}

在這個(gè)小小的例子中,任何人都可以調(diào)用 setMyNumber 在我們的合約中存下一個(gè) uint 并且與他們的地址相綁定。 然后,他們調(diào)用 whatIsMyNumber 就會(huì)返回他們存儲(chǔ)的 uint。

使用 msg.sender 很安全,因?yàn)樗哂幸蕴粎^(qū)塊鏈的安全保障 —— 除非竊取與以太坊地址相關(guān)聯(lián)的私鑰,否則是沒(méi)有辦法修改其他人的數(shù)據(jù)的。

實(shí)戰(zhàn)演練

我們來(lái)修改前邊的_createZombie 方法,將僵尸分配給函數(shù)調(diào)用者吧。

1、首先,在得到新的僵尸 id 后,更新 zombieToOwner 映射,在 id 下面存入 msg.sender。

2、然后,我們?yōu)檫@個(gè) msg.sender 名下的 ownerZombieCount 加 1。

跟在 JavaScript 中一樣, 在 Solidity 中你也可以用 ++ 使 uint 遞增。

uint number = 0;
number++;
// `number` 現(xiàn)在是 `1`了

修改兩行代碼即可。

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        // 從這里開(kāi)始,msg.sender表示當(dāng)前調(diào)用者的地址
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender] ++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
三、Require

我們成功讓用戶通過(guò)調(diào)用 createRandomZombie 函數(shù) 并輸入一個(gè)名字來(lái)創(chuàng)建新的僵尸。 但是,如果用戶能持續(xù)調(diào)用這個(gè)函數(shù)來(lái)創(chuàng)建出無(wú)限多個(gè)僵尸加入他們的軍團(tuán),這游戲就太沒(méi)意思了!

于是,我們作出限定:每個(gè)玩家只能調(diào)用一次這個(gè)函數(shù)。 這樣一來(lái),新玩家可以在剛開(kāi)始玩游戲時(shí)通過(guò)調(diào)用它,為其軍團(tuán)創(chuàng)建初始僵尸。

我們?cè)鯓硬拍芟薅總€(gè)玩家只調(diào)用一次這個(gè)函數(shù)呢?

答案是使用requirerequire使得函數(shù)在執(zhí)行過(guò)程中,當(dāng)不滿足某些條件時(shí)拋出錯(cuò)誤,并停止執(zhí)行:

function sayHiToVitalik(string _name) public returns (string) {
  // 比較 _name 是否等于 "Vitalik". 如果不成立,拋出異常并終止程序
  // (敲黑板: Solidity 并不支持原生的字符串比較, 我們只能通過(guò)比較
  // 兩字符串的 keccak256 哈希值來(lái)進(jìn)行判斷)
  require(keccak256(_name) == keccak256("Vitalik"));
  // 如果返回 true, 運(yùn)行如下語(yǔ)句
  return "Hi!";
}

如果你這樣調(diào)用函數(shù) sayHiToVitalik(“Vitalik”) ,它會(huì)返回“Hi!”。而如果調(diào)用的時(shí)候使用了其他參數(shù),它則會(huì)拋出錯(cuò)誤并停止執(zhí)行。

因此,在調(diào)用一個(gè)函數(shù)之前,用 require 驗(yàn)證前置條件是非常有必要的。

實(shí)戰(zhàn)演練

在我們的僵尸游戲中,我們不希望用戶通過(guò)反復(fù)調(diào)用 createRandomZombie 來(lái)給他們的軍隊(duì)創(chuàng)建無(wú)限多個(gè)僵尸 —— 這將使得游戲非常無(wú)聊。

我們使用了 require 來(lái)確保這個(gè)函數(shù)只有在每個(gè)用戶第一次調(diào)用它的時(shí)候執(zhí)行,用以創(chuàng)建初始僵尸。

1、在 createRandomZombie 的前面放置 require 語(yǔ)句。 使得函數(shù)先檢查 ownerZombieCount [msg.sender] 的值為 0 ,不然就拋出一個(gè)錯(cuò)誤。

注意:在 Solidity 中,關(guān)鍵詞放置的順序并不重要

雖然參數(shù)的兩個(gè)位置是等效的。 但是,由于我們的答案檢查器比較呆板,它只能認(rèn)定其中一個(gè)為正確答案

于是在這里,我們就約定把 ownerZombieCount [msg.sender] 放前面吧

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        
        // require 判斷
        require(ownerZombieCount[msg.sender] == 0);

        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
四、繼承 Inheritance

我們的游戲代碼越來(lái)越長(zhǎng)。 當(dāng)代碼過(guò)于冗長(zhǎng)的時(shí)候,最好將代碼和邏輯分拆到多個(gè)不同的合約中,以便于管理。

有個(gè)讓 Solidity 的代碼易于管理的功能,就是合約 inheritance (繼承):

contract Doge {
  function catchphrase() public returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract BabyDoge is Doge {
  function anotherCatchphrase() public returns (string) {
    return "Such Moon BabyDoge";
  }
}

由于 BabyDoge 是從 Doge 那里 inherits (繼承)過(guò)來(lái)的。 這意味著當(dāng)你編譯和部署了 BabyDoge,它將可以訪問(wèn) catchphrase()anotherCatchphrase()和其他我們?cè)?Doge 中定義的其他公共函數(shù)。

這可以用于邏輯繼承(比如表達(dá)子類(lèi)的時(shí)候,Cat 是一種 Animal)。 但也可以簡(jiǎn)單地將類(lèi)似的邏輯組合到不同的合約中以組織代碼。

實(shí)戰(zhàn)演練

在接下來(lái)的章節(jié)中,我們將要為僵尸實(shí)現(xiàn)各種功能,讓它可以“獵食”和“繁殖”。 通過(guò)將這些運(yùn)算放到父類(lèi) ZombieFactory 中,使得所有 ZombieFactory 的繼承者合約都可以使用這些方法。

ZombieFactory 下創(chuàng)建一個(gè)叫 ZombieFeeding 的合約,它是繼承自 ZombieFactory 合約的。

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

// Start here (合約繼承)
contract ZombieFeeding is ZombieFactory {

}
五、引入Import

在這一節(jié)中,我們將對(duì)上邊那個(gè)很大的合約進(jìn)行拆分。

上邊的代碼已經(jīng)夠長(zhǎng)了,我們把它分成多個(gè)文件以便于管理。 通常情況下,當(dāng) Solidity 項(xiàng)目中的代碼太長(zhǎng)的時(shí)候我們就是這么做的。

在 Solidity 中,當(dāng)你有多個(gè)文件并且想把一個(gè)文件導(dǎo)入另一個(gè)文件時(shí),可以使用 import 語(yǔ)句:

import "./someothercontract.sol";

contract newContract is SomeOtherContract {

}

這樣當(dāng)我們?cè)诤霞s(contract)目錄下有一個(gè)名為 someothercontract.sol 的文件( ./ 就是同一目錄的意思),它就會(huì)被編譯器導(dǎo)入。

實(shí)戰(zhàn)演練

現(xiàn)在我們已經(jīng)建立了一個(gè)多文件架構(gòu),并用 import 來(lái)讀取來(lái)自另一個(gè)文件中合約的內(nèi)容:

1.將 zombiefactory.sol 導(dǎo)入到我們的新文件 zombiefeeding.sol 中。

zombiefactory.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

zombiefeeding.sol

pragma solidity ^0.4.19;

// put import statement here(導(dǎo)入合約)
// import "./zombiefactory.sol";  // 導(dǎo)入另一個(gè)文件不能用單引號(hào),只能用雙引號(hào),否則會(huì)報(bào)錯(cuò)
import "./zombiefactory.sol";

contract ZombieFeeding is ZombieFactory {

}
六、Storage與Memory

在 Solidity 中,有兩個(gè)地方可以存儲(chǔ)變量 —— storagememory。

Storage 變量是指永久存儲(chǔ)在區(qū)塊鏈中的變量。 Memory 變量則是臨時(shí)的,當(dāng)外部函數(shù)對(duì)某合約調(diào)用完成時(shí),內(nèi)存型變量即被移除。 你可以把它想象成存儲(chǔ)在你電腦的硬盤(pán)或是RAM中數(shù)據(jù)的關(guān)系。

大多數(shù)時(shí)候你都用不到這些關(guān)鍵字,默認(rèn)情況下 Solidity 會(huì)自動(dòng)處理它們。 狀態(tài)變量(在函數(shù)之外聲明的變量)默認(rèn)為“存儲(chǔ)”形式,并永久寫(xiě)入?yún)^(qū)塊鏈;而在函數(shù)內(nèi)部聲明的變量是“內(nèi)存”型的,它們函數(shù)調(diào)用結(jié)束后消失。

然而也有一些情況下,你需要手動(dòng)聲明存儲(chǔ)類(lèi)型,主要用于處理函數(shù)內(nèi)的 結(jié)構(gòu)體數(shù)組 時(shí):

contract SandwichFactory {
  struct Sandwich {
    string name;
    string status;
  }

  Sandwich[] sandwiches;

  function eatSandwich(uint _index) public {
    // Sandwich mySandwich = sandwiches[_index];

    // ^ 看上去很直接,不過(guò) Solidity 將會(huì)給出警告
    // 告訴你應(yīng)該明確在這里定義 `storage` 或者 `memory`。

    // 所以你應(yīng)該明確定義 `storage`:
    Sandwich storage mySandwich = sandwiches[_index];
    // ...這樣 `mySandwich` 是指向 `sandwiches[_index]`的指針
    // 在存儲(chǔ)里,另外...
    mySandwich.status = "Eaten!";
    // ...這將永久把 `sandwiches[_index]` 變?yōu)閰^(qū)塊鏈上的存儲(chǔ)

    // 如果你只想要一個(gè)副本,可以使用`memory`:
    Sandwich memory anotherSandwich = sandwiches[_index + 1];
    // ...這樣 `anotherSandwich` 就僅僅是一個(gè)內(nèi)存里的副本了
    // 另外
    anotherSandwich.status = "Eaten!";
    // ...將僅僅修改臨時(shí)變量,對(duì) `sandwiches[_index + 1]` 沒(méi)有任何影響
    // 不過(guò)你可以這樣做:
    sandwiches[_index + 1] = anotherSandwich;
    // ...如果你想把副本的改動(dòng)保存回區(qū)塊鏈存儲(chǔ)
  }
}

如果你還沒(méi)有完全理解究竟應(yīng)該使用哪一個(gè),也不用擔(dān)心 —— 在本教程中,我們將告訴你何時(shí)使用 storage 或是 memory,并且當(dāng)你不得不使用到這些關(guān)鍵字的時(shí)候,Solidity 編譯器也發(fā)警示提醒你的。

現(xiàn)在,只要知道在某些場(chǎng)合下也需要你顯式地聲明 storagememory就夠了!

實(shí)戰(zhàn)演練

是時(shí)候給我們的僵尸增加“獵食”和“繁殖”功能了!

當(dāng)一個(gè)僵尸獵食其他生物體時(shí),它自身的DNA將與獵物生物的DNA結(jié)合在一起,形成一個(gè)新的僵尸DNA。

1、創(chuàng)建一個(gè)名為 feedAndMultiply 的函數(shù)。 使用兩個(gè)參數(shù):_zombieId( uint類(lèi)型 )和_targetDna (也是 uint 類(lèi)型)。 設(shè)置屬性為 public 的。

2、我們不希望別人用我們的僵尸去捕獵。 首先,我們確保對(duì)自己僵尸的所有權(quán)。 通過(guò)添加一個(gè)require 語(yǔ)句來(lái)確保 msg.sender 只能是這個(gè)僵尸的主人(類(lèi)似于我們?cè)?createRandomZombie 函數(shù)中做過(guò)的那樣)。

注意:同樣,因?yàn)槲覀兊拇鸢笝z查器比較呆萌,只認(rèn)識(shí)把 msg.sender 放在前面的答案,如果你切換了參數(shù)的順序,它就不認(rèn)得了。 但你正常編碼時(shí),如何安排參數(shù)順序都是正確的。

1、為了獲取這個(gè)僵尸的DNA,我們的函數(shù)需要聲明一個(gè)名為 myZombie 數(shù)據(jù)類(lèi)型為Zombie的本地變量(這是一個(gè) storage 型的指針)。 將其值設(shè)定為在 zombies 數(shù)組中索引為_zombieId所指向的值。

到目前為止,包括函數(shù)結(jié)束符 } 的那一行, 總共4行代碼。

zombiefeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract ZombieFeeding is ZombieFactory {

  // Start here
  function feedAndMultiply(uint _zombieId, uint _targetDna) public {
      require(msg.sender == zombieToOwner[_zombieId]);
      Zombie storage myZombie = zombies[_zombieId];
  }

}
七、實(shí)戰(zhàn)應(yīng)用

我們來(lái)把 feedAndMultiply 函數(shù)寫(xiě)完吧。

獲取新的僵尸DNA的公式很簡(jiǎn)單:計(jì)算獵食僵尸DNA和被獵僵尸DNA之間的平均值。
示例:

function testDnaSplicing() public {
  uint zombieDna = 2222222222222222;
  uint targetDna = 4444444444444444;
  uint newZombieDna = (zombieDna + targetDna) / 2;
  // newZombieDna 將等于 3333333333333333
}

以后,我們也可以讓函數(shù)變得更復(fù)雜些,比方給新的僵尸的 DNA 增加一些隨機(jī)性之類(lèi)的。但現(xiàn)在先從最簡(jiǎn)單的開(kāi)始 —— 以后還可以回來(lái)完善它嘛。

實(shí)戰(zhàn)演練

1、首先我們確保 _targetDna 不長(zhǎng)于16位。要做到這一點(diǎn),我們可以設(shè)置 _targetDna_targetDna%dnaModulus ,并且只取其最后16位數(shù)字。

2、接下來(lái)為我們的函數(shù)聲明一個(gè)名叫 newDna 的 uint類(lèi)型的變量,并將其值設(shè)置為 myZombie的 DNA 和 _targetDna 的平均值(如上例所示)。

注意:您可以用 myZombie.namemyZombie.dna 訪問(wèn) myZombie 的屬性。

一旦我們計(jì)算出新的DNA,再調(diào)用 _createZombie 就可以生成新的僵尸了。如果你忘了調(diào)用這個(gè)函數(shù)所需要的參數(shù),可以查看 zombiefactory.sol 選項(xiàng)卡。請(qǐng)注意,需要先給它命名,所以現(xiàn)在我們把新的僵尸的名字設(shè)為NoName - 我們回頭可以編寫(xiě)一個(gè)函數(shù)來(lái)更改僵尸的名字。

注意:對(duì)于 Solidity 高手,你可能會(huì)注意到我們的代碼存在一個(gè)問(wèn)題。別擔(dān)心,下一章會(huì)解決這個(gè)問(wèn)題的 ;)

zombiefeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract ZombieFeeding is ZombieFactory {

  function feedAndMultiply(uint _zombieId, uint _targetDna) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    // start here

    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;

    _createZombie("NoName", newDna);

  }

}
八、函數(shù)可見(jiàn)性

我們上面的代碼有問(wèn)題!

編譯的時(shí)候編譯器就會(huì)報(bào)錯(cuò)。

錯(cuò)誤在于,我們嘗試從 ZombieFeeding 中調(diào)用 _createZombie 函數(shù),但 _createZombie 卻是 ZombieFactoryprivate (私有)函數(shù)。這意味著任何繼承自 ZombieFactory 的子合約都不能訪問(wèn)它。

internal 和 external

publicprivate 屬性之外,Solidity 還使用了另外兩個(gè)描述函數(shù)可見(jiàn)性的修飾詞:internal(內(nèi)部) 和 external(外部)。

internalprivate 類(lèi)似,不過(guò), 如果某個(gè)合約繼承自其父合約,這個(gè)合約即可以訪問(wèn)父合約中定義的“內(nèi)部”函數(shù)。(嘿,這聽(tīng)起來(lái)正是我們想要的那樣?。?。

externalpublic 類(lèi)似,只不過(guò)這些函數(shù)只能在合約之外調(diào)用 - 它們不能被合約內(nèi)的其他函數(shù)調(diào)用。稍后我們將討論什么時(shí)候使用 externalpublic。

聲明函數(shù) internal 或 external 類(lèi)型的語(yǔ)法,與聲明 private 和 public類(lèi) 型相同:

contract Sandwich {
  uint private sandwichesEaten = 0;

  function eat() internal {
    sandwichesEaten++;
  }
}

contract BLT is Sandwich {
  uint private baconSandwichesEaten = 0;

  function eatWithBacon() public returns (string) {
    baconSandwichesEaten++;
    // 因?yàn)閑at() 是internal 的,所以我們能在這里調(diào)用
    eat();
  }
}
實(shí)戰(zhàn)演練

_createZombie() 函數(shù)的屬性從 private 改為 internal , 使得其他的合約也能訪問(wèn)到它。

我們已經(jīng)成功把你的注意力集中在到zombiefactory.sol這個(gè)基類(lèi)合約上了。

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    // 在這里修改函數(shù)的功能 private => internal
    function _createZombie(string _name, uint _dna) internal {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
九、與其他合約的交互

是時(shí)候讓我們的僵尸去捕獵! 那僵尸最喜歡的食物是什么呢?

為了做到這一點(diǎn),我們要讀出 CryptoKitties 智能合約中的 kittyDna。這些數(shù)據(jù)是公開(kāi)存儲(chǔ)在區(qū)塊鏈上的。區(qū)塊鏈?zhǔn)遣皇呛芸幔?/p>

別擔(dān)心 —— 我們的游戲并不會(huì)傷害到任何真正的CryptoKitty。 我們只 讀取 CryptoKitties 數(shù)據(jù),但卻無(wú)法在物理上刪除它。

與其他合約的交互

如果我們的合約需要和區(qū)塊鏈上的其他的合約會(huì)話,則需先定義一個(gè) interface (接口)。

先舉一個(gè)簡(jiǎn)單的栗子。 假設(shè)在區(qū)塊鏈上有這么一個(gè)合約:

contract LuckyNumber {
  mapping(address => uint) numbers;

  function setNum(uint _num) public {
    numbers[msg.sender] = _num;
  }

  function getNum(address _myAddress) public view returns (uint) {
    return numbers[_myAddress];
  }
}

這是個(gè)很簡(jiǎn)單的合約,您可以用它存儲(chǔ)自己的幸運(yùn)號(hào)碼,并將其與您的以太坊地址關(guān)聯(lián)。 這樣其他人就可以通過(guò)您的地址查找您的幸運(yùn)號(hào)碼了。

現(xiàn)在假設(shè)我們有一個(gè)外部合約,使用 getNum 函數(shù)可讀取其中的數(shù)據(jù)。

首先,我們定義 LuckyNumber 合約的 interface

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

請(qǐng)注意,這個(gè)過(guò)程雖然看起來(lái)像在定義一個(gè)合約,但其實(shí)內(nèi)里不同:

首先,我們只聲明了要與之交互的函數(shù) —— 在本例中為 getNum —— 在其中我們沒(méi)有使用到任何其他的函數(shù)或狀態(tài)變量。

其次,我們并沒(méi)有使用大括號(hào)({})定義函數(shù)體,我們單單用分號(hào)(;)結(jié)束了函數(shù)聲明。這使它看起來(lái)像一個(gè)合約框架。

編譯器就是靠這些特征認(rèn)出它是一個(gè)接口的。

在我們的 app 代碼中使用這個(gè)接口,合約就知道其他合約的函數(shù)是怎樣的,應(yīng)該如何調(diào)用,以及可期待什么類(lèi)型的返回值。

在下一課中,我們將真正調(diào)用其他合約的函數(shù)。目前我們只要聲明一個(gè)接口,用于調(diào)用 CryptoKitties 合約就行了。

實(shí)戰(zhàn)演練

我們已經(jīng)為你查看過(guò)了 CryptoKitties 的源代碼,并且找到了一個(gè)名為 getKitty的函數(shù),它返回所有的加密貓的數(shù)據(jù),包括它的“基因”(我們的僵尸游戲要用它生成新的僵尸)。

該函數(shù)如下所示:

function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
) {
    Kitty storage kit = kitties[_id];

    // if this variable is 0 then it"s not gestating
    isGestating = (kit.siringWithId != 0);
    isReady = (kit.cooldownEndBlock <= block.number);
    cooldownIndex = uint256(kit.cooldownIndex);
    nextActionAt = uint256(kit.cooldownEndBlock);
    siringWithId = uint256(kit.siringWithId);
    birthTime = uint256(kit.birthTime);
    matronId = uint256(kit.matronId);
    sireId = uint256(kit.sireId);
    generation = uint256(kit.generation);
    genes = kit.genes;
}

這個(gè)函數(shù)看起來(lái)跟我們習(xí)慣的函數(shù)不太一樣。 它竟然返回了...一堆不同的值! 如果您用過(guò) JavaScript 之類(lèi)的編程語(yǔ)言,一定會(huì)感到奇怪 —— 在 Solidity中,您可以讓一個(gè)函數(shù)返回多個(gè)值。

現(xiàn)在我們知道這個(gè)函數(shù)長(zhǎng)什么樣的了,就可以用它來(lái)創(chuàng)建一個(gè)接口:

1.定義一個(gè)名為 KittyInterface 的接口。 請(qǐng)注意,因?yàn)槲覀兪褂昧?contract 關(guān)鍵字, 這過(guò)程看起來(lái)就像創(chuàng)建一個(gè)新的合約一樣。

2.在interface里定義了 getKitty 函數(shù)(不過(guò)是復(fù)制/粘貼上面的函數(shù),但在 returns 語(yǔ)句之后用分號(hào),而不是大括號(hào)內(nèi)的所有內(nèi)容。

zombiefeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

// Create KittyInterface here
contract KittyInterface {
    function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
    );
}

contract ZombieFeeding is ZombieFactory {

  function feedAndMultiply(uint _zombieId, uint _targetDna) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    _createZombie("NoName", newDna);
  }

}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24137.html

相關(guān)文章

  • 以太開(kāi)發(fā)實(shí)戰(zhàn)學(xué)習(xí)-solidity語(yǔ)法 (三)

    摘要:接上一節(jié),繼續(xù)學(xué)習(xí)高級(jí)語(yǔ)法。添加語(yǔ)句,并且將后兩位數(shù)替換為添加參數(shù)四部署以太坊實(shí)現(xiàn)實(shí)現(xiàn)我們只用編譯和部署,就可以將這個(gè)合約部署到以太坊了。 接上一節(jié),繼續(xù)學(xué)習(xí)solidity高級(jí)語(yǔ)法。 一、使用接口 繼續(xù)前面上一節(jié) NumberInterface 的例子,我們既然將接口定義為: contract NumberInterface { function getNum(address _...

    tianren124 評(píng)論0 收藏0
  • 區(qū)塊鏈技術(shù)學(xué)習(xí)指引

    摘要:引言給迷失在如何學(xué)習(xí)區(qū)塊鏈技術(shù)的同學(xué)一個(gè)指引,區(qū)塊鏈技術(shù)是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術(shù),應(yīng)該先了解下比特幣。但區(qū)塊鏈技術(shù)不單應(yīng)用于比特幣,還有非常多的現(xiàn)實(shí)應(yīng)用場(chǎng)景,想做區(qū)塊鏈應(yīng)用開(kāi)發(fā),可進(jìn)一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學(xué)習(xí)指引 原文已更新,請(qǐng)讀者前往原文閱讀 本章的文章越來(lái)越多,本文是一個(gè)索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...

    Cristic 評(píng)論0 收藏0
  • 以太智能合約開(kāi)發(fā)第一篇:IDE對(duì)solidity語(yǔ)法的支持

    摘要:原文發(fā)表于以太坊智能合約開(kāi)發(fā)第一篇對(duì)語(yǔ)法的支持最近在研究以太坊智能合約的開(kāi)發(fā)。是一種語(yǔ)法類(lèi)似的高級(jí)語(yǔ)言,它被設(shè)計(jì)成以編譯的方式生成以太坊虛擬機(jī)代碼。 原文發(fā)表于:以太坊智能合約開(kāi)發(fā)第一篇:IDE對(duì)solidity語(yǔ)法的支持 最近在研究以太坊智能合約的開(kāi)發(fā)。隨著研究的深入,準(zhǔn)備寫(xiě)一個(gè)系列教程,將我的實(shí)際經(jīng)驗(yàn)與大家分享,供大家參考借鑒。 solidity是什么? 以太坊官方推薦使用Sol...

    xiangzhihong 評(píng)論0 收藏0
  • 以太開(kāi)發(fā)實(shí)戰(zhàn)學(xué)習(xí)-高級(jí)Solidity理論 (五)

    摘要:接上篇文章,這里繼續(xù)學(xué)習(xí)高級(jí)理論。實(shí)戰(zhàn)演練我們來(lái)寫(xiě)一個(gè)返回某玩家的整個(gè)僵尸軍團(tuán)的函數(shù)。但這樣每做一筆交易,都會(huì)改變僵尸軍團(tuán)的秩序。在這里開(kāi)始五可支付截至目前,我們只接觸到很少的函數(shù)修飾符。 接上篇文章,這里繼續(xù)學(xué)習(xí)Solidity高級(jí)理論。 一、深入函數(shù)修飾符 接下來(lái),我們將添加一些輔助方法。我們?yōu)槟鷦?chuàng)建了一個(gè)名為 zombiehelper.sol 的新文件,并且將 zombiefee...

    sushi 評(píng)論0 收藏0
  • 以太開(kāi)發(fā)實(shí)戰(zhàn)學(xué)習(xí)-Web3.js(十)

    摘要:如果當(dāng)前在以太坊上有大量掛起事務(wù)或者用戶發(fā)送了過(guò)低的價(jià)格,我們的事務(wù)可能需要等待數(shù)個(gè)區(qū)塊才能被包含進(jìn)去,往往可能花費(fèi)數(shù)分鐘。 接上篇 Web3.js,這節(jié)課繼續(xù)學(xué)習(xí)Web3.js 的相關(guān)知識(shí)。 一、發(fā)送事務(wù) 這下我們的界面能檢測(cè)用戶的 MetaMask 賬戶,并自動(dòng)在首頁(yè)顯示它們的僵尸大軍了,有沒(méi)有很棒? 現(xiàn)在我們來(lái)看看用 send 函數(shù)來(lái)修改我們智能合約里面的數(shù)據(jù)。 相對(duì) call...

    Scott 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<