一、意義:
在之前內容的學習中,我們發現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"
>
>
- 另起一個終端,查看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客戶端連接到私有網絡