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

資訊專欄INFORMATION COLUMN

Solidity 簡(jiǎn)易教程0x001

FrozenMap / 2763人閱讀

摘要:函數(shù)和可用于檢查條件并在條件不滿足時(shí)拋出異常。函數(shù)只能用于測(cè)試內(nèi)部錯(cuò)誤,并檢查非變量。函數(shù)和狀態(tài)變量?jī)H在當(dāng)前定義它們的合約中使用,并且不能被派生合約使用。派生合約可以訪問(wèn)所有非私有成員,包括內(nèi)部函數(shù)和狀態(tài)變量,但無(wú)法通過(guò)來(lái)外部訪問(wèn)。

Solidity是以太坊的主要編程語(yǔ)言,它是一種靜態(tài)類型的 JavaScript-esque 語(yǔ)言,是面向合約的、為實(shí)現(xiàn)智能合約而創(chuàng)建的高級(jí)編程語(yǔ)言,設(shè)計(jì)的目的是能在以太坊虛擬機(jī)(EVM)上運(yùn)行。

本文基于CryptoZombies,教程地址為:https://cryptozombies.io/zh/lesson/2

地址(address)

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

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

0x0cE446255506E92DF41614C46F1d6df9Cc969183
這是 CryptoZombies 團(tuán)隊(duì)的地址,為了表示支持CryptoZombies,可以贊賞一些以太幣!

address:地址類型存儲(chǔ)一個(gè) 20 字節(jié)的值(以太坊地址的大小)。 地址類型也有成員變量,并作為所有合約的基礎(chǔ)。

address 類型是一個(gè)160位的值,且不允許任何算數(shù)操作。這種類型適合存儲(chǔ)合約地址或外部人員的密鑰對(duì)。
映射(mapping)
Mappings 和哈希表類似,它會(huì)執(zhí)行虛擬初始化,以使所有可能存在的鍵都映射到一個(gè)字節(jié)表示為全零的值。

映射是這樣定義的:

//對(duì)于金融應(yīng)用程序,將用戶的余額保存在一個(gè) uint類型的變量中:
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í)的形式為 mapping(_KeyType => _ValueType)。 其中 _KeyType 可以是除了映射、變長(zhǎng)數(shù)組、合約、枚舉以及結(jié)構(gòu)體以外的幾乎所有類型。 _ValueType 可以是包括映射類型在內(nèi)的任何類型。

對(duì)映射的取值操作如下:

userIdToName[12]
// 如果鍵12 不在 映射中,得到的結(jié)果是0
映射中,實(shí)際上并不存儲(chǔ) key,而是存儲(chǔ)它的 keccak256 哈希值,從而便于查詢實(shí)際的值。所以映射是沒(méi)有長(zhǎng)度的,也沒(méi)有 key 的集合或 value 的集合的概念。,你不能像操作python字典那應(yīng)該獲取到當(dāng)前 Mappings 的所有鍵或者值。
特殊變量

在 Solidity 中,在全局命名空間中已經(jīng)存在了(預(yù)設(shè)了)一些特殊的變量和函數(shù),他們主要用來(lái)提供關(guān)于區(qū)塊鏈的信息或一些通用的工具函數(shù)。

msg.sender

msg.sender指的是當(dāng)前調(diào)用者(或智能合約)的 address。

注意:在 Solidity 中,功能執(zhí)行始終需要從外部調(diào)用者開(kāi)始。 一個(gè)合約只會(huì)在區(qū)塊鏈上什么也不做,除非有人調(diào)用其中的函數(shù)。所以對(duì)于每一個(gè)外部函數(shù)調(diào)用,包括 msg.sender 和 msg.value 在內(nèi)所有 msg 成員的值都會(huì)變化。這里包括對(duì)庫(kù)函數(shù)的調(diào)用。

以下是使用 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ù)的。

以下是其它的一些特殊變量。

區(qū)塊和交易屬性

block.blockhash(uint blockNumber) returns (bytes32):指定區(qū)塊的區(qū)塊哈希——僅可用于最新的 256 個(gè)區(qū)塊且不包括當(dāng)前區(qū)塊;而 blocks 從 0.4.22 版本開(kāi)始已經(jīng)不推薦使用,由 blockhash(uint blockNumber) 代替

block.coinbase (address): 挖出當(dāng)前區(qū)塊的礦工地址

block.difficulty (uint): 當(dāng)前區(qū)塊難度

block.gaslimit (uint): 當(dāng)前區(qū)塊 gas 限額

block.number (uint): 當(dāng)前區(qū)塊號(hào)

block.timestamp (uint): 自 unix epoch 起始當(dāng)前區(qū)塊以秒計(jì)的時(shí)間戳

gasleft() returns (uint256):剩余的 gas

msg.data (bytes): 完整的 calldata

msg.gas (uint): 剩余 gas - 自 0.4.21 版本開(kāi)始已經(jīng)不推薦使用,由 gesleft() 代替

msg.sender (address): 消息發(fā)送者(當(dāng)前調(diào)用)

msg.sig (bytes4): calldata 的前 4 字節(jié)(也就是函數(shù)標(biāo)識(shí)符)

msg.value (uint): 隨消息發(fā)送的 wei 的數(shù)量

now (uint): 目前區(qū)塊時(shí)間戳(block.timestamp)

tx.gasprice (uint): 交易的 gas 價(jià)格

tx.origin (address): 交易發(fā)起者(完全的調(diào)用鏈)

錯(cuò)誤處理

Solidity 使用狀態(tài)恢復(fù)異常來(lái)處理錯(cuò)誤。這種異常將撤消對(duì)當(dāng)前調(diào)用(及其所有子調(diào)用)中的狀態(tài)所做的所有更改,并且還向調(diào)用者標(biāo)記錯(cuò)誤。

函數(shù) assertrequire 可用于檢查條件并在條件不滿足時(shí)拋出異常。

assert 函數(shù)只能用于測(cè)試內(nèi)部錯(cuò)誤,并檢查非變量。

require 函數(shù)用于確認(rèn)條件有效性,例如輸入變量,或合約狀態(tài)變量是否滿足條件,或驗(yàn)證外部合約調(diào)用返回的值。

這里主要介紹 require

require使得函數(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)證前置條件是非常有必要的。

注意:在 Solidity 中,關(guān)鍵詞放置的順序并不重要
// 以下兩個(gè)語(yǔ)句等效
require(keccak256(_name) == keccak256("Vitalik"));
require(keccak256("Vitalik") == keccak256(_name));
外/內(nèi)部函數(shù)

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

internalprivate 類似,不過(guò),如果某個(gè)合約繼承自其父合約,這個(gè)合約即可以訪問(wèn)父合約中定義的“內(nèi)部(internal)”函數(shù)。

externalpublic 類似,只不過(guò)external函數(shù)只能在合約之外調(diào)用 - 它們不能被合約內(nèi)的其他函數(shù)調(diào)用。

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

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();
  }
}

Solidity 有兩種函數(shù)調(diào)用(內(nèi)部調(diào)用不會(huì)產(chǎn)生實(shí)際的 EVM 調(diào)用或稱為消息調(diào)用,而外部調(diào)用則會(huì)產(chǎn)生一個(gè) EVM 調(diào)用), 函數(shù)和狀態(tài)變量有四種可見(jiàn)性類型。 函數(shù)可以指定為 external ,public ,internal 或者 private,默認(rèn)情況下函數(shù)類型為 public。 對(duì)于狀態(tài)變量,不能設(shè)置為 external ,默認(rèn)是 internal 。

external :

外部函數(shù)作為合約接口的一部分,意味著我們可以從其他合約和交易中調(diào)用。 一個(gè)外部函數(shù) f 不能從內(nèi)部調(diào)用(即 f 不起作用,但 this.f() 可以)。 當(dāng)收到大量數(shù)據(jù)的時(shí)候,外部函數(shù)有時(shí)候會(huì)更有效率。

public :

public 函數(shù)是合約接口的一部分,可以在內(nèi)部或通過(guò)消息調(diào)用。對(duì)于公共狀態(tài)變量, 會(huì)自動(dòng)生成一個(gè) getter 函數(shù)。

internal :

這些函數(shù)和狀態(tài)變量只能是內(nèi)部訪問(wèn)(即從當(dāng)前合約內(nèi)部或從它派生的合約訪問(wèn)),不使用 this 調(diào)用。

private :

private 函數(shù)和狀態(tài)變量?jī)H在當(dāng)前定義它們的合約中使用,并且不能被派生合約使用。

合約中的所有內(nèi)容對(duì)外部觀察者都是可見(jiàn)的。設(shè)置一些 private 類型只能阻止其他合約訪問(wèn)和修改這些信息, 但是對(duì)于區(qū)塊鏈外的整個(gè)世界它仍然是可見(jiàn)的。

可見(jiàn)性標(biāo)識(shí)符的定義位置,對(duì)于狀態(tài)變量來(lái)說(shuō)是在類型后面,對(duì)于函數(shù)是在參數(shù)列表和返回關(guān)鍵字中間。

pragma solidity ^0.4.16;

contract C {
    // 對(duì)于函數(shù)是在參數(shù)列表和返回關(guān)鍵字中間。
    function f(uint a) private pure returns (uint b) { return a + 1; }
    function setData(uint a) internal { data = a; }
    uint public data;  // 對(duì)于狀態(tài)變量來(lái)說(shuō)是在類型后面
}
函數(shù)多值返回

和 python 類似,Solidity 函數(shù)支持多值返回,比如:

function multipleReturns() internal returns(uint a, uint b, uint c) {
  return (1, 2, 3);
}

function processMultipleReturns() external {
  uint a;
  uint b;
  uint c;
  // 這樣來(lái)做批量賦值:
  (a, b, c) = multipleReturns();
}

// 或者如果我們只想返回其中一個(gè)變量:
function getLastReturnValue() external {
  uint c;
  // 可以對(duì)其他字段留空:
  (,,c) = multipleReturns();
}
這里留空字段使用,的方式太不直觀了,還不如 python/go 使用下劃線_代替無(wú)用字段。
Storage與Memory

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

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

storage 和 memory 放到狀態(tài)變量名前邊,在類型后邊,格式如下:
變量類型 變量名

大多數(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ǔ)類型,主要用于處理函數(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)合下也需要你顯式地聲明 storage 或 memory就夠了!

繼承

Solidity 的繼承和 Python 的繼承相似,支持多重繼承。
看下面這個(gè)例子:

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

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

// 可以多重繼承。請(qǐng)注意,Doge 也是 BabyDoge 的基類,
// 但只有一個(gè) Doge 實(shí)例(就像 C++ 中的虛擬繼承)。
contract BlackBabyDoge is Doge, BabyDoge {
  function color() public returns (string) {
    return "Black";
  }
}

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

Solidity使用 is 從另一個(gè)合約派生。派生合約可以訪問(wèn)所有非私有成員,包括內(nèi)部函數(shù)和狀態(tài)變量,但無(wú)法通過(guò) this 來(lái)外部訪問(wèn)。

基類構(gòu)造函數(shù)的參數(shù)

派生合約需要提供基類構(gòu)造函數(shù)需要的所有參數(shù)。這可以通過(guò)兩種方式來(lái)完成:

pragma solidity ^0.4.0;

contract Base {
    uint x;
    // 這是注冊(cè) Base 和設(shè)置名稱的構(gòu)造函數(shù)。
    function Base(uint _x) public { x = _x; }
}

contract Derived is Base(7) {
    function Derived(uint _y) Base(_y * _y) public {
    }
}

contract Derived1 is Base {
    function Derived1(uint _y) Base(_y * _y) public {
    }
}

一種方法直接在繼承列表中調(diào)用基類構(gòu)造函數(shù)(is Base(7))。 另一種方法是像 修飾器 modifier 使用方法一樣, 作為派生合約構(gòu)造函數(shù)定義頭的一部分,(Base(_y * _y))。 如果構(gòu)造函數(shù)參數(shù)是常量并且定義或描述了合約的行為,使用第一種方法比較方便。 如果基類構(gòu)造函數(shù)的參數(shù)依賴于派生合約,那么必須使用第二種方法。 如果像這個(gè)簡(jiǎn)單的例子一樣,兩個(gè)地方都用到了,優(yōu)先使用 修飾器modifier 風(fēng)格的參數(shù)。

抽象合約

合約函數(shù)可以缺少實(shí)現(xiàn),如下例所示(請(qǐng)注意函數(shù)聲明頭由 ; 結(jié)尾):

pragma solidity ^0.4.0;

contract Feline {
    function utterance() public returns (bytes32);
}

這些合約無(wú)法成功編譯(即使它們除了未實(shí)現(xiàn)的函數(shù)還包含其他已經(jīng)實(shí)現(xiàn)了的函數(shù)),但他們可以用作基類合約:

pragma solidity ^0.4.0;

contract Feline {
    function utterance() public returns (bytes32);
}

contract Cat is Feline {
    function utterance() public returns (bytes32) { return "miaow"; }
}

如果合約繼承自抽象合約,并且沒(méi)有通過(guò)重寫(xiě)來(lái)實(shí)現(xiàn)所有未實(shí)現(xiàn)的函數(shù),那么它本身就是抽象的。

接口(Interface)

接口類似于抽象合約,但是它們不能實(shí)現(xiàn)任何函數(shù)。還有進(jìn)一步的限制:

無(wú)法繼承其他合約或接口。

無(wú)法定義構(gòu)造函數(shù)。

無(wú)法定義變量。

無(wú)法定義結(jié)構(gòu)體

無(wú)法定義枚舉。

首先,看一下一個(gè)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è)接口的。

就像繼承其他合約一樣,合約可以繼承接口。

可以在合約中這樣使用接口:

contract MyContract {
  address NumberInterfaceAddress = 0xab38...;
  // ^ 這是FavoriteNumber合約在以太坊上的地址
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
  // 現(xiàn)在變量 `numberContract` 指向另一個(gè)合約對(duì)象

  function someFunction() public {
    // 現(xiàn)在我們可以調(diào)用在那個(gè)合約中聲明的 `getNum`函數(shù):
    uint num = numberContract.getNum(msg.sender);
    // ...在這兒使用 `num`變量做些什么
  }
}

通過(guò)這種方式,只要將合約的可見(jiàn)性設(shè)置為public(公共)或external(外部),它們就可以與以太坊區(qū)塊鏈上的任何其他合約進(jìn)行交互。

與其他合約的交互

如果一個(gè)合約需要和區(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)碼,并將其與調(dià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);
}

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

下面是一個(gè)示例代碼,會(huì)用到上邊的知識(shí)點(diǎn):

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;

    // 創(chuàng)建一個(gè)叫做 zombieToOwner 的映射。其鍵是一個(gè)uint,值為 address。映射屬性為public
    mapping (uint => address) public zombieToOwner;
    // 創(chuàng)建一個(gè)名為 ownerZombieCount 的映射,其中鍵是 address,值是 uint
    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 來(lái)確保這個(gè)函數(shù)只有在每個(gè)用戶第一次調(diào)用它的時(shí)候執(zhí)行,用以創(chuàng)建初始僵尸
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

// CryptoKitties 合約提供了getKitty 函數(shù),它返回所有的加密貓的數(shù)據(jù),包括它的“基因”(僵尸游戲要用它生成新的僵尸)。
// 一個(gè)獲取 kitty 的接口
contract KittyInterface {
  
  // 在interface里定義了 getKitty 函數(shù) 在 returns 語(yǔ)句之后用分號(hào)
  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
  );
}

//ZombieFeeding繼承自 `ZombieFactory 合約
contract ZombieFeeding is ZombieFactory {
  
  // CryptoKitties 合約的地址
  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  // 創(chuàng)建一個(gè)名為 kittyContract 的 KittyInterface,并用 ckAddress 為它初始化 
  KittyInterface kittyContract = KittyInterface(ckAddress);
  
  function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
      // 確保對(duì)自己僵尸的所有權(quán)
      require(msg.sender == zombieToOwner[_zombieId]);
      // 聲明一個(gè)名為 myZombie 數(shù)據(jù)類型為Zombie的 storage 類型本地變量
      Zombie storage myZombie = zombies[_zombieId];
      _targetDna = _targetDna % dnaModulus;
      uint newDna = (myZombie.dna + _targetDna) / 2;
      // Add an if statement here
      if (keccak256(_species) == keccak256("kitty")){
          newDna = newDna - newDna%100 + 99;
      }
      _createZombie("NoName", newDna);
  }
  
  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    // 多值返回,這里只需要最后一個(gè)值
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }
}
這段代碼看起來(lái)內(nèi)容有點(diǎn)多,可以拆分一下,把 ZombieFactory代碼提取到一個(gè)新的文件zombiefactory.sol,現(xiàn)在就可以使用 import 語(yǔ)句來(lái)導(dǎo)入另一個(gè)文件的代碼。
import

在 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)入。

這一點(diǎn)和 go 類似,在同一目錄下文件中的內(nèi)容可以直接使用,而不用使用 xxx.name 的形式。
測(cè)試調(diào)用

編譯和部署 ZombieFeeding,就可以將這個(gè)合約部署到以太坊了。最終完成的這個(gè)合約繼承自 ZombieFactory,因此它可以訪問(wèn)自己和父輩合約中的所有 public 方法。

下面是一個(gè)與ZombieFeeding合約進(jìn)行交互的例子, 這個(gè)例子使用了 JavaScript 和 web3.js:

var abi = /* abi generated by the compiler */
var ZombieFeedingContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFeeding = ZombieFeedingContract.at(contractAddress)

// 假設(shè)我們有我們的僵尸ID和要攻擊的貓咪ID
let zombieId = 1;
let kittyId = 1;

// 要拿到貓咪的DNA,我們需要調(diào)用它的API。這些數(shù)據(jù)保存在它們的服務(wù)器上而不是區(qū)塊鏈上。
// 如果一切都在區(qū)塊鏈上,我們就不用擔(dān)心它們的服務(wù)器掛了,或者它們修改了API,
// 或者因?yàn)椴幌矚g我們的僵尸游戲而封殺了我們
let apiUrl = "https://api.cryptokitties.co/kitties/" + kittyId
$.get(apiUrl, function(data) {
  let imgUrl = data.image_url
  // 一些顯示圖片的代碼
})

// 當(dāng)用戶點(diǎn)擊一只貓咪的時(shí)候:
$(".kittyImage").click(function(e) {
  // 調(diào)用我們合約的 `feedOnKitty` 函數(shù)
  ZombieFeeding.feedOnKitty(zombieId, kittyId)
})

// 偵聽(tīng)來(lái)自我們合約的新僵尸事件好來(lái)處理
ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  // 這個(gè)函數(shù)用來(lái)顯示僵尸:
  generateZombie(result.zombieId, result.name, result.dna)
})
參考鏈接

Solidity 文檔:https://solidity-cn.readthedocs.io/zh/develop/index.html

cryptozombie-lessons2 僵尸攻擊人類:https://cryptozombies.io/zh/lesson/2

Solidity 簡(jiǎn)易教程


最后,感謝女朋友支持和包容,比??

也可以在公號(hào)輸入以下關(guān)鍵字獲取歷史文章:公號(hào)&小程序 | 設(shè)計(jì)模式 | 并發(fā)&協(xié)程

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

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

相關(guān)文章

  • Solidity 簡(jiǎn)易教程

    摘要:語(yǔ)句以分號(hào)結(jié)尾狀態(tài)變量狀態(tài)變量是被永久地保存在合約中。中,實(shí)際上是代名詞,一個(gè)位的無(wú)符號(hào)整數(shù)。下面的語(yǔ)句被認(rèn)為是修改狀態(tài)修改狀態(tài)變量。事件事件是合約和區(qū)塊鏈通訊的一種機(jī)制。一旦它被發(fā)出,監(jiān)聽(tīng)該事件的都將收到通知。 Solidity是以太坊的主要編程語(yǔ)言,它是一種靜態(tài)類型的 JavaScript-esque 語(yǔ)言,是面向合約的、為實(shí)現(xiàn)智能合約而創(chuàng)建的高級(jí)編程語(yǔ)言,設(shè)計(jì)的目的是能在以太坊虛...

    chenatu 評(píng)論0 收藏0
  • 以太坊開(kāi)發(fā)語(yǔ)言solidity入門(mén)

    摘要:什么是以太坊是一種面向智能合約的高級(jí)語(yǔ)言,其語(yǔ)法與類似。如果希望快速進(jìn)行以太坊開(kāi)發(fā),那請(qǐng)看我們精心打造的教程以太坊入門(mén)教程,主要介紹智能合約與應(yīng)用開(kāi)發(fā),適合入門(mén)。 這篇關(guān)于Solidity教程的博客展示了很多Solidity特性。本教程假定你對(duì)以太坊虛擬機(jī)和編程有一定的了解。 以太坊,世界計(jì)算機(jī)提供了一個(gè)非常強(qiáng)大的全球共享基礎(chǔ)設(shè)施,使用名為Solidity的編程語(yǔ)言構(gòu)建去中心化應(yīng)用程序...

    hqman 評(píng)論0 收藏0
  • 以太坊開(kāi)發(fā)語(yǔ)言solidity入門(mén)

    摘要:什么是以太坊是一種面向智能合約的高級(jí)語(yǔ)言,其語(yǔ)法與類似。如果希望快速進(jìn)行以太坊開(kāi)發(fā),那請(qǐng)看我們精心打造的教程以太坊入門(mén)教程,主要介紹智能合約與應(yīng)用開(kāi)發(fā),適合入門(mén)。 這篇關(guān)于Solidity教程的博客展示了很多Solidity特性。本教程假定你對(duì)以太坊虛擬機(jī)和編程有一定的了解。 以太坊,世界計(jì)算機(jī)提供了一個(gè)非常強(qiáng)大的全球共享基礎(chǔ)設(shè)施,使用名為Solidity的編程語(yǔ)言構(gòu)建去中心化應(yīng)用程序...

    tigerZH 評(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
  • 智能合約語(yǔ)言 Solidity 教程系列6 - 結(jié)構(gòu)體與映射

    摘要:本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接智能合約語(yǔ)言教程系列結(jié)構(gòu)體與映射原文已更新,請(qǐng)讀者前往原文閱讀教程系列第篇結(jié)構(gòu)體與映射。不能聲明一個(gè)同時(shí)將自身作為成員,這個(gè)限制是基于結(jié)構(gòu)體的大小必須是有限的。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:智能合約語(yǔ)言Solidity教程系列6 - 結(jié)構(gòu)體與映射原文已更新,請(qǐng)讀者前往原文閱讀 Solidity 教程系列第6篇 - Solidity 結(jié)構(gòu)體與...

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

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

0條評(píng)論

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