一呻此、合約開發(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
规伐, uint16
, uint32
匣缘, 等…… 但一般來講你愿意使用簡單的 uint
猖闪, 除非在某些特殊情況下,這我們后面會講肌厨。
實戰(zhàn)演練2
我們的僵尸DNA將由一個十六位數(shù)字組成培慌。
- 定義
dnaDigits
為uint
數(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
, 令其等于10
的dnaDigits
次方胁镐。
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)建的僵尸的name
和dna
靠益,來自于函數(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.第一行代碼取
_str
的keccak256
散列值生成一個偽隨機十六進制數(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ù):_name
和randDna
。 - 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)建和管理娶视。