亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

以太坊開發(fā)實(shí)戰(zhàn)學(xué)習(xí)-高級(jí)Solidity理論(四)

feng409 / 1694人閱讀

摘要:第一個(gè)例子,在你把智能協(xié)議傳上以太坊之后,它就變得不可更改這種永固性意味著你的代碼永遠(yuǎn)不能被調(diào)整或更新。允許將合約所有權(quán)轉(zhuǎn)讓給他人。為何要來驅(qū)動(dòng)以太坊就像一個(gè)巨大緩慢但非常安全的電腦。

通過前邊的 Solidity 基礎(chǔ)語法學(xué)習(xí),我們已經(jīng)有了Solidity編程經(jīng)驗(yàn),在這節(jié)就要學(xué)學(xué) Ethereum 開發(fā)的技術(shù)細(xì)節(jié),編寫真正的 DApp 時(shí)必知的:智能協(xié)議的所有權(quán)Gas的花費(fèi),代碼優(yōu)化,和代碼安全。
一、智能協(xié)議的永固性

到現(xiàn)在為止,我們講的 Solidity 和其他語言沒有質(zhì)的區(qū)別,它長(zhǎng)得也很像 JavaScript.

但是,在有幾點(diǎn)以太坊上的 DApp 跟普通的應(yīng)用程序有著天壤之別。

第一個(gè)例子,在你把智能協(xié)議傳上以太坊之后,它就變得不可更改, 這種永固性意味著你的代碼永遠(yuǎn)不能被調(diào)整或更新。

你編譯的程序會(huì)一直,永久的,不可更改的,存在以太網(wǎng)上。這就是Solidity代碼的安全性如此重要的一個(gè)原因。如果你的智能協(xié)議有任何漏洞,即使你發(fā)現(xiàn)了也無法補(bǔ)救。你只能讓你的用戶們放棄這個(gè)智能協(xié)議,然后轉(zhuǎn)移到一個(gè)新的修復(fù)后的合約上。

但這恰好也是智能合約的一大優(yōu)勢(shì)。 代碼說明一切。 如果你去讀智能合約的代碼,并驗(yàn)證它,你會(huì)發(fā)現(xiàn), 一旦函數(shù)被定義下來,每一次的運(yùn)行,程序都會(huì)嚴(yán)格遵照函數(shù)中原有的代碼邏輯一絲不茍地執(zhí)行,完全不用擔(dān)心函數(shù)被人篡改而得到意外的結(jié)果。

外部依賴關(guān)系

在上邊的文章中,我們將加密小貓(CryptoKitties)合約的地址硬編碼到DApp中去了。有沒有想過,如果加密小貓出了點(diǎn)問題,比方說,集體消失了會(huì)怎么樣? 雖然這種事情幾乎不可能發(fā)生,但是,如果小貓沒了,我們的 DApp 也會(huì)隨之失效 -- 因?yàn)槲覀冊(cè)?DApp 的代碼中用“硬編碼”的方式指定了加密小貓的地址,如果這個(gè)根據(jù)地址找不到小貓,我們的僵尸也就吃不到小貓了,而按照前面的描述,我們卻沒法修改合約去應(yīng)付這個(gè)變化!

因此,我們不能硬編碼,而要采用“函數(shù)”,以便于 DApp 的關(guān)鍵部分可以以參數(shù)形式修改。

比方說,我們不再一開始就把獵物地址給寫入代碼,而是寫個(gè)函數(shù) setKittyContractAddress, 運(yùn)行時(shí)再設(shè)定獵物的地址,這樣我們就可以隨時(shí)去鎖定新的獵物,也不用擔(dān)心加密小貓集體消失了。

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

請(qǐng)修改前邊的代碼,使得可以通過程序更改CryptoKitties合約地址。

1、刪除采用硬編碼 方式的 ckAddress 代碼行。

2、之前創(chuàng)建 kittyContract 變量的那行代碼,修改為對(duì) kittyContract 變量的聲明 -- 暫時(shí)不給它指定具體的實(shí)例。

3、創(chuàng)建名為 setKittyContractAddress 的函數(shù), 它帶一個(gè)參數(shù) _address(address類型), 可見性設(shè)為external。

4、在函數(shù)內(nèi)部,添加一行代碼,將 kittyContract 變量設(shè)置為返回值:KittyInterface(_address)。

注意:你可能會(huì)注意到這個(gè)功能有個(gè)安全漏洞,別擔(dān)心 - 咱們到下一章里解決它;)

zombiefeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

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 {

  // 1. 移除這一行:
  // address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;

  // 2. 只聲明變量:
  // KittyInterface kittyContract = KittyInterface(ckAddress);
  KittyInterface kittyContract;

  // 3. 增加 setKittyContractAddress 方法
  function setKittyContractAddress(address _address) external {
    kittyContract = KittyInterface(_address);

  }

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

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}
二、Ownable Contracts

上面代碼中,您有沒有發(fā)現(xiàn)任何安全漏洞呢?

呀!setKittyContractAddress 可見性居然申明為“外部的”(external),豈不是任何人都可以調(diào)用它! 也就是說,任何調(diào)用該函數(shù)的人都可以更改 CryptoKitties 合約的地址,使得其他人都沒法再運(yùn)行我們的程序了。

我們確實(shí)是希望這個(gè)地址能夠在合約中修改,但我可沒說讓每個(gè)人去改它呀。

要對(duì)付這樣的情況,通常的做法是指定合約的“所有權(quán)” - 就是說,給它指定一個(gè)主人(沒錯(cuò),就是您),只有主人對(duì)它享有特權(quán)。
Ownable

下面是一個(gè) Ownable 合約的例子: 來自 OpenZeppelin Solidity 庫的 Ownable 合約。 OpenZeppelin 是主打安保和社區(qū)審查的智能合約庫,您可以在自己的 DApps中引用。等把這一課學(xué)完,您不要催我們發(fā)布下一課,最好利用這個(gè)時(shí)間把 OpenZeppelin 的網(wǎng)站看看,保管您會(huì)學(xué)到很多東西!

把樓下這個(gè)合約讀讀通,是不是還有些沒見過代碼?別擔(dān)心,我們隨后會(huì)解釋。

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }
}

下面有沒有您沒學(xué)過的東東?

構(gòu)造函數(shù):function Ownable()是一個(gè) constructor (構(gòu)造函數(shù)),構(gòu)造函數(shù)不是必須的,它與合約同名,構(gòu)造函數(shù)一生中唯一的一次執(zhí)行,就是在合約最初被創(chuàng)建的時(shí)候。

函數(shù)修飾符:modifier onlyOwner()。 修飾符跟函數(shù)很類似,不過是用來修飾其他已有函數(shù)用的, 在其他語句執(zhí)行前,為它檢查下先驗(yàn)條件。 在這個(gè)例子中,我們就可以寫個(gè)修飾符 onlyOwner 檢查下調(diào)用者,確保只有合約的主人才能運(yùn)行本函數(shù)。我們下一章中會(huì)詳細(xì)講述修飾符,以及那個(gè)奇怪的_;。

indexed 關(guān)鍵字:別擔(dān)心,我們還用不到它。

所以 Ownable 合約基本都會(huì)這么干:

1、合約創(chuàng)建,構(gòu)造函數(shù)先行,將其 owner 設(shè)置為msg.sender(其部署者)

2、為它加上一個(gè)修飾符 onlyOwner,它會(huì)限制陌生人的訪問,將訪問某些函數(shù)的權(quán)限鎖定在 owner 上。

3、允許將合約所有權(quán)轉(zhuǎn)讓給他人。

onlyOwner 簡(jiǎn)直人見人愛,大多數(shù)人開發(fā)自己的 Solidity DApps,都是從復(fù)制/粘貼 Ownable 開始的,從它再繼承出的子類,并在之上進(jìn)行功能開發(fā)。

既然我們想把 setKittyContractAddress 限制為 onlyOwner ,我們也要做同樣的事情。

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

首先,將 Ownable 合約的代碼復(fù)制一份到新文件 ownable.sol 中。 接下來,創(chuàng)建一個(gè) ZombieFactory,繼承 Ownable。

1.在程序中導(dǎo)入 ownable.sol 的內(nèi)容。 如果您不記得怎么做了,參考下 zombiefeeding.sol

2.修改 ZombieFactory 合約, 讓它繼承自 Ownable。 如果您不記得怎么做了,看看 zombiefeeding.sol。

ownable.sol 文件:

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }


  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }


  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

}

zombiefactory.sol

pragma solidity ^0.4.19;

// 1. 在這里導(dǎo)入
import "./ownable.sol";

// 2. 在這里繼承:
contract ZombieFactory is Ownable{

    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) 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);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}
三、onlyOwner函數(shù)修飾符

現(xiàn)在我們有了個(gè)基本版的合約 ZombieFactory 了,它繼承自 Ownable 接口,我們也可以給 ZombieFeeding 加上 onlyOwner 函數(shù)修飾符。

這就是合約繼承的工作原理。記得:

ZombieFeeding 是個(gè) ZombieFactory
ZombieFactory 是個(gè) Ownable
函數(shù)修飾符modifier

函數(shù)修飾符看起來跟函數(shù)沒什么不同,不過關(guān)鍵字modifier 告訴編譯器,這是個(gè)modifier(修飾符),而不是個(gè)function(函數(shù))。它不能像函數(shù)那樣被直接調(diào)用,只能被添加到函數(shù)定義的末尾,用以改變函數(shù)的行為。

再仔細(xì)讀讀 onlyOwner:

/**
 * @dev 調(diào)用者不是‘主人’,就會(huì)拋出異常
 */
modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

onlyOwner 函數(shù)修飾符是這么用的:

contract MyContract is Ownable {
  event LaughManiacally(string laughter);

  //注意! `onlyOwner`上場(chǎng) :
  function likeABoss() external onlyOwner {
    LaughManiacally("Muahahahaha");
  }
}

注意 likeABoss 函數(shù)上的 onlyOwner 修飾符。 當(dāng)你調(diào)用 likeABoss 時(shí),首先執(zhí)行 onlyOwner 中的代碼, 執(zhí)行到 onlyOwner 中的_; 語句時(shí),程序再返回并執(zhí)行 likeABoss 中的代碼。

可見,盡管函數(shù)修飾符也可以應(yīng)用到各種場(chǎng)合,但最常見的還是放在函數(shù)執(zhí)行之前添加快速的 require 檢查。

因?yàn)榻o函數(shù)添加了修飾符 onlyOwner,使得唯有合約的主人(也就是部署者)才能調(diào)用它。

注意:主人對(duì)合約享有的特權(quán)當(dāng)然是正當(dāng)?shù)?,不過也可能被惡意使用。比如,萬一,主人添加了個(gè)后門,允許他偷走別人的僵尸呢?

所以非常重要的是,部署在以太坊上的 DApp,并不能保證它真正做到去中心,你需要閱讀并理解它的源代碼,才能防止其中沒有被部署者惡意植入后門;作為開發(fā)人員,如何做到既要給自己留下修復(fù) bug 的余地,又要盡量地放權(quán)給使用者,以便讓他們放心你,從而愿意把數(shù)據(jù)放在你的 DApp 中,這確實(shí)需要個(gè)微妙的平衡。

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

現(xiàn)在我們可以限制第三方對(duì) setKittyContractAddress 的訪問,除了我們自己,誰都無法去修改它。

1、將 onlyOwner 函數(shù)修飾符添加到 setKittyContractAddress
中。

zombiefeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

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 {

  KittyInterface kittyContract;

  // 修改這個(gè)函數(shù),添加權(quán)限onlyOwner
  function setKittyContractAddress(address _address) external onlyOwner {
    kittyContract = KittyInterface(_address);
  }

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

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}
四、Gas

現(xiàn)在我們懂了如何在禁止第三方修改我們的合約的同時(shí),留個(gè)后門給咱們自己去修改。

讓我們來看另一種使得 Solidity 編程語言與眾不同的特征:

Gas-驅(qū)動(dòng)以太坊DApps的能源

在 Solidity 中,你的用戶想要每次執(zhí)行你的 DApp 都需要支付一定的 gas,gas 可以用以太幣購買,因此,用戶每次跑 DApp 都得花費(fèi)以太幣

一個(gè) DApp 收取多少 gas 取決于功能邏輯的復(fù)雜程度。每個(gè)操作背后,都在計(jì)算完成這個(gè)操作所需要的計(jì)算資源,(比如,存儲(chǔ)數(shù)據(jù)就比做個(gè)加法運(yùn)算貴得多), 一次操作所需要花費(fèi)的 gas 等于這個(gè)操作背后的所有運(yùn)算花銷的總和。

由于運(yùn)行你的程序需要花費(fèi)用戶的真金白銀,在以太坊中代碼的編程語言,比其他任何編程語言都更強(qiáng)調(diào)優(yōu)化。同樣的功能,使用笨拙的代碼開發(fā)的程序,比起經(jīng)過精巧優(yōu)化的代碼來,運(yùn)行花費(fèi)更高,這顯然會(huì)給成千上萬的用戶帶來大量不必要的開銷。

為何要gas來驅(qū)動(dòng)?

以太坊就像一個(gè)巨大、緩慢、但非常安全的電腦。當(dāng)你運(yùn)行一個(gè)程序的時(shí)候,網(wǎng)絡(luò)上的每一個(gè)節(jié)點(diǎn)都在進(jìn)行相同的運(yùn)算,以驗(yàn)證它的輸出 —— 這就是所謂的”去中心化“ 由于數(shù)以千計(jì)的節(jié)點(diǎn)同時(shí)在驗(yàn)證著每個(gè)功能的運(yùn)行,這可以確保它的數(shù)據(jù)不會(huì)被被監(jiān)控,或者被刻意修改。

可能會(huì)有用戶用無限循環(huán)堵塞網(wǎng)絡(luò),抑或用密集運(yùn)算來占用大量的網(wǎng)絡(luò)資源,為了防止這種事情的發(fā)生,以太坊的創(chuàng)建者為以太坊上的資源制定了價(jià)格,想要在以太坊上運(yùn)算或者存儲(chǔ),你需要先付費(fèi)

注意:如果你使用側(cè)鏈,倒是不一定需要付費(fèi),比如咱們?cè)?Loom Network 上構(gòu)建的 CryptoZombies 就免費(fèi)。你不會(huì)想要在以太坊主網(wǎng)上玩兒“魔獸世界”吧? - 所需要的 gas 可能會(huì)買到你破產(chǎn)。但是你可以找個(gè)算法理念不同的側(cè)鏈來玩它。我們將在以后的課程中咱們會(huì)討論到,什么樣的 DApp 應(yīng)該部署在太坊主鏈上,什么又最好放在側(cè)鏈。
省gas的招數(shù) 省 gas 的招數(shù):結(jié)構(gòu)封裝(Struct packing)

在第1課中,我們提到除了基本版的 uint 外,還有其他變種 uintuint8,uint16,uint32等。

通常情況下我們不會(huì)考慮使用 uint 變種,因?yàn)闊o論如何定義 uint的大小,Solidity 為它保留256位的存儲(chǔ)空間。例如,使用 uint8 而不是uint(uint256)不會(huì)為你節(jié)省任何 gas。

除非,把 uint 綁定到 struct 里面。

如果一個(gè) struct 中有多個(gè) uint,則盡可能使用較小的 uint, Solidity 會(huì)將這些 uint 打包在一起,從而占用較少的存儲(chǔ)空間。例如:

struct NormalStruct {
  uint a;
  uint b;
  uint c;
}

struct MiniMe {
  uint32 a;
  uint32 b;
  uint c;
}

// 因?yàn)槭褂昧私Y(jié)構(gòu)打包,`mini` 比 `normal` 占用的空間更少
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30);

所以,當(dāng) uint 定義在一個(gè) struct 中的時(shí)候,盡量使用最小的整數(shù)子類型以節(jié)約空間。 并且把同樣類型的變量放一起(即在 struct 中將把變量按照類型依次放置),這樣 Solidity 可以將存儲(chǔ)空間最小化。例如,有兩個(gè) struct

uint c; uint32 a; uint32 b;uint32 a; uint c; uint32 b;

前者比后者需要的gas更少,因?yàn)榍罢甙?b>uint32放一起了。

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

咱們給僵尸添2個(gè)新功能:le??velreadyTime - 后者是用來實(shí)現(xiàn)一個(gè)“冷卻定時(shí)器”,以限制僵尸獵食的頻率。

讓我們回到 zombiefactory.sol。

1、為 Zombie 結(jié)構(gòu)體 添加兩個(gè)屬性:leveluint32)和readyTimeuint32)。因?yàn)橄M愋蛿?shù)據(jù)打成一個(gè)包,所以把它們放在結(jié)構(gòu)體的末尾。

32位足以保存僵尸的級(jí)別和時(shí)間戳了,這樣比起使用普通的uint(256位),可以更緊密地封裝數(shù)據(jù),從而為我們省點(diǎn) gas。
zombiefactory.sol

pragma solidity ^0.4.19;

import "./ownable.sol";

contract ZombieFactory is Ownable {

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

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

    struct Zombie {
        string name;
        uint dna;
        // 在這里添加數(shù)據(jù)
        uint32 level;
        uint32 readyTime;
    }

    Zombie[] public zombies;

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

    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);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}
五、時(shí)間單位

level 屬性表示僵尸的級(jí)別。以后,在我們創(chuàng)建的戰(zhàn)斗系統(tǒng)中,打勝仗的僵尸會(huì)逐漸升級(jí)并獲得更多的能力。

readyTime 稍微復(fù)雜點(diǎn)。我們希望增加一個(gè)“冷卻周期”,表示僵尸在兩次獵食或攻擊之之間必須等待的時(shí)間。如果沒有它,僵尸每天可能會(huì)攻擊和繁殖1,000次,這樣游戲就太簡(jiǎn)單了。

為了記錄僵尸在下一次進(jìn)擊前需要等待的時(shí)間,我們使用了 Solidity 的時(shí)間單位。

時(shí)間單位

Solidity 使用自己的本地時(shí)間單位。

變量 now 將返回當(dāng)前的unix時(shí)間戳(自1970年1月1日以來經(jīng)過的秒數(shù))。我寫這句話時(shí) unix 時(shí)間是 1515527488。

注意:Unix時(shí)間傳統(tǒng)用一個(gè)32位的整數(shù)進(jìn)行存儲(chǔ)。這會(huì)導(dǎo)致“2038年”問題,當(dāng)這個(gè)32位的unix時(shí)間戳不夠用,產(chǎn)生溢出,使用這個(gè)時(shí)間的遺留系統(tǒng)就麻煩了。所以,如果我們想讓我們的 DApp 跑夠20年,我們可以使用64位整數(shù)表示時(shí)間,但為此我們的用戶又得支付更多的 gas。真是個(gè)兩難的設(shè)計(jì)啊!

Solidity 還包含秒(seconds),分鐘(minutes),小時(shí)(hours),天(days),周(weeks)年(years) 等時(shí)間單位。它們都會(huì)轉(zhuǎn)換成對(duì)應(yīng)的秒數(shù)放入 uint 中。所以 1分鐘 就是 60,1小時(shí)是 3600(60秒×60分鐘),1天是86400(24小時(shí)×60分鐘×60秒),以此類推。

下面是一些使用時(shí)間單位的實(shí)用案例:

uint lastUpdated;

// 將‘上次更新時(shí)間’ 設(shè)置為 ‘現(xiàn)在’
function updateTimestamp() public {
  lastUpdated = now;
}

// 如果到上次`updateTimestamp` 超過5分鐘,返回 "true"
// 不到5分鐘返回 "false"
function fiveMinutesHavePassed() public view returns (bool) {
  return (now >= (lastUpdated + 5 minutes));
}

有了這些工具,我們可以為僵尸設(shè)定”冷靜時(shí)間“功能

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

現(xiàn)在咱們給DApp添加一個(gè)“冷卻周期”的設(shè)定,讓僵尸兩次攻擊或捕獵之間必須等待 1天。

1、聲明一個(gè)名為 cooldownTimeuint,并將其設(shè)置為 1 days。(沒錯(cuò),”1 days“使用了復(fù)數(shù), 否則通不過編譯器)

2、因?yàn)樵谏弦徽轮形覀兘o Zombie 結(jié)構(gòu)體中添加 levelreadyTime 兩個(gè)參數(shù),所以現(xiàn)在創(chuàng)建一個(gè)新的 Zombie 結(jié)構(gòu)體時(shí),需要修改 _createZombie(),在其中把新舊參數(shù)都初始化一下。

3、修改 zombies.push 那一行, 添加加2個(gè)參數(shù):1(表示當(dāng)前的 level )和uint32(now + cooldownTime 現(xiàn)在+冷靜時(shí)間)(表示下次允許攻擊的時(shí)間 readyTime)。

注意:必須使用 uint32(...) 進(jìn)行強(qiáng)制類型轉(zhuǎn)換,因?yàn)?now 返回類型 uint256。所以我們需要明確將它轉(zhuǎn)換成一個(gè) uint32 類型的變量。

now + cooldownTime 將等于當(dāng)前的unix時(shí)間戳(以秒為單位)加上”1天“里的秒數(shù) - 這將等于從現(xiàn)在起1天后的unix時(shí)間戳。然后我們就比較,看看這個(gè)僵尸的 readyTime是否大于 now,以決定再次啟用僵尸的時(shí)機(jī)有沒有到來。

下一節(jié)中,我們將討論如何通過 readyTime 來規(guī)范僵尸的行為。
zombiefactory.sol

pragma solidity ^0.4.19;

import "./ownable.sol";

contract ZombieFactory is Ownable {

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

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;
    // 1. 在這里定義 `cooldownTime`
    uint cooldownTime = 1 days;

    struct Zombie {
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
    }

    Zombie[] public zombies;

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

    function _createZombie(string _name, uint _dna) internal {
        // 2. 修改下面這行:
        uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime))) - 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);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}
六、時(shí)間周期定時(shí)器

現(xiàn)在,Zombie 結(jié)構(gòu)體中定義好了一個(gè) readyTime 屬性,讓我們跳到 zombiefeeding.sol, 去實(shí)現(xiàn)一個(gè)”冷卻周期定時(shí)器“。

按照以下步驟修改 feedAndMultiply

1、”捕獵“行為會(huì)觸發(fā)僵尸的”冷卻周期“

2、僵尸在這段”冷卻周期“結(jié)束前不可再捕獵小貓

這將限制僵尸,防止其無限制地捕獵小貓或者整天不停地繁殖。將來,當(dāng)我們?cè)黾討?zhàn)斗功能時(shí),我們同樣用”冷卻周期“限制僵尸之間打斗的頻率。

首先,我們要定義一些輔助函數(shù),設(shè)置并檢查僵尸的 readyTime。

將結(jié)構(gòu)體作為參數(shù)傳入
由于結(jié)構(gòu)體的存儲(chǔ)指針可以以參數(shù)的方式傳遞給一個(gè) private 或 internal 的函數(shù),因此結(jié)構(gòu)體可以在多個(gè)函數(shù)之間相互傳遞。

遵循這樣的語法:

function _doStuff(Zombie storage _zombie) internal {
  // do stuff with _zombie
}

這樣我們可以將某僵尸的引用直接傳遞給一個(gè)函數(shù),而不用是通過參數(shù)傳入僵尸ID后,函數(shù)再依據(jù)ID去查找。

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

1、先定義一個(gè) _triggerCooldown 函數(shù)。它要求一個(gè)參數(shù),_zombie,表示一某個(gè)僵尸的存儲(chǔ)指針。這個(gè)函數(shù)可見性設(shè)置為 internal。

2、在函數(shù)中,把 _zombie.readyTime 設(shè)置為 uint32(now + cooldownTime)。

3、接下來,創(chuàng)建一個(gè)名為 _isReady 的函數(shù)。這個(gè)函數(shù)的參數(shù)也是名為 _zombie 的 Zombie storage。這個(gè)功能只具有 internal 可見性,并返回一個(gè) bool 值。

4、函數(shù)計(jì)算返回(_zombie.readyTime <= now),值為 truefalse。這個(gè)功能的目的是判斷下次允許獵食的時(shí)間是否已經(jīng)到了。

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

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 {

  KittyInterface kittyContract;

  function setKittyContractAddress(address _address) external onlyOwner {
    kittyContract = KittyInterface(_address);
  }

  // 1. 在這里定義 `_triggerCooldown` 函數(shù)
  function _triggerCooldown(Zombie storage _zombie) internal {
      _zombie.readyTime = uint32(now + cooldownTime);
  }

  // 2. 在這里定義 `_isReady` 函數(shù)
  function _isReady(Zombie storage _zombie) internal view returns (bool) {
     return (_zombie.readyTime <= now);
  }

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

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}
七、公有函數(shù)和安全性

現(xiàn)在來修改 feedAndMultiply ,實(shí)現(xiàn)冷卻周期。

回顧一下這個(gè)函數(shù),前一課上我們將其可見性設(shè)置為public。你必須仔細(xì)地檢查所有聲明為 publicexternal的函數(shù),一個(gè)個(gè)排除用戶濫用它們的可能,謹(jǐn)防安全漏洞。請(qǐng)記住,如果這些函數(shù)沒有類似 onlyOwner 這樣的函數(shù)修飾符,用戶能利用各種可能的參數(shù)去調(diào)用它們。

檢查完這個(gè)函數(shù),用戶就可以直接調(diào)用這個(gè)它,并傳入他們所希望的 _targetDnaspecies 。打個(gè)游戲還得遵循這么多的規(guī)則,還能不能愉快地玩耍啊!

仔細(xì)觀察,這個(gè)函數(shù)只需被 feedOnKitty() 調(diào)用,因此,想要防止漏洞,最簡(jiǎn)單的方法就是設(shè)其可見性為 internal

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

1、目前函數(shù) feedAndMultiply 可見性為 public。我們將其改為 internal 以保障合約安全。因?yàn)槲覀儾幌M脩粽{(diào)用它的時(shí)候塞進(jìn)一堆亂七八糟的 DNA。

2、feedAndMultiply 過程需要參考 cooldownTime。首先,在找到 myZombie 之后,添加一個(gè) require 語句來檢查 _isReady() 并將 myZombie 傳遞給它。這樣用戶必須等到僵尸的 冷卻周期 結(jié)束后才能執(zhí)行 feedAndMultiply 功能。

3、在函數(shù)結(jié)束時(shí),調(diào)用 _triggerCooldown(myZombie),標(biāo)明捕獵行為觸發(fā)了僵尸新的冷卻周期。

zombiefeeding.sol

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

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 {

  KittyInterface kittyContract;

  function setKittyContractAddress(address _address) external onlyOwner {
    kittyContract = KittyInterface(_address);
  }

  function _triggerCooldown(Zombie storage _zombie) internal {
    _zombie.readyTime = uint32(now + cooldownTime);
  }

  function _isReady(Zombie storage _zombie) internal view returns (bool) {
      return (_zombie.readyTime <= now);
  }

  // 1. 使這個(gè)函數(shù)的可見性為 internal
  function feedAndMultiply(uint _zombieId, uint _targetDna, string species) internal {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    // 2. 在這里為 `_isReady` 增加一個(gè)檢查
    require(_isReady(myZombie));

    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(species) == keccak256("kitty")) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
    // 3. 調(diào)用 `triggerCooldown`
    _triggerCooldown(myZombie);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

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

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

相關(guān)文章

  • 以太開發(fā)實(shí)戰(zhàn)學(xué)習(xí)-高級(jí)Solidity理論 (五)

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

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

    摘要:接上篇文章,這里繼續(xù)學(xué)習(xí)高級(jí)理論。將這個(gè)函數(shù)的定義修改為其使用修飾符。我們將用一個(gè)到的隨機(jī)數(shù)來確定我們的戰(zhàn)斗結(jié)果。在這個(gè)教程中,簡(jiǎn)單起見我們將這個(gè)狀態(tài)保存在結(jié)構(gòu)體中,將其命名為和。在第六章我們計(jì)算出來一個(gè)到的隨機(jī)數(shù)。 接上篇文章,這里繼續(xù)學(xué)習(xí)Solidity高級(jí)理論。 一、重構(gòu)通用邏輯 不管誰調(diào)用我們的 attack 函數(shù) —— 我們想確保用戶的確擁有他們用來攻擊的僵尸。如果你能用其他...

    qc1iu 評(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)用開發(fā),可進(jìn)一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學(xué)習(xí)指引 原文已更新,請(qǐng)讀者前往原文閱讀 本章的文章越來越多,本文是一個(gè)索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...

    Cristic 評(píng)論0 收藏0
  • 以太開發(fā)實(shí)戰(zhàn)學(xué)習(xí)-solidity語法 (三)

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

    tianren124 評(píng)論0 收藏0
  • 2018以太智能合約編程語言solidity的最佳IDEs

    摘要:使用基于以太坊的智能合約的集成開發(fā)環(huán)境。以太坊教程,主要介紹智能合約與應(yīng)用開發(fā),適合入門。以太坊,主要是介紹使用進(jìn)行智能合約開發(fā)交互,進(jìn)行賬號(hào)創(chuàng)建交易轉(zhuǎn)賬代幣開發(fā)以及過濾器和事件等內(nèi)容。 Solidity是一種以智能合約為導(dǎo)向的編程語言。這是一種只有四年的年輕語言,旨在幫助開發(fā)基于以太坊數(shù)字貨幣的智能合約。 理解它官方文檔應(yīng)該是學(xué)習(xí)Solidity的最佳來源:solidity.read...

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

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

0條評(píng)論

feng409

|高級(jí)講師

TA的文章

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