帶你玩轉區塊鏈--以太坊框架truffle及私有鏈的搭建-第二章-第三節【以太坊篇】

一、意義:

          在之前內容的學習中,我們發現compile、deploy等文件在不同的項目中是可以互用的,根據低聚合高耦合的代碼設計規則,我們可以將此部分封裝成一個框架,從而更好的完成開發。所以ETH官方推出了Truffle框架。那麼,Truffle到底爲我們帶來了什麼呢?讓我們來一探究竟。

二、準備工作:

特殊要求:請將node版本降低到11.15.X版本,node12、13版本會因爲不兼容報錯,博主在這上面花了2個小時才解決。

1.安裝truffle

npm install truffle -g

2.官方文檔地址

github:
https://github.com/trufflesuite/truffle

官方文檔:
https://www.trufflesuite.com/docs/truffle/overview

三、truffle的學習:

1.創建一個工程

$ truffle
Truffle v3.4.11 - a development framework for Ethereum
$ cd myproject
$ truffle init

ps:

1.由於某些原因在安裝過程中,可能會卡着不動,這時候您需要科學上網或者使用淘寶鏡像cnpm安裝

truffle-config.js是win端的設置,truffle.js是mac或linux的設置。在創建工程後,windows端需要刪除truffle.js,不然可能會報錯,linux或mac端不用管。

2.使用truffle部署一個合約

1.在contracts中創建Simple.sol文件,並寫入。文件名需和合約名一致

pragma solidity ^0.4.24;
contract Simple{
    uint256 tmp;
    function setValue(uint256 value)public {
        tmp=value;
    }
    
    function getValue()public view returns(uint256){
        return tmp;
    }
}

2.執行編譯命令

truffle compile

3.在migrations文件夾中創建2_Simple_migrations.js文件並寫入

const Migrations = artifacts.require("Migrations");
module.exports = function(deployer) {
  deployer.deploy(Migrations);
};

4.設置truffle-config.js、修改默認文件爲:

module.exports = {
  networks: {
    ganacheNetwork: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*" // Match any network id
    }
  }
};



//修改編譯器
module.exports = {
  compilers: {
    solc: {
      version: <string>, // A version or constraint - Ex. "^0.5.0"
                         // Can also be set to "native" to use a native solc
      docker: <boolean>, // Use a version obtained through docker
      parser: "solcjs",  // Leverages solc-js purely for speedy parsing
      settings: {
        optimizer: {
          enabled: <boolean>,
          runs: <number>   // Optimize for how many times you intend to run the code
        },
        evmVersion: <string> // Default: "petersburg"
      }
    }
  }
}

4.連接ganache後執行部署命令

truffle migrate --network ganacheNetwork

從上述例子中,我們可以發現truffle的第一個優勢:1.深度集成。開發,測試,部署一行命令都可以搞定。

 

3.Truffle自帶控制檯develop

     在truffle中默認提供了一個與以太坊節點仿真器工具develop,將數據存儲在了內存中,它可以更加快速的測試,但看不到數據。默認端口爲9545,啓動命令爲truffle develop ,在啓動後直接執行compile進行編譯,直接執行migrate進行部署。

1.develop與合約交互

var deployinstance;
Test01.deployed().then(instance=>{deployinstance=instance})

//輸入deployinstance  ,這時返回值不是未定義,而是有合約實例了
deployinstance  

 //與合約進行交互。格式如:deployinstance.getValue.call({from:xxx}).then()
deployinstance.getValue.call()
  
//交互,格式如:deployinstance.setValue(500,{from:xxx}).then(),from可省略,默認爲第一個賬戶
deployinstance.setValue(500) 

2.合約測試Test

       由於博主truffle版本問題,在本地運行報錯,錯誤原因爲:"before all" hook: prepare suite。這是由於truffle版本的問題,在truffle4中不會遇見此問題,測試代碼如下:

import 'truffle/Assert.sol';
import 'truffle/DeployedAddresses.sol';
import '../contracts/Test01.sol';
contract TestTest01{
    //合約大寫,函數名小寫
    function testSet() public {
        //Test01 ss=Test01(DeployedAddresses.Test01());
        Test01 ss=new Test01();
        ss.setValue(500);
        uint256 res=ss.getValue();
        Assert.equal(res,500,"res should be 500,error");
    }
}

3.重新部署合約

migrate --reset

若還是失敗,則手動刪除build文件夾後,重新compile

從上述例子中,我們可以發現truffle的第二個優勢:2.提供了控制檯,使用框架構建後,可以直接在命令行調用輸出結果,可極大方便開發調試

4.truffle react框架

1.安裝

新建一個文件夾,進入該文件夾內使用命令 truffle unbox react命令下載該框架。

安裝過程錯誤處理

1.1由於該框架有230多M,所以需要耐心等待,如果下載過程出錯了,請刪掉原文件夾後重新下載。

1.2若安裝過程中,出現node-gyp錯誤,請將本地python版本降低至2.7.11,再將本地vs裝visual studio 2015,博主在這裏搞了一下午。若您需要python2和python3共存,請將環境變量重新設置。

2.啓動項目

進入項目文件後,cd client後 輸入npm start,即可啓動項目

3.運行合約實例、以及源碼解析

    由於原項目本身附帶了一個SimpileStorage.sol的合約,所以我只需對這個合約進行研究。首先進入develop模式後進行compile和migrate,這時web頁面會出現界面,並訪問metamask將value修改爲5,若有界面顯示value卻沒有修改,則需要在metamask中設置一個隨機的ChainID。項目部分源碼解析如下:(App.js)

import React, { Component } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";  //第一步引入合約
import getWeb3 from "./getWeb3";

import "./App.css";

class App extends Component {
  state = { storageValue: 7, web3: null, accounts: null, contract: null };

  componentDidMount = async () => {
    try {
      // Get network provider and web3 instance.
      const web3 = await getWeb3();                   //第二步創建web3對象

      // Use web3 to get the user's accounts.
      const accounts = await web3.eth.getAccounts();

      // Get the contract instance.
      const networkId = await web3.eth.net.getId();  //第三步返回當前網絡ID
      //第四步,拿到網絡對象
      const deployedNetwork = SimpleStorageContract.networks[networkId]; 
      //第五步獲取實例
      const instance = new web3.eth.Contract( 
        SimpleStorageContract.abi,//ABI
        deployedNetwork && deployedNetwork.address,//合約地址
      );

      // Set web3, accounts, and contract to the state, and then proceed with an
      // example of interacting with the contract's methods.
      this.setState({ web3, accounts, contract: instance }, this.runExample);
    } catch (error) {
      // Catch any errors for any of the above operations.
      alert(
        `Failed to load web3, accounts, or contract. Check console for details.`,
      );
      console.error(error);
    }
  };

  runExample = async () => {
    const { accounts, contract } = this.state;

    // Stores a given value, 5 by default.
    await contract.methods.set(5).send({ from: accounts[0] });

    // Get the value from the contract to prove it worked.
    const response = await contract.methods.get().call();
    console.log(response,"oooooooooooooo")

    // Update state with the result.
    this.setState({ storageValue: response });
  };

  render() {
    if (!this.state.web3) {
      return <div>Loading Web3, accounts, and contract...</div>;
    }
    return (
      <div className="App">
        <h1>Good to Go!</h1>
        <p>Your Truffle Box is installed and ready.</p>
        <h2>Smart Contract Example</h2>
        <p>
          If your contracts compiled and migrated successfully, below will show
          a stored value of 5 (by default).
        </p>
        <p>
          Try changing the value stored on <strong>line 40</strong> of App.js.
        </p>
        <div>The stored value is: {this.state.storageValue}</div>
      </div>
    );
  }
}

export default App;

5.Web3知識補充

let Web3 = require('web3')
let web3 = new Web3('http://127.0.0.1:7545')
console.log('version :', web3.version)
//獲取賬戶
let accounts
//error first callback style
//舊版本web3只支持方式一形式,不支持方式二方式三
web3.eth.getAccounts((err, res) => {
    console.log('獲取賬戶方式一:回調函數形式')
    if (err) {
        console.log('err:', err)
    }

    // console.log(res)
})
web3.eth.getAccounts().then(res => {
    console.log('獲取賬戶方式二:then形式')
    // console.log(res)
}).catch(e => {
    console.log(e)
})

let f = async () => {
    try {
        let accounts = await web3.eth.getAccounts()
        console.log('獲取賬戶方式三:async/await形式')
        // console.log(accounts)

        let balance1 = await web3.eth.getBalance(accounts[0])
        console.log('balance1:', balance1)

        //balance1: Promise { <pending> }
        // let balance1 = web3.eth.getBalance(accounts[0])

        let defaultAccount = web3.eth.defaultAccount
        console.log('default:', defaultAccount)
        //設置默認賬號
        web3.eth.defaultAccount = accounts[2]
        console.log('new default:', web3.eth.defaultAccount)

        let defaultBlock = web3.eth.defaultBlock
        console.log('defaultBlock:', defaultBlock)


        //由賬戶0向賬戶1轉10eth
        let res = await web3.eth.sendTransaction({
            // from: accounts[0], //如果不指定from,那麼會使用defaultAccount的值
            to: accounts[1],
            gas: '6000000',
            value: web3.utils.toWei('10', 'ether'),
        })
        //修改defaultBlock
        // web3.eth.defaultBlock = 3
        web3.eth.defaultBlock = 'latest'
        console.log('defaultBlock:', web3.eth.defaultBlock)

        let balance2 = await web3.eth.getBalance(accounts[2])
        console.log('balance2:', balance2)

    } catch (e) {
        console.log(e)
    }
}

f()

 6.Web3-Utils

由於js處理大數據會導致精度缺失,我們可以在區塊鏈計算中引入Bignumber模塊,或使用web3.utils中的toBN模塊。在兩個方法後結果都是採用的科學技術法的數值,其中符號: s    指數: e   尾數: c,爲了更好的觀察數據,這裏我們只需用toString()轉換一下即可。

6.1Bignumber

 安裝

npm i bignumber.js

處理加法

let BigNumber=require('bignumber.js');
let x=new BigNumber('10000000000000000000000000000000000000000050');
let y=new BigNumber('10');
let res=x.plus(y);
console.log(res.toString());
//- 詳細示例
var BigNumber = require('bignumber.js');

console.log('====相等?====')
x = new BigNumber(123.4567);
y = new BigNumber(123456.7e-3);
z = new BigNumber(x);
console.log(x.eq(y))

console.log('====加法====')
m = new BigNumber(10101, 2);
n = new BigNumber("ABCD", 16);  //16代表進制
console.log(m.plus(n))
console.log(m.plus(n).toString())

console.log('====減法====')
x = new BigNumber(0.5)
y = new BigNumber(0.4)
console.log(0.5 - 0.4)
console.log(x.minus(y).toString())

console.log('====乘法====')
x = new BigNumber('2222222222222222222222222222222222')
y = new BigNumber('7777777777777777777777777777777777', 16)
console.log(x.times(y).toString())

console.log('====除法====')
console.log(x.div(y).toString())
console.log(x.div(y).toFixed(6).toString())  //6代表小數位

console.log('==== x = -123.456====')
x = new BigNumber(-123.456)

console.log(x)

console.log("尾數x.c:",x.c)
console.log("指數x.e:",x.e)
console.log("符號x.s",x.s)

6.2web3.utils.toBN()

let Web3=require('web3');

let web3=new Web3();
let x=web3.utils.toBN('100000000000000000000000000050');
let y=web3.utils.toBN('10');

let res=x.add(y);
console.log(res.toString());

6.3 單位轉換

var Web3 = require('Web3')
var web3 = new Web3()

console.log('\n將wei轉換爲ether, Gwei, Mwei')
console.log(web3.utils.fromWei('12345567890876433', 'ether'))
console.log(web3.utils.fromWei('12345567890876433', 'Gwei'))
console.log(web3.utils.fromWei('12345567890876433', 'Mwei'))

console.log('\n轉換爲Wei')
console.log(web3.utils.toWei('1', 'ether'))
console.log(web3.utils.toWei('1', 'Gwei'))
console.log(web3.utils.toWei('1', 'Mwei'))

 

6.4轉換爲十六進制

var Web3 = require('Web3')
var web3 = new Web3()

console.log(web3.utils.toHex('a'))
console.log(web3.utils.toHex(1234))
console.log(web3.utils.toHex({name:'Duke'}))

//將所有傳入的數據都當做字符串進行處理,然後按照ASCII的16進制返回
//如果內部有單引號,則自動轉化成雙引號,再在外部用單引號括起來
console.log(JSON.stringify({name:'Duke'}))
console.log(web3.utils.toHex('{"name":"Duke"}'))

console.log(web3.utils.toHex(JSON.stringify({name:'Duke'})))

console.log(web3.utils.toHex([1,2,3,4]))
console.log(web3.utils.toHex('[1,2,3,4]'))

//執行結果
0x61
0x4d2
0x7b226e616d65223a2244756b65227d
{"name":"Duke"}
0x7b226e616d65223a2244756b65227d
0x7b226e616d65223a2244756b65227d
0x5b312c322c332c345d
0x5b312c322c332c345d

十六進制與ASCII轉換

var Web3 = require('Web3')
var web3 = new Web3()

console.log("先將字符串'xyz'轉換爲ascii,然後轉化爲十六進制")
var str = web3.utils.fromAscii('xyz')
console.log(str)

console.log("先將十六進制轉換爲ascii,然後轉化爲字符串")
str = web3.utils.toAscii('0x78797a')
console.log(str)

生成hash字符串

var Web3 = require('Web3')
var web3 = new Web3()

var hash0 = web3.utils.sha3('abc')
console.log(hash0)

//對結果0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45進行hash
var hash1 = (web3.utils.sha3(hash0))
console.log(hash1)

四、私有網絡搭建

1. 準備創世文件 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"
}

2. 創建一個文件夾,指定爲私有網絡數據存儲位置

mkdir myPrivateNet
cd myPrivateNet
mv genesis.json ./      //<--這個genesis.json放到這裏是便於管理,位置不限,能訪問即可

3. 創建私有鏈

==//init 務必要加上,否則可以創建成功,但是後面有問題==

geth --datadir "./node1" init genesis.json

執行效果

[duke ~/ethStudy/myPrivateNet]$ geth --datadir "./node1" init genesis.json
INFO [07-16|07:49:26] Maximum peer count                       ETH=25 LES=0 total=25
INFO [07-16|07:49:26] Allocated cache and file handles         database=/Users/duke/ethStudy/myPrivateNet/node1/geth/chaindata cache=16 handles=16
INFO [07-16|07:49:26] Writing custom genesis block 
INFO [07-16|07:49:26] Persisted trie from memory database      nodes=0 size=0.00B time=8.302µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [07-16|07:49:26] Successfully wrote genesis state         database=chaindata                                              hash=942f59…a2588a
INFO [07-16|07:49:26] Allocated cache and file handles         database=/Users/duke/ethStudy/myPrivateNet/node1/geth/lightchaindata cache=16 handles=16
INFO [07-16|07:49:26] Writing custom genesis block 
INFO [07-16|07:49:26] Persisted trie from memory database      nodes=0 size=0.00B time=1.506µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [07-16|07:49:26] Successfully wrote genesis state         database=lightchaindata                                              hash=942f59…a2588a

4. 啓動私有鏈節點

geth --datadir "./node1" --networkid 72 --port 30301 console
  • --datadir: 指定節點數據存儲路徑,此處會自動創建 node1,之所以這樣命名,是因爲後面會創建node2,便於模擬多個節點交互

  • --networkid: 當前網絡的id,寫在genesis.json中的

  • --port 端口

  • console: 表明同時啓動一個交互的終端(非必要)

這個關鍵字可以省略,省略的話表明只啓動節點服務,不創建交互終端,後面如果想接入這個節點的話,可以通過attach命令接入,後面講。​
效果:

[duke ~/ethStudy/myPrivateNet]$ geth --datadir "./node1" --networkid 72 --port 30301 console
INFO [07-16|07:49:59] Maximum peer count                       ETH=25 LES=0 total=25
INFO [07-16|07:49:59] Starting peer-to-peer node               instance=Geth/v1.8.11-stable-dea1ce05/darwin-amd64/go1.10.3
INFO [07-16|07:49:59] Allocated cache and file handles         database=/Users/duke/ethStudy/myPrivateNet/node1/geth/chaindata cache=768 handles=128
INFO [07-16|07:49:59] Initialised chain configuration          config="{ChainID: 72 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Constantinople: <nil> Engine: unknown}"
INFO [07-16|07:49:59] Disk storage enabled for ethash caches   dir=/Users/duke/ethStudy/myPrivateNet/node1/geth/ethash count=3
INFO [07-16|07:49:59] Disk storage enabled for ethash DAGs     dir=/Users/duke/.ethash                                 count=2
INFO [07-16|07:49:59] Initialising Ethereum protocol           versions="[63 62]" network=72
INFO [07-16|07:49:59] Loaded most recent local header          number=0 hash=942f59…a2588a td=16384
INFO [07-16|07:49:59] Loaded most recent local full block      number=0 hash=942f59…a2588a td=16384
INFO [07-16|07:49:59] Loaded most recent local fast block      number=0 hash=942f59…a2588a td=16384
INFO [07-16|07:49:59] Regenerated local transaction journal    transactions=0 accounts=0
INFO [07-16|07:49:59] Starting P2P networking 
INFO [07-16|07:50:02] UDP listener up                          self=enode://58e6eec1f00e0b2c221053c580c5f9c5b482bac142c97e711200e1709a736a5c4b9751d5637de524858b208bad7f3ecc6d17c36065801be438ee3b00fe931e35@192.168.1.65:30301
INFO [07-16|07:50:02] RLPx listener up                         self=enode://58e6eec1f00e0b2c221053c580c5f9c5b482bac142c97e711200e1709a736a5c4b9751d5637de524858b208bad7f3ecc6d17c36065801be438ee3b00fe931e35@192.168.1.65:30301
INFO [07-16|07:50:02] IPC endpoint opened                      url=/Users/duke/ethStudy/myPrivateNet/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.11-stable-dea1ce05/darwin-amd64/go1.10.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> INFO [07-16|07:50:02] Mapped network port                      proto=tcp extport=30301 intport=30301 interface="UPNP IGDv1-IP1"

> 
>
  1. 另起一個終端,查看node1下面的內容
[duke ~/ethStudy/myPrivateNet]$ tree node1 -d
node1
├── geth
│   ├── chaindata
│   ├── lightchaindata
│   └── nodes
└── keystore

5 directories

至此,私有網絡已搭建完成,下面開始執行各項命令

五、私有鏈基本命令

instance: Geth/v1.8.11-stable-dea1ce05/darwin-amd64/go1.10.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

1. 查看當前網絡賬戶

> eth.accounts
[]  <--當前網絡賬戶爲空
>

注,,我們在創世塊中可以預先指定一些賬戶,在alloc字段

"alloc": {
      "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": {
        "balance": "300000"
      },
      "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": {
        "balance": "400000"
      }
}

2. 創建賬戶

> 
> personal.newAccount("1111")
"0xe5c9766404eb3633163c85ef13e7a1f79cdee922"
> 
> 
> eth.accounts
["0xe5c9766404eb3633163c85ef13e7a1f79cdee922"]
>

其中,"1111"是這個賬戶的密碼

3. 多創建幾個賬戶,並查看當前節點礦工

> personal.newAccount("111")
"0x5032641c3f09bc8995a60c485b9ca4ac3d073fd8"
> personal.newAccount("111")
"0x864ced8aff4b883c99ab1c5156f2d51c47b25f8f"
> personal.newAccount("111")
"0xf3b1e1712a311443db772c55e12c9634912199ab"
> personal.newAccount("111")
"0xef89f85b7e276dcf738276df88888298cd9272ee"
>
> eth.coinbase
INFO [07-16|07:54:10] Etherbase automatically configured       address=0x5032641c3f09bc8995a60C485B9Ca4ac3d073fD8
"0x5032641c3f09bc8995a60c485b9ca4ac3d073fd8"
>

可以看到,默認的礦工是第一個賬戶,可以通過eth.accounts來查看當前節點的所有賬戶,爲了方便記憶,我們把所有的賬戶密碼都設置爲”1111“。​

查看一下keystore目錄,可以看到創建了四個賬戶文件

[duke ~/ethStudy/myPrivateNet]$ ls node1/keystore/
UTC--2018-07-15T23-52-31.825281356Z--5032641c3f09bc8995a60c485b9ca4ac3d073fd8
UTC--2018-07-15T23-53-42.431125900Z--864ced8aff4b883c99ab1c5156f2d51c47b25f8f
UTC--2018-07-15T23-53-44.166684858Z--f3b1e1712a311443db772c55e12c9634912199ab
UTC--2018-07-15T23-53-45.567447952Z--ef89f85b7e276dcf738276df88888298cd9272ee
[duke ~/ethStudy/myPrivateNet]$

4. 手動修改挖礦的賬戶

> miner.setEtherbase(eth.accounts[1])
true
> eth.coinbase
"0x864ced8aff4b883c99ab1c5156f2d51c47b25f8f"
>

可以看到coinbase的值已經改變,挖礦的時候,獎勵會發給指定的賬戶。​
==注,每次節點重新啓動時,會自動將coinbase設置爲第一個賬戶。==

私鏈重啓後,挖礦人重新自動恢復爲第一個主賬戶

5. 查看賬戶餘額

> eth.getBalance(eth.accounts[0])
0
> eth.getBalance(eth.accounts[1])
0

沒有挖礦,也沒有轉賬,當前餘額均爲0。​

6. 挖礦

> miner.start()

執行效果:

> miner.start()
INFO [07-16|07:56:48] Updated mining threads                   threads=0
INFO [07-16|07:56:48] Transaction pool price threshold updated price=18000000000
INFO [07-16|07:56:48] Starting mining operation 
null
> INFO [07-16|07:56:48] Commit new mining work                   number=1 txs=0 uncles=0 elapsed=166.057µs
INFO [07-16|07:56:50] Successfully sealed new block            number=1 hash=2a840c…6e5118
INFO [07-16|07:56:50] 🔨 mined potential block                  number=1 hash=2a840c…6e5118
INFO [07-16|07:56:50] Commit new mining work                   number=2 txs=0 uncles=0 elapsed=165.72µs
INFO [07-16|07:56:50] Successfully sealed new block            number=2 hash=f220e8…ffef15
INFO [07-16|07:56:50] 🔨 mined potential block                  number=2 hash=f220e8…ffef15
INFO [07-16|07:56:50] Commit new mining work                   number=3 txs=0 uncles=0 elapsed=133.196µs

執行後開始刷屏,停止挖礦

> miner.stop()

再次查看餘額​

> eth.getBalance(eth.coinbase)
65000000000000000000
> 
> eth.getBalance(eth.accounts[0])
0
>

如果挖礦不成功,請更換目錄,重新創建區塊鏈

單位是wei,礦工得到獎勵,其他賬戶爲零。

可以使用web3接口,將金額進行轉換成ether單位,每個塊獎勵5個ether,共13個塊,所以是5 * 13 = 65個獎勵

> web3.fromWei(eth.getBalance(eth.coinbase), "ether")
65
> eth.blockNumber
13
>

7. 檢查節點

> admin.peers
[]
>

當前網絡僅一個節點,所以返回空​

8. 創建一個新節點

步驟同上​

geth --datadir "./node2" init genesis.json
geth --datadir "./node2" --networkid 72 --port 30302

注意:

  • ==init操作千萬不要忘記執行,直接執行啓動也會成功運行,但是後續添加節點等總是失敗,一定要注意!==

  • ==此處我們創建node2,port修改爲30302,但是沒有指定 console,這是爲了演示attach命令==

  • ==兩個節點想互連的話,必須都指定--networkid 72==

9. 使用attach參數,創建接入節點的控制檯

geth attach ipc:note2/geth.ipc

執行效果:

[duke ~/ethStudy/myPrivateNet]$ geth attach ipc:note2/geth.ipc 
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.11-stable-dea1ce05/darwin-amd64/go1.10.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

>

此處geth.ipc在node2下面,這個節點啓動後,自動生成,節點停止後自動刪除。

在node2中,創建新賬戶

> personal.newAccount("111")
"0x6cf4d0785589497d3b5fca83571097f5cd6df352"
>
> personal.newAccount("111")
"0x1757a2db3fd67f0724bffffc33428faa7e9552f7"
>

10. 在node2中,查看當前節點的信息

> admin.nodeInfo

執行結果:

> admin.nodeInfo
{
  enode: "enode://3e707df543403f724144c8d51656b1399c1b9d5ae1d50f5739e02ef06130f1a11fd02baa320552ce1e6f90a7f45f4e37b1caa4d0f45eefcc97606cc16c150197@192.168.1.65:30302",
  id: "3e707df543403f724144c8d51656b1399c1b9d5ae1d50f5739e02ef06130f1a11fd02baa320552ce1e6f90a7f45f4e37b1caa4d0f45eefcc97606cc16c150197",
  ip: "192.168.1.65",
  listenAddr: "[::]:30302",
  name: "Geth/v1.8.11-stable-dea1ce05/darwin-amd64/go1.10.3",
  ports: {
    discovery: 30302,
    listener: 30302
  },
  protocols: {
    eth: {
      config: {
        chainId: 72,
        eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
        eip155Block: 0,
        eip158Block: 0,
        homesteadBlock: 0
      },
      difficulty: 1725312,
      genesis: "0x942f596f99dc8879b426b59080824662e1f97587353d087487fea0a0e2a2588a",
      head: "0x17fa69432921727241ea299ab7f12611e09bb5a8473e71f1250f89832661ed86",
      network: 72
    }
  }
}
>

encode字段能唯一標識這個節點,我們將它添加到node1中。

也可以直接獲取enode字段

> admin.nodeInfo.enode
"enode://3e707df543403f724144c8d51656b1399c1b9d5ae1d50f5739e02ef06130f1a11fd02baa320552ce1e6f90a7f45f4e37b1caa4d0f45eefcc97606cc16c150197@192.168.1.65:30302"
>

11. 在node1中添加node2節點

切換到node1終端,執行下面的命令:

> admin.addPeer("enode://3e707df543403f724144c8d51656b1399c1b9d5ae1d50f5739e02ef06130f1a11fd02baa320552ce1e6f90a7f45f4e37b1caa4d0f45eefcc97606cc16c150197@192.168.1.65:30302")

添加後,查看節點信息,可以看到,已經添加進來

> admin.peers

[{
    caps: ["eth/63"],
    id: "3e707df543403f724144c8d51656b1399c1b9d5ae1d50f5739e02ef06130f1a11fd02baa320552ce1e6f90a7f45f4e37b1caa4d0f45eefcc97606cc16c150197",
    name: "Geth/v1.8.11-stable-dea1ce05/darwin-amd64/go1.10.3",
    network: {
      inbound: false,
      localAddress: "192.168.0.107:57154",
      remoteAddress: "192.168.1.65:30302",
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 16384,
        head: "0x942f596f99dc8879b426b59080824662e1f97587353d087487fea0a0e2a2588a",
        version: 63
      }
    }
}]
>

12. 發送交易

由node1賬戶向node2賬戶發送一筆交易

在node2中獲取第一個賬戶的地址,該賬戶當前餘額爲零

> eth.accounts[0]
"0x6cf4d0785589497d3b5fca83571097f5cd6df352"
> eth.getBalance(eth.accounts[0])
0

切換回node1,執行如下命令:

查看node1中賬戶餘額狀態:

> eth.getBalance(eth.accounts[0])
0
> eth.getBalance(eth.accounts[1])
65000000000000000000

我們使用accounts[1]向node2的accounts[0]轉賬15個ether

> eth.sendTransaction({from : eth.accounts[1], to: "0x6cf4d0785589497d3b5fca83571097f5cd6df352", value: web3.toWei(15, "ether")})
>

此時會報錯如下

Error: authentication needed: password or unlock
    at web3.js:3143:20
    at web3.js:6347:15
    at web3.js:5081:36
    at <anonymous>:1:2
>

這說明當前賬戶被鎖定,需要解鎖後才能發起交易。

13. 解鎖這個from指定的賬戶

> personal.unlockAccount(eth.accounts[1])

效果:

> personal.unlockAccount(eth.accounts[1])
Unlock account 0x864ced8aff4b883c99ab1c5156f2d51c47b25f8f
Passphrase: 
true
>

重新發送交易,可以看到如下效果,交易創建成功,同時返回交易hash

> eth.sendTransaction({from : eth.accounts[1], to: "0x6cf4d0785589497d3b5fca83571097f5cd6df352", value: web3.toWei(15, "ether")})

INFO [07-16|08:38:58] Submitted transaction                    fullhash=0x1cd7f14436a088586d9bb75db6cadec08f4293d270b21c72b237605d4b921bea recipient=0x6cf4D0785589497D3B5fca83571097F5cD6dF352
"0x1cd7f14436a088586d9bb75db6cadec08f4293d270b21c72b237605d4b921bea"
>

14. 查看交易狀態

交易發送到網絡中後,需要礦工打包才能寫入區塊,查看當前該交易的狀態

>txpool.status

效果

> txpool.status
{
  pending: 1,  //<--- 等待被打包
  queued: 0
}
>

在node2中啓動挖礦,然後停止挖礦,在node1中重新查看

node2:

> miner.start()
null
> miner.stop()
true
>

 node1:

> INFO [07-16|08:41:44] Block synchronisation started 
INFO [07-16|08:41:44] Imported new chain segment               blocks=2 txs=1 mgas=0.021 elapsed=565.360ms mgasps=0.037 number=15 hash=b2be14…d19460 cache=3.33kB
INFO [07-16|08:41:44] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=4.209ms   mgasps=0.000 number=16 hash=752b6a…2164c9 cache=3.73kB
INFO [07-16|08:41:44] Mining too far in the future             wait=2s
INFO [07-16|08:41:47] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=4.799ms   mgasps=0.000 number=17 hash=68b112…82e15e cache=4.12kB
INFO [07-16|08:41:47] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=4.190ms   mgasps=0.000 number=18 hash=9ca0f2…2685c9 cache=4.51kB
INFO [07-16|08:41:47] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=2.166ms   mgasps=0.000 number=19 hash=7376ea…8c9461 cache=4.90kB
INFO [07-16|08:41:47] Mining too far in the future             wait=2s
INFO [07-16|08:41:51] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=4.604ms   mgasps=0.000 number=20 hash=35227e…9c2aa3 cache=5.29kB

> 
> txpool.status

{
  pending: 0,
  queued: 0
}
>

可以看到,pending數目變爲0,說明交易已經被打包到區塊內。

查看交易後的金額,

node1中:

> eth.getBalance(eth.accounts[1])
49999622000000000000
>

node1 的 accounts[1] 賬戶變爲 65 - 15 - 手續費 = 49.99,正確

node2中:

> eth.getBalance(eth.accounts[0])
50000378000000000000

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
50.000378
>

我們發現,accounts[0]的值並非15,爲何?

原因是這個賬戶也是coinbase賬戶,剛剛參與了挖礦,得到了挖礦的獎勵

> eth.blockNumber
20

當前區塊數爲20,有13個塊是從node1節點同步過來的,剛剛我們運行miner.start()一共產生了7個區塊,

每個區塊獎勵5, 共得到獎勵 35ether,加上轉賬獲得的15個,爲50,再加上手續費,所以總數爲50.000378。

六、Ethereum連接私有鏈

Ethereum默認安裝目錄

  • Mac: ~/Library/Ethereum
  • Linux: ~/.ethereum
  • Windows: %APPDATA%\Ethereum

我們啓動Ethereum客戶端,此時連接的是測試網絡:

 

我們通過命令行,查看它起的節點命令如下:

Ethereum啓動geth命令:

Mac查看方式 :

ps -ef | grep geth

返回如下信息:

/Users/duke/Library/Application Support/Mist/binaries/Geth/unpacked/geth --testnet --syncmode light --cache 1024 --ipcpath /Users/duke/Library/Ethereum/geth.ipc

Windows查看方式:

wmic process where caption="geth.exe" get caption,commandline /value

請自行查看。

Ethereum客戶端每次都要指定特定的geth.ipc,指定--testnet 來和測試網絡連接,--testnet參數就是Ropsten網絡。

我們可以事先拉起一個geth服務,其中將存儲數據參數指定爲我們的私有網絡,這樣Mist在啓動時,就會自動連接到我們的服務,而不會重新起服務。

Mac:

geth  --datadir ./node1  --ipcpath /Users/duke/Library/Ethereum/geth.ipc

Windows的ipc是固定的:

geth  --datadir .\node1  --ipcpath \\.\pipe\geth.ipc

重新打開Etherueum錢包,界面如下:

 

在命令行,我們通過attach建立一個終端,查看賬戶信息,我們發現與圖形界面一致。

Mac:

geth attach ipc:/Users/duke/Library/Ethereum/geth.ipc

Windows :

geth attach ipc:\\.\pipe\geth.ipc

==注意,此時的ipc應該使用上面的geth.ipc,注意路徑,這個錢包自動生成的,並不在我們的node1目錄下面。==

執行

> eth.accounts
["0x5032641c3f09bc8995a60c485b9ca4ac3d073fd8", "0x864ced8aff4b883c99ab1c5156f2d51c47b25f8f", "0xf3b1e1712a311443db772c55e12c9634912199ab", "0xef89f85b7e276dcf738276df88888298cd9272ee"]
>

至此,我們完成了私有鏈的搭建

我們學會如下知識點:

  • 瞭解geth搭建私有鏈
  • 熟悉基本的web3接口命令
  • 使用Ethereum客戶端連接到私有網絡
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章