Geth以太坊搭建私有链

Truffle
用来本地编译、部署智能合约的工具,属于Solidity的开发框架。

Testrpc
在本地使用内存模拟的一个以太坊环境,对于开发调试来说,更为方便快捷,当你的合约在testrpc中测试通过后,再可以部署到geth中去。

Geth
全称 go-ethereum 目前使用最广泛的以太坊客户端,使用go语言开发,可以使用geth搭建以太坊私有网络、挖矿、管理帐号、部署智能合约等。本文介绍使用geth来搭建本地私有网络。

1、安装

https://ethereum.github.io/go-ethereum/install/#install-on-ubuntu-via-ppas

本机在macOS系统中进行安装:

brew tap ethereum/ethereum
brew install ethereum

安装完成后,在命令行输入

geth --help //能成功显示输出帮助,则表示已经成功安装

2、搭建私有链

一、准备创世区块配置文件
以太坊支持自定义创世区块,要运行私有链,我们就需要定义自己的创世区块,创世区块信息写在一个json格式的配置文件中。

{
  "config": { "chainId": 10, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00" }

二、初始化:写入创世区块

准备好创世区块配置文件后,需要将配置文件写入区块链中。
创建目录:privateChain和privateChain/data0
进入privateChain,执行初始化命令:

 geth --datadir data0 init genesis.json

看到以下输出,说明初始化成功

WARN [03-12|16:03:09] No etherbase set and no accounts found as default INFO [03-12|16:03:09] Allocated cache and file handles database=/Users/hiracer/Documents/Learn/ethereum/privateChain/data0/geth/chaindata cache=16 handles=16 INFO [03-12|16:03:09] Writing custom genesis block INFO [03-12|16:03:09] Successfully wrote genesis state database=chaindata hash=5e1fc7…d790e0 INFO [03-12|16:03:09] Allocated cache and file handles database=/Users/hiracer/Documents/Learn/ethereum/privateChain/data0/geth/lightchaindata cache=16 handles=16 INFO [03-12|16:03:09] Writing custom genesis block INFO [03-12|16:03:09] Successfully wrote genesis state database=lightchaindata hash=5e1fc7…d790e0

初始化成功后,会在数据目录data0中生成geth和keystore两个文件夹
其中geth/chaindata目录中存放的是区块数据,keystore存放的是账户数据

三、启动私有链节点
在命令行中输入以下命令启动节点:

geth --datadir data0 --networkid 1108 console

上述主题命令是geth console,表示启动节点并进入交互式控制台, –networkid 1108表示这个私有链的网络id为1108,网络id在连接到其他节点的时候会用到,以太坊公网的网络id是1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络id。

Welcome to the Geth JavaScript console!

instance: Geth/v1.7.3-stable/darwin-amd64/go1.9.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

>

这是一个交互式的Javascript执行环境,在这里面可以执行Javascript代码,其中>是命令提示符。在这个环境里也内置了一些用来操作以太坊的Javascript对象,可以直接使用这些对象。这些对象主要包括:

eth:包含一些跟操作区块链相关的方法
net:包含以下查看p2p网络状态的方法
admin:包含一些与管理节点相关的方法
miner:包含启动&停止挖矿的一些方法
personal:主要包含一些管理账户的方法
txpool:包含一些查看交易内存池的方法
web3:包含了以上对象,还包含一些单位换算的方法

3、geth console使用

进入以太坊Javascript Console后,就可以使用里面的内置对象做一些操作,这些内置对象提供的功能很丰富,比如查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。

一、创建账户
前面只是搭建了私有链,并没有自己的账户,可以在js console中输入eth.accounts来验证:

> eth.accounts
[]

接下来使用personal对象来创建一个账户:

> personal.newAccount()
Passphrase:
Repeat passphrase:
"0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2"

账户默认会保存在数据目录的keystore文件夹中。查看目录结构,发现data0/keystore中多了一个文件,这个文件就对应刚才创建的账户,这是json格式的文本文件,可以打开查看,里面存的是私钥经过密码加密后的信息。

二、查看账户余额
eth对象提供了查看账户余额的方法:

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

也可以直接使用账户地址查看:

> eth.getBalance('0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2')
0

三、启动/停止挖矿
通过miner.start()来启动挖矿:

> miner.start(2)
INFO [03-13|11:29:08] Updated mining threads threads=2
INFO [03-13|11:29:08] Transaction pool price threshold updated price=18000000000
INFO [03-13|11:29:08] Starting mining operation
null
> INFO [03-13|11:29:08] Commit new mining work number=1 txs=0 uncles=0 elapsed=148.187µs

其中start的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的DAG文件,这个过程有点慢,等进度达到100%后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。

如果想停止挖矿,在js console中输入miner.stop():

> miner.stop()

本地测试过程中,发现miner.start()之后,一直没有进行挖矿,翻阅部分资料说是,需要发送一笔交易才能触发挖矿。

出现此问题的原因在于geth版本更新之后,–dev模式下新增了一个参数项:

--dev               Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
--dev.period value  Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)

我们先看一下上面的两个参数,–dev是我们常用的参数,之前版本中我们只用使用–dev然后执行miner.start()就可以挖矿,但是在后面的版本中,当我们会发现只有发送交易了才会挖一个块。

引起此问题的原因就是新增了–dev.period value配置项。此配置默认值为0,也就是说只有有pending中的交易才会挖矿。

明白了这个参数的含义之后,解决问题就很简答了,之前的–dev参数依旧使用,然后再在后面添加–dev.period 1,注意,参数值为1,不是默认的0。

再重新启动节点,然后执行挖矿,先不管返回是否是null,执行之后,无论查看日志或执行eth.blockNumber都会发现块在不停的增高。

挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:

> eth.coinbase
"0x0416f04c403099184689990674f5b4259dc46bd8"

现在的coinbase是账户0,要想使挖矿奖励进入其他账户,通过miner.setEtherbase()将其他账户设置成coinbase即可:

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

挖到矿以后,账户里0里面应该就有余额了:

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

getBalance()返回值的单位是wei,wei是以太币的最小单位,1个以太币=10的18次方个wei。要查看有多少个以太币,可以用web3.fromWei()将返回值换算成以太币:

> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
150

四、发送交易

目前账户1的余额还是0:

> user1=eth.accounts[1]
"0x4f2e33670d20c57fe440846c46f03cc4816e5815"
> eth.getBalance(user1)
0

发送一笔交易,从user0转移5个以太币到user1

> user0=eth.accounts[0]
"0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2"
> amount = web3.toWei(5,'ether')
"5000000000000000000"
> eth.sendTransaction({from:user0,to:user1,value:amount})
Error: authentication needed: password or unlock
    at web3.js:3143:20
    at web3.js:6347:15
    at web3.js:5081:36
    at :1:1

发现会报错,原因是账户每隔一段时间就会被锁住,要发送交易,必须先解锁账户,由于我们要从账户0发送交易,所以要解锁账户0:

> personal.unlockAccount(user0)
Unlock account 0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2
Passphrase:
true

然后再发送交易:

> eth.sendTransaction({from:user0,to:user1,value:amount})
INFO [03-13|13:52:35] Submitted transaction                    fullhash=0xb36f58e4e44f923de4ca9503d5654a2991ad14189879c03a55579c2c8c92a976 recipient=0x4F2E33670d20c57FE440846c46f03cC4816E5815
"0xb36f58e4e44f923de4ca9503d5654a2991ad14189879c03a55579c2c8c92a976"

此时交易已经提交到区块链,返回了交易的hash,但还未被处理,这可以通过查看txpool来验证:

> txpool.status
{ pending: 1, queued: 0 }

pengding表示已经提交但还未被处理的交易。
要使交易被处理,必须要挖矿。这里我们启动挖矿,然后等待挖到一个区块之后就停止挖矿:

> miner.start(1);admin.sleepBlocks(1);miner.stop();

当miner.stop()返回true后,txpool中pending的交易数量应该为0了,说明交易已经被处理了:

> txpool.status
{ pending: 0, queued: 0 }

此时,交易已经生效,账户一应该已经收到了5个以太币了:

> web3.fromWei(eth.getBalance(user1),'ether')
5

五、查看交易和区块
查看当前区块总数:

> eth.blockNumber
31

通过交易hash查看交易

> eth.getTransaction("0xb36f58e4e44f923de4ca9503d5654a2991ad14189879c03a55579c2c8c92a976")
{
  blockHash: "0x92d70c7673ea8083f69a37b6d4bf69799dcbd9fa56fe17629ba2553c1a50b0c2",
  blockNumber: 31,
  from: "0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0xb36f58e4e44f923de4ca9503d5654a2991ad14189879c03a55579c2c8c92a976",
  input: "0x",
  nonce: 0,
  r: "0x694540553d29725cab204cd511983de5437c28ad691a9633a2fda38394a4e2ec",
  s: "0x1960d4a3e849b7e1d869672ae57f396703087565c537ef4bd5d9a0fd88cd7e56",
  to: "0x4f2e33670d20c57fe440846c46f03cc4816e5815",
  transactionIndex: 0,
  v: "0x38",
  value: 5000000000000000000
}

通过区块号查看区块:

> eth.getBlock(23)
{ difficulty: 132416, extraData: "0xd883010703846765746887676f312e392e338664617277696e", gasLimit: 3212883, gasUsed: 0, hash: "0xf7d601b8f3403747fb695c7484cc6f8be363d96e25071bfc63bee4eb30f202b7", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2", mixHash: "0x2a7fa527f2c97ec2869b75bebb663384df29a0b1e9c2bba0aedd24f2870e82a2", nonce: "0x181678361a1e7007", number: 23, parentHash: "0xd7c668fe589f3c882640dadb64866625af313c3871022ee70c88614fa97f31e4", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 536, stateRoot: "0xc32b875f230c79adfd35bbe333b1d31eca4264f56600fdd5f4c1d4b7a1623062", timestamp: 1520912662, totalDifficulty: 3160512, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] }

4、创建私有链集群

1、启动第一个节点

geth --datadir data0 --networkid 1108 console

2、获取节点实例的encode url:

> admin.nodeInfo.enode
"enode://6b5d0336c25f49ae871fa8c4b35368bbb45090f12da33efa3038aa79ae9561c5df93f048f8fd350b26883fc97d3f3aa714168e326b0d681a68e8d066f2174fe5@[::]:30303?discport=0"

3、再打开一个终端,初始化第二个节点:

geth --datadir data1 --networkid 1108 init genesis.json
geth --datadir data1 --networkid 1108 --port 30304 --bootnodes "enode://c1f6afa8c620b842b6ff36b9321ec2f7dd6a08037a31ee093e9acc4c5a13bcb93b5ce95b10768eedafe144f55c79858a8b8e59f121674dbe267d23dba1df3e7a@172.16.70.76:30303" console

> personal.newAccount("111")

上面的命令中,–bootndoes是设置当前节点启动后,直接通过设置–bootndoes的值来链接第一个节点,–bootnoedes 的值可以通过在第一个节的命令行中,输入admin.nodeInfo.enode命令打印出来,需要注意的是需要把@[::]换成实际电脑的IP地址
也可以不设置 –bootnodes, 直接启动,启动后进入命令行, 通过命令admin.addPeer(enodeUrlOfFirst Instance)把它作为一个peer添加进来.

为了确认连接成功,第二个节点输入:

> admin.nodeInfo
{ enode: "enode://705569b7356248896e755e50058be4be2882fe9daeab38a7e0d9739ffff7d93fd9317495f2d515842fddc4de590fa203abbd5b36544313b79ec6b15abe069391@[::]:30304", id: "705569b7356248896e755e50058be4be2882fe9daeab38a7e0d9739ffff7d93fd9317495f2d515842fddc4de590fa203abbd5b36544313b79ec6b15abe069391", ip: "::", listenAddr: "[::]:30304", name: "Geth/v1.7.3-stable/darwin-amd64/go1.9.3", ports: { discovery: 30304, listener: 30304 },
  protocols: { eth: { difficulty: 4482432, genesis: "0x5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0", head: "0x01ffb031dbfb969b97016aa656d5e51f24d00c66376feb1bf47441e49a011582", network: 1108 }
  }
}

第一个节点输入:

> net.peerCount
1
> admin.peers
[{
    caps: ["eth/63"],
    id: "705569b7356248896e755e50058be4be2882fe9daeab38a7e0d9739ffff7d93fd9317495f2d515842fddc4de590fa203abbd5b36544313b79ec6b15abe069391",
    name: "Geth/v1.7.3-stable/darwin-amd64/go1.9.3",
    network: {
      localAddress: "192.168.0.164:30303",
      remoteAddress: "192.168.0.164:60307"
    },
    protocols: {
      eth: {
        difficulty: 131072,
        head: "0x5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0",
        version: 63
      }
    }
}]

从得到的结果可以看出,第一个节点有1个peer链接, 链接的node id为:
“705569b7356248896e755e50058be4be2882fe9daeab38a7e0d9739ffff7d93fd9317495f2d515842fddc4de590fa203abbd5b36544313b79ec6b15abe069391”
这个id,正好就是第二个节点的id.

3、连接成功后,使用第一个节点挖矿的账户,向第二个节点账户发送以太币

先查看第二个节点账户信息:

> user0 = eth.accounts[0]
"0x47f411dda34b1948ea52b9c002ae208e59309971"
> eth.getBalance(user0)
0
> eth.blockNumber
33

在第一个节点命令行中,执行下面操作:


> personal.unlockAccount(user0)
Unlock account 0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2
Passphrase:
true
> eth.sendTransaction({from:user0,to:"0x47f411dda34b1948ea52b9c002ae208e59309971",value:web3.toWei(1,"ether")})
INFO [03-15|10:26:33] Submitted transaction                    fullhash=0xb4ac38d07a1399cf92ec0d19c6c494b068246b09adefc98ba6f9650e23116a58 recipient=0x47F411ddA34b1948Ea52B9C002Ae208E59309971
"0xb4ac38d07a1399cf92ec0d19c6c494b068246b09adefc98ba6f9650e23116a58"
> eth.pendingTransactions
[{
    blockHash: null,
    blockNumber: null,
    from: "0x23c93c3f08b7a1c4322b8738c471d4dd40cd29e2",
    gas: 90000,
    gasPrice: 18000000000,
    hash: "0xb4ac38d07a1399cf92ec0d19c6c494b068246b09adefc98ba6f9650e23116a58",
    input: "0x",
    nonce: 2,
    r: "0x37cd321e669372cec0f60708ffda00af765ddd67faa8d0508f4d90490460aaa4",
    s: "0x51e682f348e53d66e22fc58923ad016861f18858ee80cffc240e1a7cd95fdf4",
    to: "0x47f411dda34b1948ea52b9c002ae208e59309971",
    transactionIndex: 0,
    v: "0x38",
    value: 1000000000000000000
}]

eth.sendTransaction就是执行发送以太币的操作, 参数from, to分别是发送账户和接收账户, web3.toWei(1, “ether”)是将1单位”ether”转换为相应的”Wei”数量.

执行挖矿

再在第二个节点的命令行输入:

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

你可能感兴趣的:(区块链专题)