本視頻課程面向初學(xué)者芽死,介紹什么是區(qū)塊鏈乏梁,什么是智能合約,內(nèi)容涵蓋以太坊開(kāi)發(fā)相關(guān)的基本概念关贵,Gregory(Dapp University 創(chuàng)始人遇骑,專注于講解在以太坊區(qū)塊鏈上構(gòu)建DApp應(yīng)用程序教程。Gregory大神的以太坊課程在youtube上超過(guò)五千位訂閱者揖曾,所有課程超過(guò)10萬(wàn)人次觀看落萎,深受區(qū)塊鏈技術(shù)開(kāi)發(fā)用戶的喜愛(ài) )大神在課程中手把手地教大家如何構(gòu)建一個(gè)基于以太坊的完整去中心化應(yīng)用 —— 區(qū)塊鏈投票系統(tǒng)。
課程介紹
本視頻課程面向初學(xué)者炭剪,介紹什么是區(qū)塊鏈练链,什么是智能合約,內(nèi)容涵蓋以太坊開(kāi)發(fā)相關(guān)的基本概念奴拦,Gregory大神在課程中手把手地教大家如何構(gòu)建一個(gè)基于以太坊的完整去中心化應(yīng)用 —— 區(qū)塊鏈投票系統(tǒng)媒鼓。
通過(guò)本課程的學(xué)習(xí),你將掌握:
1、以太坊區(qū)塊鏈開(kāi)發(fā)的基本知識(shí)
2绿鸣、開(kāi)發(fā)和部署以太坊合約所需的軟件環(huán)境
3疚沐、使用高級(jí)語(yǔ)言(solidity)編寫(xiě)以太坊智能合約
4、使用NodeJS編譯潮模、部署合約并與之交互
5亮蛔、使用Truffle框架開(kāi)發(fā)分布式應(yīng)用
6、使用控制臺(tái)或網(wǎng)頁(yè)與合約進(jìn)行交互
課程內(nèi)容
什么是區(qū)塊鏈?
我們用一個(gè)比喻來(lái)明白什么是區(qū)塊鏈再登,它的工作原理是怎樣尔邓?讓我們先看一下網(wǎng)頁(yè)應(yīng)用程序晾剖。
當(dāng)我們和網(wǎng)頁(yè)應(yīng)用程序交互時(shí)锉矢,你使用網(wǎng)頁(yè)瀏覽器去連接我們的應(yīng)用中心服務(wù)器。所有的網(wǎng)頁(yè)應(yīng)用都基于中心服務(wù)器齿尽,所有的數(shù)據(jù)都在中心數(shù)據(jù)庫(kù)沽损。任何時(shí)候在應(yīng)用程序上交易,你都需要和網(wǎng)頁(yè)中心服務(wù)器溝通循头。
如果我們?cè)诰W(wǎng)頁(yè)應(yīng)用程序上投票選舉绵估,投票選舉時(shí)我們會(huì)有以下問(wèn)題:
1. 數(shù)據(jù)庫(kù)里的數(shù)據(jù)可能會(huì)被改變:或者重復(fù)投票:也可能投票數(shù)據(jù)全部被刪除。
2. 網(wǎng)頁(yè)服務(wù)器上的源代碼在任何時(shí)候都可能會(huì)被改變卡骂。
我們不愿意在網(wǎng)頁(yè)上創(chuàng)建應(yīng)用程序国裳。我們更愿意把它創(chuàng)建在大家能連接的區(qū)塊鏈上,確保一人投票一次全跨,且不可篡改缝左。讓我們看看在區(qū)塊鏈上怎么去實(shí)現(xiàn)它。
區(qū)塊鏈不是一個(gè)中央服務(wù)器和一個(gè)數(shù)據(jù)庫(kù)浓若,而是一個(gè)網(wǎng)絡(luò)和一個(gè)數(shù)據(jù)庫(kù)渺杉。區(qū)塊鏈?zhǔn)怯?jì)算機(jī)的對(duì)等網(wǎng)絡(luò),稱為節(jié)點(diǎn)挪钓,共享網(wǎng)絡(luò)中的所有數(shù)據(jù)和代碼是越。因此,如果您是連接到區(qū)塊鏈的設(shè)備碌上,則您是網(wǎng)絡(luò)中的一個(gè)節(jié)點(diǎn)倚评,并且您可以與網(wǎng)絡(luò)中的所有其他計(jì)算機(jī)節(jié)點(diǎn)通話。您也擁有區(qū)塊鏈上所有數(shù)據(jù)和代碼的副本馏予。區(qū)塊鏈上沒(méi)有中央服務(wù)器天梧,只有不同設(shè)備之間在同屬性下的網(wǎng)絡(luò)點(diǎn)對(duì)點(diǎn)交流。
區(qū)塊鏈取代中心化數(shù)據(jù)庫(kù)吗蚌,所有的交易都在區(qū)塊鏈上腿倚,所有包含的記錄稱之為區(qū)塊。所有連接起來(lái)的區(qū)塊叫區(qū)塊鏈,區(qū)塊鏈之間創(chuàng)建公共賬本敷燎,公共賬本代表區(qū)塊鏈里面的所有數(shù)據(jù)暂筝。
公共賬本的所有數(shù)據(jù)都通過(guò)加密技術(shù)哈希密碼進(jìn)行保護(hù),并通過(guò)共識(shí)算法進(jìn)行驗(yàn)證硬贯。網(wǎng)絡(luò)上節(jié)點(diǎn)的參與確保網(wǎng)絡(luò)上分散的所有數(shù)據(jù)副本都是一樣的焕襟。這就是我們?yōu)槭裁匆趨^(qū)塊鏈上構(gòu)建投票應(yīng)用程序,因?yàn)槲覀兿M_保我們的投票都被計(jì)算在內(nèi)饭豹,而且沒(méi)有被篡改鸵赖。
如果我們的投票應(yīng)用程序應(yīng)用在區(qū)塊鏈上會(huì)怎么樣?
對(duì)于新用戶來(lái)講拄衰,新用戶需要一個(gè)帶錢(qián)包地址和有ETH的賬戶它褪。ETH也就是以太坊的加密貨幣。一旦連接到區(qū)塊鏈網(wǎng)絡(luò)翘悉,投票會(huì)在區(qū)塊鏈上消耗一些ETH茫打。
這些交易費(fèi)用稱之為“燃?xì)狻薄.?dāng)開(kāi)始投票是妖混,網(wǎng)絡(luò)上幫助完成這筆交易的礦工將獲得這筆交易費(fèi)用的ETH老赤。我投票的記錄就永遠(yuǎn)被記錄下來(lái)了。
記住一點(diǎn):投票是一種交易制市,交易會(huì)消耗ETH抬旺,但讀取數(shù)據(jù)是免費(fèi)的。
什么是智能合約祥楣?
智能合約就是在以太坊虛擬機(jī)(EVM) 上執(zhí)行我們代碼的過(guò)程开财。
智能合約的編程語(yǔ)言是Solidity,有點(diǎn)像Javascript荣堰,但有點(diǎn)不一樣床未。Solidity編程語(yǔ)言可以實(shí)現(xiàn)我們所有的商務(wù)交易邏輯。
如果說(shuō)公共賬本代表網(wǎng)頁(yè)的數(shù)據(jù)庫(kù)振坚,那么智能合約就是實(shí)現(xiàn)所有商務(wù)邏輯交易的地方薇搁。
現(xiàn)在讓我們快速的看看我們構(gòu)建的DApp的結(jié)構(gòu)。
傳統(tǒng)的前端應(yīng)用程序用的是HTML, CSS, 和 Javascript.語(yǔ)言渡八。
代替?zhèn)鹘y(tǒng)的前端應(yīng)用程序是后端服務(wù)器啃洋,客戶安裝連接本地以太坊區(qū)塊鏈,去用Solidity語(yǔ)言編譯去中心化選舉屎鳍。部署智能合約到本地區(qū)塊鏈上進(jìn)行投票選舉宏娄。
讓我們一起來(lái)看看區(qū)塊鏈的工作原理,為什么我們要用區(qū)塊鏈代替下當(dāng)前的中心化應(yīng)用程序逮壁。
我們要?jiǎng)?chuàng)建什么孵坚?
我們將構(gòu)建一個(gè)客戶端應(yīng)用程序,與我們?cè)趨^(qū)塊鏈上的智能合約進(jìn)行對(duì)話。這個(gè)客戶端應(yīng)用程序?qū)⒂幸粋€(gè)候選人列表卖宠,列出每個(gè)候選人的ID巍杈,姓名和投票數(shù)。 它會(huì)有一個(gè)表格扛伍,我們可以為我們想要的候選人投一票筷畦。 它還顯示我們?cè)凇澳膸簟毕逻B接到區(qū)塊鏈的帳戶。
安裝依賴項(xiàng)
創(chuàng)建DApp之前, 首先需要安裝依賴項(xiàng).
節(jié)點(diǎn)包管理器 (NPM)
我們需要的第一個(gè)依賴是Node Package Manager或NPM刺洒,它隨Node.js一起的鳖宾。你可以看看你的節(jié)點(diǎn)是否已經(jīng)安裝了,你可以去你的終端并輸入:
$ node -v
Truffle 框架
下一個(gè)依賴項(xiàng)是Truffle框架它可以使我們?cè)谝蕴粎^(qū)塊鏈上創(chuàng)建分布式應(yīng)用逆航,提供一套用solidity編譯的智能合約工具鼎文,也可以幫助我們測(cè)試智能合約部署區(qū)塊鏈,它也為我們開(kāi)發(fā)客戶端應(yīng)用程序提供了一個(gè)地方纸泡。在命令行中這樣安裝truffle和NPM:
$ npm install -g truffle
Ganache
下一個(gè)依賴項(xiàng)是Ganache漂问,一個(gè)本地內(nèi)存區(qū)塊鏈。你也可以在Truffle Framework 網(wǎng)址下載安裝Ganache女揭。它將會(huì)提供以太坊外部賬戶,每個(gè)賬戶里都有100個(gè)假的ETH和它的錢(qián)包地址栏饮。
Metamask谷歌擴(kuò)展插件
我們下一個(gè)要安裝的依賴項(xiàng)是:Metamask谷歌擴(kuò)展插件吧兔。Metamask幫助我們用個(gè)人賬戶連接本地區(qū)塊鏈網(wǎng)絡(luò)進(jìn)行交互。開(kāi)始之前袍嬉,記得安裝Metamask谷歌擴(kuò)展插件哦境蔼。
語(yǔ)法高亮(Syntax Highlighting)
下一個(gè)依賴項(xiàng)是語(yǔ)法高亮,很多編輯器和IEDs沒(méi)有語(yǔ)法高亮伺通,我建議在用solidity編程是安裝語(yǔ)法高亮箍土。所以你要安裝一個(gè)這樣的安轉(zhuǎn)包,教程里用我用的是Sublime Text罐监,我已經(jīng)在“以太坊”包下載了這個(gè)語(yǔ)法高亮吴藻。
Smoke Test - 步驟一
好了,現(xiàn)在我們所以的依賴項(xiàng)都安裝好了弓柱,讓我們開(kāi)始創(chuàng)建我們的DApp吧沟堡。
首先,下載Ganache并打開(kāi)矢空,確保你的本地區(qū)塊鏈正常運(yùn)作航罗。
Ganache給我們10個(gè)賬戶,每個(gè)賬戶100個(gè)假的ETH屁药。
每個(gè)帳戶都有一個(gè)唯一的地址和一個(gè)私鑰粥血。每個(gè)帳戶地址將作為我們選舉中每個(gè)選民的唯一標(biāo)識(shí)符。現(xiàn)在讓我們創(chuàng)建一個(gè)Dapp的項(xiàng)目目錄:
$ mkdir election
$ cd election
現(xiàn)在我們進(jìn)入項(xiàng)目?jī)?nèi)部了,Truffle box使其快速運(yùn)行复亏。這個(gè)教程我們使用PET Shop box.從您的項(xiàng)目目錄中, 像這樣從命令欄安裝 pet shop box:
$ truffle unbox petshop
看看 pet shop box給我們帶來(lái)什么:
· contracts 目錄:?this is where all smart contacts live. We already have a Migration contract that handles our migrations to the blockchain.
這就是智能合約所在的地方绢彤。在區(qū)塊鏈上我們已經(jīng)有Migration(遷移)合約處理我們的遷移。
· migrations 目錄:這是所有migration文件所在地方蜓耻。當(dāng)在區(qū)塊鏈上更新區(qū)塊鏈狀態(tài)時(shí)就需要migration茫舶。就像網(wǎng)頁(yè)開(kāi)發(fā)框架里面需要migration來(lái)更新數(shù)據(jù)狀態(tài)一樣。
· node_modules 目錄:?這是所有的節(jié)點(diǎn)依賴項(xiàng)的大本營(yíng)刹淌。
· src 目錄:?這是我們開(kāi)發(fā)客戶端應(yīng)用程序的地方饶氏。
· test 目錄:?這是我們?yōu)橹悄芎霞s編寫(xiě)測(cè)試的地方。
· truffle.js?文件:這是 Truffle項(xiàng)目的主要配置文件.
讓我們一起來(lái)在合約目錄下創(chuàng)建新合約吧:
$ touch contracts/Election.sol
我們首先創(chuàng)建一個(gè)“煙霧測(cè)試”有勾,以確保我們已經(jīng)正確設(shè)置了項(xiàng)目,并成功地將合約部署到區(qū)塊鏈疹启。打開(kāi)文件并從下面的代碼開(kāi)始:
pragma solidity 0.4.2;
contract Election {
// Read/write candidate
string public candidate;
// Constructor
function Election () public {
candidate = "Candidate 1";
}
}
現(xiàn)在我們已經(jīng)為智能合約創(chuàng)建了基礎(chǔ),讓我們看看是否可以將其部署到區(qū)塊鏈蔼卡。因此喊崖,我們需要在migrations目錄中創(chuàng)建一個(gè)新文件。從您的項(xiàng)目根目錄中雇逞,像這樣從命令行中創(chuàng)建一個(gè)新文件:
$ touch migrations/2_deploy_contracts.js
請(qǐng)注意荤懂,我們使用數(shù)字為migrations目錄中的所有文件編號(hào),以便Truffle知道執(zhí)行它們的順序塘砸。讓我們創(chuàng)建一個(gè)這樣新的migration部署的合約:
var Election = artifacts.require("./Election.sol");
module.exports = function(deployer) {
deployer.deploy(Election);
};
首先节仿,我們需要?jiǎng)?chuàng)建合約,并將其分配給名為“Election”的變量掉蔬。 接下來(lái)廊宪,我們將其添加到已部署合同的清單中,以確保在我們運(yùn)行migrations時(shí)將其部署女轿。 現(xiàn)在讓我們像這樣從命令行中運(yùn)行migrations
$ truffle migrate:
現(xiàn)在我們已經(jīng)成功將我們的智能合約遷移到當(dāng)?shù)氐囊蕴粎^(qū)塊鏈, 讓我們打開(kāi)控制臺(tái)與智能合約交互箭启。 你可以像這樣從命令欄中打開(kāi)truffle控制器:
$ truffle console
現(xiàn)在我們進(jìn)入了控制臺(tái),讓我們來(lái)看看我們部署的智能合約的一個(gè)實(shí)例蛉迹,看看我們是否可以從合約中讀取候選人的名字傅寡。從控制臺(tái)運(yùn)行此代碼:
Election.deployed().then(function(instance) { app = instance })
這里的Election 是我們?cè)趍igration文件中創(chuàng)建的變量的名稱。我們?cè)谶@個(gè)合約例子里檢索了一個(gè)部署deployed() 函數(shù)婿禽,分配給一個(gè)app變量在promise的回調(diào)函數(shù)中∩蜕現(xiàn)在我們可以像這樣讀取候選變量的值:
app.candidate()
// => 'Candidate 1'
恭喜你!你剛剛編寫(xiě)了第一份智能合約扭倾,并將其部署到區(qū)塊鏈中淀零,并檢索了一些數(shù)據(jù)。
步驟二:候選人名單膛壹。
我們需要一種方法來(lái)存儲(chǔ)多個(gè)候選人驾中,并存儲(chǔ)關(guān)于每個(gè)候選人的多個(gè)屬性唉堪。我們希望跟蹤候選人的ID,姓名和投票數(shù)肩民。 以下是我們將如何對(duì)候選人進(jìn)行模擬:
contract Election {
// Model a Candidate
struct Candidate {
uint id;
string name;
uint voteCount;
}
// ...
}
我們使用Solidity Struct為候選人建模唠亚,Solidity允許我們創(chuàng)建自己的結(jié)構(gòu)類型,就像我們?cè)谶@里為候選人所做的那樣持痰。 我們指定這個(gè)結(jié)構(gòu)有一個(gè)無(wú)符號(hào)整數(shù)類型的ID灶搜,字符串類型的名稱和無(wú)符號(hào)整數(shù)類型的voteCount。 簡(jiǎn)單地聲明這個(gè)結(jié)構(gòu)體實(shí)際上不會(huì)給我們一個(gè)候選人工窍。 我們需要實(shí)例化它并在將它寫(xiě)入存儲(chǔ)之前將其分配給一個(gè)變量割卖。
接下來(lái)我們需要的是一個(gè)儲(chǔ)存候選人的地方。我們需要一個(gè)地方來(lái)存儲(chǔ)我們剛剛創(chuàng)建的一種結(jié)構(gòu)類型患雏。我們可以使用Solidity映射進(jìn)行此操作鹏溯。 Solidity中的映射類似于關(guān)聯(lián)數(shù)組或哈希,它將鍵 - 值對(duì)關(guān)聯(lián)起來(lái)淹仑。 我們可以像這樣創(chuàng)建這個(gè)映射:
contract Election {
// Model a Candidate
struct Candidate {
uint id;
string name;
uint voteCount;
}
// Read/write Candidates
mapping(uint => Candidate) public candidates;
// ...
}
在這種情況下丙挽,映射的關(guān)鍵是一個(gè)無(wú)符號(hào)整數(shù),并且該值是我們剛剛定義的Candidate結(jié)構(gòu)類型。 這基本上給了我們一個(gè)基于id的查找每個(gè)候選人。 由于該映射被分配給一個(gè)狀態(tài)變量奔害,因此只要我們?yōu)槠浞峙湫碌逆I值對(duì),我們就會(huì)將數(shù)據(jù)寫(xiě)入?yún)^(qū)塊鏈瞬浓。 接下來(lái),我們將該映射的可見(jiàn)性設(shè)置為public蓬坡,以獲得getter函數(shù),就像我們?cè)跓熿F測(cè)試中使用候選名稱一樣磅叛。
接下來(lái)屑咳,我們通過(guò)一個(gè)像這樣的計(jì)數(shù)器緩存狀態(tài)變量來(lái)跟蹤選舉中存在多少候選者:
contract Election {
// Model a Candidate
struct Candidate {
uint id;
string name;
uint voteCount;
}
// Read/write Candidates
mapping(uint => Candidate) public candidates;
// Store Candidates Count
uint public candidatesCount;
// ...
}
在Solidity中,無(wú)法確定映射的大小弊琴,也無(wú)法對(duì)它進(jìn)行迭代兆龙。 這是因?yàn)樯形捶峙渲档挠成渲械娜魏捂I都會(huì)返回默認(rèn)值(在這種情況下為空候選)。 例如敲董,如果我們?cè)谶@次選舉中只有2名候選人紫皇,并且我們?cè)噲D查找候選人#99,那么映射將返回一個(gè)空的候選人結(jié)構(gòu)腋寨。 這種行為使得不可能知道有多少候選者存在聪铺,因此我們必須使用計(jì)數(shù)器緩存
接下來(lái),我們創(chuàng)建一個(gè)函數(shù)萄窜,將候選添加到我們創(chuàng)建的映射中:
contract Election {
// ...
function addCandidate (string _name) private {
candidatesCount ++;
candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
}
}
我們已經(jīng)聲明了函數(shù)addCandidate铃剔,它接受一個(gè)表示候選人姓名的字符串類型參數(shù)撒桨。 在函數(shù)內(nèi)部,我們遞增候選計(jì)數(shù)器緩存以表示添加了新的候選項(xiàng)键兜。 然后我們用一個(gè)新的Candidate結(jié)構(gòu)更新映射凤类,使用當(dāng)前的候選計(jì)數(shù)作為關(guān)鍵。 這個(gè)Candidate結(jié)構(gòu)體使用來(lái)自當(dāng)前候選計(jì)數(shù)的候選id普气,函數(shù)參數(shù)中的名稱以及初始投票計(jì)數(shù)初始化為0.請(qǐng)注意谜疤,此函數(shù)的可見(jiàn)性是私有的,因?yàn)槲覀冎幌朐诤贤姓{(diào)用它现诀。
現(xiàn)在夷磕,我們可以通過(guò)在構(gòu)造函數(shù)內(nèi)兩次調(diào)用“addCandidate”函數(shù)來(lái)添加兩個(gè)候選人,如下所示:
contract Election {
// ...
function Election () public {
addCandidate("Candidate 1");
addCandidate("Candidate 2");
}
// ...
}
當(dāng)我們將合同部署到區(qū)塊鏈時(shí)赶盔,這個(gè)遷移將會(huì)執(zhí)行企锌,并且會(huì)有兩名候選人參加我們的選舉。此時(shí)于未,您的完整合同代碼應(yīng)如下所示:
pragma solidity ^0.4.2;
contract Election {
// Model a Candidate
struct Candidate {
uint id;
string name;
uint voteCount;
}
// Read/write candidates
mapping(uint => Candidate) public candidates;
// Store Candidates Count
uint public candidatesCount;
function Election () public {
addCandidate("Candidate 1");
addCandidate("Candidate 2");
}
function addCandidate (string _name) private {
candidatesCount ++;
candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
}
}
現(xiàn)在讓我們像這樣遷移我們的合同:
$ truffle migrate --reset
現(xiàn)在嘗試與控制臺(tái)內(nèi)的候選人進(jìn)行交互撕攒。
現(xiàn)在讓我們編寫(xiě)一些測(cè)試來(lái)確保我們的智能合約正確初始化。首先烘浦,讓我解釋為什么在開(kāi)發(fā)智能合同時(shí)測(cè)試如此重要抖坪。
Testing
現(xiàn)在我們來(lái)寫(xiě)一些測(cè)試。確保你有Ganache第一次運(yùn)行闷叉。 然后擦俐,從項(xiàng)目根目錄的命令行中創(chuàng)建一個(gè)新的測(cè)試文件,如下所示::
$ touch test/election.js
我們將用the Mocha testing framwork和the Chai assertion library在這個(gè)文件中使用Javascript編寫(xiě)我們所有的測(cè)試握侧。這些與Truffle框架捆綁在一起蚯瞧。 我們將使用Javascript編寫(xiě)所有這些測(cè)試,以模擬客戶端與我們的智能合約的交互品擎,就像我們?cè)诳刂婆_(tái)中所做的一樣埋合。 這里是測(cè)試的所有代碼:
var Election = artifacts.require("./Election.sol");
contract("Election", function(accounts) {
var electionInstance;
it("initializes with two candidates", function() {
return Election.deployed().then(function(instance) {
return instance.candidatesCount();
}).then(function(count) {
assert.equal(count, 2);
});
});
it("it initializes the candidates with the correct values", function() {
return Election.deployed().then(function(instance) {
electionInstance = instance;
return electionInstance.candidates(1);
}).then(function(candidate) {
assert.equal(candidate[0], 1, "contains the correct id");
assert.equal(candidate[1], "Candidate 1", "contains the correct name");
assert.equal(candidate[2], 0, "contains the correct votes count");
return electionInstance.candidates(2);
}).then(function(candidate) {
assert.equal(candidate[0], 2, "contains the correct id");
assert.equal(candidate[1], "Candidate 2", "contains the correct name");
assert.equal(candidate[2], 0, "contains the correct votes count");
});
});
});
第一個(gè)測(cè)試通過(guò)檢查候選人數(shù)量是否等于2來(lái)檢查合同是否被初始化了正確的候選人數(shù)。
下一個(gè)考試考查每個(gè)候選人在選舉中的價(jià)值萄传,確保每個(gè)候選人都有正確的ID甚颂,姓名和投票數(shù)。
現(xiàn)在讓我們像這樣從命令行運(yùn)行測(cè)試:
$ truffle test
看秀菱,編譯通過(guò)振诬。
關(guān)注蜜蜂區(qū)塊鏈學(xué)院,這里有最新最全的區(qū)塊鏈學(xué)習(xí)課程和資料衍菱。
課程合作聯(lián)系郵箱:bd@51bitbee.com