原文:
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
到目前為止,我們一直在使用瀏覽器 Solidity 來編寫和編譯 Solidity 代碼。 我們使用 web3.js 來測(cè)試我們的合同。 我們也可以使用 Solidity 在線 IDE 進(jìn)行測(cè)試。 因?yàn)槲覀冎痪幾g了一個(gè)小合同,而且導(dǎo)入的內(nèi)容很少,所以這似乎沒問題。 當(dāng)您開始構(gòu)建大型和復(fù)雜的智能合約時(shí),您將開始遇到使用當(dāng)前流程進(jìn)行編譯和測(cè)試的問題。 在本章中,我們將學(xué)習(xí)有關(guān) truffle 的知識(shí),它使得構(gòu)建企業(yè)級(jí) DApps 變得容易,通過構(gòu)建一個(gè)另類幣。 除了比特幣之外的所有加密貨幣都被稱為另類幣。
在本章中,我們將涵蓋以下主題:
-
什么是 節(jié)點(diǎn),以及如何使用它?
-
事件主題是什么?
-
使用 包處理合同。
-
安裝 truffle 并探索 truffle 命令行工具和配置文件
-
使用 truffle 編譯、部署和測(cè)試 Solidity 代碼
-
通過 NPM 和 EthPM 進(jìn)行包管理
-
使用 truffle 控制臺(tái)和編寫外部腳本
-
使用 truffle 構(gòu)建 DApp 的客戶端
是一個(gè)基于 Node.js 的以太坊節(jié)點(diǎn),用于測(cè)試和開發(fā)。 它模擬完整節(jié)點(diǎn)行為,使以太坊應(yīng)用程序的開發(fā)速度加快。 它還包括所有流行的 RPC 函數(shù)和功能(例如事件),并且可以以確定性的方式運(yùn)行,以使開發(fā)變得輕松愉快。
它是用 JavaScript 編寫的,并作為一個(gè) 包分發(fā)。 在撰寫本文時(shí),最新版本的 是 3.0.3,至少需要 Node.js 版本 6.9.1 才能正常運(yùn)行。
它將所有內(nèi)容保存在內(nèi)存中; 因此,每次節(jié)點(diǎn)重新啟動(dòng)時(shí),它都會(huì)丟失先前的狀態(tài)。
有三種方法可以使用 模擬以太坊節(jié)點(diǎn)。 每種方法都有自己的用例。 讓我們來探索一下。
testrpc 命令可用于模擬以太坊節(jié)點(diǎn)。 要安裝此命令行應(yīng)用程序,您需要全局安裝 :
這里是可以提供的各種選項(xiàng):
-
或 :這指定了啟動(dòng)時(shí)要生成的帳戶數(shù)。
-
或 :這指定了自動(dòng)挖礦的秒數(shù)。 默認(rèn)值為 0,沒有自動(dòng)挖礦。
-
或 :每次運(yùn)行節(jié)點(diǎn)時(shí),它將生成 10 個(gè)確定性地址; 也就是說,當(dāng)您提供此標(biāo)志時(shí),每次都會(huì)生成相同的地址集。 這個(gè)選項(xiàng)還可以根據(jù)預(yù)定義的助記詞生成確定性地址。
-
或 :默認(rèn)情況下鎖定可用帳戶。 當(dāng)此選項(xiàng)在不帶 選項(xiàng)的情況下使用時(shí),將不會(huì)創(chuàng)建 HD 錢包。
-
或 :使用特定的 HD 錢包助記詞來生成初始地址。
-
或:要監(jiān)聽的端口號(hào)。默認(rèn)為 8545。
-
或:要監(jiān)聽的主機(jī)名。默認(rèn)為 Node 的默認(rèn)值。
-
或:生成 HD 錢包助記詞所需的任意數(shù)據(jù)。
-
或:使用自定義的 gas 價(jià)格(默認(rèn)為 1)。如果在向節(jié)點(diǎn)發(fā)送交易時(shí)未提供 gas 價(jià)格,則將使用此 gas 價(jià)格。
-
或:使用自定義的 gas 限制(默認(rèn)為 0x47E7C4)。如果在向節(jié)點(diǎn)發(fā)送交易時(shí)未提供 gas 限制,則將使用此 gas 限制。
-
或:這是從另一個(gè)當(dāng)前運(yùn)行的以太坊節(jié)點(diǎn)在給定塊中分叉。輸入應(yīng)該是其他客戶端的 HTTP 位置和端口;例如,??梢赃x擇指定要從中分叉的塊,使用@標(biāo)記:。
-
:輸出用于調(diào)試的虛擬機(jī)操作碼。
-
:此選項(xiàng)用于導(dǎo)入賬戶。它可以任意多次指定,傳遞任意的私鑰和它們關(guān)聯(lián)的余額來生成初始地址。在使用時(shí),不會(huì)為你創(chuàng)建一個(gè) HD 錢包。
-
或:指定任意次數(shù),傳遞要解鎖的特定賬戶的地址或賬戶索引。當(dāng)與同時(shí)使用時(shí),將覆蓋指定帳戶的鎖定狀態(tài):。還可以通過編號(hào)指定解鎖賬戶:。該功能也可以用于冒充帳戶和解鎖您原本無法訪問的地址。在與功能一起使用時(shí),可以使用以任何區(qū)塊鏈上的地址進(jìn)行交易,這在測(cè)試和動(dòng)態(tài)分析中非常有用。
-
:用于指定此節(jié)點(diǎn)所屬的網(wǎng)絡(luò) ID。
請(qǐng)注意,私鑰長(zhǎng)度為 64 個(gè)字符,必須輸入為帶有 0x 前綴的十六進(jìn)制字符串。余額可以輸入為整數(shù),也可以輸入為以太幣金額的 0x 前綴十六進(jìn)制值。
你可以像這樣將用作提供者:
你可以像這樣將用作通用 HTTP 服務(wù)器:
和都接受一個(gè)允許您指定行為的對(duì)象。此參數(shù)是可選的??捎眠x項(xiàng)如下:
-
:值是對(duì)象的數(shù)組。每個(gè)對(duì)象都應(yīng)該有一個(gè)帶有十六進(jìn)制值的余額鍵。也可以指定 secretKey 鍵,它表示帳戶的私鑰。如果沒有 secretKey,地址將根據(jù)給定的余額自動(dòng)生成。如果指定,密鑰用于確定帳戶的地址。
-
: 輸出用于調(diào)試的 VM 操作碼。
-
: 值是實(shí)現(xiàn) 函數(shù)的對(duì)象。
-
: 使用特定的 HD 錢包助記詞生成初始地址。
-
: 作為服務(wù)器運(yùn)行時(shí)要監(jiān)聽的端口號(hào)。
-
: 生成 HD 錢包助記詞所需的任意數(shù)據(jù)。
-
: 啟動(dòng)時(shí)要生成的帳戶數(shù)。
-
: 與前述 選項(xiàng)相同。
-
: 與 選項(xiàng)相同。用于指定此節(jié)點(diǎn)所屬的網(wǎng)絡(luò) ID。
-
: 第一個(gè)區(qū)塊應(yīng)該開始的日期。與 方法一起使用此功能來測(cè)試“依賴于時(shí)間”的代碼。
-
: 指定默認(rèn)情況下是否鎖定帳戶。
-
: 一個(gè)指定應(yīng)解鎖哪些帳戶的地址或地址索引的數(shù)組。
這是使用 提供的 RPC 方法列表:
還有一些特殊的非標(biāo)準(zhǔn)方法未包含在原始 RPC 規(guī)范中:
-
: 在當(dāng)前區(qū)塊中快照區(qū)塊鏈的狀態(tài)。不帶參數(shù)。返回創(chuàng)建的快照的整數(shù) ID。
-
: 將區(qū)塊鏈狀態(tài)恢復(fù)到先前的快照。接受一個(gè)參數(shù),即要恢復(fù)到的快照 ID。如果未傳遞快照 ID,則將恢復(fù)到最新的快照。返回 true。
-
: 向前跳轉(zhuǎn)時(shí)間。帶一個(gè)參數(shù),即要增加的秒數(shù)。返回秒數(shù)的總時(shí)間調(diào)整。
-
: 強(qiáng)制挖掘一個(gè)區(qū)塊。不帶參數(shù)。獨(dú)立于挖礦是否啟動(dòng)或停止而挖掘一個(gè)區(qū)塊。
主題是用于索引事件的值。您不能沒有主題搜索事件。每當(dāng)調(diào)用事件時(shí),都會(huì)生成一個(gè)默認(rèn)主題,該主題被視為事件的第一個(gè)主題。一個(gè)事件最多可以有四個(gè)主題。主題始終以相同的順序生成。您可以使用一個(gè)或多個(gè)主題搜索事件。
第一個(gè)主題是事件的簽名。剩下的三個(gè)主題是索引參數(shù)的值。如果索引參數(shù)是、或,那么其 keccak-256 哈希就是主題。
讓我們舉一個(gè)示例來理解主題。假設(shè)有這樣一個(gè)事件:
這里生成了四個(gè)主題。它們分別是:
-
:這是第一個(gè)主題。它是使用生成的。在這里,您可以看到所有類型都是規(guī)范形式的。
-
:這是第二個(gè)主題。它是使用生成的。
-
第三和第四個(gè)主題分別為和,也就是值的十六進(jìn)制表示。它們分別使用和計(jì)算得出。
在內(nèi)部,您的以太坊節(jié)點(diǎn)將使用主題構(gòu)建索引,以便您可以輕松找到基于簽名和索引值的事件。
假設(shè)您想獲取前述事件的事件調(diào)用,其中第一個(gè)參數(shù)是,第三個(gè)參數(shù)是或;那么,您可以通過以下方式使用找到它們:
因此,我們?cè)谶@里要求節(jié)點(diǎn)返回所有已被合約地址觸發(fā)的來自區(qū)塊鏈的事件,其第一個(gè)主題為,第二個(gè)主題為,第三個(gè)主題為或。
在上述代碼中,請(qǐng)注意數(shù)組值的順序。順序很重要。
在學(xué)習(xí) truffle 之前,學(xué)習(xí)非常重要,因?yàn)榕c truffle 緊密集成。 truffle 測(cè)試、與 truffle 中的合約交互的代碼、部署代碼等都是使用編寫的。
API 是一個(gè) JavaScript 和 Node.js 庫,它使得與以太坊智能合約的交互變得容易。到目前為止,我們一直在使用 web3.js 來部署和調(diào)用智能合約函數(shù),這很好,但旨在使與以太坊智能合約的交互變得更加容易。下面是一些的特點(diǎn),這使得它在處理智能合約方面比 web3.js 更優(yōu)秀:
-
同步事務(wù)以更好地控制流程(即,直到您保證它們已經(jīng)被挖掘,事務(wù)才會(huì)完成)。
-
基于 Promise 的 API。不再有回調(diào)地獄。與 ES6 和 async/await 很好地配合使用。
-
交易的默認(rèn)值,例如來自 或 。
-
返回每個(gè)同步交易的日志、交易收據(jù)和交易哈希。
在我們開始使用 之前,你需要知道它不允許我們使用存儲(chǔ)在以太坊節(jié)點(diǎn)之外的賬戶來簽署交易;也就是說,它沒有類似于 的功能。 API 假設(shè)你的 DApp 的每個(gè)用戶都有自己的以太坊節(jié)點(diǎn)運(yùn)行,并且他們的賬戶存儲(chǔ)在該節(jié)點(diǎn)中。實(shí)際上,這就是 DApp 應(yīng)該工作的方式,因?yàn)槿绻總€(gè) DApp 的客戶端都開始讓用戶創(chuàng)建和管理賬戶,那么用戶將需要管理這么多賬戶,并且對(duì)于開發(fā)人員來說,每次構(gòu)建客戶端都要為每個(gè)客戶端開發(fā)錢包管理器都是痛苦的?,F(xiàn)在,問題是客戶端如何知道用戶在哪里存儲(chǔ)賬戶以及以什么格式?因此,出于可移植性的考慮,建議你假設(shè)用戶的賬戶存儲(chǔ)在他們的個(gè)人節(jié)點(diǎn)中,并且為了管理賬戶,他們使用類似以太坊錢包應(yīng)用的東西。由于以太坊節(jié)點(diǎn)中存儲(chǔ)的賬戶由以太坊節(jié)點(diǎn)本身簽名,因此不再需要 。每個(gè)用戶都需要有自己的節(jié)點(diǎn),并且不能共享節(jié)點(diǎn),因?yàn)楫?dāng)一個(gè)賬戶被解鎖時(shí),任何人都可以使用它,這將使用戶能夠竊取他人的以太幣并從他人的賬戶中進(jìn)行交易。
如果你使用的應(yīng)用需要你托管自己的節(jié)點(diǎn)并在其中管理賬戶,請(qǐng)確保不允許所有人對(duì)該節(jié)點(diǎn)進(jìn)行 JSON-RPC 調(diào)用;相反,只有本地應(yīng)用程序才能進(jìn)行調(diào)用。另外,請(qǐng)確保不要將賬戶長(zhǎng)時(shí)間保持解鎖狀態(tài),并在不再需要賬戶時(shí)立即將其鎖定。
如果你的應(yīng)用程序需要?jiǎng)?chuàng)建和簽署原始交易的功能,那么你可以使用 來開發(fā)和測(cè)試智能合約,而在你的應(yīng)用程序中,你可以像之前一樣與合約交互。
在撰寫本文時(shí),最新版本的 API 是 1.1.10。在引入 之前,你需要先引入 web3.js,因?yàn)槟阈枰獎(jiǎng)?chuàng)建一個(gè)提供程序來與 API 一起工作,這樣 將在內(nèi)部使用提供程序來進(jìn)行 JSON-RPC 調(diào)用。
要在 Node.js 應(yīng)用中安裝 ,只需在應(yīng)用目錄中運(yùn)行以下命令:
然后使用以下代碼進(jìn)行引入:
若要在瀏覽器中使用 ,你可以在 倉庫的 dist 目錄中找到瀏覽器分發(fā)版。
在 HTML 中,你可以這樣排隊(duì):
現(xiàn)在你將擁有一個(gè)全局變量 。
在開始學(xué)習(xí)關(guān)于 API 之前,我們需要建立一個(gè)測(cè)試環(huán)境,這將幫助我們?cè)趯W(xué)習(xí)過程中測(cè)試我們的代碼。
首先,通過僅運(yùn)行命令來運(yùn)行代表網(wǎng)絡(luò) ID 10 的節(jié)點(diǎn)。我們隨機(jī)選擇了網(wǎng)絡(luò) ID 10 用于開發(fā)目的,但您可以自由選擇其他網(wǎng)絡(luò) ID。只需確保它不是 1,因?yàn)橹骶W(wǎng)始終用于實(shí)時(shí)應(yīng)用程序,而不用于開發(fā)和測(cè)試目的。
然后,創(chuàng)建一個(gè) HTML 文件并將此代碼放入其中:
下載 和 。您可以在找到的瀏覽器構(gòu)建。
現(xiàn)在讓我們來探索的 API。基本上,有兩個(gè) API,即合同抽象 API 和合同實(shí)例 API。合同抽象 API 表示關(guān)于合同(或庫)的各種信息,例如其 ABI;未鏈接的字節(jié)碼;如果合同已部署,則其在各個(gè)以太坊網(wǎng)絡(luò)中的地址;如果已部署,則其依賴的庫在各個(gè)以太坊網(wǎng)絡(luò)中的地址;以及合同的事件。抽象 API 是為所有合同抽象存在的一組函數(shù)。合同實(shí)例表示特定網(wǎng)絡(luò)中部署的合同。實(shí)例 API 是向合同實(shí)例提供的 API。它是根據(jù)您的源文件中可用的函數(shù)動(dòng)態(tài)創(chuàng)建的。針對(duì)特定合同的合同實(shí)例是通過表示相同合同的合同抽象創(chuàng)建的。
合同抽象 API 是使與 web3.js 相比非常特殊的東西。這里是它的特殊之處:
-
它將根據(jù)連接的網(wǎng)絡(luò)自動(dòng)獲取默認(rèn)值,如庫地址、合同地址等,因此每次更改網(wǎng)絡(luò)時(shí)都不必編輯源代碼。
-
您可以選擇只在某些網(wǎng)絡(luò)中監(jiān)聽某些事件。
-
它使在運(yùn)行時(shí)輕松將庫鏈接到合同的字節(jié)碼。一旦您探索了如何使用 API,您將發(fā)現(xiàn)還有其他幾個(gè)好處。
在討論如何創(chuàng)建合同抽象及其方法之前,讓我們編寫一個(gè)樣本合同,該合同抽象將代表。這是樣本合同:
該合同將轉(zhuǎn)換為,并使用庫將轉(zhuǎn)換為。在主網(wǎng)絡(luò)上的地址為,但在其他網(wǎng)絡(luò)上,我們需要部署以測(cè)試合同。在繼續(xù)之前,請(qǐng)使用瀏覽器 Solidity 編譯它,因?yàn)槟鷮⑿枰?ABI 和字節(jié)碼。
現(xiàn)在讓我們創(chuàng)建一個(gè)代表合同和庫的合同抽象。這是代碼,請(qǐng)將其放入文件中:
以下是前述代碼的工作原理:
-
首先,我們創(chuàng)建一個(gè)提供者。使用此提供者,將與節(jié)點(diǎn)通信。
-
然后,我們?yōu)楹贤瑒?chuàng)建一個(gè)合同抽象。要?jiǎng)?chuàng)建合同抽象,我們使用函數(shù)。此函數(shù)接受一個(gè)包含有關(guān)合同的各種信息的對(duì)象。此對(duì)象可以稱為工件對(duì)象。和屬性是強(qiáng)制的。對(duì)象的其他屬性是可選的。屬性指向合同的 ABI,而屬性指向合同的未鏈接二進(jìn)制代碼。
-
然后,我們有一個(gè)網(wǎng)絡(luò)屬性,指示各種網(wǎng)絡(luò)中有關(guān)合同的信息。在這里,我們說在網(wǎng)絡(luò) ID 1 中,依賴項(xiàng)部署在地址,因此在網(wǎng)絡(luò) 1 中部署合同時(shí),它會(huì)自動(dòng)鏈接它。在網(wǎng)絡(luò)對(duì)象下,我們還可以放置一個(gè)屬性,指示合同已部署到此網(wǎng)絡(luò)并且這是合同地址。在對(duì)象中還有一個(gè)對(duì)象,指定我們感興趣捕獲的合同事件。對(duì)象的鍵是事件的主題,而是事件的 ABI。
-
然后,我們通過傳遞新的提供者實(shí)例調(diào)用對(duì)象的方法。這是傳遞提供者的一種方式,以便可以與節(jié)點(diǎn)通信。 API 不提供全局設(shè)置提供者的方法;相反,您需要為每個(gè)合同抽象設(shè)置提供者。這是一個(gè)功能,使我們可以輕松連接和處理多個(gè)網(wǎng)絡(luò)。
-
然后,我們調(diào)用對(duì)象的方法。這是設(shè)置合同抽象當(dāng)前表示的網(wǎng)絡(luò) ID 的方法;也就是說,在合同抽象的所有操作中,使用映射到此網(wǎng)絡(luò) ID 的值。此方法將自動(dòng)檢測(cè)我們的節(jié)點(diǎn)連接到哪個(gè)網(wǎng)絡(luò) ID,并將其自動(dòng)設(shè)置。如果您想要手動(dòng)設(shè)置網(wǎng)絡(luò) ID 或在運(yùn)行時(shí)更改它,則可以使用。如果更改網(wǎng)絡(luò) ID,則確保提供者也指向相同網(wǎng)絡(luò)的節(jié)點(diǎn),因?yàn)榉駝t將無法將網(wǎng)絡(luò) ID 與正確的鏈接、地址和事件進(jìn)行映射。
-
然后,我們?yōu)檫M(jìn)行了交易的默認(rèn)值設(shè)置。此方法獲取并可選地設(shè)置交易默認(rèn)值。如果沒有傳遞任何參數(shù)調(diào)用,它將簡(jiǎn)單地返回表示當(dāng)前默認(rèn)值的對(duì)象。如果傳遞了對(duì)象,則將設(shè)置新的默認(rèn)值。
-
我們對(duì)庫執(zhí)行了相同的操作,以便為其創(chuàng)建一個(gè)合約抽象。
合約實(shí)例表示特定網(wǎng)絡(luò)中部署的合約。使用合約抽象實(shí)例,我們需要?jiǎng)?chuàng)建合約實(shí)例。創(chuàng)建合約實(shí)例有三種方法:
-
: 此函數(shù)接受您的合約所需的任何構(gòu)造函數(shù)參數(shù),并將合約的新實(shí)例部署到合約抽象設(shè)置要使用的網(wǎng)絡(luò)中。最后一個(gè)參數(shù)是可選的,您可以使用它來傳遞事務(wù)參數(shù),包括事務(wù)來自地址、燃?xì)庀拗坪腿細(xì)鈨r(jià)格。此函數(shù)返回一個(gè)承諾,當(dāng)交易被挖掘時(shí),它會(huì)解析為新部署地址處的合約抽象實(shí)例。此方法不會(huì)對(duì)合約抽象所代表的構(gòu)件對(duì)象進(jìn)行任何更改。在使用此方法之前,請(qǐng)確保它能找到字節(jié)碼所依賴的庫的地址,以供設(shè)置要使用的網(wǎng)絡(luò)。
-
: 此函數(shù)創(chuàng)建一個(gè)新的合約抽象實(shí)例,表示傳入地址處的合約。它返回一個(gè)“可被解析的”對(duì)象(出于向后兼容性,尚未實(shí)際承諾)。在確保代碼存在于指定地址所在的網(wǎng)絡(luò)之后,它會(huì)解析為一個(gè)合約抽象實(shí)例。
-
: 這就像一樣,但地址是從構(gòu)件對(duì)象中檢索的。像一樣,是可解析的,并且將解析為代表部署合約的合約實(shí)例,以確保代碼存在于該位置,并且地址存在于設(shè)置為使用的網(wǎng)絡(luò)上。
讓我們部署并獲取合約的合約實(shí)例。在中,我們需要使用首先部署庫,然后將庫的部署地址添加到抽象中,將抽象鏈接到抽象,然后使用部署合約以獲取合約的實(shí)例。但在中,我們只需要部署并獲取其實(shí)例,因?yàn)槲覀円呀?jīng)在那里部署了。這是執(zhí)行所有這些操作的代碼:
這是上述代碼的工作原理:
-
首先,我們檢測(cè)網(wǎng)絡(luò) ID。如果網(wǎng)絡(luò) ID 是,那么我們部署合約和庫,如果網(wǎng)絡(luò) ID 是,那么我們只部署合約。
-
在 ,我們部署了 合同并獲得了它的合同實(shí)例。
-
然后,我們更新 抽象,以便它知道當(dāng)前網(wǎng)絡(luò)中合同的地址。更新抽象的界面類似于直接更新 artifacts 對(duì)象。如果你連接到網(wǎng)絡(luò) ID 1,那么它將覆蓋已設(shè)置的 地址。
-
然后,我們將部署的 鏈接到 抽象中。鏈接將更新鏈接并將庫的事件復(fù)制到該抽象當(dāng)前網(wǎng)絡(luò)所代表的 。庫可以被多次鏈接,并且會(huì)覆蓋其先前的鏈接。
-
我們將 部署到當(dāng)前網(wǎng)絡(luò)。
-
我們更新 抽象以存儲(chǔ)在其代表的當(dāng)前網(wǎng)絡(luò)中合同的地址,以便以后可以使用 獲取實(shí)例。
-
在網(wǎng)絡(luò) ID 1 的情況下,我們只部署 就可以了。
-
現(xiàn)在你只需簡(jiǎn)單地更改連接的網(wǎng)絡(luò)并重新啟動(dòng)你的應(yīng)用程序,你的應(yīng)用程序就會(huì)相應(yīng)地運(yùn)行。例如,在開發(fā)者的機(jī)器上,應(yīng)用程序?qū)⑦B接到開發(fā)網(wǎng)絡(luò),而在生產(chǎn)服務(wù)器上,它將連接到主網(wǎng)絡(luò)。顯然,你可能不希望每次運(yùn)行前述文件時(shí)都部署合同,因此在部署合同后,你可以實(shí)際上更新 artifacts 對(duì)象并在代碼中檢查合同是否已部署。如果尚未部署,則只需部署。而不是手動(dòng)更新 artifacts 對(duì)象,你可以將 artifacts 存儲(chǔ)在數(shù)據(jù)庫中或文件中,并編寫代碼在合同部署完成后自動(dòng)更新它們。
每個(gè)合同實(shí)例都是基于 Solidity 合同源不同,并且 API 是動(dòng)態(tài)創(chuàng)建的。以下是合同實(shí)例的各種 API:
-
:這是合同實(shí)例的一個(gè)函數(shù),它接受一個(gè)回調(diào),每當(dāng)合同在當(dāng)前網(wǎng)絡(luò) ID 下觸發(fā)符合事件簽名的事件時(shí)就會(huì)調(diào)用。你也可以使用的函數(shù)來捕捉特定事件,而不是全部事件。在前述合同中,要捕捉 ping 事件,你可以使用 。
-
:此函數(shù)用于向合同發(fā)送以太幣。它接受兩個(gè)參數(shù);即第一個(gè)參數(shù)是要傳輸?shù)?wei 數(shù)量,第二個(gè)參數(shù)是一個(gè)可選對(duì)象,可用于設(shè)置交易的 ,指示以太幣是從哪個(gè)地址發(fā)送的。此調(diào)用返回一個(gè) promise,并且當(dāng)其被挖掘時(shí),該 promise 解析為交易的詳細(xì)信息。
-
我們可以使用 或 調(diào)用合約的任何方法。前者發(fā)送交易,而后者僅在 EVM 上調(diào)用方法,并且更改不是持久的。這兩種方法都返回一個(gè) promise。在第一種情況下,promise 解析為交易的結(jié)果,即一個(gè)包含交易哈希、日志和交易收據(jù)的對(duì)象。而在第二種情況下,它解析為方法 的返回值。這兩種方法都接受函數(shù)參數(shù)和一個(gè)可選的最后一個(gè)參數(shù),該參數(shù)是一個(gè)對(duì)象,用于設(shè)置交易的 、、 等。
Truffle 是一個(gè)開發(fā)環(huán)境(提供命令行工具來編譯、部署、測(cè)試和構(gòu)建)、框架(提供各種包來編寫測(cè)試、部署代碼、構(gòu)建客戶端等)和資產(chǎn)管道(發(fā)布包并使用其他人發(fā)布的包)來構(gòu)建基于以太坊的 DApps。
Truffle 可在 OS X、Linux 和 Windows 上運(yùn)行。Truffle 要求你安裝 Node.js 版本 5.0+。在撰寫本文時(shí),Truffle 的最新穩(wěn)定版本是 3.1.2,我們將使用此版本。要安裝 Truffle,只需運(yùn)行以下命令:
在繼續(xù)之前,請(qǐng)確保你正在運(yùn)行 network ID 為 10 的 testrpc。原因與前面討論的相同。
首先,你需要為你的應(yīng)用創(chuàng)建一個(gè)目錄。將目錄命名為。在目錄內(nèi),運(yùn)行以下命令來初始化你的項(xiàng)目:
完成后,你將得到一個(gè)項(xiàng)目結(jié)構(gòu),其中包括以下項(xiàng)目:
-
contracts:Truffle 期望找到 合約的目錄。
-
migrations:包含合約部署代碼的文件目錄。
-
:用于測(cè)試智能合約的測(cè)試文件位置。
-
:主要的 Truffle 配置文件。
默認(rèn)情況下, 會(huì)為你提供一組示例合約( 和 ),它們就像是建立在以太坊之上的一個(gè)簡(jiǎn)單的代幣。
這是 MetaCoin 智能合約的源代碼,僅供參考:
將 10 k metacoins 分配給部署合約的賬戶地址。10 k 是存在的比特幣的總量?,F(xiàn)在這個(gè)用戶可以使用 函數(shù)將這些 metacoins 發(fā)送給任何人。你可以隨時(shí)使用 來查找你的賬戶余額。假設(shè)一個(gè) metacoin 等于兩個(gè)以太幣,你可以使用 獲取以太幣的余額。
庫用于計(jì)算以太中 的價(jià)值。為此,它提供了 方法。
在 Truffle 中編譯合約會(huì)生成帶有 和 設(shè)置的構(gòu)建對(duì)象。要編譯,請(qǐng)運(yùn)行以下命令:
Truffle 只會(huì)編譯自上次編譯以來發(fā)生變化的合約,以避免不必要的編譯。如果你想要覆蓋這種行為,可以在前面的命令中加上選項(xiàng)。
你可以在目錄中找到這些構(gòu)建文件。你可以根據(jù)需要自由編輯這些文件。這些文件在運(yùn)行和命令時(shí)會(huì)被修改。
在編譯之前,以下是需要注意的幾件事:
-
Truffle 期望你的合約文件定義與其文件名完全匹配的合約。例如,如果你有一個(gè)名為的文件,那么合約文件中應(yīng)該存在其中一個(gè):或。
-
文件名匹配區(qū)分大小寫,這意味著如果你的文件名沒有大寫,那么你的合約名也不應(yīng)該有大寫。
-
你可以使用 Solidity 的命令聲明合約依賴關(guān)系。Truffle 將按照正確的順序編譯合約,并在必要時(shí)自動(dòng)鏈接庫。依賴關(guān)系必須相對(duì)于當(dāng)前 Solidity 文件指定,并以或開頭。
Truffle 版本 3.1.2 使用的編譯器版本是 0.4.8。目前 Truffle 不支持更改編譯器版本,因此是固定的。
文件是用于配置項(xiàng)目的 JavaScript 文件。該文件可以執(zhí)行任何必要的代碼來創(chuàng)建項(xiàng)目的配置。它必須導(dǎo)出一個(gè)代表你的項(xiàng)目配置的對(duì)象。以下是文件的默認(rèn)內(nèi)容:
此對(duì)象可以包含各種屬性。但最基本的一個(gè)是。屬性指定了哪些網(wǎng)絡(luò)可用于部署,以及在與每個(gè)網(wǎng)絡(luò)交互時(shí)的特定交易參數(shù)(例如、、等)。默認(rèn)的是 100,000,000,000,是 4712388,是以太坊客戶端中的第一個(gè)可用合約。
你可以指定任意數(shù)量的網(wǎng)絡(luò)。請(qǐng)編輯配置文件為以下內(nèi)容:
在上面的代碼中,我們定義了兩個(gè)名為和的網(wǎng)絡(luò)。
當(dāng)在 Windows 上使用命令提示符時(shí),默認(rèn)的配置文件名稱可能會(huì)與可執(zhí)行文件發(fā)生沖突。如果是這種情況,我們建議您使用 Windows PowerShell 或 Git BASH,因?yàn)檫@些 shell 不會(huì)出現(xiàn)此沖突?;蛘?#xff0c;您可以將配置文件重命名為以避免此沖突。
即使是最小的項(xiàng)目也會(huì)與至少兩個(gè)區(qū)塊鏈進(jìn)行交互:一個(gè)是開發(fā)者機(jī)器上的,比如 EthereumJS TestRPC,另一個(gè)代表著開發(fā)者最終將部署其應(yīng)用程序的網(wǎng)絡(luò)(例如主 Ethereum 網(wǎng)絡(luò)或私有聯(lián)盟網(wǎng)絡(luò))。
因?yàn)榫W(wǎng)絡(luò)在運(yùn)行時(shí)由合約抽象自動(dòng)檢測(cè),這意味著您只需要部署一次您的應(yīng)用或前端。當(dāng)您的應(yīng)用運(yùn)行時(shí),運(yùn)行的以太坊客戶端將確定使用哪些 artifacts,并且這將使您的應(yīng)用非常靈活。
包含將合約部署到以太坊網(wǎng)絡(luò)的代碼的 JavaScript 文件稱為遷移。這些文件負(fù)責(zé)分階段執(zhí)行部署任務(wù),并且根據(jù)您的部署需求會(huì)隨著時(shí)間而變化。隨著項(xiàng)目的發(fā)展,您將創(chuàng)建新的遷移腳本以在區(qū)塊鏈上進(jìn)一步發(fā)展。先前運(yùn)行的遷移歷史記錄在區(qū)塊鏈上通過特殊的 合約記錄。如果您已經(jīng)看過 和 目錄的內(nèi)容,那么您會(huì)注意到 合約的存在。該合約應(yīng)始終存在,并且除非您知道自己在做什么,否則不應(yīng)觸摸它。
在遷移目錄中,您會(huì)注意到文件名以數(shù)字為前綴;也就是說,您會(huì)找到 和 文件。要記錄遷移是否成功運(yùn)行,需要帶有編號(hào)前綴。
合約(存儲(chǔ)在 中)存儲(chǔ)著對(duì)應(yīng)于 文件夾中找到的最后一個(gè)應(yīng)用的遷移腳本的編號(hào)。 合約總是首先部署。編號(hào)約定為 ,其中 x 從 1 開始。你的應(yīng)用合約通常從編號(hào)為 2 的腳本開始。
因此,由于 合約存儲(chǔ)了應(yīng)用的最后部署腳本的編號(hào),truffle 將不會(huì)再次運(yùn)行這些腳本。另一方面,未來,您的應(yīng)用可能需要部署修改過的或新的合約。為了實(shí)現(xiàn)這一點(diǎn),您會(huì)創(chuàng)建一個(gè)增加編號(hào)的新腳本,描述需要發(fā)生的步驟。然后,再次運(yùn)行一次后,它們將不會(huì)再次運(yùn)行。
在遷移文件的開頭,我們通過 方法告訴 truffle 我們想要與之交互的合約。這個(gè)方法類似于 Node 的 ,但在我們的情況下,它具體返回一個(gè)合約抽象,我們可以在部署腳本的其余部分中使用它。
所有遷移必須通過 語法導(dǎo)出一個(gè)函數(shù)。每個(gè)遷移導(dǎo)出的函數(shù)應(yīng)將一個(gè) 對(duì)象作為其第一個(gè)參數(shù)。該對(duì)象通過提供清晰的 API 來部署智能合約,并執(zhí)行一些部署的更普通的職責(zé),例如保存部署后的 artifacts 文件以供以后使用,鏈接庫等,來協(xié)助部署。 對(duì)象是您進(jìn)行部署任務(wù)分階段的主要接口。
這里是 deployer 對(duì)象的方法。所有方法都是同步的:
-
: 部署由合約抽象對(duì)象指定的特定合約,可選擇傳入構(gòu)造函數(shù)參數(shù)。對(duì)于單例合約非常有用,這樣您的 DApp 只存在一個(gè)該合約的實(shí)例。這將在部署后設(shè)置合約的地址(也就是 artifacts 文件中的屬性將等于新部署的地址),并覆蓋存儲(chǔ)的任何先前地址。您還可以選擇傳遞一個(gè)合約數(shù)組或數(shù)組的數(shù)組,以加速多個(gè)合約的部署。此外,最后一個(gè)參數(shù)是一個(gè)可選對(duì)象,它可以包含一個(gè)單一鍵,即。如果設(shè)置為,則如果已經(jīng)部署了合約,則部署器將不會(huì)部署此合約。此方法返回一個(gè) promise。
-
: 將已部署的庫鏈接到一個(gè)或多個(gè)合約。參數(shù)可以是單個(gè)合約抽象或多個(gè)合約抽象的數(shù)組。如果目標(biāo)中的任何合約不依賴于被鏈接的庫,則部署器將忽略該合約。此方法返回一個(gè) promise。
-
: 用于運(yùn)行任意的部署步驟。在遷移期間調(diào)用特定合約函數(shù)以添加、編輯和重新組織合約數(shù)據(jù)時(shí)使用它。在回調(diào)函數(shù)內(nèi)部,您將使用合約抽象 API 部署和鏈接合約。
可以根據(jù)要部署到的網(wǎng)絡(luò)條件性地運(yùn)行部署步驟。為了有條件地分階段部署步驟,編寫遷移以便它們接受名為的第二個(gè)參數(shù)。一個(gè)示例用例可以是,許多流行的庫已經(jīng)部署到了主網(wǎng)絡(luò);因此,當(dāng)使用這些網(wǎng)絡(luò)時(shí),我們不會(huì)再次部署庫,而只會(huì)鏈接它們。以下是一個(gè)代碼示例:
在項(xiàng)目中,您會(huì)發(fā)現(xiàn)兩個(gè)遷移文件,即和。第一個(gè)文件不應(yīng)該被編輯,除非您知道自己在做什么。您可以自由處理另一個(gè)文件。以下是文件的代碼:
這里,我們首先為庫和合約創(chuàng)建了抽象。無論使用哪個(gè)網(wǎng)絡(luò),我們都會(huì)部署庫,然后將庫鏈接到網(wǎng)絡(luò),最后部署網(wǎng)絡(luò)。
要運(yùn)行遷移,也就是部署合約,請(qǐng)運(yùn)行以下命令:
在這里,我們告訴 truffle 在開發(fā)網(wǎng)絡(luò)上運(yùn)行遷移。如果我們不提供選項(xiàng),則默認(rèn)使用名稱為的網(wǎng)絡(luò)。
在運(yùn)行前述命令后,您會(huì)注意到 truffle 將自動(dòng)更新 artifacts 文件中的庫和合約地址,并更新鏈接。
以下是你可以提供給 migrate 子命令的一些其他重要選項(xiàng):
-
:從頭開始運(yùn)行所有遷移,而不是從上次完成的遷移開始運(yùn)行。
-
-:從特定遷移運(yùn)行合同。
你可以隨時(shí)使用 命令在各種網(wǎng)絡(luò)中找到項(xiàng)目的合同和庫的地址。
單元測(cè)試是測(cè)試應(yīng)用程序的一種類型。這是一個(gè)過程,其中應(yīng)用程序的最小可測(cè)試部分(稱為單元)被單獨(dú)和獨(dú)立地檢查是否正確運(yùn)行。單元測(cè)試可以手動(dòng)進(jìn)行,但通常是自動(dòng)化的。
Truffle 默認(rèn)提供了一個(gè)單元測(cè)試框架來自動(dòng)化測(cè)試你的合同。它在運(yùn)行測(cè)試文件時(shí)提供了一個(gè)干凈的環(huán)境;也就是說,truffle 會(huì)在每個(gè)測(cè)試文件開始時(shí)重新運(yùn)行所有遷移,以確保你有一套新鮮的合同進(jìn)行測(cè)試。
Truffle 讓你以兩種不同的方式編寫簡(jiǎn)單易管理的測(cè)試:
-
在 JavaScript 中,從應(yīng)用客戶端執(zhí)行你的合同
-
在 Solidity 中,從其他合同執(zhí)行你的合同
兩種測(cè)試方式都有各自的優(yōu)點(diǎn)和缺點(diǎn)。我們將學(xué)習(xí)兩種編寫測(cè)試的方式。
所有測(cè)試文件都應(yīng)位于 目錄中。Truffle 只會(huì)運(yùn)行具有以下文件擴(kuò)展名的測(cè)試文件:、、、 和 。所有其他文件都會(huì)被忽略。
在運(yùn)行自動(dòng)化測(cè)試時(shí), 比其他客戶端快得多。此外, 包含 truffle 利用的特殊功能,可將測(cè)試運(yùn)行時(shí)間加速近 90%。作為一般的工作流程,我們建議你在正常開發(fā)和測(cè)試期間使用 ,然后在準(zhǔn)備部署到實(shí)時(shí)或生產(chǎn)網(wǎng)絡(luò)時(shí),再對(duì) go-ethereum 或其他官方以太坊客戶端運(yùn)行一次測(cè)試。
Truffle 的 JavaScript 測(cè)試框架是基于 mocha 構(gòu)建的。Mocha 是一個(gè)用于編寫測(cè)試的 JavaScript 框架,而 chai 是一個(gè)斷言庫。
測(cè)試框架用于組織和執(zhí)行測(cè)試,而斷言庫提供了驗(yàn)證事物是否正確的工具。斷言庫使得測(cè)試代碼變得更容易,這樣你就不必執(zhí)行成千上萬的 if 語句。大多數(shù)測(cè)試框架不包含斷言庫,并允許用戶插入他們想要使用的庫。
在繼續(xù)之前,你需要學(xué)習(xí)如何使用 mocha 和 chai 編寫測(cè)試。要學(xué)習(xí) mocha,請(qǐng)?jiān)L問 ,要學(xué)習(xí) chai,請(qǐng)?jiān)L問 。
你的測(cè)試應(yīng)該存在于 目錄中,并且它們應(yīng)該以 擴(kuò)展名結(jié)尾。
合約抽象是從 JavaScript 實(shí)現(xiàn)合約交互的基礎(chǔ)。因?yàn)?truffle 無法檢測(cè)到你在測(cè)試中需要與哪些合約交互,所以你需要顯式地請(qǐng)求這些合約。你可以通過使用方法來實(shí)現(xiàn)。因此,在測(cè)試文件中應(yīng)該首先為你想要測(cè)試的合約創(chuàng)建抽象。
然后,應(yīng)該編寫實(shí)際的測(cè)試。結(jié)構(gòu)上,你的測(cè)試應(yīng)該與 mocha 的大部分測(cè)試保持基本一致。測(cè)試文件應(yīng)該包含 mocha 可識(shí)別為自動(dòng)化測(cè)試的代碼。使 truffle 測(cè)試與 mocha 不同的是函數(shù):此函數(shù)的工作方式與完全相同,只是它會(huì)告訴 truffle 運(yùn)行所有遷移。函數(shù)的工作方式如下:
-
在運(yùn)行每個(gè)函數(shù)之前,你的合約都會(huì)被重新部署到運(yùn)行中的以太坊節(jié)點(diǎn)上,因此其中的測(cè)試會(huì)以清潔的合約狀態(tài)運(yùn)行。
-
函數(shù)提供了由你的以太坊節(jié)點(diǎn)提供的一系列帳戶,你可以用它們來編寫測(cè)試。
由于 truffle 在內(nèi)部使用 mocha,所以當(dāng) truffle 功能不必要時(shí),仍然可以使用來運(yùn)行普通的 mocha 測(cè)試。
下面是 truffle 生成的用于測(cè)試合約的默認(rèn)測(cè)試代碼。你可以在文件中找到這段代碼:
在上述代碼中,你可以看到所有合約的交互代碼都是使用庫編寫的。這段代碼很容易理解。
最后,truffle 為你提供了對(duì) mocha 配置的訪問,以便你可以更改 mocha 的行為。mocha 的配置放在文件導(dǎo)出的對(duì)象的屬性下。例如,看一下這個(gè):
Solidity 測(cè)試代碼放在文件中。在使用 Solidity 編寫測(cè)試之前,請(qǐng)注意以下事項(xiàng):
-
Solidity 測(cè)試不應(yīng)該繼承任何合約。這樣可以使你的測(cè)試盡可能簡(jiǎn)潔,并完全控制你編寫的合約。
-
Truffle 為你提供了默認(rèn)的斷言庫,但你可以隨時(shí)更改此庫以滿足你的需求。
-
你應(yīng)該能夠針對(duì)任何以太坊客戶端運(yùn)行你的 Solidity 測(cè)試。
要學(xué)習(xí)如何在 Solidity 中編寫測(cè)試,讓我們來探索 truffle 生成的默認(rèn) Solidity 測(cè)試代碼。這是代碼,可以在文件中找到:
以下是上述代碼的工作原理:
-
諸如之類的斷言函數(shù)由庫提供。這是默認(rèn)的斷言庫;但是,只要該庫觸發(fā)正確的斷言事件,您就可以包含自己的斷言庫,以便與 truffle 的測(cè)試運(yùn)行器松散集成。斷言函數(shù)觸發(fā)事件,這些事件被 truffle 捕獲,并顯示信息。這是 truffle 中 Solidity 斷言庫的架構(gòu)。您可以在中找到所有可用的斷言函數(shù)()。
-
在導(dǎo)入路徑中,,是包名。我們稍后將詳細(xì)了解包。
-
您部署的合約地址(即作為遷移的一部分部署的合約)可通過庫獲得。這由 truffle 提供,并在運(yùn)行每個(gè)測(cè)試套件之前重新編譯和重新鏈接。此庫提供了所有已部署合約的函數(shù),形式為。然后,這將返回一個(gè)地址,您可以使用該地址訪問該合約。
-
要使用部署的合約,您必須將合約代碼導(dǎo)入測(cè)試套件。請(qǐng)注意,上述示例中的。此導(dǎo)入是相對(duì)于測(cè)試合約的,測(cè)試合約存在于目錄中,它會(huì)超出測(cè)試目錄以找到合約。然后,它使用該合約將地址轉(zhuǎn)換為類型。
-
所有測(cè)試合約都必須以大寫字母開頭。這將其與測(cè)試輔助工具和項(xiàng)目合約(即受測(cè)試約束的合約)區(qū)分開來,讓測(cè)試運(yùn)行器知道哪些合約代表測(cè)試套件。
-
像測(cè)試合約名稱一樣,所有測(cè)試函數(shù)都必須以小寫字母開頭。每個(gè)測(cè)試函數(shù)都按照其在測(cè)試文件中的出現(xiàn)順序作為單個(gè)事務(wù)執(zhí)行(例如您的 JavaScript 測(cè)試)。由提供的斷言函數(shù)觸發(fā)測(cè)試運(yùn)行器評(píng)估以確定測(cè)試結(jié)果的事件。斷言函數(shù)返回一個(gè)表示斷言結(jié)果的布爾值,您可以使用它來提前返回測(cè)試以防止執(zhí)行錯(cuò)誤(即將暴露的錯(cuò)誤)。
-
你提供了許多測(cè)試鉤子,如下例所示。這些鉤子是、、和,與你的 JavaScript 測(cè)試中 mocha 提供的相同鉤子。你可以使用這些鉤子在每個(gè)測(cè)試之前和之后或在每個(gè)套件運(yùn)行之前和之后執(zhí)行設(shè)置和拆卸操作。像測(cè)試函數(shù)一樣,每個(gè)鉤子都作為單個(gè)事務(wù)執(zhí)行。注意,一些復(fù)雜的測(cè)試需要執(zhí)行大量設(shè)置可能會(huì)超出單個(gè)事務(wù)的燃?xì)庀拗?#xff1b;你可以通過創(chuàng)建帶有不同后綴的多個(gè)鉤子來避免這種限制,如下例所示:
- 這個(gè)測(cè)試合約還顯示了你的函數(shù)和函數(shù)都共享相同的合約狀態(tài)。你可以在測(cè)試之前設(shè)置合約數(shù)據(jù),在測(cè)試期間使用該數(shù)據(jù),并在準(zhǔn)備下一個(gè)測(cè)試之前重置它。注意,就像你的 JavaScript 測(cè)試一樣,你的下一個(gè)測(cè)試函數(shù)將從上一個(gè)運(yùn)行的測(cè)試函數(shù)的狀態(tài)繼續(xù)。
Truffle 沒有直接的方法來測(cè)試你的合約是否應(yīng)該拋出異常(也就是說,對(duì)于使用 throw 來表示預(yù)期錯(cuò)誤的合約)。但是有一個(gè)笨拙的解決方案,你可以在這里找到:。
要向你的 Solidity 測(cè)試合約發(fā)送以太幣,它應(yīng)該有一個(gè)名為的返回的公共函數(shù)。這可以直接寫成一個(gè)函數(shù)或一個(gè)公共變量。當(dāng)你的測(cè)試合約部署到網(wǎng)絡(luò)上時(shí),truffle 會(huì)從你的測(cè)試賬戶向你的測(cè)試合約發(fā)送那個(gè)金額的以太幣。然后,你的測(cè)試合約可以使用那個(gè)以太幣在你的測(cè)試合約中腳本以太交互。注意,是可選的,不是必需的。例如,看看下面的代碼:
Truffle 以一種不執(zhí)行回退函數(shù)的方式向你的測(cè)試合約發(fā)送以太幣,所以你仍然可以在你的 Solidity 測(cè)試中使用回退函數(shù)進(jìn)行高級(jí)測(cè)試用例。
要運(yùn)行你的測(cè)試腳本,只需運(yùn)行這個(gè)命令:
或者,你可以指定要運(yùn)行的特定文件的路徑。例如,看看這個(gè):
一個(gè) truffle 包是智能合約和它們的構(gòu)件的集合。一個(gè)包可以依賴于零個(gè)或多個(gè)包,也就是說,你可以使用包的智能合約和構(gòu)件。當(dāng)在你自己的項(xiàng)目中使用包時(shí),重要的是要注意有兩個(gè)地方你將使用包的合約和構(gòu)件:在你的項(xiàng)目的合約中和在你的項(xiàng)目的 JavaScript 代碼中(遷移和測(cè)試)。
使用 truffle 創(chuàng)建的項(xiàng)目默認(rèn)具有特定的布局,使它們可以被用作包。包中最重要的目錄如下:
-
(由 truffle 創(chuàng)建)
第一個(gè)目錄是你的合約目錄,包括原始的 Solidity 合約。第二個(gè)目錄是目錄,其中保存著以文件形式的構(gòu)建產(chǎn)物。
Truffle 支持兩種包構(gòu)建方式:和包。你必須知道什么是包,但讓我們來看看什么是包。是以太坊的包注冊(cè)表。你可以在找到所有的包。它遵循 ERC190 () 規(guī)范來發(fā)布和消費(fèi)智能合約包。
Truffle 默認(rèn)帶有 npm 集成,并且知道項(xiàng)目中的目錄(如果存在)。這意味著你可以通過 npm 使用和分發(fā)合約或庫,使你的代碼對(duì)其他人可用,其他人的代碼對(duì)你可用。你的項(xiàng)目中也可以有一個(gè)文件。你可以在項(xiàng)目中簡(jiǎn)單地安裝任何包,并在任何 JavaScript 文件中導(dǎo)入它,但只有包含前面提到的兩個(gè)目錄的情況下,它才會(huì)被稱為 truffle 包。在 truffle 項(xiàng)目中安裝包與在任何應(yīng)用程序中安裝包相同。
當(dāng)安裝 EthPM 包時(shí),如果不存在,將創(chuàng)建一個(gè)目錄。該目錄可以類似于目錄進(jìn)行處理。
通過 EthPM 安裝包幾乎與通過 NPM 安裝包一樣簡(jiǎn)單。你只需運(yùn)行以下命令:
您還可以安裝特定版本的包:
像 NPM 一樣,EthPM 版本遵循語義版本規(guī)范。你的項(xiàng)目也可以定義一個(gè)文件,它類似于 npm 包的。要安裝文件中列出的所有依賴項(xiàng),請(qǐng)運(yùn)行以下命令:
一個(gè)示例的文件如下所示:
創(chuàng)建和發(fā)布一個(gè) truffle 的包與創(chuàng)建任何其他包的過程相同。要了解如何創(chuàng)建和發(fā)布一個(gè)包,請(qǐng)?jiān)L問。無論您將包發(fā)布為包還是包,都需要運(yùn)行命令。運(yùn)行此命令時(shí),它會(huì)刪除配置文件中僅匹配通配符的所有網(wǎng)絡(luò) ID 的構(gòu)件。這是因?yàn)檫@些地址對(duì)于消費(fèi)此包的其他項(xiàng)目來說是無效的,因?yàn)檫@些網(wǎng)絡(luò)很可能是私有的,因?yàn)樗鼈儍H用于開發(fā)目的。除非你知道你在做什么,否則不應(yīng)省略此命令。它將無法刪除作為常量列出的私有網(wǎng)絡(luò)的任何構(gòu)件,因此您需要手動(dòng)刪除它們。
要在您的合同內(nèi)使用包的合同,只需像 Solidity 的語句一樣簡(jiǎn)單。當(dāng)您的路徑不是明確相對(duì)或絕對(duì)時(shí),它表示您正在尋找特定命名包中的文件??紤]使用()的示例:
由于路徑?jīng)]有以開頭,truffle 知道要在您的項(xiàng)目的或目錄中查找文件夾。從那里,它解析路徑以提供您請(qǐng)求的合同。
要在 JavaScript 代碼中與包的構(gòu)件進(jìn)行交互,您只需要求該包的文件,然后使用將它們轉(zhuǎn)換為可用的抽象:
有時(shí)候,你可能希望你的合同與包先前部署的合同進(jìn)行交互。由于部署的地址存在于包的文件中,Solidity 代碼不能直接讀取這些文件的內(nèi)容。因此,使 Solidity 代碼訪問文件中的地址的流程是通過在 Solidity 代碼中定義函數(shù)來設(shè)置依賴合同地址,并在合同部署后使用 JavaScript 調(diào)用這些函數(shù)來設(shè)置依賴合同地址。
所以你可以像這樣定義你的合同代碼:
這是您的遷移應(yīng)該看起來像的:
有時(shí),為了測(cè)試和調(diào)試目的或手動(dòng)執(zhí)行交易,與您的合同交互是很好的。Truffle 為您提供了通過交互式控制臺(tái)輕松執(zhí)行此操作的方式,您的合同可用并且可以立即使用。
要打開控制臺(tái),請(qǐng)運(yùn)行此命令:
控制臺(tái)基于您的項(xiàng)目配置連接到以太坊節(jié)點(diǎn)。前述命令還接受選項(xiàng),以指定要連接的特定節(jié)點(diǎn)。
以下是控制臺(tái)的特點(diǎn):
-
您可以在控制臺(tái)中運(yùn)行該命令。例如,您可以在控制臺(tái)中鍵入,它將被解釋為在控制臺(tái)外部運(yùn)行的方式一樣。
-
所有編譯的合約都可供使用并準(zhǔn)備就緒。
-
在每個(gè)命令(如)之后,您的合約都將重新提供,因此您可以立即開始使用新分配的地址和二進(jìn)制文件。
-
對(duì)象已經(jīng)可用,并設(shè)置為連接到您的以太坊節(jié)點(diǎn)。
-
所有返回 Promise 的命令都將自動(dòng)解析并打印結(jié)果,無需對(duì)簡(jiǎn)單命令使用。例如,您可以編寫如下代碼:
通常,您可能希望運(yùn)行與您的合約交互的外部腳本。Truffle 提供了一種簡(jiǎn)單的方法來實(shí)現(xiàn)這一點(diǎn),根據(jù)您所需的網(wǎng)絡(luò)引導(dǎo)您的合約,并根據(jù)項(xiàng)目配置自動(dòng)連接到您的以太坊節(jié)點(diǎn)。
要運(yùn)行外部腳本,請(qǐng)運(yùn)行此命令:
為了正確運(yùn)行外部腳本,truffle 期望它們導(dǎo)出一個(gè)接受單個(gè)參數(shù)作為回調(diào)函數(shù)的函數(shù)。只要在此腳本中調(diào)用回調(diào)函數(shù),您可以隨意執(zhí)行任何操作?;卣{(diào)函數(shù)接受一個(gè)錯(cuò)誤作為其唯一參數(shù)。如果提供了錯(cuò)誤,執(zhí)行將停止,并且進(jìn)程將返回一個(gè)非零退出代碼。
外部腳本必須遵循以下結(jié)構(gòu):
現(xiàn)在您已經(jīng)知道如何使用 truffle 編譯、部署和測(cè)試智能合約,是時(shí)候?yàn)槲覀兊拇鷰艠?gòu)建一個(gè)客戶端了。在介紹如何使用 truffle 構(gòu)建客戶端之前,您需要知道它不允許我們使用存儲(chǔ)在以太坊節(jié)點(diǎn)外部的賬戶進(jìn)行交易簽名;也就是說,它沒有類似于的東西,原因與相同。
使用 truffle 構(gòu)建客戶端首先意味著在客戶端源代碼中集成 truffle 的構(gòu)件,然后準(zhǔn)備客戶端源代碼以進(jìn)行部署。
要構(gòu)建客戶端,您需要運(yùn)行此命令:
當(dāng)運(yùn)行此命令時(shí),truffle 將通過檢查項(xiàng)目配置文件中的屬性來確定如何構(gòu)建客戶端。
可以使用命令行工具構(gòu)建客戶端。當(dāng)屬性為字符串時(shí),truffle 會(huì)假定我們要運(yùn)行一個(gè)命令來構(gòu)建客戶端,因此會(huì)將字符串作為命令運(yùn)行。該命令會(huì)獲得充足的環(huán)境變量以與 truffle 集成。
您可以通過類似的配置代碼讓 truffle 運(yùn)行命令行工具來構(gòu)建客戶端:
JavaScript 函數(shù)可用于構(gòu)建客戶端。當(dāng) 屬性是一個(gè)函數(shù)時(shí),松露將在我們想要構(gòu)建客戶端時(shí)運(yùn)行該函數(shù)。該函數(shù)提供了有關(guān)項(xiàng)目如何與松露集成的大量信息。
你可以讓松露運(yùn)行一個(gè)函數(shù)來構(gòu)建客戶端,使用類似的配置代碼:
你也可以創(chuàng)建一個(gè)對(duì)象,其中包含一個(gè)像這里的 方法。這對(duì)于想要發(fā)布一個(gè)構(gòu)建客戶端的包的人來說是很好的。
松露提供了 包,被稱為松露的默認(rèn)構(gòu)建器。該構(gòu)建器導(dǎo)出一個(gè)對(duì)象,其中有一個(gè)與之前提到的方法完全相同的構(gòu)建方法。
默認(rèn)構(gòu)建器可用于為你的 DApp 構(gòu)建 web 客戶端,其服務(wù)器僅提供靜態(tài)文件,并且所有功能都在前端。
在我們深入了解如何使用默認(rèn)構(gòu)建器之前,首先使用以下命令安裝它:
現(xiàn)在將你的配置文件更改為這樣:
默認(rèn)構(gòu)建器讓你完全控制你想要組織客戶端文件和文件夾的方式。
這個(gè)配置描述了 (左側(cè))的文件、文件夾和構(gòu)成 內(nèi)容的文件數(shù)組(右側(cè))。每個(gè)目標(biāo)將通過處理右側(cè)的文件根據(jù)它們的文件擴(kuò)展名進(jìn)行生成,將結(jié)果連接在一起,然后將結(jié)果文件(目標(biāo))保存到構(gòu)建目標(biāo)中。在這里,右側(cè)指定了一個(gè)字符串而不是一個(gè)數(shù)組,如果需要,該文件將被處理,然后直接復(fù)制過去。如果字符串以 結(jié)尾,它將被解釋為一個(gè)目錄,目錄將被直接復(fù)制過去而不進(jìn)行進(jìn)一步處理。右側(cè)指定的所有路徑都是相對(duì)于 目錄的。
你可以隨時(shí)更改此配置和目錄結(jié)構(gòu)。例如,你不需要 和 目錄,但請(qǐng)確保你相應(yīng)地編輯你的配置。
如果你希望默認(rèn)的構(gòu)建器將松露集成到你的 web 應(yīng)用程序的前端,請(qǐng)確保你有一個(gè)名為 的構(gòu)建目標(biāo),該默認(rèn)構(gòu)建器可以將代碼追加到其中。它不會(huì)將松露與任何其他文件名集成。
這里是默認(rèn)構(gòu)建器的特點(diǎn):
-
自動(dòng)導(dǎo)入你的編譯合同構(gòu)件、部署的合同信息和以太坊節(jié)點(diǎn)配置到客戶端源代碼中
-
包括建議的依賴項(xiàng),包括 web3 和
-
編譯 和 文件
-
編譯文件
-
最小化 文件
你可以使用 命令,它會(huì)監(jiān)視 目錄、 目錄和配置文件的更改。當(dāng)有更改時(shí),它會(huì)重新編譯合約并生成新的構(gòu)建文件,然后重新構(gòu)建客戶端。但它不會(huì)運(yùn)行遷移和測(cè)試。
現(xiàn)在讓我們?yōu)槲覀兊?DApp 編寫一個(gè)客戶端,并使用 truffle 的默認(rèn)構(gòu)建器構(gòu)建它。首先,根據(jù)我們?cè)O(shè)置的先前配置創(chuàng)建文件和目錄:創(chuàng)建一個(gè) 目錄,在其中創(chuàng)建一個(gè) 文件和兩個(gè)名為 和 的目錄。在 目錄中,創(chuàng)建一個(gè)名為 的文件,在 目錄中,下載并放置 Bootstrap 4 的 CSS 文件。你可以在 找到它。
在 文件中,放置以下代碼:
在上述代碼中,我們加載了 和 文件。我們有兩個(gè)表單:一個(gè)用于向不同賬戶發(fā)送 metacoins,另一個(gè)用于檢查賬戶的 metacoins 余額。在第一個(gè)表單中,用戶必須選擇一個(gè)賬戶,然后輸入要發(fā)送的 metacoin 金額和要發(fā)送到的地址。而在第二個(gè)表單中,用戶只需選擇其想要檢查 metacoin 余額的地址。
在 文件中,放置以下代碼:
以下是代碼的工作原理:
-
使構(gòu)件對(duì)象可在 全局對(duì)象下使用。
-
它還通過將變量名設(shè)置為合約名,為所有可用的合約提供了合約抽象作為全局變量。
-
它還通過已設(shè)置提供程序?yàn)?web3 對(duì)象提供了支持。它還為合約抽象設(shè)置了提供程序。它使得 web3 對(duì)象連接到名為 的網(wǎng)絡(luò),如果不存在,則默認(rèn)值為 。
-
在上述代碼中,首先,我們等待頁面加載完成,一旦加載完成,我們就檢索連接節(jié)點(diǎn)中的賬戶列表,并在兩個(gè)表單中顯示它們。我們還調(diào)用 抽象的 方法。
-
然后,我們?yōu)閮蓚€(gè)表單都設(shè)置了 事件處理程序。它們都按預(yù)期執(zhí)行其操作,并在彈出窗口中顯示結(jié)果。
-
當(dāng)?shù)谝粋€(gè)表單提交時(shí),我們獲取 合約的部署實(shí)例,并使用正確的參數(shù)調(diào)用 方法。
-
當(dāng)?shù)诙€(gè)表單提交時(shí),我們通過調(diào)用 EVM 中的 方法而不是廣播交易來檢索所選賬戶的余額。
現(xiàn)在繼續(xù)運(yùn)行 truffle build 命令,你會(huì)注意到 truffle 將在 目錄中創(chuàng)建 、 和 文件,并在其中放置客戶端的最終部署代碼。
Truffle 帶有內(nèi)置的 Web 服務(wù)器。這個(gè) Web 服務(wù)器只是以適當(dāng)?shù)?MIME 類型設(shè)置提供目錄中的文件。除此之外,它沒有配置為執(zhí)行任何其他操作。
要運(yùn)行 Web 服務(wù)器,請(qǐng)運(yùn)行此命令:
默認(rèn)情況下,服務(wù)器在端口號(hào) 8080 上運(yùn)行。但你可以使用選項(xiàng)指定不同的端口號(hào)。
類似于 truffle watch,這個(gè) Web 服務(wù)器還監(jiān)視目錄、目錄和配置文件中的更改。當(dāng)有更改時(shí),它重新編譯合約并生成新的工件文件,然后重建客戶端。但它不會(huì)運(yùn)行遷移和測(cè)試。
由于 truffle-default-builder 將最終可部署的代碼放在構(gòu)建目錄中,你只需運(yùn)行即可通過 Web 提供文件。
讓我們測(cè)試我們的 Web 客戶端。訪問,你會(huì)看到這個(gè)截圖:
所選框中的帳戶地址對(duì)你而言會(huì)有所不同?,F(xiàn)在在部署合約時(shí),合約將所有的 metacoins 分配給部署合約的地址;所以在這里,第一個(gè)帳戶將有 10,000 個(gè) metacoins 的余額。現(xiàn)在從第一個(gè)帳戶發(fā)送五個(gè) metacoins 到第二個(gè)帳戶,然后點(diǎn)擊提交。你會(huì)看到類似以下截圖的屏幕:
現(xiàn)在通過在第二個(gè)表單的選擇框中選擇第二個(gè)帳戶然后點(diǎn)擊“檢查余額”按鈕來檢查第二個(gè)帳戶的余額。你會(huì)看到類似以下截圖的屏幕:
在本章中,我們深入了解了如何使用 truffle 構(gòu)建 DApps 及其相應(yīng)的客戶端。我們看到 truffle 如何簡(jiǎn)化編寫、編譯、部署和測(cè)試 DApps 的過程。我們還看到了使用在客戶端之間輕松切換網(wǎng)絡(luò)而無需觸及源代碼的簡(jiǎn)便之處。現(xiàn)在,你已經(jīng)準(zhǔn)備好開始使用 truffle 構(gòu)建企業(yè)級(jí) DApps 了。
在下一章中,我們將使用 truffle 和以太坊鬧鐘 DApp 構(gòu)建一個(gè)分散式鬧鐘應(yīng)用程序,該應(yīng)用程序在時(shí)間上為你支付報(bào)酬。
只需用冒號(hào)替換。
聯(lián)盟(通常由多個(gè)參與者組成,如銀行、電子商務(wù)網(wǎng)站、政府實(shí)體、醫(yī)院等)可以利用區(qū)塊鏈技術(shù)解決許多問題,并使事情變得更快更便宜。盡管他們找出了區(qū)塊鏈如何幫助他們,以太坊的區(qū)塊鏈實(shí)現(xiàn)并不完全適合他們的所有情況。雖然有其他區(qū)塊鏈實(shí)現(xiàn)(例如 Hyperledger)專門為聯(lián)盟而構(gòu)建,但正如我們?cè)谡緯袑W(xué)到的以太坊一樣,我們將看到如何使用以太坊構(gòu)建聯(lián)盟區(qū)塊鏈?;旧?#xff0c;我們將使用 Parity 來構(gòu)建聯(lián)盟區(qū)塊鏈。雖然 Parity 還有其他替代方案,例如 J.P. Morgan 的 quorum,但在撰寫本書時(shí),Parity 已經(jīng)存在一段時(shí)間,許多企業(yè)已經(jīng)在使用它,而其他替代方案尚未被任何企業(yè)使用。但根據(jù)您的需求,Parity 可能不是最佳解決方案;因此,在決定使用哪種解決方案之前,請(qǐng)先調(diào)查所有其他解決方案。
在本章中,我們將涵蓋以下主題:
-
為什么以太坊不適合聯(lián)盟區(qū)塊鏈?
-
什么是 Parity 節(jié)點(diǎn)以及其特點(diǎn)?
-
什么是權(quán)威證明共識(shí)協(xié)議,以及 Parity 支持哪些類型的 PoA?
-
Aura 共識(shí)協(xié)議是如何工作的?
-
下載并安裝 Parity
-
使用 Parity 構(gòu)建聯(lián)盟區(qū)塊鏈
要理解什么是聯(lián)盟區(qū)塊鏈,或者換句話說,聯(lián)盟需要什么樣的區(qū)塊鏈實(shí)現(xiàn),讓我們來看一個(gè)例子。銀行希望建立一個(gè)區(qū)塊鏈來使資金轉(zhuǎn)賬更加簡(jiǎn)單、快速和便宜。在這種情況下,以下是他們需要的東西:
-
速度:他們需要一個(gè)能夠在幾乎實(shí)時(shí)確認(rèn)交易的區(qū)塊鏈網(wǎng)絡(luò)。目前,以太坊區(qū)塊鏈網(wǎng)絡(luò)的出塊時(shí)間為 12 秒,客戶通常需要等待幾分鐘才能確認(rèn)一筆交易。
-
有權(quán)限:他們希望區(qū)塊鏈?zhǔn)怯袡?quán)限的。權(quán)限本身意味著各種不同的事情。例如:權(quán)限可能包括獲得加入網(wǎng)絡(luò)的許可,可能包括獲得創(chuàng)建區(qū)塊的許可,也可能包括獲得發(fā)送特定交易的許可等等。
-
安全性:對(duì)于私有網(wǎng)絡(luò)來說,PoW 并不足夠安全,因?yàn)閰⑴c者數(shù)量有限;因此,沒有足夠的算力來保證其安全性。因此,需要一種能夠保持區(qū)塊鏈安全和不可變的共識(shí)協(xié)議。
-
隱私性:盡管網(wǎng)絡(luò)是私有的,但網(wǎng)絡(luò)本身仍然需要隱私。有兩種隱私形式:
-
身份隱私: 身份隱私是使身份無法追蹤的行為。我們先前看到的獲得身份隱私的解決方案是使用多個(gè)以太坊賬戶地址。但是,如果使用多個(gè)以太坊賬戶,則智能合約將無法通過所有這些賬戶是否實(shí)際屬于同一用戶的所有權(quán)驗(yàn)證而失敗。
-
數(shù)據(jù)隱私: 有時(shí),我們不希望數(shù)據(jù)對(duì)網(wǎng)絡(luò)中的所有節(jié)點(diǎn)可見,而是只對(duì)特定節(jié)點(diǎn)可見。
總的來說,在本章中,我們將學(xué)習(xí)如何解決以太坊中的這些問題。
PoA 是區(qū)塊鏈的一種共識(shí)機(jī)制,其共識(shí)是通過參考驗(yàn)證者列表(當(dāng)它們與實(shí)體聯(lián)系在一起時(shí)稱為權(quán)威)來實(shí)現(xiàn)的。驗(yàn)證者是允許參與共識(shí)的一組賬戶/節(jié)點(diǎn);它們驗(yàn)證交易和區(qū)塊。
與 PoW 或 PoS 不同,它不涉及挖礦機(jī)制。有各種類型的 PoA 協(xié)議,它們根據(jù)它們的實(shí)際工作方式而有所不同。Hyperledger 和 Ripple 基于 PoA。Hyperledger 基于 PBFT,而 Ripple 使用迭代過程。
Parity 是一款專為正確性/可驗(yàn)證性、模塊化、低占用空間和高性能而編寫的以太坊節(jié)點(diǎn)。它是用 Rust 編程語言編寫的,這是一種以效率為重點(diǎn)的混合命令式/OO/函數(shù)式語言。它是由 Parity Technologies 專業(yè)開發(fā)的。在撰寫本書時(shí),Parity 的最新版本是 1.7.0,我們將使用此版本。我們將學(xué)習(xí)構(gòu)建聯(lián)盟區(qū)塊鏈所需的所有內(nèi)容。要深入學(xué)習(xí) Parity,您可以參考官方文檔。
它比 go-ethereum 有更多的功能,例如 web3 dapp 瀏覽器,更先進(jìn)的賬戶管理等等。但它之所以特別之處在于,它支持權(quán)威證明(PoA)以及 PoW。Parity 目前支持 Aura 和 Tendermint PoA 協(xié)議。未來,它可能支持更多的 PoA 協(xié)議。當(dāng)前,Parity 推薦使用 Aura 而不是 Tendermint,因?yàn)?Tendermint 仍在開發(fā)中。
Aura 對(duì)于權(quán)限區(qū)塊鏈比 PoW 是一個(gè)更好的解決方案,因?yàn)樗哂懈玫膮^(qū)塊時(shí)間,并在私有網(wǎng)絡(luò)中提供了更好的安全性。
讓我們高層次地看一下 Aura 是如何工作的。Aura 要求在每個(gè)節(jié)點(diǎn)中指定相同的驗(yàn)證者列表。這是參與共識(shí)的賬戶地址列表。一個(gè)節(jié)點(diǎn)可能是一個(gè)驗(yàn)證節(jié)點(diǎn),也可能不是。即使是驗(yàn)證節(jié)點(diǎn)也需要擁有此列表,以便它自己能夠達(dá)成共識(shí)。
如果驗(yàn)證者列表永遠(yuǎn)不會(huì)改變,此列表可以在創(chuàng)世文件中提供為靜態(tài)列表;或者可以在智能合約中提供,以便可以動(dòng)態(tài)更新,并且每個(gè)節(jié)點(diǎn)都知道它。在智能合約中,你可以配置各種關(guān)于誰可以添加新驗(yàn)證者的策略。
區(qū)塊時(shí)間在創(chuàng)世文件中是可配置的。由你決定區(qū)塊時(shí)間。在私有網(wǎng)絡(luò)中,低至三秒的區(qū)塊時(shí)間效果良好。在 Aura 中,每隔三秒鐘選擇一個(gè)驗(yàn)證者,該驗(yàn)證者負(fù)責(zé)創(chuàng)建、驗(yàn)證、簽名和廣播區(qū)塊。我們不需要太多了解實(shí)際的選擇算法,因?yàn)檫@不會(huì)影響我們的 dapp 開發(fā)。但這是計(jì)算下一個(gè)驗(yàn)證者的公式,。選擇算法足夠智能,給每個(gè)人平等的機(jī)會(huì)。當(dāng)其他節(jié)點(diǎn)接收到一個(gè)區(qū)塊時(shí),它們會(huì)檢查它是否來自下一個(gè)有效的驗(yàn)證者;如果不是,它們會(huì)拒絕它。與 PoW 不同,當(dāng)一個(gè)驗(yàn)證者創(chuàng)建一個(gè)區(qū)塊時(shí),它不會(huì)獲得以太幣獎(jiǎng)勵(lì)。在 Aura 中,當(dāng)沒有交易時(shí),是否生成空區(qū)塊由我們決定。
你一定在想,如果由于某種原因,下一個(gè)驗(yàn)證節(jié)點(diǎn)無法創(chuàng)建和廣播下一個(gè)區(qū)塊會(huì)發(fā)生什么。為了理解這一點(diǎn),讓我們舉個(gè)例子:假設(shè) A 是下一個(gè)區(qū)塊(即第五個(gè)區(qū)塊)的驗(yàn)證者,B 是第六個(gè)區(qū)塊的驗(yàn)證者。假設(shè)區(qū)塊時(shí)間為五秒。如果 A 未能廣播一個(gè)區(qū)塊,那么五秒后當(dāng) B 輪到時(shí),它將廣播一個(gè)區(qū)塊。所以實(shí)際上并沒有發(fā)生什么嚴(yán)重的事情。區(qū)塊時(shí)間戳將顯示這些細(xì)節(jié)。
你可能也想知道是否有可能出現(xiàn)網(wǎng)絡(luò)以多個(gè)不同的區(qū)塊鏈結(jié)束的情況,就像在 PoW 中兩個(gè)礦工同時(shí)挖礦的情況一樣。是的,這種情況可能有很多種。讓我們舉一個(gè)例子,了解一種可能發(fā)生的情況以及網(wǎng)絡(luò)如何自動(dòng)解決它。假設(shè)有五個(gè)驗(yàn)證者:A、B、C、D 和 E。區(qū)塊時(shí)間為五秒。假設(shè)首先選擇 A,并且它廣播了一個(gè)區(qū)塊,但由于某種原因,該區(qū)塊未到達(dá) D 和 E;因此他們會(huì)認(rèn)為 A 沒有廣播該區(qū)塊?,F(xiàn)在假設(shè)選擇算法選擇 B 來生成下一個(gè)區(qū)塊;那么 B 將在 A 的區(qū)塊之上生成下一個(gè)區(qū)塊并廣播給所有節(jié)點(diǎn)。D 和 E 將拒絕它,因?yàn)榍耙粋€(gè)區(qū)塊的哈希值不匹配。因此,D 和 E 將形成不同的鏈,而 A、B 和 C 將形成不同的鏈。A、B 和 C 將拒絕來自 D 和 E 的區(qū)塊,而 D 和 E 將拒絕來自 A、B 和 C 的區(qū)塊。這個(gè)問題在節(jié)點(diǎn)之間解決,因?yàn)?A、B 和 C 持有的區(qū)塊鏈比 D 和 E 持有的區(qū)塊鏈更準(zhǔn)確;因此 D 和 E 將用 A、B 和 C 持有的區(qū)塊鏈替換他們的區(qū)塊鏈版本。這兩個(gè)版本的區(qū)塊鏈將具有不同的準(zhǔn)確度分?jǐn)?shù),第一個(gè)區(qū)塊鏈的分?jǐn)?shù)將比第二個(gè)區(qū)塊鏈的分?jǐn)?shù)更高。當(dāng) B 廣播它的區(qū)塊時(shí),它還將提供其區(qū)塊鏈的分?jǐn)?shù),由于其分?jǐn)?shù)更高,D 和 E 將用 B 的區(qū)塊鏈替換他們的區(qū)塊鏈。這就是沖突是如何解決的。區(qū)塊鏈的鏈分?jǐn)?shù)是使用來計(jì)算的。首先按長(zhǎng)度對(duì)鏈進(jìn)行評(píng)分(區(qū)塊越多,越好)。對(duì)于長(zhǎng)度相等的鏈,選擇最后一個(gè)區(qū)塊更老的鏈。
你可以深入了解 Aura 在。
Parity 需要 Rust 版本 1.16.0 來構(gòu)建。推薦通過 rustup 安裝 Rust。
如果你還沒有安裝 rustup,你可以像這樣安裝它。
在基于 Linux 的操作系統(tǒng)上,運(yùn)行此命令:
Parity 還需要安裝、、/、和軟件包。
在 OS X 上,運(yùn)行此命令:
Parity 還需要 clang。Clang 隨 Xcode 命令行工具一起提供,或者可以使用 Homebrew 安裝。
確保你安裝了帶有 C++支持的 Visual Studio 2015。接下來,從下載并運(yùn)行 rustup 安裝程序,啟動(dòng)"VS2015 x64 Native Tools Command Prompt",并使用以下命令安裝和設(shè)置工具鏈:
現(xiàn)在,在您的操作系統(tǒng)上安裝了 rust 之后,您可以運(yùn)行以下簡(jiǎn)單的單行命令來安裝 parity:
要檢查是否安裝了 parity,運(yùn)行以下命令:
如果成功安裝了 parity,則會(huì)看到一個(gè)子命令和選項(xiàng)的列表。
現(xiàn)在是時(shí)候設(shè)置我們的聯(lián)合區(qū)塊鏈了。我們將使用 Aura 來創(chuàng)建兩個(gè)互相連接的驗(yàn)證節(jié)點(diǎn)進(jìn)行共識(shí)。我們會(huì)在同一臺(tái)計(jì)算機(jī)上設(shè)置它們。
首先,打開兩個(gè) shell 窗口。其中一個(gè)是給第一個(gè)驗(yàn)證者,另一個(gè)是給第二個(gè)驗(yàn)證者。第一個(gè)節(jié)點(diǎn)將包含兩個(gè)賬戶,第二個(gè)節(jié)點(diǎn)將包含一個(gè)賬戶。第一個(gè)節(jié)點(diǎn)的第二個(gè)賬戶將被分配一些初始 ether,這樣網(wǎng)絡(luò)就會(huì)有一些 ether。
在第一個(gè) shell 中,運(yùn)行以下命令兩次:
兩次都會(huì)要求您輸入密碼。現(xiàn)在只需為兩個(gè)賬戶設(shè)置相同的密碼。
在第二個(gè) shell 中,只運(yùn)行一次以下命令:
就像以前一樣,輸入密碼。
每個(gè)網(wǎng)絡(luò)的節(jié)點(diǎn)共享一個(gè)通用的規(guī)范文件。該文件告訴節(jié)點(diǎn)關(guān)于創(chuàng)世塊,誰是驗(yàn)證者等信息。我們將創(chuàng)建一個(gè)智能合約,其中將包含驗(yàn)證者列表。有兩種類型的驗(yàn)證器合約:非報(bào)告合約和報(bào)告合約。我們只需要提供一個(gè)。
區(qū)別在于非報(bào)告合同只返回驗(yàn)證器列表,而報(bào)告合同可以對(duì)良性(良性的不端行為可能只是未從指定驗(yàn)證者那里接收到一個(gè)塊)和惡意不端行為(惡意的不端行為是發(fā)布了兩個(gè)不同的塊來同一個(gè)步驟)采取行動(dòng)。
非報(bào)告合同至少應(yīng)該具有以下接口:
函數(shù)將在每個(gè)塊上被調(diào)用以確定當(dāng)前列表。然后,切換規(guī)則由實(shí)施該方法的合同確定。
報(bào)告合同至少應(yīng)該具有以下接口:
當(dāng)存在良性或惡意行為時(shí),共識(shí)引擎分別調(diào)用 和 函數(shù)。
讓我們創(chuàng)建一個(gè)報(bào)告合同。下面是一個(gè)基本示例:
這段代碼是不言而喻的。確保在驗(yàn)證器數(shù)組中用第一個(gè)驗(yàn)證者 1 和驗(yàn)證者 2 節(jié)點(diǎn)的第一個(gè)地址替換地址,因?yàn)槲覀儗⑹褂眠@些地址進(jìn)行驗(yàn)證?,F(xiàn)在使用您感到舒適的任何方法編譯前述合同。
現(xiàn)在我們來創(chuàng)建規(guī)范文件。創(chuàng)建一個(gè)名為 的文件,并將以下代碼放入其中:
下面是之前文件的工作原理:
-
屬性用于設(shè)置共識(shí)協(xié)議和協(xié)議特定參數(shù)。在這里,引擎是 ,即 aura。 確定了 gas 限制調(diào)整,具有通常的 值。在 屬性中,我們有一個(gè) 屬性,這是報(bào)告合約的地址。 是以秒為單位的區(qū)塊時(shí)間。
-
在 屬性中,只有網(wǎng)絡(luò) ID 是重要的;其他的對(duì)所有鏈都是標(biāo)準(zhǔn)的。
-
對(duì)于 共識(shí)有一些標(biāo)準(zhǔn)值。
-
用于列出網(wǎng)絡(luò)中存在的初始帳戶和合約。前四個(gè)是標(biāo)準(zhǔn)的以太坊內(nèi)置合約;這些應(yīng)該包括以使用 Solidity 合約編寫語言。第五個(gè)是報(bào)告合約。確保將字節(jié)碼替換為您的字節(jié)碼,在 參數(shù)中使用。最后一個(gè)帳戶是驗(yàn)證器 1 shell 中生成的第二個(gè)帳戶。它用于向網(wǎng)絡(luò)提供以太。將此地址替換為您自己的地址。
在我們繼續(xù)之前,創(chuàng)建另一個(gè)名為 的文件。在該文件中,放置您創(chuàng)建的帳戶的密碼。這個(gè)文件將被驗(yàn)證者用來解鎖帳戶以簽署區(qū)塊。
現(xiàn)在我們已經(jīng)準(zhǔn)備好啟動(dòng)我們的驗(yàn)證節(jié)點(diǎn)所需的所有基本要求。在第一個(gè) shell 中,運(yùn)行以下命令來啟動(dòng)第一個(gè)驗(yàn)證節(jié)點(diǎn):
以下是前述命令的工作原理:
-
用于指定規(guī)范文件的路徑。
-
用于指定數(shù)據(jù)目錄。
-
確保即使沒有交易,也會(huì)產(chǎn)生區(qū)塊。
-
用于指定節(jié)點(diǎn)將用于簽名區(qū)塊的地址,即驗(yàn)證者的地址。如果存在惡意權(quán)限,則建議使用 ;這將確保正確的鏈?zhǔn)亲铋L(zhǎng)的。確保將地址更改為您生成的地址,即在此 shell 中生成的第一個(gè)地址。
-
用于指定密碼文件。
在第二個(gè) shell 中,運(yùn)行以下命令來啟動(dòng)第二個(gè)驗(yàn)證節(jié)點(diǎn):
在這里,請(qǐng)確保將地址更改為您在此 shell 中生成的地址。
最后,我們需要連接這兩個(gè)節(jié)點(diǎn)。打開一個(gè)新的 shell 窗口,并運(yùn)行以下命令來查找連接到第二個(gè)節(jié)點(diǎn)的 URL:
您將會(huì)得到這樣的輸出:
現(xiàn)在運(yùn)行以下命令,將 URL 和 IP 地址中的編碼 URL 替換為 127.0.0.1:
您應(yīng)該得到這個(gè)輸出:
節(jié)點(diǎn)應(yīng)該在控制臺(tái)中指示 0/1/25 對(duì)等節(jié)點(diǎn),這意味著它們彼此連接。這是一個(gè)參考圖像:
我們看到了 parity 如何解決速度和安全性的問題。Parity 目前沒有提供任何特定于許可和隱私的內(nèi)容。讓我們看看如何在 parity 中實(shí)現(xiàn)這一點(diǎn):
-
權(quán)限管理:Parity 網(wǎng)絡(luò)可以通過配置每個(gè)節(jié)點(diǎn)的服務(wù)器,僅允許特定 IP 地址的連接,來實(shí)現(xiàn)權(quán)限管理,決定誰可以加入,誰不可以加入。即使 IP 地址沒有被阻止,要連接網(wǎng)絡(luò)中的一個(gè)節(jié)點(diǎn),新節(jié)點(diǎn)也需要一個(gè)我們之前看到的 enode 地址,而這個(gè)地址是無法猜測(cè)的。因此,默認(rèn)情況下存在基本的保護(hù)。但是沒有強(qiáng)制執(zhí)行這一點(diǎn)。網(wǎng)絡(luò)中的每個(gè)節(jié)點(diǎn)都必須自行處理此問題。類似的權(quán)限管理,可以通過智能合約來確定誰可以創(chuàng)建區(qū)塊,誰不可以創(chuàng)建。最后,節(jié)點(diǎn)可以發(fā)送什么樣的交易目前是不可配置的。
-
身份隱私:通過仍然啟用所有權(quán)檢查的技術(shù),可以實(shí)現(xiàn)身份隱私。在設(shè)置所有權(quán)時(shí),所有者需要指定一個(gè)不確定的非對(duì)稱加密的公鑰。每當(dāng)它想要通過所有權(quán)檢查時(shí),它將提供常見文本的加密形式,合約將解密它并查看賬戶是否為所有者。合約應(yīng)確保相同的加密數(shù)據(jù)不會(huì)被檢查兩次。
-
數(shù)據(jù)隱私:如果您只是使用區(qū)塊鏈來存儲(chǔ)數(shù)據(jù),可以使用對(duì)稱加密來加密數(shù)據(jù),并與您想要查看數(shù)據(jù)的人共享密鑰。但無法對(duì)加密數(shù)據(jù)進(jìn)行操作。如果需要對(duì)輸入數(shù)據(jù)進(jìn)行操作并仍然保護(hù)隱私,則各方必須完全建立一個(gè)不同的區(qū)塊鏈網(wǎng)絡(luò)。
總的來說,在本章中,我們學(xué)習(xí)了如何使用 Parity 以及 aura 的工作原理,以及在 Parity 中實(shí)現(xiàn)權(quán)限管理和隱私的一些技巧。現(xiàn)在,您至少應(yīng)該有足夠的信心為使用區(qū)塊鏈的聯(lián)盟構(gòu)建一個(gè)概念驗(yàn)證?,F(xiàn)在,您可以繼續(xù)探索其他解決方案,例如 Hyperledger 1.0 和 Quorum,用于構(gòu)建聯(lián)盟區(qū)塊鏈。目前,以太坊正式致力于使其更適合聯(lián)盟;因此,請(qǐng)密切關(guān)注各種區(qū)塊鏈信息來源,了解市場(chǎng)上的新動(dòng)態(tài)。