以太坊開發(fā)實戰(zhàn)學習-Solidity初學(一)

一呻此、合約開發(fā)流程

語言:使用 node.js 開發(fā)該項目
大概流程:

合約代碼編寫(Solidity)-> 合約編譯(solc)-> 合約部署(web3)

開發(fā)語言及工具:

  • 區(qū)塊鏈節(jié)點:ganache-cli
  • 基礎環(huán)境:node
  • 合約開發(fā)語言:Solidity
  • 合約編譯器:solc
  • 合約訪問庫:web3.js

二湾戳、基礎環(huán)境安裝

  • 1、安裝 node.js
  • 2餐禁、安裝 ganache-cli
sudo npm install -g ganache-cli

運行:

ganache-cli

輸出:

?  ~ ganache-cli
Ganache CLI v6.1.0 (ganache-core: 2.1.0)

Available Accounts
==================
(0) 0x2c0e940b0732a3b49cb7dccc26e14cb4801dd1c3
(1) 0x65afabcf1fdb19ef88f8433af90136de56e7e412
(2) 0x65111c1fa94e15e8e3bdedb466004f67d6b46bab
(3) 0xfa44030a4216193d19a811267528e86cf1851e48
(4) 0xc29473dca76a2ebbb8b1badf6a8093c11b56ea84
(5) 0x06e55addeef67a46015e2790be1ada1deb3c9c70
(6) 0xc1ec7f3d08692d0bdd70d6ab3d5701f22f53a521
(7) 0x42e52cbb5e226ef8c2c9bf54737b87ccf94ebb08
(8) 0x8cebfdb948266022d2499118b0989b290d146d4c
(9) 0x17b791127c57dff3eb31cc203e404536ef7e0ba7

Private Keys
==================
(0) 3bb508f1c2c35083f7d69466830067c6582e4464ba61daffc947bb1aa98618e9
(1) fc06e722c10cd80b1b5b43355f81363dcbe6dcc8d3c59387f69c68ce99f36c53
(2) 07f37ed746ba88da289eaa780d6155d9fee456106d85169ad92a526c22192695
(3) 2619b581c083d20ff84db2688f4a9d836206ee37e993bc8cb1e089ad68c8673f
(4) c3f61de226b5d5c06cb941f93a2a3ec321dabc53a8fb68bee64d3aed5bc130e6
(5) f86e7b7e7a9cf7532004694cb22997ac521567b7c8e480dbee23e426ed787234
(6) 2035f13d8d64109f21e4eb32970e5934cddcd27bc55439634f49d4479c7abe77
(7) 3395049c4f8749b17e154c47199fa42ce538ed051b6240afc55f49d30406a4f0
(8) 976f56be1b1cd9f5c420a3fdb71eb3a8c3875a7bd3fba20c342389ba97b0a165
(9) a2a7a190ee76cdb0675b8af773fba55187ff4a0fc6c1e1021e717d19e0d591ee

HD Wallet
==================
Mnemonic:      result casino this poverty sleep joy toy sort onion spider bind evolve
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

ganache 默認會自動創(chuàng)建 10 個賬戶传睹,每個賬戶有 100 個以太幣(ETH:Ether)乾戏。 可以把賬戶視為銀行賬戶,以太幣就是以太坊生態(tài)系統(tǒng)中的貨幣斑粱。

輸出的最后一句話畸悬,描述了節(jié)點仿真器的監(jiān)聽地址和端口為localhost:8545,在使用 web3.js 時,需要傳入這個地址來告訴web3js庫應當連接到哪一個節(jié)點蹋宦。

三披粟、合約設計

我們使用 Solidity 語言來編寫合約。如果你熟悉面向?qū)ο蟮拈_發(fā)和JavaScript冷冗,那么學習Solidity 應該非常簡單守屉。可以將合約類比于OOP的類:合約中的屬性用來聲明合約的狀態(tài)蒿辙,而合約中的方法則提供修改狀態(tài)的訪問接口拇泛。

重點:

  • 合約狀態(tài)是持久化到區(qū)塊鏈上的,因此對合約狀態(tài)的修改需要消耗以太幣思灌。
  • 只有在合約部署到區(qū)塊鏈的時候俺叭,才會調(diào)用構(gòu)造函數(shù),并且只調(diào)用一次泰偿。
  • 與 web 世界里每次部署代碼都會覆蓋舊代碼不同熄守,在區(qū)塊鏈上部署的合約是不可改變的,也就是說耗跛,如果你更新合約并再次部署裕照,舊的合約仍然會在區(qū)塊鏈上存在,并且合約的狀態(tài)數(shù)據(jù)也依然存在调塌。新的部署將會創(chuàng)建合約的一 個新的實例晋南。

四、合約語法

從最基本的開始入手:

Solidity 的代碼都包裹在合約里面. 一份合約就是以太幣應用的基本模塊羔砾, 所有的變量和函數(shù)都屬于一份合約, 它是你所有應用的起點.

一份名為 HelloWorld 的空合約如下:

contract HelloWorld {

}

1负间、版本指令

所有的 Solidity 源碼都必須冠以 "version pragma" — 標明 Solidity 編譯器的版本. 以避免將來新的編譯器可能破壞你的代碼。

例如: pragma solidity ^0.4.19; (當前 Solidity 的最新版本是 0.4.19).

綜上所述姜凄, 下面就是一個最基本的合約 — 每次建立一個新的項目時的第一段代碼:

contract.sol 合約文件

// 1\. 這里寫版本指令
pragma solidity ^0.4.19;

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

}

實戰(zhàn)演習1:

為了建立我們的僵尸部隊政溃, 讓我們先建立一個基礎合約,稱為 ZombieFactory檀葛。

  • 建立一個版本為 0.4.19,我們的合約基于這個版本的編譯器腹缩。
  • 建立一個空合約 ZombieFactory屿聋。

Contract.sol

pragma solidity ^0.4.19; // 1\. 這里寫版本指令

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

}

2、狀態(tài)變量和整數(shù)

狀態(tài)變量是被永久地保存在合約中藏鹊。也就是說它們被寫入以太幣區(qū)塊鏈中. 想象成寫入一個數(shù)據(jù)庫润讥。

示例:

contract Example {
  // 這個無符號整數(shù)將會永久的被保存在區(qū)塊鏈中
  uint myUnsignedInteger = 100;
}

在上面的例子中,定義 myUnsignedInteger 為 uint 類型盘寡,并賦值100楚殿。

無符號整數(shù): uint

uint 無符號數(shù)據(jù)類型, 指其值不能是負數(shù)竿痰,對于有符號的整數(shù)存在名為 int 的數(shù)據(jù)類型脆粥。

注: Solidity中砌溺, uint 實際上是 uint256 代名詞, 一個256位的無符號整數(shù)变隔。你也可以定義位數(shù)少的uints — uint8规伐, uint16uint32匣缘, 等…… 但一般來講你愿意使用簡單的 uint猖闪, 除非在某些特殊情況下,這我們后面會講肌厨。

實戰(zhàn)演練2

我們的僵尸DNA將由一個十六位數(shù)字組成培慌。

  • 定義 dnaDigitsuint 數(shù)據(jù)類型, 并賦值 16

Contract.sol

pragma solidity ^0.4.19; // 1\. 這里寫版本指令

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

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

3柑爸、數(shù)學運算

在 Solidity 中吵护,數(shù)學運算很直觀明了,與其它程序設計語言相同:

  • 加法: x + y
  • 減法: x - y,
  • 乘法: x * y
  • 除法: x / y
  • 取模 / 求余: x % y (例如, 13 % 5 余 3, 因為13除以5竖配,余3)

Solidity 還支持 乘方操作 (如:x 的 y次方) // 例如: 5 ** 2 = 25

uint x = 5 ** 2; // equal to 5^2 = 25

實戰(zhàn)演練3

為了保證我們的僵尸的DNA只含有16個字符何址,我們先造一個uint數(shù)據(jù),讓它等于10^16进胯。這樣一來以后我們可以用模運算符 % 把一個整數(shù)變成16位用爪。

  • 建立一個uint類型的變量,名字叫dnaModulus, 令其等于 10dnaDigits 次方胁镐。

Contract.sol

pragma solidity ^0.4.19; // 1\. 這里寫版本指令

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

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

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

4偎血、結(jié)構(gòu)體

有時你需要更復雜的數(shù)據(jù)類型,Solidity 提供了 結(jié)構(gòu)體:

struct Person {
  uint age;
  string name;
}

結(jié)構(gòu)體允許你生成一個更復雜的數(shù)據(jù)類型盯漂,它有多個屬性颇玷。

注:我們剛剛引進了一個新類型, string。 字符串用于保存任意長度的 UTF-8 編碼數(shù)據(jù)就缆。 如: string greeting = "Hello world!"帖渠。

實戰(zhàn)演練4

在我們的程序中,我們將創(chuàng)建一些僵尸竭宰!每個僵尸將擁有多個屬性空郊,所以這是一個展示結(jié)構(gòu)體的完美例子。

  • 1.建立一個struct 命名為 Zombie切揭。
  • 2.我們的 Zombie 結(jié)構(gòu)體有兩個屬性: name (類型為 string), 和 dna (類型為 uint)狞甚。

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

5、數(shù)組

如果你想建立一個集合廓旬,可以用 數(shù)組這樣的數(shù)據(jù)類型. Solidity 支持兩種數(shù)組: 靜態(tài) 數(shù)組和動態(tài) 數(shù)組:

// 固定長度為2的靜態(tài)數(shù)組:
uint[2] fixedArray;
// 固定長度為5的string類型的靜態(tài)數(shù)組:
string[5] stringArray;
// 動態(tài)數(shù)組哼审,長度不固定,可以動態(tài)添加元素:
uint[] dynamicArray;

你也可以建立一個 結(jié)構(gòu)體類型的數(shù)組 例如,上一節(jié)提到的 Person:

Person[] people; // dynamic Array, we can keep adding to it

記咨堋:狀態(tài)變量被永久保存在區(qū)塊鏈中十气。所以在你的合約中創(chuàng)建動態(tài)數(shù)組來保存成結(jié)構(gòu)的數(shù)據(jù)是非常有意義的。

公共數(shù)組

你可以定義 public 數(shù)組, Solidity 會自動創(chuàng)建 getter 方法. 語法如下:

Person[] public people;

其它的合約可以從這個數(shù)組讀取數(shù)據(jù)(但不能寫入數(shù)據(jù))旁赊,所以這在合約中是一個有用的保存公共數(shù)據(jù)的模式桦踊。

實戰(zhàn)演練5

為了把一個僵尸部隊保存在我們的APP里,并且能夠讓其它APP看到這些僵尸终畅,我們需要一個公共數(shù)組籍胯。

  • 創(chuàng)建一個數(shù)據(jù)類型為 Zombie 的結(jié)構(gòu)體數(shù)組,用 public 修飾离福,命名為:zombies.

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;
}

6杖狼、定義函數(shù)

在 Solidity 中函數(shù)定義的句法如下:

function eatHamburgers(string _name, uint _amount) {

}

這是一個名為 eatHamburgers 的函數(shù),它接受兩個參數(shù):一個 string類型的 和 一個 uint類型的⊙現(xiàn)在函數(shù)內(nèi)部還是空的蝶涩。

注:: 習慣上函數(shù)里的變量都是以(_)開頭 (但不是硬性規(guī)定) 以區(qū)別全局變量。我們整個教程都會沿用這個習慣絮识。

我們的函數(shù)定義如下:

eatHamburgers("vitalik", 100);

實戰(zhàn)演練6

在我們的應用里绿聘,我們要能創(chuàng)建一些僵尸,讓我們寫一個函數(shù)做這件事吧次舌!

  • 建立一個函數(shù) createZombie熄攘。 它有兩個參數(shù): _name (類型為string), 和 _dna (類型為uint)。

暫時讓函數(shù)空著——我們在后面會增加內(nèi)容彼念。
Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){

    }
}

7挪圾、使用結(jié)構(gòu)體和數(shù)組

創(chuàng)建新的結(jié)構(gòu)體

還記得上個例子中的 Person 結(jié)構(gòu)嗎?

struct Person {
  uint age;
  string name;
}

Person[] public people;

現(xiàn)在我們學習創(chuàng)建新的 Person 結(jié)構(gòu)逐沙,然后把它加入到名為 people 的數(shù)組中.

// 創(chuàng)建一個新的Person:
Person satoshi = Person(172, "Satoshi");

// 將新創(chuàng)建的satoshi添加進people數(shù)組:
people.push(satoshi);

你也可以兩步并一步哲思,用一行代碼更簡潔:

people.push(Person(16, "Vitalik"));

注:array.push() 在數(shù)組的 尾部 加入新元素 ,所以元素在數(shù)組中的順序就是我們添加的順序吩案, 如:

uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// numbers is now equal to [5, 10, 15]

實戰(zhàn)演練7

讓我們創(chuàng)建名為createZombie的函數(shù)來做點兒什么吧棚赔。

  • 1.在函數(shù)體里新創(chuàng)建一個 Zombie, 然后把它加入 zombies 數(shù)組中徘郭。 新創(chuàng)建的僵尸的 namedna靠益,來自于函數(shù)的參數(shù)。
  • 2.讓我們用一行代碼簡潔地完成它崎岂。

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }
}

8捆毫、私有 / 公共函數(shù)

Solidity 定義的函數(shù)的屬性默認公共闪湾。 這就意味著任何一方 (或其它合約) 都可以調(diào)用你合約里的函數(shù)冲甘。

顯然,不是什么時候都需要這樣,而且這樣的合約易于受到攻擊江醇。 所以將自己的函數(shù)定義為私有是一個好的編程習慣濒憋,只有當你需要外部世界調(diào)用它時才將它設置為公共
如何定義一個私有的函數(shù)呢陶夜?

uint[] numbers;
function _addToArray(uint _number) private {
  numbers.push(_number);
}

這意味著只有我們合約中的其它函數(shù)才能夠調(diào)用這個函數(shù)凛驮,給 numbers 數(shù)組添加新成員。

可以看到条辟,在函數(shù)名字后面使用關(guān)鍵字 private 即可黔夭。和函數(shù)的參數(shù)類似,私有函數(shù)的名字用(_)起始羽嫡。

實戰(zhàn)演練8

我們合約的函數(shù) createZombie 的默認屬性是公共的本姥,這意味著任何一方都可以調(diào)用它去創(chuàng)建一個僵尸。 咱們來把它變成私有吧杭棵!

  • 1.變 createZombie 為私有函數(shù)婚惫,不要忘記遵守命名的規(guī)矩哦!

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    /*
    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }
    */

     // 7.創(chuàng)建函數(shù)(改為私有方法)
    function _createZombie(string _name, uint _dna) private {
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }

}

9魂爪、函數(shù)的更多屬性

本節(jié)中我們將學習函數(shù)的返回值和修飾符先舷。

返回值

要想函數(shù)返回一個數(shù)值,按如下定義:

string greeting = "What's up dog";

function sayHello() public returns (string) {
  return greeting;
}

Solidity 里滓侍,函數(shù)的定義里可包含返回值的數(shù)據(jù)類型(如本例中 string)蒋川。

函數(shù)的修飾符view,returns

上面的函數(shù)實際上沒有改變 Solidity 里的狀態(tài),即粗井,它沒有改變?nèi)魏沃祷蛘邔懭魏螙|西尔破。
這種情況下我們可以把函數(shù)定義為 view, 意味著它只能讀取數(shù)據(jù)不能更改數(shù)據(jù):

function sayHello() public view returns (string) {}

Solidity 還支持 pure 函數(shù), 表明這個函數(shù)甚至都不訪問應用里的數(shù)據(jù),例如:

function _multiply(uint a, uint b) private pure returns (uint) {
  return a * b;
}

這個函數(shù)甚至都不讀取應用里的狀態(tài) — 它的返回值完全取決于它的輸入?yún)?shù)浇衬,在這種情況下我們把函數(shù)定義為 pure.

注:可能很難記住何時把函數(shù)標記為 pure/view懒构。 幸運的是, Solidity 編輯器會給出提示耘擂,提醒你使用這些修飾符胆剧。

實戰(zhàn)演練9

我們想建立一個幫助函數(shù),它根據(jù)一個字符串隨機生成一個DNA數(shù)據(jù)醉冤。

  • 1.創(chuàng)建一個 private 函數(shù)秩霍,命名為 _generateRandomDna。它只接收一個輸入變量 _str (類型 string), 返回一個 uint 類型的數(shù)值蚁阳。
  • 2.此函數(shù)只讀取我們合約中的一些變量铃绒,所以標記為view
  • 3.函數(shù)內(nèi)部暫時留空螺捐,以后我們再添加代碼颠悬。

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    /*
    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }
    */

     // 7.創(chuàng)建函數(shù)(改為私有方法)
    function _createZombie(string _name, uint _dna) private {
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }

    // 9.函數(shù)修飾符 private, view, returns 返回值
    function _generateRandomDna(string _str) private view returns (uint){

      }

}

10矮燎、Keccak256 和 類型轉(zhuǎn)換

散列函數(shù) Keccak256

如何讓 _generateRandomDna 函數(shù)返回一個全(半) 隨機的 uint?

Ethereum 內(nèi)部有一個散列函數(shù)keccak256,它用了SHA3版本赔癌。一個散列函數(shù)基本上就是把一個字符串轉(zhuǎn)換為一個256位的16進制數(shù)字诞外。字符串的一個微小變化會引起散列數(shù)據(jù)極大變化。

這在 Ethereum 中有很多應用灾票,但是現(xiàn)在我們只是用它造一個偽隨機數(shù)峡谊。

示例:

//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");

顯而易見,輸入字符串只改變了一個字母刊苍,輸出就已經(jīng)天壤之別了既们。

注: 在區(qū)塊鏈中安全地產(chǎn)生一個隨機數(shù)是一個很難的問題, 本例的方法不安全正什,但是在我們的Zombie DNA算法里不是那么重要贤壁,已經(jīng)很好地滿足我們的需要了。

類型轉(zhuǎn)換

有時你需要變換數(shù)據(jù)類型埠忘。例如:

uint8 a = 5;
uint b = 6;
// 將會拋出錯誤脾拆,因為 a * b 返回 uint, 而不是 uint8:
uint8 c = a * b;
// 我們需要將 b 轉(zhuǎn)換為 uint8:
uint8 c = a * uint8(b);

上面, a * b 返回類型是 uint, 但是當我們嘗試用 uint8 類型接收時, 就會造成潛在的錯誤。如果把它的數(shù)據(jù)類型轉(zhuǎn)換為 uint8, 就可以了莹妒,編譯器也不會出錯名船。

實戰(zhàn)演練10

_generateRandomDna 函數(shù)添加代碼! 它應該完成如下功能:

  • 1.第一行代碼取 _strkeccak256 散列值生成一個偽隨機十六進制數(shù),類型轉(zhuǎn)換為 uint, 最后保存在類型為 uint 名為 rand 的變量中旨怠。
  • 2.我們只想讓我們的DNA的長度為16位 (還記得 dnaModulus?)渠驼。所以第二行代碼應該 return 上面計算的數(shù)值對 dnaModulus 求余數(shù)(%)。

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    /*
    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }
    */

     // 7.創(chuàng)建函數(shù)(改為私有方法)
    function _createZombie(string _name, uint _dna) private {
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }

    // 9.函數(shù)修飾符 private, view, returns 返回值
    function _generateRandomDna(string _str) private view returns (uint){
        // 10.散列并取模
        uint rand = uint(keccak256(_str));  // 注意這里需要將string類型轉(zhuǎn)為uint類型
        return rand % dnaModulus;
    }

      }

}

11鉴腻、綜合應用

我們就快完成我們的隨機僵尸制造器了迷扇,來寫一個公共的函數(shù)把所有的部件連接起來。

寫一個公共函數(shù)爽哎,它有一個參數(shù)蜓席,用來接收僵尸的名字,之后用它生成僵尸的DNA课锌。

實戰(zhàn)演練11

  • 1.創(chuàng)建一個 public 函數(shù)厨内,命名為createRandomZombie. 它將被傳入一個變量 _name (數(shù)據(jù)類型是 string)。 (注: 定義公共函數(shù) public 和定義一個私有 private 函數(shù)的做法一樣)渺贤。
  • 2.函數(shù)的第一行應該調(diào)用 _generateRandomDna 函數(shù)雏胃,傳入 _name 參數(shù), 結(jié)果保存在一個類型為 uint 的變量里,命名為 randDna志鞍。
  • 3.第二行調(diào)用 _createZombie 函數(shù)瞭亮, 傳入?yún)?shù): _namerandDna
  • 4.整個函數(shù)應該是4行代碼 (包括函數(shù)的結(jié)束符號 } )固棚。

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    /*
    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }
    */

     // 7.創(chuàng)建函數(shù)(改為私有方法)
    function _createZombie(string _name, uint _dna) private {
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }

    // 9.函數(shù)修飾符 private, view, returns 返回值
    function _generateRandomDna(string _str) private view returns (uint){
        // 10.散列并取模
        uint rand = uint(keccak256(_str));  // 注意這里需要將string類型轉(zhuǎn)為uint類型
        return rand % dnaModulus;
    }

     // 11统翩、事件
    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

12吭露、事件

我們的合約幾乎就要完成了隶校!讓我們加上一個事件.

事件 是合約和區(qū)塊鏈通訊的一種機制饲常。你的前端應用“監(jiān)聽”某些事件涯呻,并做出反應黍翎。

示例:

// 這里建立事件
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public {
  uint result = _x + _y;
  //觸發(fā)事件面徽,通知app
  IntegersAdded(_x, _y, result);
  return result;
}

你的 app 前端可以監(jiān)聽這個事件。JavaScript 實現(xiàn)如下:

YourContract.IntegersAdded(function(error, result) { 
  // 干些事
}

實戰(zhàn)演練12

我們想每當一個僵尸創(chuàng)造出來時匣掸,我們的前端都能監(jiān)聽到這個事件趟紊,并將它顯示出來。

  • 1.定義一個 事件 叫做 NewZombie碰酝。 它有3個參數(shù): zombieId (uint)霎匈, name (string), 和 dna (uint)送爸。
  • 2.修改 _createZombie 函數(shù)使得當新僵尸造出來并加入zombies數(shù)組后铛嘱,生成事件NewZombie
  • 3.需要定義僵尸id袭厂。 array.push() 返回數(shù)組的長度類型是uint - 因為數(shù)組的第一個元素的索引是 0墨吓, array.push() - 1 將是我們加入的僵尸的索引。 zombies.push() - 1 就是 id纹磺,數(shù)據(jù)類型是 uint帖烘。在下一行中你可以把它用到 NewZombie 事件中。

Contract.sol

// 1\. 這里寫版本指令
pragma solidity ^0.4.19; 

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

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

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

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

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

    }

    // 6.數(shù)組類型為結(jié)構(gòu)體的公共數(shù)組
    Zombie[] public zombies;

    /*
    // 7.創(chuàng)建函數(shù)
    function createZombie(string _name, uint _dna){
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
        zombies.push(Zombie(_name, _dna));
    }
    */

     // 7.創(chuàng)建函數(shù)(改為私有方法)
    function _createZombie(string _name, uint _dna) private {
         // 8.使用結(jié)構(gòu)體和數(shù)組(初始化全局數(shù)組)
         // zombies.push(Zombie(_name, _dna));

        // 12橄杨、數(shù)組長度減一就是當前的數(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類型轉(zhuǎn)為uint類型
        return rand % dnaModulus;
    }

     // 11、綜合函數(shù)
    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

五式矫、Web3.js

我們的 Solidity 合約完工了乡摹! 現(xiàn)在我們要寫一段 JavaScript 前端代碼來調(diào)用這個合約。

以太坊有一個 JavaScript 庫采转,名為Web3.js趟卸。

在后面的課程里,我們會進一步地教你如何安裝一個合約氏义,如何設置Web3.js锄列。 但是現(xiàn)在我們通過一段代碼來了解 Web3.js 是如何和我們發(fā)布的合約交互的吧。

如果下面的代碼你不能全都理解惯悠,不用擔心邻邮。

// 下面是調(diào)用合約的方式:
var abi = /* abi是由編譯器生成的 */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* 發(fā)布之后在以太坊上生成的合約地址 */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory` 能訪問公共的函數(shù)以及事件

// 某個監(jiān)聽文本輸入的監(jiān)聽器:
$("#ourButton").click(function(e) {
  var name = $("#nameInput").val()
  //調(diào)用合約的 `createRandomZombie` 函數(shù):
  ZombieFactory.createRandomZombie(name)
})

// 監(jiān)聽 `NewZombie` 事件, 并且更新UI
var event = ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  generateZombie(result.zombieId, result.name, result.dna)
})

// 獲取 Zombie 的 dna, 更新圖像
function generateZombie(id, name, dna) {
  let dnaStr = String(dna)
  // 如果dna少于16位,在它前面用0補上
  while (dnaStr.length < 16)
    dnaStr = "0" + dnaStr

  let zombieDetails = {
    // 前兩位數(shù)構(gòu)成頭部.我們可能有7種頭部, 所以 % 7
    // 得到的數(shù)在0-6,再加上1,數(shù)的范圍變成1-7
    // 通過這樣計算:
    headChoice: dnaStr.substring(0, 2) % 7 + 1,
    // 我們得到的圖片名稱從head1.png 到 head7.png

    // 接下來的兩位數(shù)構(gòu)成眼睛, 眼睛變化就對11取模:
    eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
    // 再接下來的兩位數(shù)構(gòu)成衣服克婶,衣服變化就對6取模:
    shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
    //最后6位控制顏色. 用css選擇器: hue-rotate來更新
    // 360度:
    skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
    eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
    clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
    zombieName: name,
    zombieDescription: "A Level 1 CryptoZombie",
  }
  return zombieDetails
}

我們的 JavaScript 所做的就是獲取由zombieDetails 產(chǎn)生的數(shù)據(jù), 并且利用瀏覽器里的 JavaScript 神奇功能 (我們用 Vue.js)筒严,置換出圖像以及使用CSS過濾器丹泉。在后面的課程中,你可以看到全部的代碼鸭蛙。

六摹恨、Truffle框架學習

Truffle 是一個DApp開發(fā)框架,它簡化了去中心化應用的構(gòu)建和管理娶视。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晒哄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肪获,更是在濱河造成了極大的恐慌寝凌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孝赫,死亡現(xiàn)場離奇詭異较木,居然都是意外死亡,警方通過查閱死者的電腦和手機青柄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門伐债,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人致开,你說我怎么就攤上這事泳赋。” “怎么了喇喉?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵祖今,是天一觀的道長。 經(jīng)常有香客問我拣技,道長千诬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任膏斤,我火速辦了婚禮徐绑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘莫辨。我一直安慰自己傲茄,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布沮榜。 她就那樣靜靜地躺著盘榨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蟆融。 梳的紋絲不亂的頭發(fā)上草巡,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音型酥,去河邊找鬼山憨。 笑死查乒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的郁竟。 我是一名探鬼主播玛迄,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棚亩!你這毒婦竟也來了蓖议?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蔑舞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嘹屯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體攻询,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年州弟,在試婚紗的時候發(fā)現(xiàn)自己被綠了钧栖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡婆翔,死狀恐怖拯杠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啃奴,我是刑警寧澤潭陪,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站最蕾,受9級特大地震影響依溯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘟则,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一黎炉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧醋拧,春花似錦慷嗜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至菌赖,卻和暖如春干奢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盏袄。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工忿峻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留薄啥,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓逛尚,卻偏偏與公主長得像垄惧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子绰寞,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

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