以太坊教程:搭建環境、編寫編譯一個智慧合約

jimi發表於2018-05-23

本以太坊教程主要是介紹:搭建一個開發環境、編寫編譯一個智慧合約。

以太坊是什麼

以太坊(Ethereum)是一個開源的有智慧合約功能的公共區塊鏈平臺。透過其專用加密貨幣以太幣(Ether)提供去中心化的虛擬機器(“以太虛擬機器” Ethereum Virtual Machine)來處理點對點合約。

以太坊的概念首次在 2013 至 2014 年間由程式設計師 Vitalik Buterin,受比特幣啟發後提出,大意為 “下一代加密貨幣與去中心化應用平臺”,在 2014 年透過 ICO 眾籌得以開始發展。目前以太幣是市值第二高的加密貨幣,僅次於比特幣。

以太坊區塊鏈是什麼?

以太坊區塊鏈有 2 個主要元件:

  • 資料儲存:網路中每筆交易都儲存在區塊鏈上。當你部署合約時,就是一筆交易。當你執行合約功能時,也是另一筆交易。所有的這些交易都是公開的,每個人都可以看到並進行驗證。這個資料永遠也無法篡改。為了確保網路中的所有節點都有著同一份資料複製,並且沒有向區塊鏈中寫入任何的無效資料,以太坊使用一個叫做工作量證明的演算法來保證網路安全。

  • 程式碼:就資料的層面而言,區塊鏈就是儲存交易。在以太坊的世界裡,你可以透過一個叫 Solidity 的語言編寫邏輯/應用程式碼(也就是智慧合約)。然後用 solidity 編譯器將程式碼編譯為以太坊位元組碼,並將位元組碼部署到區塊鏈上(也有一些其他的語言可以寫合約,不過 solidity 是到目前為止用得最多也是相對更容易的選擇)。所以,以太坊不僅僅會儲存交易資料,它還會儲存和執行智慧合約程式碼。

可以簡單的理解以太坊區塊鏈的作用就是儲存資料和程式碼,並在 EVM(Ethereum Virtual Machine,以太坊虛擬機器)中執行程式碼。

要準備的基礎知識

為了進行以太坊開發,你應該對以下語言/技術有基本瞭解:

  • 熟悉某種面嚮物件語言(如 Python,Java,go)
  • HTML/CSS/Javascript
  • 基本的命令列互動如 Linux shell 命令
  • 理解資料庫的基本概念

為了構建以太坊去中心化應用即 Dapp(Decentralized application),以太坊有一個非常方便的 JavaScript 庫即 web3.js,你也可以在一些 js 框架中直接引入該庫構建應用,比如 react,angular,vue 等。

示例:一個以太坊投票應用

以太坊教程示例中,我們將會構建一個簡單的去中心化投票應用。所謂去中心化應用,就是一個不只存在於某一中心化伺服器上的應用。在網路中成百上千的電腦上,會執行著非常多的應用副本,這使得它幾乎不可能出現當機的情況。你將會構建一個投票應用,在這個應用中,你可以初始化參與選舉的候選者,並對候選者投票,而這些投票將會被記錄在區塊鏈上。你將會經歷編寫投票合約,部署到區塊鏈並與之互動的整個過程。你將會了解什麼是一個合約,將合約部署到區塊鏈上並與之互動意味著什麼。

本質上,區塊鏈就像是一個分散式資料庫,這個資料庫維護了一個不斷增長的記錄連結串列。如果熟悉關係型資料庫,你應該知道一張表裡有很多行的資料。現在,對資料進行批(batch)量處理(比如每批 100 行),並將每個處理的批次相連。就可以形成一個區塊鏈了!在區塊鏈裡,每個批次的資料就叫一個塊(block),塊裡的每一行就叫一筆交易(transaction)。

現在,你對以太坊已經有了基本瞭解,我們可以開始構建投票的 dapp 了。這將會加強你對以太坊的認識,並且初略瞭解以太坊的功能。

以太坊開發環境搭建

Linux

示例是 Ubuntu 16.04 下的學習環境搭建,你只需要成功安裝了 nodejs 和 npm,就可以繼續專案的下一步了。

我們透過 npm 安裝 ganache 和 web3 包來為以太坊教程提供支撐。我們也需要安裝 solc 來編譯合約。

下面是安裝過程:

$ sudo apt-get update
$ curl -sL https://deb.nodesource.com/setup_7.x -o nodesource_setup.sh
$ sudo bash nodesource_setup.sh
$ sudo apt-get install nodejs
$ node --version
v7.4.0
$ npm --version
4.0.5
$ mkdir -p ethereum_voting_dapp/chapter1
$ cd ethereum_voting_dapp/chapter1
$ npm install ganache-cli web3@0.20.1 solc
$ node_modules/.bin/ganache-cli

如果安裝成功,執行命令 node_modules/.bin/ganache-cli,應該能夠看到下面的輸出。

Ganache CLI v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0x5c252a0c0475f9711b56ab160a1999729eccce97
(1) 0x353d310bed379b2d1df3b727645e200997016ba3
(2) 0xa3ddc09b5e49d654a43e161cae3f865261cabd23
(3) 0xa8a188c6d97ec8cf905cc1dd1cd318e887249ec5
(4) 0xc0aa5f8b79db71335dacc7cd116f357d7ecd2798
(5) 0xda695959ff85f0581ca924e549567390a0034058
(6) 0xd4ee63452555a87048dcfe2a039208d113323790
(7) 0xc60c8a7b752d38e35e0359e25a2e0f6692b10d14
(8) 0xba7ec95286334e8634e89760fab8d2ec1226bf42
(9) 0x208e02303fe29be3698732e92ca32b88d80a2d36

Private Keys
==================
(0) a6de9563d3db157ed9926a993559dc177be74a23fd88ff5776ff0505d21fed2b
(1) 17f71d31360fbafbc90cad906723430e9694daed3c24e1e9e186b4e3ccf4d603
(2) ad2b90ce116945c11eaf081f60976d5d1d52f721e659887fcebce5c81ee6ce99
(3) 68e2288df55cbc3a13a2953508c8e0457e1e71cd8ae62f0c78c3a5c929f35430
(4) 9753b05bd606e2ffc65a190420524f2efc8b16edb8489e734a607f589f0b67a8
(5) 6e8e8c468cf75fd4de0406a1a32819036b9fa64163e8be5bb6f7914ac71251cc
(6) c287c82e2040d271b9a4e071190715d40c0b861eb248d5a671874f3ca6d978a9
(7) cec41ef9ccf6cb3007c759bf3fce8ca485239af1092065aa52b703fd04803c9d
(8) c890580206f0bbea67542246d09ab4bef7eeaa22c3448dcb7253ac2414a5362a
(9) eb8841a5ae34ff3f4248586e73fcb274a7f5dd2dc07b352d2c4b71132b3c73f0

HD Wallet
==================
Mnemonic:   cancel better shock lady capable main crunch alcohol derive alarm duck umbrella
Base HD Path: m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

為了便於測試,ganache 預設會建立 10 個賬戶,每個賬戶有 100 個以太。如果你還不懂什麼是賬戶,把它想象成存錢的銀行賬戶就可以了(以太(Ether,ETH)就是以太坊生態系統中的 錢/貨幣)。你需要用這個賬戶建立交易,傳送/接收以太。

MacOS

如果你還沒有安裝 homebrew,請按照 https://brew.sh/ 的指示安裝 homebrew。homebrew 是一個包管理器,它可以幫助我們安裝開發所需的所有其他軟體。按照下面的指示安裝所有其他所需的包。

$ brew update
$ brew install nodejs
$ node --version
v7.10.0
$ npm --version
4.2.0
$ mkdir -p ethereum_voting_dapp/chapter1
$ cd ethereum_voting_dapp/chapter1
$ npm install ganache-cli web3@0.20.1 solc
$ node_modules/.bin/ganache-cli

我們透過 npm 安裝 ganache 和 web3 包。我們也需要安裝 solc 來編譯合約。

如果安裝成功,執行命令 node_modules/.bin/ganache-cli,應該能夠看到右圖所示的輸出。

Ganache CLI v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0x5c252a0c0475f9711b56ab160a1999729eccce97
(1) 0x353d310bed379b2d1df3b727645e200997016ba3
(2) 0xa3ddc09b5e49d654a43e161cae3f865261cabd23
(3) 0xa8a188c6d97ec8cf905cc1dd1cd318e887249ec5
(4) 0xc0aa5f8b79db71335dacc7cd116f357d7ecd2798
(5) 0xda695959ff85f0581ca924e549567390a0034058
(6) 0xd4ee63452555a87048dcfe2a039208d113323790
(7) 0xc60c8a7b752d38e35e0359e25a2e0f6692b10d14
(8) 0xba7ec95286334e8634e89760fab8d2ec1226bf42
(9) 0x208e02303fe29be3698732e92ca32b88d80a2d36

Private Keys
==================
(0) a6de9563d3db157ed9926a993559dc177be74a23fd88ff5776ff0505d21fed2b
(1) 17f71d31360fbafbc90cad906723430e9694daed3c24e1e9e186b4e3ccf4d603
(2) ad2b90ce116945c11eaf081f60976d5d1d52f721e659887fcebce5c81ee6ce99
(3) 68e2288df55cbc3a13a2953508c8e0457e1e71cd8ae62f0c78c3a5c929f35430
(4) 9753b05bd606e2ffc65a190420524f2efc8b16edb8489e734a607f589f0b67a8
(5) 6e8e8c468cf75fd4de0406a1a32819036b9fa64163e8be5bb6f7914ac71251cc
(6) c287c82e2040d271b9a4e071190715d40c0b861eb248d5a671874f3ca6d978a9
(7) cec41ef9ccf6cb3007c759bf3fce8ca485239af1092065aa52b703fd04803c9d
(8) c890580206f0bbea67542246d09ab4bef7eeaa22c3448dcb7253ac2414a5362a
(9) eb8841a5ae34ff3f4248586e73fcb274a7f5dd2dc07b352d2c4b71132b3c73f0

HD Wallet
==================
Mnemonic:   cancel better shock lady capable main crunch alcohol derive alarm duck umbrella
Base HD Path: m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

為了便於測試,ganache 預設會建立 10 個賬戶,每個賬戶有 100 個以太。如果你還不懂什麼是以太坊賬戶,把它想象成存錢的銀行賬戶就可以了(以太(Ether, ETH)就是以太坊生態系統中的錢/貨幣)。你需要用這個賬戶建立交易,傳送/接收以太。

Windows

  • 安裝 Visual Studio Community Edition。如果你選擇定製安裝,那麼至少應該安裝 Visual C++(目前的版本是 VS 2017)
  • 安裝 Windows SDK for Windows
  • 安裝 Python 2.7 如果你還沒有安裝的話,並且確保將它加入到環境變數 PATH
  • 安裝 git 如果你還沒有安裝並加入到 PATH
  • 安裝 OpenSSL。確保選擇了正確的安裝包,並且只安裝完整版(而不是輕裝版)。你必須將 OpenSSL 安裝到推薦安裝的位置 -- 不要改變安裝路徑
  • 下載和安裝 node v8.1.2 。不推薦使用版本 v6.11.0 搭配 VS2017
  • 執行命令 npm install ganache-cli web3@0.20.1 solc

Solidity Contracts

現在已經安裝好 ganache 並執行,我們將會開始編寫第一個以太坊智慧合約。

我們會使用 solidity 程式語言來編寫合約。如果你熟悉物件導向程式設計,學習用 solidity 寫合約應該非常簡單。我們會寫一個叫做 Voting 的合約(可以把合約看成是面對物件程式語言的一個類),這個合約有以下內容:

  • 一個建構函式,用來初始化一些候選者。
  • 一個用來投票的方法(對投票數加 1)
  • 一個返回候選者所獲得的總票數的方法

當你把合約部署到區塊鏈的時候,就會呼叫建構函式,並只呼叫一次。與 web 世界裡每次部署程式碼都會覆蓋舊程式碼不同,在區塊鏈上部署的合約是不可改變的,也就是說,如果你更新合約並再次部署,舊的合約仍然會在區塊鏈上存在,並且資料仍在。新的部署將會建立合約的一個新的例項。

pragma solidity ^0.4.18;

contract Voting {

  mapping (bytes32 => uint8) public votesReceived;
  bytes32[] public candidateList;

  function Voting(bytes32[] candidateNames) public {
    candidateList = candidateNames;
  }

  function totalVotesFor(bytes32 candidate) view public returns (uint8) {
    require(validCandidate(candidate));
    return votesReceived[candidate];
  }

  function voteForCandidate(bytes32 candidate) public {
    require(validCandidate(candidate));
    votesReceived[candidate]  += 1;
  }

  function validCandidate(bytes32 candidate) view public returns (bool) {
    for(uint i = 0; i < candidateList.length; i++) {
      if (candidateList[i] == candidate) {
        return true;
      }
    }
    return false;
   }
}

將右側程式碼複製到一個叫做 Voting.sol 的檔案中,並儲存到 chapter1 目錄下面。

程式碼和解釋

  • Line 1. 我們必須指定程式碼將會哪個版本的編譯器進行編譯
  • Line 3. mapping 相當於一個關聯陣列或者是字典,是一個鍵值對。mapping votesReceived 的鍵是候選者的名字,型別為 bytes32。mapping 的值是一個未賦值的整型,儲存的是投票數。
  • Line 4. 在很多程式語言中,僅僅透過 votesReceived.keys 就可以獲取所有的候選者姓名。但是,但是在 solidity 中沒有這樣的方法,所以我們必須單獨管理一個候選者陣列 candidateList。
  • Line 14. 注意到 votesReceived[key] 有一個預設值 0,所以你不需要將其初始化為 0,直接加 1 即可。

你也會注意到每個函式有個可見性說明符(visibility specifier)(比如本例中的 public)。這意味著,函式可以從合約外呼叫。如果你不想要其他任何人呼叫這個函式,你可以把它設定為私有(private)函式。如果你不指定可見性,編譯器會丟擲一個警告。最近 solidity 編譯器進行了一些改進,如果使用者忘記了對私有函式進行標記導致了外部可以呼叫私有函式,編譯器會捕獲這個問題。 這裡 可以看到所有的可見性說明符。

你也會在一些函式上看到一個修飾符 view。它通常用來告訴編譯器函式是隻讀的(也就是說,呼叫該函式,區塊鏈狀態並不會更新)。所有的修飾符都可以在 這裡 看到。

編譯智慧合約

我們將會使用上一節安裝的 solc 庫來編譯程式碼。如果你還記得的話,之前我們提到過 web3js 是一個庫,它能夠讓你透過 RPC 與區塊鏈進行互動。我們將會在 node 控制檯裡用這個庫部署合約,並與區塊鏈進行互動。

首先,在終端中執行 node 進入 node 控制檯,初始化 web3 物件,並向區塊鏈查詢獲取所有的賬戶。

確保與此同時 ganache 已經在另一個視窗中執行

為了編譯合約,先從 Voting.sol 中載入程式碼並繫結到一個 string 型別的變數,然後像下邊這樣對合約進行編譯。

$ node

In the node console
> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
> web3.eth.accounts
['0x5c252a0c0475f9711b56ab160a1999729eccce97'
'0x353d310bed379b2d1df3b727645e200997016ba3'
'0xa3ddc09b5e49d654a43e161cae3f865261cabd23'
'0xa8a188c6d97ec8cf905cc1dd1cd318e887249ec5'
'0xc0aa5f8b79db71335dacc7cd116f357d7ecd2798'
'0xda695959ff85f0581ca924e549567390a0034058'
'0xd4ee63452555a87048dcfe2a039208d113323790'
'0xc60c8a7b752d38e35e0359e25a2e0f6692b10d14'
'0xba7ec95286334e8634e89760fab8d2ec1226bf42'
'0x208e02303fe29be3698732e92ca32b88d80a2d36']


> code = fs.readFileSync('Voting.sol').toString()
> solc = require('solc')
> compiledCode = solc.compile(code)

當你成功地編譯好合約,列印 compiledCode 物件(直接在 node 控制檯輸入 compiledCode 就可以看到內容),你會注意到有兩個重要的欄位,它們很重要,你必須要理解:

  • 1.compiledCode.contracts[':Voting'].bytecode: 這就是 Voting.sol 編譯好後的位元組碼。也是要部署到區塊鏈上的程式碼。
  • 2.compiledCode.contracts[':Voting'].interface: 這是一個合約的介面或者說模板(叫做 abi 定義),它告訴了使用者在這個合約裡有哪些方法。在未來無論何時你想要跟任意一個合約進行互動,你都會需要這個 abi 定義。你可以在這裡 看到 ABI 的更多內容。

教程參考匯智網的 DAPP 開發入門教程,如果大家等不及部落格更新,也可以直接訪問這個以太坊教程

相關文章