1. 程式人生 > >如何在私有區塊鏈上編寫、部署以及與以太坊進行互動的智慧合約

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

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

這裡的規則是:如果通讀本文,則必須自行在私有以太坊區塊鏈上部署一個智慧合約。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

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

這裡寫圖片描述

初始化兩個節點的鏈。

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

開啟控制檯

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

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

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

這裡還有更多的選擇。

-networkidgenesis.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.coinbase
0x538341f72db4b64e320e6c7c698499ca68a6880c

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

> 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 0x554585d7c4e5b5569158c33684657772c0d0b7e1
Passphrase:
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的部署資訊!但是,不是從一個單一的行上輸入巨大的字串,而是要從一個json檔案中匯入資訊。必須把資料分開。

//childContractv1.json
{
  "abi": [{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"answers","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuestion","outputs":[{"name":"","type":"string"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_answer","type":"bool"}],"name":"answerQuestion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_question","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}],
  "byteCode": "0x6060604052341561000f57600080fd5b6040516106d23803806106d28339810160405280805182019190505033600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610082929190610089565b505061012e565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100ca57805160ff19168380011785556100f8565b828001600101855582156100f8579182015b828111156100f75782518255916020019190600101906100dc565b5b5090506101059190610109565b5090565b61012b91905b8082111561012757600081600090555060010161010f565b5090565b90565b6105958061013d6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635e9618e71461005c578063eff38f92146100a9578063f9e049611461014c575b600080fd5b341561006757600080fd5b610093600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610171565b6040518082815260200191505060405180910390f35b34156100b457600080fd5b6100bc610189565b6040518080602001858152602001848152602001838152602001828103825286818151815260200191508051906020019080838360005b8381101561010e5780820151818401526020810190506100f3565b50505050905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b341561015757600080fd5b61016f60048080351515906020019091905050610287565b005b60006020528060005260406000206000915090505481565b610191610555565b600080600060016003546004546000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200............................600460008282540392505081905550610550565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541480156104e3575080155b1561054f5760026000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600160036000828254039250508190555060016004600082825401925050819055505b5b5b5b50565b6020604051908101604052806000815250905600a165627a7a7230582043defebf8fa91b1cd010927004a7ff4816a1040b9cabd4ddd22122a9816742ff0029"
}

繼續直接複製這個檔案,到Remix上並與Remxi的編譯器一起工作。值得一提的是byteCode,需要確保字串以“0x”開始。當從Remix複製位元組碼欄位時,只能得到數字。

NodeJS時間

上面每次說到節點,意思是geth / blockchain節點。在這裡,將再次看到“node”這個詞,但是當看到大寫字母N時,它的意思是NodeJS。

已經將v1合約編譯並存儲在一個檔案中。現在需要執行一個Node(這個Node首字母大寫了,所以它代表NoteJS)例項。將有四個端點:

  • GET’/’將會有一個表單提出一個新的問題,
  • POST’/ questions / new’在區塊鏈上部署新問題合約,
  • GET’/ questions?address = 0xXXXX …’將依據當前的答案顯示問題以及一個傳送或更新答案的表單,
    -POST’/ questions?address = 0xXXXX …’處理應答。

部署問題

前言,在進入區塊鏈之前,從來沒用使用過Node,所以有一些語法和實踐可能會在這裡無效。對於程式碼,會通過與區塊鏈互動的三個端點,首先是部署新問題的post請求。這些程式碼需要連線到本地執行的geth

const Web3 = require('web3');
const net = require('net');
    const compiledContract = require('./contracts/contractv1');

web3IPC = '/Users/jackschultz/Library/PrivEth/geth.ipc';
let web3 = new Web3(web3IPC, net);

const byteCode = compiledContract.byteCode;
const QuestionContract = new web3.eth.Contract(compiledContract.abi);

web3.eth.getCoinbase(function(err, cba) {
 coinbaseAddress = cba;
 console.log(coinbaseAddress);
});
const coinbasePassphrase = 'passphrase';

app.post('/', (req, res) => {
  const question = req.body.question;
  web3.eth.personal.unlockAccount(coinbaseAddress, coinbasePassphrase, function(err, uares) {
QuestionContract.deploy({data: byteCode, arguments: 
}).send({from: coinbaseAddress, gas: 2000000})
 .on('receipt', function (receipt) {
     console.log("Contract Address: " + receipt.contractAddress);
     res.redirect('/questions?address=' + receipt.contractAddress);
});
  });
});

當到達端點時,從主體獲取請求後的第一步是解鎖正在部署的帳戶。這是必要的,不模仿別人。一旦得到回撥,將部署合約,其中交易的資料是整個的位元組碼,然後將問題字串傳遞給合約中的init函式。指定從coinbase地址傳送它,並說明要投入2000000wei(如果想知道它到底有多小,那就是0.000000000002 以太幣)。

更多的回撥,可以用在這裡,但現在唯一感興趣的是“收據”,合約被挖掘以後,它的地址在哪裡。就使用者介面而言,這樣寫的方式是在重定向到問題的頁面之前,頁面會掛起,等待合約被挖掘。對於廣泛使用的DAPP(Decentralized App,去中心化的應用程式)來說,這可能不是一個好主意,因為公共以太坊挖掘區塊平均約為14.5秒。但是這裡的私有區塊鏈上,把難度設定的如此之低,以至於區塊很快被挖掘完,所以這不是問題。

檢視問題

現在既然有一個問題存在,就想繼續討論它!使用web3.utils.isAddress函式來驗證地址不僅是一個有效的十六進位制字串,而且還驗證校驗和是有效的,確保它是一個存在的地址。

然後getQuestion 方法返回一個結果,這是一個返回值的字典。在本文的例子中,這是一個問題,true的數量,false的數量,以及執行節點的人是否回答了這個問題。

app.get('/questions', function(req, res) {
  const contractAddress = req.query.address;
  if (web3.utils.isAddress(contractAddress)) {
QuestionContract.options.address = contractAddress;
  const info = QuestionContract.methods.getQuestion().call(function(err, gqres) {
  //using number strings to get the data from the method      
  const question = gqres['0'];
  const trues = gqres['1'];
  const falses = gqres['2'];
  const currentAnswerInt = parseInt(gqres['3'], 10);
  data = {contractAddress: contractAddress, question: question, currentAnswerInt: currentAnswerInt, trues: trues, falses: falses};
  res.render('question', data);
});
  }
          else {
        res.status(404).send("No question with that address.");
  }
});

回答問題

當釋出這個問題url時,經過大部分相同的過程驗證輸入,驗證地址,然後用所需引數呼叫answerQuestion方法。隨著問題建立函式的出現,將讓瀏覽器掛起,直到有更新交易的區塊被挖掘出來。

app.post('/questions', function(req, res) {
  const contractAddress = req.query.address;
  const answerValue = req.body.answer == 'true' ? true : false;
  if (web3.utils.isAddress(contractAddress)) {
web3.eth.personal.unlockAccount(coinbaseAddress, coinbasePassphrase, function(err, uares) {
  QuestionContract.options.address = contractAddress;
  QuestionContract.methods.answerQuestion(answerValue).send({from: coinbaseAddress, gas: 2000000})
    .on('receipt', function (receipt) {
      console.log(`Question with address ${contractAddress} updated.`);
      res.redirect('/questions?address=' + contractAddress);
    }
  );
});
  }
});

HTML

至於HTML,不打算費心把它貼在這裡,因為它很簡單。我不喜歡使用CSS模板,因為像這樣在後臺提交併不重要。當談論到執行程式碼時,會看到以下基本介面的螢幕截圖。

執行程式碼

現在所有的程式碼都在那裡。控制檯上有四個選項卡開啟。兩個正在執行geth

geth --datadir /Users/jackschultz/Library/PrivEth --networkid 40 --port 30301 --nodiscover console

geth --datadir /Users/jackschultz/Library/PrivEth2 --networkid 40 --port 30302 --nodiscover console

另外兩個正在執行Node應用程式,連線到單獨的geth過程,並執行在不同的本地主機埠上。添加了配置檔案,將它們命名為主檔案和輔助檔案,指向ipc和該節點應當執行的埠。

NODE_ENV=primary node app.js

NODE_ENV=secondary node app.js

在這裡放一些圖片,讓閱讀的人可以更多地瞭解我在螢幕上看到的內容。在此基礎上,開啟瀏覽器並開始互動。首先是進入主頁,可以問一個問題。

這裡寫圖片描述

他們會嗎??

然後當點選提交按鈕時,會看到Node應用程式的日誌記錄,在geth控制檯中,將啟動礦工,然後在這個交易完成後停止它。

這裡寫圖片描述

是時候回答問題了。

回答的話,要提交表單,然後開始和停止挖礦。當自己做這件事的時候,一件有趣的事情就是在提交答案之前先啟動礦工,這樣就可以瞭解在創始塊中定義的這個小難度級別的挖掘速度。

檢查下面的終端。在頂級Node終端中,將看到有關驗證地址的一些日誌記錄,然後當重定向到同一頁面但具有更新資訊時記錄。在geth控制檯中,可以看到交易何時提交,以及這筆交易是在哪個區塊進行的。

這裡寫圖片描述

顯然他們會。

現在從主節點回答了這個問題,接著看看第二個節點。

在圖片的右側,會看到前兩個終端顯示Node和geth互動,然後底部是主要的geth,可以看到它接收到一個交易的區塊,因為這兩個geth節點是peers。在埠4002上的節點回答問題後,重新載入了埠4001上的頁面,可以看到下圖的結果。

這裡寫圖片描述

當然會的。

為了證明可以切換回false,把埠4002的答案改成了false(這是錯誤的,因為雄鹿隊肯定會打入季後賽),然後可以看到控制檯記錄了所經歷的資訊。

這裡寫圖片描述

截圖後,改回答案為true。

結論

如果已經看到了這裡,並且讓自己的程式碼執行起來了,恭喜。 像大多數帖子一樣,這比我最初想象的要長得多。這樣做的目的是完成並解釋智慧合約的所有步驟,而不是隻給出中間的某個地方。

如上所述,如果有任何反饋,在Twitter上取得聯絡或發表意見。

SDCC 2017之資料庫線上峰會即將強勢來襲,秉承乾貨實料(案例)的內容原則,邀請了來自阿里巴巴、騰訊、微博、網易等多家企業的資料庫專家及高校研究學者,圍繞Oracle、MySQL、PostgreSQL、Redis等熱點資料庫技術展開,從核心技術的深挖到高可用實踐的剖析,打造精華壓縮式分享,舉一反三,思辨互搏,報名及更多詳情可點選此處檢視
這裡寫圖片描述