如何在私人區塊鏈上編寫、部署以及與以太坊進行互動的智能合約

來源:互聯網
上載者:User

原文:How To Write, Deploy, and Interact with Ethereum Smart Contracts on a Private Blockchain 
作者:jack_schultz 
翻譯:無阻我飛揚

摘要:本文作者以極長的篇幅給出了在私人區塊鏈上編寫、部署以及與以太坊進行互動的智能合約的較為完整的代碼、相關細節步驟、使用者介面等。作者是希望藉助他這篇文章,大家可以自行在私人以太坊區塊鏈上編寫並部署一個智能合約,以下是譯文。

這裡的規則是:如果通讀本文,則必須自行在私人以太坊區塊鏈上部署一個智能合約。Github上給出了所有我使用的代碼,所以你沒有理由不去做。

但是如果不遵守規則,只是想閱讀一下而已,希望這有助於提供一個從無到有做出一個區塊鏈應用程式的視角。

最後,你會建立出一個私人以太坊專用區塊鏈,串連兩個不同的節點作為peers,編寫並編譯一個智能合約,有一個允許使用者提出問題的Web介面,在區塊鏈上部署問題,然後讓使用者來回答。

如果感到困惑,遇到錯誤,或者想說點別的,那就寫一篇評論,在Twitter上取得聯絡或發表意見。

這裡是Github的repo,所以繼續並fork它(如果不想複製粘貼所有的代碼),如果有想要分享的更新,我會把它放到讀我檔案中。 私人區塊鏈建立

要建立一個單獨的節點,需要以下genesis.json代碼,它代表私人區塊鏈上的初始塊。

//genesis.json{ "alloc": {}, "config": {   "chainID": 72,   "homesteadBlock": 0,   "eip155Block": 0,   "eip158Block": 0 }, "nonce": "0x0000000000000000", "difficulty": "0x4000", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "timestamp": "0x00", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "gasLimit": "0xffffffff"}

如果希望對欄位有一個完整的解釋,看看這個堆疊溢位的解答。這個案例中的difficulty是很低的,因為不希望在測試網路上等待很長的時間,區塊才能被挖掘出來,然後gasLimit 的值高到允許區塊中的一個節點可以完成的工作量能夠處理每個交易。

去開啟一個終端,確保geth(以太坊用戶端)以任何適用於你的作業系統的方式進行安裝,然後cd(dos命令)到儲存genesis.json的檔案夾。運行以下命令,將初始化該節點的區塊鏈。

$ geth --datadir "/Users/USERNAME/Library/PrivEth" init genesis.json

-datadir指定區塊鏈所有資料的位置。在Mac作業系統上,預設是 ~/Library/Ethereum目錄。由於有多個節點在運行,所以不能讓它們共用相同的資料檔案夾,因此需要具體指定。Linux和Windows機器具有不同的預設datadir,所以請查看這些資料一般應該位於何處。

用genesis.json檔案運行完初始化命令之後,去檢查那個--datadir目錄,會看到一堆檔案,所以隨意四處看看吧。現在沒有必要,但是最終還是要去看看。

對於這樣一個區塊鏈,需要多個節點。要使區塊鏈成為peers,它們需要擁有相同的創始檔案。所以要從同一個目錄運行和上面相同的命令,但是這次使用了不同的datadir。

geth --datadir "/Users/USERNAME/Library/PrivEth2" init genesis.json
1 2

這裡所有的代碼,將在同一個目錄下工作。代碼是一樣的,但是使用命令列選項,可以通過命令列參數區分這些進程。

初始化兩個節點的鏈。

當通過一個不同的--datadir運行geth,無論從哪裡運行命令,都將運行單獨的節點。只要記得每次指定--datadir,那麼它就不會回到預設值。另外請注意,我更改了這些datadirs的名稱,所以會在螢幕截圖中看到不同的名稱。 開啟控制台

到目前為止,已經做了三件事。1)在選擇的工作目錄中建立了一個genesis.json 檔案,2)為一個節點選擇一個目錄存放區塊鏈,並初始化第一個區塊,3)為另外一個節點選擇一個不同的目錄存放區塊鏈。很少的代碼和一些命令。

下一步能夠登入到每個節點的geth控制台。控制台將啟動geth進程並運行它,也給了在終端上運行一些web3命令的方法。

geth --datadir "/Users/jackschultz/Library/EthPrivLocal" --networkid 72 --port 30301 --nodiscover console
1 2

這裡還有更多的選擇。

-networkid與genesis.json檔案中的類似,在這裡所需要做的是確保不使用網路ID 1-4。

-port指定.ipc檔案將要用到的連接埠。這就是使用web3.js庫串連資料庫的方式,預設連接埠是30303。所以將它保留在那個地區,但這是第一個節點,所以它的連接埠是30301。

nodiscover告訴geth最初不要找peers。這一點在這個案例中確實很重要。這是一個私人網路。不希望節點在沒有指定的情況下嘗試串連到其它節點,不希望這些節點在沒有告訴它們的情況下被發現。

在第一個geth節點啟動並執行情況下,在有第二個—datadir的不同終端運行相同的命令,節點在不同的連接埠上運行。

啟動控制台。 為每個節點建立初始Coinbase帳戶

當用上面的命令運行控制台時,想要建立主coinbase帳戶。如果感到好奇,使用密碼短語“passphrase”,將來Node應用程式會用到“passphrase”。

> personal.listAccounts[]> personal.newAccount()Passphrase:Repeat passphrase:0x538341f72db4b64e320e6c7c698499ca68a6880c> personal.listAccounts[“0x538341f72db4b64e320e6c7c698499ca68a6880c”]

在另一個節點的控制台中運行相同的命令。

建立新的帳戶。

由於這是該節點建立的第一個帳戶,因此會看到它也列在其中

> eth.coinbase0x538341f72db4b64e320e6c7c698499ca68a6880c

通過運行可以在控制台上抓取的另一條資訊

> personal.listWallets[{accounts:[{    address:“0x538341f72db4b64e320e6c7c698499ca68a6880c”,    url:“keystore:///Users/jackschultz/Library/EthPrivLocal/keystore/UTC--2017-12-09T16-21-48.056824000Z--538341f72db4b64e320e6c7c698499ca68a6880c”}],status:“locked”,url:“keystore:///Users/jackschultz/Library/EthPrivLocal/keystore/UTC--2017-12-09T16-21-48.056824000Z--538341f72db4b64e320e6c7c698499ca68a6880c”}]

在那裡會看到更多有關帳戶的資訊,而不是只有地址。還會看到帳戶資訊的儲存位置,它會在指定的--datadir。所以如果仍然好奇資料是如何儲存在檔案系統中的,那就去查看一下目錄。 以Peers串連節點

有多個節點正在運行,需要以peers串連它們。首先檢查我們是否有peers

> admin.peers[]

好難過。這是我們期望的,在非1-4網路ID和nodiscover的標誌上啟動控制台。這意味著需要告知每個節點用特定的命令串連到另一個節點。通過分享enode 地址的方式來做。

> admin.nodeInfo.enode“enode:// 13b835d68917bd4970502b53d8125db1e124b466f6473361c558ea481e31ce4197843ec7d8684011b15ce63def5eeb73982d04425af3a0b6f3437a030878c8a9 @ [:]:30301 discport = 0”

這是geth用來串連到不同節點的enode資訊,在這些不同的節點它們能夠分享交易和成功挖掘資訊。

要使用這個URL串連節點,需要調用addPeer函數。

如果要複製從其中一個節點admin.nodeInfo.enode的傳回值,請在另一個節點中運行以下命令。

> admin.addPeer(“enode:// 13b835d68917bd4970502b53d8125db1e124b466f6473361c558ea481e31ce4197843ec7d8684011b15ce63def5eeb73982d04425af3a0b6f3437a030878c8a9 @ [::]:30301。discport = 0”)

這告知一個節點如何到達另一個節點,並請求另一個節點串連起來,它們都將成為彼此的peers。如需檢驗,請在兩個節點上運行admin.peers命令,將看到它們串連在一起。代碼如下:

> admin.peers[{caps: ["eth/63"],id: "99bf59fe629dbea3cb3da94be4a6cff625c40da21dfffacddc4f723661aa1aa77cd4fb7921eb437b0d5e9333c01ed57bfc0d433b9f718a2c95287d3542f2e9a8",name: "Geth/v1.7.1-stable-05101641/darwin-amd64/go1.9.1",network: {    localAddress: "[::1]:30301",    remoteAddress: "[::1]:50042"},protocols: {    eth: {        difficulty: 935232,        head: "0x8dd2dc7968328c8bbd5aacc53f87e590a469e5bde3945bee0f6ae13392503d17",        version: 63    }}}]

要添加peer,只需要告訴一個節點串連到另一個節點,然後檢查另一個節點,就會看到如下輸出: 

Peers on peers。 檢查餘額並挖掘

既然節點串連起來了,就不是錢的事了。在開始挖掘之前,檢查一下主賬戶的餘額。

> eth.getBalance(eth.coinbase)0>

再一次如此悲傷。由於沒有把這個帳戶分配給創始區塊,需要開始為這些賬戶挖礦。

在控制台中,運行miner.start()為這個節點開始挖掘,然後運行miner.stop()可以停止挖掘。在挖掘時,不僅要看帳號得到多少以太幣,還要觀察兩個節點之間點對點的互動。

在下面的圖片中,會看到檢查了兩個節點各自的主帳戶餘額。然後在節點1上開始挖掘,讓它運行大約5秒,然後在7個完整區塊之後停止挖掘。檢查另一邊的餘額,有35個以太幣,在控制台中這個數字代表Wei。在另一個節點上,將會看到它收到了從節點1挖掘的7個區塊的資訊。

開始挖掘。 交易

使用智能合約需要專門的交易,但在實現這一點之前,要知道如何建立一個交易,將以太幣發送到另一個帳戶。

在一個節點上,採用coinbase賬戶並解鎖它。

> coinbaseAddress = eth.coinbase> personal.unlockAccount(coinbaseAddress)Unlock account 0x554585d7c4e5b5569158c33684657772c0d0b7e1Passphrase:True

現在從另一個節點的coinbase帳戶複製地址,並回到未解鎖的帳戶節點

> hisAddress = "0x846774a81e8e48379c6283a3aa92e9036017172a"

在此之後,sendTransaction命令有點簡單。

> eth.sendTransaction({from: eth.coinbase, to: hisAddress, value: 100000000})INFO [12-09|10:29:36] Submitted transaction fullhash=0x776689315d837b5f0d9220dc7c0e7315ef45907e188684a6609fde8fcd97dd57 recipient=0x846774A81E8E48379C6283a3Aa92E9036017172A"0x776689315d837b5f0d9220dc7c0e7315ef45907e188684a6609fde8fcd97dd57"

還有一件需要注意的事,而且會很容易混淆的,就是為什麼這些數位值有那麼多0。這是因為值是用wei來表示的,所以不必處理可能在不同系統上引起問題的浮點數。這將與gas(一個與計算步驟大致相當的測量法。每筆交易都需要包括一個Gas限制和一個願意為每個Gas支付的費用;礦工可以選擇進行交易和收費)一起發揮作用 ,需要開始指定合約部署和交易。

如果想知道用這個值發送了多少以太幣,命令如下:

> web3.fromWei(100000000,'ether')“0.0000000001”

要使交易發送,並且看到不同餘額的差異,需要在節點中啟動礦工,然後在挖掘了一個區塊後停止,現在檢查餘額以查看變化。

> miner.start()...............> miner.stop()> web3.eth.getBalance(eth.coinbase)59999999999900000000> web3.eth.getBalance(hisAddress)100000000

接下來看看下面的這張巨幅圖片。同樣,節點1在左邊,節點2在右邊。所以首先檢查每個節點上各自coinbase賬戶的餘額。在節點1上,複製節點2的地址,發送交易,然後從接收到提交的交易的節點登入,接著開始挖掘。會發現節點8 有txs=1,這意味著它在那個區塊挖掘了一筆交易。再多挖幾個區塊以後,停止挖掘。檢查節點1的帳戶餘額。有12個區塊,每個區塊獎勵5以太幣,但後來卻付出了100000000wei。

現在,回到節點2,檢查其coinbase帳戶的餘額,餘額是0。然後,記得重新啟動過節點1的控制台,並沒有將兩個節點設定為peers。因此,列印節點1的enode,作為一個peer將其添到節點2。在添加peer後,會看到節點2接收到錯過的塊,包括1個交易。然後再次檢查餘額,發現它有100000000Wei。

這是如何在本地發送以太幣。 間歇

到這裡,差不多完成了一半的工作。在一個擁有本地啟動並執行私人以太坊區塊鏈的終端上工作,擁有賬戶的兩個節點,彼此是peers,並且可以來回傳送交易。

這相當不錯,所以可以花一點時間冷靜下來,有一個更好的理解。但是在此刻,請繼續前進。 在Remix上編寫一個合約

繼續。隨著geth節點的運行,下一步就是簽訂合約。

當寫這樣的文章時,需要花很長時間來選擇一個簡單而有價值的例子。當試圖選擇一種合約來使用時,情況亦是如此。我決定擺在這裡的是人們可以回答是/否或真/假的問題。

下面是Solidity(是以太坊中用於開發智能合約的程式設計語言,目前開發智能合約用的最多的是Solidity)合約的最終v1代碼。在看代碼之前,有一些注意事項: 在這個例子中,只使用全域變數來解決問題,是誰問了這個問題,誰回答了這個問題,以及答案的值。Solidity也有可以儲存資料的結構,但是本文在討論部署而不是Solidity,所以不要太深入。 使用 uints來儲存是/否的答案,而不是bools。在Solidity中,如果有將地址連結到bool的映射,則預設值為FALSE。對於一個uint,預設值是零。這有了必要的三種狀態,在這裡可以用一個enum,但正如我所說,盡量保持簡單。 answerQuestion方法在邏輯和if語句中都有些複雜。如果想瞭解如何調整變數,請仔細閱讀它。

有一個get函數,返回所有想要在頁面上顯示合約狀態的資訊。可以分開來分別返回不同的資訊,但是不妨把它們放在一起,而不必多次查詢。 
-在合約中不僅有其它方式儲存這些資料,還有很多其它的方式來編寫它。例如,可以列出所有投票為true或false的賬戶,然後迴圈查詢它們是否已經回答。

pragma solidity ^0.4.0;contract Questions {  //global variables that aren't in a struct  mapping(address => uint) public answers; //integer where 0 means hasn't answered, 1 means yes, 2 means no  string question;  address asker;  uint trues;  uint falses;  /// __init__  function Questions(string _question) public {    asker = msg.sender;question = _question;  }  //We need a way to validate whether or not they've answered before.  //The default of a mapping is   function answerQuestion (bool _answer) public {    if (answers[msg.sender] == 0 && _answer) { //haven't answered yet      answers[msg.sender] = 1; //they vote true      trues += 1;}    else if (answers[msg.sender] == 0 && !_answer) {      answers[msg.sender] = 2; //falsity      falses += 1;    }    else if (answers[msg.sender] == 2 && _answer) { // false switching to true      answers[msg.sender] = 1; //true      trues += 1;      falses -= 1;        }    else if (answers[msg.sender] == 1 && !_answer) { // true switching to false      answers[msg.sender] = 2; //falsity      trues -= 1;      falses += 1;    }  }  function getQuestion() public constant returns (string, uint, uint, uint) {    return (question, trues, falses, answers[msg.sender]);      }}

把這個合約儲存在contracts/Question.sol中,而不是在本地進行編譯,使用Remix來處理大量的錯誤和代碼警告,以及編譯所需的資訊。

要查看編譯資訊,在右上方的“編譯”選項卡上單擊詳細資料按鈕,就會看到一堆資訊彈出。要尋找的資料是byteCode和ABI。右下方正是要模仿的web3的部署資訊。但是,不

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.