編者按:本文系 以太坊佈道師 王仕軍講師,在由掘金技術社區主辦,以太坊社區基金會、以太坊愛好者與 ConsenSys 協辦的《開發者的以太坊入門指南 | Jeth 第一期 - 北京場》 活動上的分享整理。Jeth 圍繞以太坊技術開發主題的系列線下活動。每期 Jeth 會邀請以太坊開發領域的優秀技術團隊和工程師在線下分享技術乾貨。旨在爲開發者提供線下技術交流互動機會,幫助開發者成長。前端
智能合約全棧介紹 - Howard | Jeth 第一期react
詳解 ERC20 代幣及衆籌 - 熊麗兵 | Jeth 第一期web
王仕軍 是掘金專欄和掘金小冊做者,他著做的《區塊鏈開發入門:從 0 到 1 構建基於以太坊智能合約的 ICO DApp》銷量近千本;他創辦和維護的微信公衆號「前端週刊」目前有2600+訂閱數;此外他仍是 async/await、styled-components 高質量技術視頻教程的做者。算法
先跟你們介紹下個人背景,我是從去年五月開始學習區塊鏈的,在場的同窗可能學習區塊鏈時間有比我還長的,因此我跟你們是在同一條起跑線上。我是作前端出身,對 JavaScript 很是熟悉,今天這個分享裏的代碼除了 Solidity 其餘的都是 JS 寫的。若是作這開發時間比較長,會了解到語言間的差異其實沒有那麼大,外界有說 Solidity 跟 JS 很像,但實際上 Solidity 語言創造的時候借鑑了 Golang、 Python 和 Javascript。我所要介紹的內容就是在我接觸以太坊開發的這一段時間內,所積累出來的,怎麼把全部區塊鏈有關的點串起來作成一個能夠用的應用,在座的可能看了很多資料,可是真正動手作東西的可能不多,接下來我就正式開始個人分享。數據庫
在講具體的事情以前咱們站在軟件開發這的角度來看一下區塊鏈在計算機科學裏面,或者說區塊鏈的技術棧構成究竟是什麼樣的,區塊鏈最底層依賴於 OS,接下來算是基礎設施層,這裏面有 TCP/IP 協議和密碼學。2017年 - 2018年期間爆發的是區塊鏈協議這一層,包括分佈式、共識算法,其中共識算法如今搞的人很是多,可是實際上在分佈式裏面已經有很成熟的研究。在應用這一層如今最成功的應該當屬於以太坊,它引入是智能合約的概念以及DApp。最上面的是表現層,這個是如今全部的互聯網產品都具備的東西,好比說你作了一個網站,一個H5頁面,甚至是作了一個APP,甚至是給開發者提供一些API或者是命令行的接口。憑我本身對這個行業的觀察,我以爲不光是前端工程師,全部作應用的工程師能夠介入的點就是應用層和表現層,只不過對前端工程師來講作表現層有自然的優點。express
接下來是具體我要分享的內容:npm
相信你們都聽過帳戶、錢包、區塊、區塊鏈等沒名詞,可是怎麼把這些東西串在一塊兒,真正去琢磨、研究的人可能很少。首先回顧一下Howard 老師分享的區塊、和區塊鏈的結構。每一個區塊鏈平臺裏面都會有賬戶的機制,最成功的區塊鏈應用你們應該都知道,叫作比特幣,比特幣能夠認爲是去中心化的銀行,那不一樣的賬戶之間會有轉賬交易,一段時間以內全部的轉賬交易聚集在一塊兒造成一個區塊,區塊的數據結構上面也有介紹,我就不展開講了。小程序
隨着時間的推移多個不一樣的區塊經過一些特定的方式連起來,就造成了區塊鏈,區塊鏈上每個區塊能夠認爲是編號,這個編號就是塊高,那不一樣的鏈產生一個區塊所須要的時間是不同的,那這個時間是出塊時間,如今比特幣大概在10分鐘左右,而以太坊須要10多秒。swift
至於以太坊,它本質是個分佈式網絡,全部的消息包括轉賬、合約部署、以及合約接口調用都要經過一個節點,節點接收到消息而後把他廣播給網絡中的全部節點,而後當交易被打包以後出來的區塊一樣也會被廣播給網絡的全部節點。後端
那怎麼跟以太坊的網絡交互呢?拿如今咱們比較熟悉的微信小程序舉例,開發者能夠經過微信小程序提供的特定框架、小程序的管理後臺去創造小程序,普通用戶能夠在微信 APP 裏面使用小程序。騰訊的服務器他自己是中心化的,只有騰訊本身去維護;對應到以太坊的網絡裏面,社區給開發者提供的工具不少,能夠用 web3.js、web3j、web3.swift,也可用 etherscan,這些工具或者語言包經過某一個節點做爲入口與以太坊網絡交互,用戶經過瀏覽 DApp 或者錢包來和網絡交互。
以太坊的賬戶和比特幣的賬戶最本質的結構是很相似的,包含了三個要素:私鑰、公鑰和地址。以太坊除了主網以外而後還有三個測試網,它的主網咱們能夠理解爲傳統軟件開發環境裏面的線上環境,Rinkeby、Kovan 和 Ropsten 是三個測試網絡,這是三個測試環境是你們均可以公用的,後面的實戰案例會用到。
說完賬戶,另一個你們很熟悉的概念是區塊鏈錢包,那好比說是像 imToken 或者是 Bitpie,還有後面咱們要介紹的 Metamask,錢包和賬戶之間有什麼關係呢。咱們用這個咱們如今所熟知的金融系統裏面的錢包和賬戶來類比一下,就好比說是我在招商銀行和建設銀行開了兩三個賬戶,那我有一個錢包裝在我兜裏面的,我實際上持有5張銀行卡,對於區塊鏈裏面我安裝了一個比特派錢包,這個錢包裏面有以太坊的賬戶和比特幣的賬戶,以太坊的賬戶我能夠有不少個,比特幣的賬戶也能夠有不少個。
智能合約本質上是一個被代碼控制的賬戶,這個賬戶自己和你在錢包裏面所擁有的賬戶是相同的,不一樣的是你所擁有的賬戶的私鑰掌握在你的手裏,智能合約的則是掌握在合約部署者的手裏。關於智能合約,能夠用咱們很是熟悉的面向對象的概念作個類比:我寫了一個類 Class,那我能夠生成不少的實例,而後在區塊鏈世界裏面我有一份智能合約源代碼,能夠部署到上面介紹的幾個以太坊網絡上面,每部署一次產生的合約實例都是不同的,是徹底不一樣的賬戶,這個應該是不少作 DApp 開發的工程師迷惑的地方,也是智能合約不能升級的緣由。如今也有一些比較 「Trick」 的方法能夠完成合約的熱更新,剛興趣的同窗能夠本身去搜。
智能合約的源代碼大多數狀況下是用 Solidity 編寫的,合約賬戶和普通賬戶之間的關係用上面這張圖說明一下,開發者經過向以太坊網絡發送一個交易建立合約實例,拿到合約實例地址,有了合約實例地址以後普通用戶能夠和合約交互。好比說是 EOS 衆籌時,其實是在以太坊上發佈了一個智能合約,天天都有人蔘與進去,參與時把代幣取回來的時候就是在調對應的合約。合約和合約之間也是能夠交互的,合約賬戶是被稱爲內部賬戶,而普通用戶賬戶一般被稱爲外部賬戶。
接下來是第二個主題 —— 開發環境的準備。須要在以太坊這個區塊鏈上作開發,要有什麼條件才能夠開始呢?只有兩個關鍵點:由於它是P2P網絡,交易、合約部署都須要節點,就是說你須要有一個節點,而後任何活動都須要有賬戶,即便說你調一個不花錢的合約方法也是須要賬戶的。
具體點來講,第一個必要條件是測試網絡節點。有好多種方法,第一個本身跑一個節點,稱其爲私鏈可能不夠準確,那這個方法實施或者是理解的成本對於不作底層的同窗稍微高一點,不建議作應用層的同窗採用;若是說想把控制權掌握在本身手裏的話,那仍是很是建議作這個事情。
本地開發調試可使用 Ganache,方便地在本地起一個節點來處理交易,還有 Remix,它提供在瀏覽器內部的 JavaScript 測試網絡。成本很是低,打開就能夠用,本地測試很是的方便,Ganache 和 Remix Javascript VM 內置了已經解鎖的賬戶,不須要去關心賬戶的私鑰或助記詞。
還可使用共享測試網絡,就是前面提到的 Rinkeby、Ropsten 和 Kovan,使用這幾個做爲網絡入口的節點,咱們不須要本身跑節點。infura.io 則是爲廣大開發者提供區塊連接入的服務,不過使用他須要咱們有本身註冊、本身管理錢包和賬戶。
這二個必要條件是賬戶和餘額,由於以太坊上的任何操做都須要賬戶纔可以發起,因此咱們須要建立錢包和賬戶。咱們開發的 DApp 是運行在瀏覽器裏面的,對於 PC 端來講錢包最好是能和瀏覽器無縫集成的。目前社區中有個很好的選擇是 Metamask,它其實是一個瀏覽器插件,可是 Metamask 歷史上出了一個安全事故,有人發了個假的 Metamask,有一部分的用戶上當了,所以你們安裝的時候必定要認準狐狸圖標。
另一點以太坊上的不少交易都是要收費的,這個也是它如今最大的痛點,部分區塊鏈項目在解決這個問題,目前咱們開發尚未辦法繞過他。在作DApp 測試的時候咱們不須要去花費真金白銀,可使用不一樣的測試網提供的 faucet 給測試網的賬戶充值,即把 ETH 充到 Metamask 錢包裏面。以上就是準備開發環境的兩個必要的條件,須要動手作的就是 Metamask、充值還有註冊 infura.io。
接下來是如何作一個完整的應用。對應到傳統的應用開發,先要有一個後端而後有一個前端,咱們先介紹後端部分。後端在以太坊上面能夠粗暴的用智能合約代替,複雜應用中全部的數據應該不是所有存儲在以太坊區塊鏈上,有一部分數據是存在傳統的數據庫裏面,這裏咱們簡化設定全部數據都存在鏈上。
作智能合約開發有兩種方式,第一種是經過 Remix 在線 IDE ,這個 IDE 還算好用。另外一種方式,對於前端來講,就是用你熟悉的編輯器加上你熟悉的語言去作。
Remix 適合作咱們快速的驗證概念和原型,在 Remix中能夠快速寫合約代碼,而後調用它的合約接口,測試它的行爲,此外還能夠測試已有的合約實例,咱們能夠從以太坊的線上環境和測試環境把合約實例加載到 Remix 裏面而後測試,也能夠經過 Remix 把合約部署到任何以太坊網絡上面。Remix 還可用來作單步調試,當你發現合約某一些接口有奇怪問題的時候能夠用 Remix 作單步調試。可是 Remix 有個明顯的缺點,在傳統的軟件開發工做流裏面,一般會有版本管理,在 Remix 裏是作不了的,但在咱們本身的工做流裏面是能夠的,咱們能夠用 Git 作版本管理,而後保存合約編譯部署的結果,最重要的一點就是能把能夠自動化的都自動化了,由於自動化以後出錯的可能性會小不少。一般在實際的工做當中或者是說學習過程中這 Remix 和自動化工做流會結合使用,來回切換使用。
接下來就是實戰 DApp 裏面用到的很是簡單的智能合約,能夠認爲這是一個以博彩原型,代碼很是簡單,合約有兩個屬性,合約的管理員,以及誰參與了博彩活動。構造函數做用是設定合約管理員,而後參與抽獎/下注接口,再而後是隨機數函數,在以太坊區塊鏈裏面沒有很是好的隨機數的方法,合約部署到以太坊上以後隨機數的代碼別人是能夠看到的,它可以知道你種子是怎麼來的,而後很容易被操控。pickWinner 是開獎接口,裏面調用了隨機數的函數,而後把整個獎池裏面的錢都轉給贏家。合約自己是不收任何手續費的,只不過是在調用接口的時候收手續費。modifier 是作了安全的限制,開獎的接口只能是合約的管理員才能夠調用。安全、權限也是智能合約要重點考慮的問題,最近兩個月爆發出來的合約的漏洞很是多,有一部分安全限制,還有一部分是溢出,值得你們關注。
合約在 Remix 裏面的工做流,簡單給你們演示下,在 Remix 裏選 JavaScript VM,它是 Remix 提供的跑在瀏覽器內存裏的一個測試網絡,它的響應速度很是快,選擇 JavaScript VM 以後默認這有幾個賬戶,裏面的餘額是 100 ETH,點擊 Deploy 把合約部署一下,能夠看到很快合約實例就有了,實例界面中紅色的是合約接口,藍色是合約屬性。而後咱們怎麼去下注呢?
能夠看到下注並不須要提供參數,下注金額須要在發起交易的地方填寫,用合約管理員下注一次,如今玩家有一我的,下面換一個賬戶,再下一次注,就是模擬兩我的,這裏就變成了兩我的。用第二個賬戶去調開獎接口時候直接報錯了,那是由於咱們的合約代碼裏面強制了必須是管理員才能夠調用,失敗的例證就是沒有人拿到獎池裏面的錢,把賬戶切回去從新調開獎接口,能夠看到第一我的拿到了獎池裏面全部的錢,這個是很是簡單的 Remix 合約工做流演示。
那怎麼把這個 Remix 裏面作的事情自動化?接下來我介紹一下怎麼在 Node.js 裏面作智能合約的工做流。我先介紹兩個工具,第一個是把智能合約的源代碼編譯,編譯會產生字節碼 ByteCode,這個是部署到測試網絡時用的;以及接口聲明 ABI,經過 ABI 實際業務代碼就能知道這個合約到底暴露了哪些接口,每一個接口接收參數的類型和數量。
接下來工做流裏作的事情就是圍繞這個圖展開的,能夠看到這裏面用到了 web3.js,能夠把 web3.js 理解爲應用層的代碼通向以太坊網絡的一個橋樑。它做爲橋樑的方式是可使用不少不一樣的插件,在 web3 裏面叫 Provider,我在瀏覽器當中運行時,Metamask也提供了一個插件;在本地的話,Ganache-cli 提供了一個插件;若是隻想調用 infura.io 提供的入口節點,那能夠經過 HTTP Provider。
咱們要作的第一步把智能合約的源代碼變成能夠部署到以太坊網絡上的 ByteCode 以及咱們應用層代碼可使用的 ABI。編譯腳本的代碼也很簡單,咱們能夠把文件讀出來,準備一個結果保存目錄,而後調用 solc 的 compile,接下來會處理編譯結構裏面的錯誤,最後把編譯結果寫到文件系統裏面。
合約構建完以後怎麼經過 web3 測試它?直接使用 Ganache-cli 就好了,咱們在測試合約代碼以前,要初始化 web3 的實例,直接使用 Ganache的 Provider 插件。測試裏面咱們會先建立一個隔離的測試環境,即跑每一個測試的時候咱們會從新部署合約,而後調用這個新合約上的方法,或者是嘗試修改他的狀態,最後對他的狀態作斷言。
接下來是部署合約,在建立 Contract 的時候咱們要把 ABI 傳進去,在 Deploy 裏把它的 ByteCode 傳進去,能夠看到咱們作任何事情都須要有賬戶,gas 是咱們願意爲這個操做最多支付多少的手續費。關於 gas 有兩個參數調節,一個是 gas limited,另外一個是 gas pressed,感興趣的同窗能夠自行去研究。
再來看一個抽獎合約完整的流程測試,先調用下注接口,用得仍是解鎖出來賬戶裏面第一個,而後取出來合約的玩家,確認玩家是咱們下注過的人,接下來檢查賬戶 initialBalance 和帳戶抽獎完成以後的餘額狀態。在這個地方調用了開獎接口,開獎以後對於部署合約的賬戶的餘額作了一個斷言,最後是咱們斷言中每次開獎以後這個合約裏面的玩家要被清空。
合約部署所須要作的事情跟合約自動化測試時作的事情有不少類似的地方,不過部署的網絡不是 Ganache-cli 提供的本地網絡,而是 Rinkaby 測試網絡,這裏用到了一個插件,咱們能夠提供一個錢包的助記詞,以及一個網絡入口的節點,它經過 HTTP 的方式給節點發送消息進行交易。接下來作的事情跟每次隔離測試環境相似,先解鎖賬戶,而後部署合約。由於咱們是部署在真實的測試網絡上,這個過程一般花費的時間比較長。每次部署完以後咱們會有一個合約賬戶的地址,咱們能夠經過這個地址跟合約交互。
最後是把整個流程串起來的過程,部署以前必需要從新編譯,而且確保全部單元測試在最新的合約上是能夠徹底經過的。
合約部署完以後,咱們在以太坊的區塊鏈上已經有一個咱們能夠直接與他交互的後端了,那接下來咱們須要寫的就是作這個應用層的代碼和後端的交互,以及給DApp加上界面。
從技術視角來看這個DApp的本質,咱們這沒有討論商業和其餘方面的東西,就是 DApp 能夠理解爲就是小白用戶可使用的,能夠和前端數據交互、讀取的界面。它的形態能夠是不少種,能夠是 Web、App,也能夠是桌面軟件。若是是很是簡單的 DApp 你能夠作成以區塊鏈爲後端的單頁應用。
接下來咱們作一個很是簡單的抽獎或者是博彩的 DApp,可使用 create-react-app,使得咱們的技術棧變得很是簡單。還有一個是 web3.js,合約工做流裏面用的比較多,在 DApp 裏面用 web3.js 是跟 ABI 打交道。若是開發完,整個合約加上 DApp 的目錄結構如同上圖的,編譯部署腳本和測試腳本分別放在對應的目錄下面。
抽獎 Dapp 能夠說是至關簡單,甚至至關簡陋,咱們能夠把界面對應到合約的源代碼上面,如今獎池的金額有 1 個 ETH ,有 1 人蔘與,但這個 Balance 屬性在以前的合約代碼中沒有提到,實際上合約實例是一個賬戶,那在任何的一個賬戶上面都是有餘額的,共有 1 我的參與抽獎,有兩個按鈕,一個是下注,調用的的合約接口是 participate;還有就是開獎,對應的接口是 pickWinner。
下面咱們來看一下大概的關鍵代碼實現。第一個就是使用 web3 把咱們的橋樑建起來,這裏面咱們假設使用這個 DApp 的用戶安裝了 Metamask,第二個關鍵的地方是咱們新建這個合約的時候不像部署和編譯腳本,傳入了一個地址,這個不是徹底新建的合約實例,而是從這個地址把這個合約加載進來。
DApp 和智能合約關鍵的交互就是兩點,一個是讀取合約數據,還有一個就是提交數據。那能夠看到這裏面咱們在界面上顯示三個屬性,一個是管理員,一個是玩家的數量還有一個是合約裏面獎池的金額,也就是合約實例他的賬戶餘額,這裏面調的三個接口都是異步的。
渲染合約數據由於是 React 語法也比較簡單,DApp 開發的同窗必定要注意單位之間的相互轉換,咱們展現給用戶的是ETH。
修改合約數據有一個是觸發點在下方,給這個按鈕綁 onClick,裏面作的事情其實是調取了合約的方法而後把組件狀態作了一些變動。在提交交易的時候那咱們在組件上設置 loading 狀態,loading 狀態下按鈕是不能從新點擊的,而後調 participate,若是安裝了 Metamask,傳給合約的值要填寫進去,等咱們這個操做完成後,咱們會把這個頁面刷新一下,合約數據狀態會被從新加載。
真正要作出用戶友好的 DApp,須要在這個基礎上作不少的事情,好比說點擊按鈕並安裝了這個 Metamask 的以後,用戶的屏幕上立刻就彈出了一個 Metamask 交易確認界面,會使用戶疑惑,我明明在用你這個網站怎麼出來了一個我沒有見過的東西,彈出 Metamask 交易確認框過程中須要給用戶一些提示,讓用戶不會感受到意外。
抽獎 DApp 流程我已經部署到我在阿里雲上的一臺機器上面,部署的過程跟傳統的 WEB 應用的部署相似,部署前須要對代碼作構建,構建使用 create-react-app 內置的構建腳本就能夠了,構建產生的是純靜態文件,而後咱們用 express 來啓動極簡的 http 服務,管理服務進程使用 pm2,部署和構建的過程也可使用 npm script 串起來。
到這裏我要分享的內容就結束了,區塊鏈領域雖然已經存在了 9 年,可是開發者大量涌入是在最近這一兩年的時間,這個領域裏面有不少的概念的同時,也有比較好的實踐,歡迎你們在微信羣裏跟我多多交流,謝謝。