摘要:可以把賬戶視為銀行賬戶,以太幣就是以太坊生態(tài)系統(tǒng)中的貨幣。重點(diǎn)合約狀態(tài)是持久化到區(qū)塊鏈上的,因此對(duì)合約狀態(tài)的修改需要消耗以太幣。也就是說(shuō)它們被寫入以太幣區(qū)塊鏈中想象成寫入一個(gè)數(shù)據(jù)庫(kù)。注中,實(shí)際上是代名詞,一個(gè)位的無(wú)符號(hào)整數(shù)。建立一個(gè)命名為。
區(qū)塊鏈火熱,作為程序猿的我,當(dāng)然也不能袖手旁觀,一位資深技術(shù)開發(fā)朋友曾笑說(shuō):這是屌絲程序猿改變命運(yùn)為數(shù)不多的機(jī)會(huì)之一。所以,從今天開始,就要步入?yún)^(qū)塊鏈的開發(fā)大潮中。一、合約開發(fā)流程
語(yǔ)言:使用 node.js 開發(fā)該項(xiàng)目
大概流程:
合約代碼編寫(Solidity)-> 合約編譯(solc)-> 合約部署(web3)
開發(fā)語(yǔ)言及工具:區(qū)塊鏈節(jié)點(diǎn):ganache-cli
基礎(chǔ)環(huán)境:node
合約開發(fā)語(yǔ)言:Solidity
合約編譯器:solc
合約訪問庫(kù):web3.js
二、基礎(chǔ)環(huán)境安裝1、安裝 node.js
2、安裝 ganache-cli
sudo npm install -g ganache-cli
運(yùn)行:
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 默認(rèn)會(huì)自動(dòng)創(chuàng)建 10 個(gè)賬戶,每個(gè)賬戶有 100 個(gè)以太幣(ETH:Ether)。 可以把賬戶視為銀行賬戶,以太幣就是以太坊生態(tài)系統(tǒng)中的貨幣。
面輸出的最后一句話,描述了節(jié)點(diǎn)仿真器的監(jiān)聽地址和端口為localhost:8545,在使用 web3.js 時(shí),需要傳入這個(gè)地址來(lái)告訴web3js庫(kù)應(yīng)當(dāng)連接到哪一個(gè)節(jié)點(diǎn)。
三、合約設(shè)計(jì)我們使用 Solidity 語(yǔ)言來(lái)編寫合約。如果你熟悉面向?qū)ο蟮拈_發(fā)和JavaScript,那么學(xué)習(xí)Solidity 應(yīng)該非常簡(jiǎn)單??梢詫⒑霞s類比于OOP的類:合約中的屬性用來(lái)聲明合約的狀態(tài),而合約中的方法則提供修改狀態(tài)的訪問接口。
重點(diǎn):
合約狀態(tài)是持久化到區(qū)塊鏈上的,因此對(duì)合約狀態(tài)的修改需要消耗以太幣。
只有在合約部署到區(qū)塊鏈的時(shí)候,才會(huì)調(diào)用構(gòu)造函數(shù),并且只調(diào)用一次。
與 web 世界里每次部署代碼都會(huì)覆蓋舊代碼不同,在區(qū)塊鏈上部署的合約是不可改變的,也就是說(shuō),如果你更新 合約并再次部署,舊的合約仍然會(huì)在區(qū)塊鏈上存在,并且合約的狀態(tài)數(shù)據(jù)也依然存在。新的部署將會(huì)創(chuàng)建合約的一 個(gè)新的實(shí)例。
四、合約語(yǔ)法從最基本的開始入手:
Solidity 的代碼都包裹在合約里面. 一份合約就是以太應(yīng)幣應(yīng)用的基本模塊, 所有的變量和函數(shù)都屬于一份合約, 它是你所有應(yīng)用的起點(diǎn).
一份名為 HelloWorld 的空合約如下:
contract HelloWorld { }1、版本指令
所有的 Solidity 源碼都必須冠以 "version pragma" — 標(biāo)明 Solidity 編譯器的版本. 以避免將來(lái)新的編譯器可能破壞你的代碼。
例如: pragma solidity ^0.4.19; (當(dāng)前 Solidity 的最新版本是 0.4.19).
綜上所述, 下面就是一個(gè)最基本的合約 — 每次建立一個(gè)新的項(xiàng)目時(shí)的第一段代碼:
contract.sol 合約文件
// 1. 這里寫版本指令 pragma solidity ^0.4.19; // 2. 這里建立智能合約 contract HelloWorld { }實(shí)戰(zhàn)演習(xí)1:
為了建立我們的僵尸部隊(duì), 讓我們先建立一個(gè)基礎(chǔ)合約,稱為 ZombieFactory。
建立一個(gè)版本為 0.4.19,我們的合約基于這個(gè)版本的編譯器。
建立一個(gè)空合約 ZombieFactory。
Contract.sol
pragma solidity ^0.4.19; // 1. 這里寫版本指令 // 2. 這里建立智能合約 contract ZombieFactory { }2、狀態(tài)變量和整數(shù)
狀態(tài)變量是被永久地保存在合約中。也就是說(shuō)它們被寫入以太幣區(qū)塊鏈中. 想象成寫入一個(gè)數(shù)據(jù)庫(kù)。
示例:
contract Example { // 這個(gè)無(wú)符號(hào)整數(shù)將會(huì)永久的被保存在區(qū)塊鏈中 uint myUnsignedInteger = 100; }
在上面的例子中,定義 myUnsignedInteger 為 uint 類型,并賦值100。
無(wú)符號(hào)整數(shù): uintuint 無(wú)符號(hào)數(shù)據(jù)類型, 指其值不能是負(fù)數(shù),對(duì)于有符號(hào)的整數(shù)存在名為 int 的數(shù)據(jù)類型。
注: Solidity中, uint 實(shí)際上是 uint256 代名詞, 一個(gè)256位的無(wú)符號(hào)整數(shù)。你也可以定義位數(shù)少的uints — uint8, uint16, uint32, 等…… 但一般來(lái)講你愿意使用簡(jiǎn)單的 uint, 除非在某些特殊情況下,這我們后面會(huì)講。
實(shí)戰(zhàn)演練2我們的僵尸DNA將由一個(gè)十六位數(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ù)學(xué)運(yùn)算
在 Solidity 中,數(shù)學(xué)運(yùn)算很直觀明了,與其它程序設(shè)計(jì)語(yǔ)言相同:
加法: x + y
減法: x - y,
乘法: x * y
除法: x / y
取模 / 求余: x % y (例如, 13 % 5 余 3, 因?yàn)?3除以5,余3)
Solidity 還支持 乘方操作 (如:x 的 y次方) // 例如: 5 ** 2 = 25
uint x = 5 ** 2; // equal to 5^2 = 25實(shí)戰(zhàn)演練3
為了保證我們的僵尸的DNA只含有16個(gè)字符,我們先造一個(gè)uint數(shù)據(jù),讓它等于10^16。這樣一來(lái)以后我們可以用模運(yùn)算符 % 把一個(gè)整數(shù)變成16位。
建立一個(gè)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í)你需要更復(fù)雜的數(shù)據(jù)類型,Solidity 提供了 結(jié)構(gòu)體:
struct Person { uint age; string name; }
結(jié)構(gòu)體允許你生成一個(gè)更復(fù)雜的數(shù)據(jù)類型,它有多個(gè)屬性。
注:我們剛剛引進(jìn)了一個(gè)新類型, string。 字符串用于保存任意長(zhǎng)度的 UTF-8 編碼數(shù)據(jù)。 如: string greeting = "Hello world!"。實(shí)戰(zhàn)演練4
在我們的程序中,我們將創(chuàng)建一些僵尸!每個(gè)僵尸將擁有多個(gè)屬性,所以這是一個(gè)展示結(jié)構(gòu)體的完美例子。
1.建立一個(gè)struct 命名為 Zombie。
2.我們的 Zombie 結(jié)構(gòu)體有兩個(gè)屬性: 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ù)組
如果你想建立一個(gè)集合,可以用 數(shù)組這樣的數(shù)據(jù)類型. Solidity 支持兩種數(shù)組: 靜態(tài) 數(shù)組和動(dòng)態(tài) 數(shù)組:
// 固定長(zhǎng)度為2的靜態(tài)數(shù)組: uint[2] fixedArray; // 固定長(zhǎng)度為5的string類型的靜態(tài)數(shù)組: string[5] stringArray; // 動(dòng)態(tài)數(shù)組,長(zhǎng)度不固定,可以動(dòng)態(tài)添加元素: uint[] dynamicArray;
你也可以建立一個(gè) 結(jié)構(gòu)體類型的數(shù)組 例如,上一節(jié)提到的 Person:
Person[] people; // dynamic Array, we can keep adding to it
記住:狀態(tài)變量被永久保存在區(qū)塊鏈中。所以在你的合約中創(chuàng)建動(dòng)態(tài)數(shù)組來(lái)保存成結(jié)構(gòu)的數(shù)據(jù)是非常有意義的。
公共數(shù)組你可以定義 public 數(shù)組, Solidity 會(huì)自動(dòng)創(chuàng)建 getter 方法. 語(yǔ)法如下:
Person[] public people;
其它的合約可以從這個(gè)數(shù)組讀取數(shù)據(jù)(但不能寫入數(shù)據(jù)),所以這在合約中是一個(gè)有用的保存公共數(shù)據(jù)的模式。實(shí)戰(zhàn)演練5
為了把一個(gè)僵尸部隊(duì)保存在我們的APP里,并且能夠讓其它APP看到這些僵尸,我們需要一個(gè)公共數(shù)組。
創(chuàng)建一個(gè)數(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) { }
這是一個(gè)名為 eatHamburgers 的函數(shù),它接受兩個(gè)參數(shù):一個(gè) string類型的 和 一個(gè) uint類型的?,F(xiàn)在函數(shù)內(nèi)部還是空的。
注:: 習(xí)慣上函數(shù)里的變量都是以(_)開頭 (但不是硬性規(guī)定) 以區(qū)別全局變量。我們整個(gè)教程都會(huì)沿用這個(gè)習(xí)慣。
我們的函數(shù)定義如下:
eatHamburgers("vitalik", 100);實(shí)戰(zhàn)演練6
在我們的應(yīng)用里,我們要能創(chuàng)建一些僵尸,讓我們寫一個(gè)函數(shù)做這件事吧!
建立一個(gè)函數(shù) createZombie。 它有兩個(gè)參數(shù): _name (類型為string), 和 _dna (類型為uint)。
暫時(shí)讓函數(shù)空著——我們?cè)诤竺鏁?huì)增加內(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)體
還記得上個(gè)例子中的 Person 結(jié)構(gòu)嗎?
struct Person { uint age; string name; } Person[] public people;
現(xiàn)在我們學(xué)習(xí)創(chuàng)建新的 Person 結(jié)構(gòu),然后把它加入到名為 people 的數(shù)組中.
// 創(chuàng)建一個(gè)新的Person: Person satoshi = Person(172, "Satoshi"); // 將新創(chuàng)建的satoshi添加進(jìn)people數(shù)組: people.push(satoshi);
你也可以兩步并一步,用一行代碼更簡(jiǎn)潔:
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]實(shí)戰(zhàn)演練7
讓我們創(chuàng)建名為createZombie的函數(shù)來(lái)做點(diǎn)兒什么吧。
1.在函數(shù)體里新創(chuàng)建一個(gè) Zombie, 然后把它加入 zombies 數(shù)組中。 新創(chuàng)建的僵尸的 name 和 dna,來(lái)自于函數(shù)的參數(shù)。
2.讓我們用一行代碼簡(jiǎn)潔地完成它。
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ù)組(初始化全局?jǐn)?shù)組) zombies.push(Zombie(_name, _dna)); } }8、私有 / 公共函數(shù)
Solidity 定義的函數(shù)的屬性默認(rèn)為公共。 這就意味著任何一方 (或其它合約) 都可以調(diào)用你合約里的函數(shù)。
顯然,不是什么時(shí)候都需要這樣,而且這樣的合約易于受到攻擊。 所以將自己的函數(shù)定義為私有是一個(gè)好的編程習(xí)慣,只有當(dāng)你需要外部世界調(diào)用它時(shí)才將它設(shè)置為公共。
如何定義一個(gè)私有的函數(shù)呢?
uint[] numbers; function _addToArray(uint _number) private { numbers.push(_number); }
這意味著只有我們合約中的其它函數(shù)才能夠調(diào)用這個(gè)函數(shù),給 numbers 數(shù)組添加新成員。
可以看到,在函數(shù)名字后面使用關(guān)鍵字 private 即可。和函數(shù)的參數(shù)類似,私有函數(shù)的名字用(_)起始。
實(shí)戰(zhàn)演練8我們合約的函數(shù) createZombie 的默認(rèn)屬性是公共的,這意味著任何一方都可以調(diào)用它去創(chuàng)建一個(gè)僵尸。 咱們來(lái)把它變成私有吧!
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ù)組(初始化全局?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)); } }9、函數(shù)的更多屬性
本節(jié)中我們將學(xué)習(xí)函數(shù)的返回值和修飾符。
返回值要想函數(shù)返回一個(gè)數(shù)值,按如下定義:
string greeting = "What"s up dog"; function sayHello() public returns (string) { return greeting; }
Solidity 里,函數(shù)的定義里可包含返回值的數(shù)據(jù)類型(如本例中 string)。
函數(shù)的修飾符view,returns上面的函數(shù)實(shí)際上沒有改變 Solidity 里的狀態(tài),即,它沒有改變?nèi)魏沃祷蛘邔懭魏螙|西。
這種情況下我們可以把函數(shù)定義為 view, 意味著它只能讀取數(shù)據(jù)不能更改數(shù)據(jù):
function sayHello() public view returns (string) {}
Solidity 還支持 pure 函數(shù), 表明這個(gè)函數(shù)甚至都不訪問應(yīng)用里的數(shù)據(jù),例如:
function _multiply(uint a, uint b) private pure returns (uint) { return a * b; }
這個(gè)函數(shù)甚至都不讀取應(yīng)用里的狀態(tài) — 它的返回值完全取決于它的輸入?yún)?shù),在這種情況下我們把函數(shù)定義為 pure.
注:可能很難記住何時(shí)把函數(shù)標(biāo)記為 pure/view。 幸運(yùn)的是, Solidity 編輯器會(huì)給出提示,提醒你使用這些修飾符。實(shí)戰(zhàn)演練9
我們想建立一個(gè)幫助函數(shù),它根據(jù)一個(gè)字符串隨機(jī)生成一個(gè)DNA數(shù)據(jù)。
1.創(chuàng)建一個(gè) private 函數(shù),命名為 _generateRandomDna。它只接收一個(gè)輸入變量 _str (類型 string), 返回一個(gè) uint 類型的數(shù)值。
2.此函數(shù)只讀取我們合約中的一些變量,所以標(biāo)記為view。
3.函數(shù)內(nèi)部暫時(shí)留空,以后我們?cè)偬砑哟a。
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ù)組(初始化全局?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)); } // 9.函數(shù)修飾符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ } }10、Keccak256 和 類型轉(zhuǎn)換 散列函數(shù) Keccak256
如何讓 _generateRandomDna 函數(shù)返回一個(gè)全(半) 隨機(jī)的 uint?
Ethereum 內(nèi)部有一個(gè)散列函數(shù)keccak256,它用了SHA3版本。一個(gè)散列函數(shù)基本上就是把一個(gè)字符串轉(zhuǎn)換為一個(gè)256位的16進(jìn)制數(shù)字。字符串的一個(gè)微小變化會(huì)引起散列數(shù)據(jù)極大變化。
這在 Ethereum 中有很多應(yīng)用,但是現(xiàn)在我們只是用它造一個(gè)偽隨機(jī)數(shù)。
示例:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5 keccak256("aaaab"); //b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9 keccak256("aaaac");
顯而易見,輸入字符串只改變了一個(gè)字母,輸出就已經(jīng)天壤之別了。
注: 在區(qū)塊鏈中安全地產(chǎn)生一個(gè)隨機(jī)數(shù)是一個(gè)很難的問題, 本例的方法不安全,但是在我們的Zombie DNA算法里不是那么重要,已經(jīng)很好地滿足我們的需要了。類型轉(zhuǎn)換
有時(shí)你需要變換數(shù)據(jù)類型。例如:
uint8 a = 5; uint b = 6; // 將會(huì)拋出錯(cuò)誤,因?yàn)?a * b 返回 uint, 而不是 uint8: uint8 c = a * b; // 我們需要將 b 轉(zhuǎn)換為 uint8: uint8 c = a * uint8(b);
上面, a * b 返回類型是 uint, 但是當(dāng)我們嘗試用 uint8 類型接收時(shí), 就會(huì)造成潛在的錯(cuò)誤。如果把它的數(shù)據(jù)類型轉(zhuǎn)換為 uint8, 就可以了,編譯器也不會(huì)出錯(cuò)。實(shí)戰(zhàn)演練10
給 _generateRandomDna 函數(shù)添加代碼! 它應(yīng)該完成如下功能:
1.第一行代碼取 _str 的 keccak256 散列值生成一個(gè)偽隨機(jī)十六進(jìn)制數(shù),類型轉(zhuǎn)換為 uint, 最后保存在類型為 uint 名為 rand 的變量中。
2.我們只想讓我們的DNA的長(zhǎng)度為16位 (還記得 dnaModulus?)。所以第二行代碼應(yīng)該 return 上面計(jì)算的數(shù)值對(duì) 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ù)組(初始化全局?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)); } // 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、綜合應(yīng)用
我們就快完成我們的隨機(jī)僵尸制造器了,來(lái)寫一個(gè)公共的函數(shù)把所有的部件連接起來(lái)。
寫一個(gè)公共函數(shù),它有一個(gè)參數(shù),用來(lái)接收僵尸的名字,之后用它生成僵尸的DNA。
實(shí)戰(zhàn)演練111.創(chuàng)建一個(gè) public 函數(shù),命名為createRandomZombie. 它將被傳入一個(gè)變量 _name (數(shù)據(jù)類型是 string)。 (注: 定義公共函數(shù) public 和定義一個(gè)私有 private 函數(shù)的做法一樣)。
2.函數(shù)的第一行應(yīng)該調(diào)用 _generateRandomDna 函數(shù),傳入 _name 參數(shù), 結(jié)果保存在一個(gè)類型為 uint 的變量里,命名為 randDna。
3.第二行調(diào)用 _createZombie 函數(shù), 傳入?yún)?shù): _name 和 randDna。
4.整個(gè)函數(shù)應(yīng)該是4行代碼 (包括函數(shù)的結(jié)束符號(hào) } )。
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ù)組(初始化全局?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)); } // 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、事件
我們的合約幾乎就要完成了!讓我們加上一個(gè)事件.
事件 是合約和區(qū)塊鏈通訊的一種機(jī)制。你的前端應(yīng)用“監(jiān)聽”某些事件,并做出反應(yīng)。
示例:
// 這里建立事件 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)聽這個(gè)事件。JavaScript 實(shí)現(xiàn)如下:
YourContract.IntegersAdded(function(error, result) { // 干些事 }實(shí)戰(zhàn)演練12
我們想每當(dāng)一個(gè)僵尸創(chuàng)造出來(lái)時(shí),我們的前端都能監(jiān)聽到這個(gè)事件,并將它顯示出來(lái)。
1.定義一個(gè) 事件 叫做 NewZombie。 它有3個(gè)參數(shù): zombieId (uint), name (string), 和 dna (uint)。
2.修改 _createZombie 函數(shù)使得當(dāng)新僵尸造出來(lái)并加入zombies數(shù)組后,生成事件NewZombie。
3.需要定義僵尸id。 array.push() 返回數(shù)組的長(zhǎng)度類型是uint - 因?yàn)閿?shù)組的第一個(gè)元素的索引是 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ù)組(初始化全局?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類型轉(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 前端代碼來(lái)調(diào)用這個(gè)合約。
以太坊有一個(gè) JavaScript 庫(kù),名為Web3.js。
在后面的課程里,我們會(huì)進(jìn)一步地教你如何安裝一個(gè)合約,如何設(shè)置Web3.js。 但是現(xiàn)在我們通過一段代碼來(lái)了解 Web3.js 是如何和我們發(fā)布的合約交互的吧。
如果下面的代碼你不能全都理解,不用擔(dān)心。
// 下面是調(diào)用合約的方式: var abi = /* abi是由編譯器生成的 */ var ZombieFactoryContract = web3.eth.contract(abi) var contractAddress = /* 發(fā)布之后在以太坊上生成的合約地址 */ var ZombieFactory = ZombieFactoryContract.at(contractAddress) // `ZombieFactory` 能訪問公共的函數(shù)以及事件 // 某個(gè)監(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補(bǔ)上 while (dnaStr.length < 16) dnaStr = "0" + dnaStr let zombieDetails = { // 前兩位數(shù)構(gòu)成頭部.我們可能有7種頭部, 所以 % 7 // 得到的數(shù)在0-6,再加上1,數(shù)的范圍變成1-7 // 通過這樣計(jì)算: headChoice: dnaStr.substring(0, 2) % 7 + 1, // 我們得到的圖片名稱從head1.png 到 head7.png // 接下來(lái)的兩位數(shù)構(gòu)成眼睛, 眼睛變化就對(duì)11取模: eyeChoice: dnaStr.substring(2, 4) % 11 + 1, // 再接下來(lái)的兩位數(shù)構(gòu)成衣服,衣服變化就對(duì)6取模: shirtChoice: dnaStr.substring(4, 6) % 6 + 1, //最后6位控制顏色. 用css選擇器: hue-rotate來(lái)更新 // 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框架學(xué)習(xí)Truffle 是一個(gè)DApp開發(fā)框架,它簡(jiǎn)化了去中心化應(yīng)用的構(gòu)建和管理。
注:本教程是匯智網(wǎng)-以太坊DApp開發(fā)入門學(xué)習(xí)筆記,感興趣的同學(xué)可以去學(xué)習(xí)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24115.html
摘要:我們目前正處于一個(gè)新興的區(qū)塊鏈開發(fā)行業(yè)中。,一種在以太坊開發(fā)人員中流行的新的簡(jiǎn)單編程語(yǔ)言,因?yàn)樗怯糜陂_發(fā)以太坊智能合約的語(yǔ)言。它是全球至少萬(wàn)開發(fā)人員使用的世界上最流行的編程語(yǔ)言之一。以太坊,主要是針對(duì)工程師使用進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。 我們目前正處于一個(gè)新興的區(qū)塊鏈開發(fā)行業(yè)中。區(qū)塊鏈技術(shù)處于初期階段,然而這種顛覆性技術(shù)已經(jīng)成功地風(fēng)靡全球,并且最近經(jīng)歷了一場(chǎng)與眾不同的繁榮。由于許多...
摘要:在新智能合約的構(gòu)造函數(shù)中,將引用我們的合約工廠的地址。以太坊,主要是針對(duì)工程師使用進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。以太坊入門教程,主要介紹智能合約與應(yīng)用開發(fā),適合入門。這里是原文用工廠模式管理多個(gè)智能合約 我們寫了一份小的計(jì)算合約作為Hello World。如果我們可以創(chuàng)建一個(gè)允許用戶創(chuàng)建自己的計(jì)數(shù)器的合約怎么辦? showImg(https://segmentfault.com/img/...
摘要:你首先編寫基本智能合約并告知你的新智能合約將從基礎(chǔ)合約繼承。這些函數(shù)和狀態(tài)變量只能在內(nèi)部訪問即從當(dāng)前合約或從中派生的合約中,而其他情況不使用它。私有函數(shù)和狀態(tài)變量?jī)H對(duì)定義它們的智能合約可見,而不是在派生合約中可見。這里是原文語(yǔ)言開發(fā)中的繼承 我們已經(jīng)探索了很多主題,在編寫智能合約時(shí)我們發(fā)現(xiàn)經(jīng)常使用相同的模式:例如,智能合約具有在構(gòu)造函數(shù)中設(shè)置的所有者,然后生成修改器以便僅讓所有者使用一...
摘要:原文發(fā)表于以太坊智能合約開發(fā)第二篇理解以太坊相關(guān)概念很多人都說(shuō)比特幣是區(qū)塊鏈,以太坊是區(qū)塊鏈。它是以太坊智能合約的運(yùn)行環(huán)境。是由以太坊節(jié)點(diǎn)提供。以太坊社區(qū)把基于智能合約的應(yīng)用稱為去中心化的應(yīng)用。 原文發(fā)表于:以太坊智能合約開發(fā)第二篇:理解以太坊相關(guān)概念 很多人都說(shuō)比特幣是區(qū)塊鏈1.0,以太坊是區(qū)塊鏈2.0。在以太坊平臺(tái)上,可以開發(fā)各種各樣的去中心化應(yīng)用,這些應(yīng)用構(gòu)成了以太坊的整個(gè)生態(tài)...
摘要:和比特幣協(xié)議有所不同的是,以太坊的設(shè)計(jì)十分靈活,極具適應(yīng)性。超級(jí)賬本區(qū)塊鏈的商業(yè)應(yīng)用超級(jí)賬本超級(jí)賬本是基金會(huì)下的眾多項(xiàng)目中的一個(gè)。證書頒發(fā)機(jī)構(gòu)負(fù)責(zé)簽發(fā)撤 showImg(https://segmentfault.com/img/bV2ge9?w=900&h=385); 從比特幣開始 一個(gè)故事告訴你比特幣的原理及運(yùn)作機(jī)制 這篇文章的定位會(huì)比較科普,盡量用類比的方法將比特幣的基本原理講出來(lái)...
閱讀 900·2023-04-26 01:37
閱讀 3373·2021-09-02 15:40
閱讀 966·2021-09-01 10:29
閱讀 2898·2019-08-29 17:05
閱讀 3427·2019-08-28 18:02
閱讀 1184·2019-08-28 18:00
閱讀 1493·2019-08-26 11:00
閱讀 2615·2019-08-26 10:27