准备工作
一、为什么用到私有链?
在以太坊的共有链上部署智能合约、发起交易需要花费以太币。而通过修改配置,可以在本机搭建一套以太坊私有链,因为与公有链没关系,既不用同步公有链庞大的数据,也不用花钱购买以太币,很好地满足了智能合约开发和测试的要求,开发好的智能合约也可以很容易地切换接口部署到以太坊公有链上。
二、需要用到哪些工具?
-
以太坊客户端
以太坊客户端用于接入以太坊网络,进行账户管理、交易、挖矿、智能合约相关的操作。目前有多种语言实现的客户端,常用的有 Go 语言实现的 go-ethereum 客户端 Geth,支持接入以太坊网络并成为一个完整节点,也可作为一个 HTTP-RPC 服务器对外提供 JSON-RPC 接口。
其他的客户端有:- Parity:Rust 语言实现;
- cpp-ethereum:C++ 语言实现;
- ethereumjs-lib:JavaScript 语言实现;
- Ethereum(J):Java 语言实现;
- ethereumH:Haskell 语言实现;
- pyethapp: Python 语言实现;
- ruby-ethereum:Ruby 语言实现;
-
智能合约编译器
以太坊支持两种智能合约的编程语言:Solidity 和 Serpent。Serpent 语言面临一些安全问题,现在已经不推荐使用了。Solidity 语法类似 JavaScript,它编译器 solc 可以把智能合约源码编译成以太坊虚拟机 EVM 可以执行的二进制码。
现在以太坊提供更方便的在线 IDE —— Remix https://remix.ethereum.org 使用 Remix,免去了安装 solc 和编译过程,它可以直接提供部署合约所需的二进制码和 ABI。 -
以太坊钱包
以太坊提供了图形界面的钱包 Ethereum Wallet 和 Mist Dapp 浏览器。钱包的功能是 Mist 的一个子集,可用于管理账户和交易;Mist 在钱包基础上,还能操作智能合约。为了演示合约部署过程,本文使用了 Geth console 操作,没有用到 Mist,当然,使用 Mist 会更简单。
以上参考 以太坊私有链搭建指南 | G2EX
搭建基于以太坊的私有链环境
以在本地 Win10 安装为例。
一、安装以太坊客户端 Geth
目前 Geth 最新的版本号是 Frost (v1.8.2)
下载地址: Releases · ethereum/go-ethereum 或 Geth 国内镜像
安装到 D:\Blockchain\Geth
二、准备创世块文件
要运行以太坊私有链,需要定义自己的创世区块,创世区块信息写在一个 JSON 格式的配置文件中。首先将下面的内容保存到一个 JSON 文件中,例如 genesis.json, 放到 D:\Blockchain\Geth。
{
"config": {
"chainID": 1024,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x400",
"extraData": "0x0",
"gasLimit": "0x2fefd8",
"nonce": "0xdeadbeefdeadbeef",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
其中,chainID 指定了独立的区块链网络 ID。网络 ID 在连接到其他节点的时候会用到,以太坊公网的网络 ID 是 1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络 ID。不同 ID 网络的节点无法相互连接。配置文件还对当前挖矿难度 difficulty、区块 Gas 消耗限制 gasLimit 等参数进行了设置。
- mixhash: 与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。.
- nonce: nonce就是一个64位随机数,用于挖矿,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。
- difficulty: 设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度
- alloc: 用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以我们不需要预置有币的账号,需要的时候自己创建即可以。
- coinbase: 矿工的账号,随便填
- timestamp: 设置创世块的时间戳
- parentHash: 上一个区块的hash值,因为是创世块,所以这个值是0
- extraData: 附加信息,随便填,可以填你的个性信息
- gasLimit: 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大。
以上解释来自 区块链开发(一)搭建基于以太坊的私有链环境 - CSDN博客
使用 CMD 进入到 geth 目录 (D:\Blockchain\Geth),执行:
geth -datadir "%cd%\chain" init genesis.json
上面的命令的主体是 geth init,表示初始化区块链,命令可以带有选项和参数,其中 --datadir 选项后面跟一个目录名,这里为 chain,表示指定数据存放目录为 chain,genesis.json 是 init 命令的参数。
运行上面的命令,会读取 genesis.json 文件,根据其中的内容,将创世区块写入到区块链中。
遇到以下错误:
Fatal: invalid genesis file: json: cannot unmarshal hex string of odd length into Go struct field Genesis.extraData of type hexutil.Bytes
Google 之后找到解决办法:Documentation on testnet and genesis might be broken · Issue #15294 · ethereum/go-ethereum
extraData 可以设置为空或者"0x", 看来这个参数也不是随便填的。改后的 genesis.json 文件:
{
"config": {
"chainID": 1024,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x400",
"extraData": "0x",
"gasLimit": "0x2fefd8",
"nonce": "0xdeadbeefdeadbeef",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
重新执行:
D:\Blockchain\Geth>geth -datadir "%cd%\chain" init genesis.json
INFO [03-20|14:43:11] Maximum peer count ETH=25 LES=0 total=25
INFO [03-20|14:43:11] Allocated cache and file handles database=D:\\Blockchain\\Geth\\chain\\geth\\chaindata cache=16 handles=16
INFO [03-20|14:43:11] Writing custom genesis block
INFO [03-20|14:43:11] Persisted trie from memory database nodes=0 size=0.00B time=0s gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [03-20|14:43:12] Successfully wrote genesis state database=chaindata hash=ed1fb6…1760ca
INFO [03-20|14:43:12] Allocated cache and file handles database=D:\\Blockchain\\Geth\\chain\\geth\\lightchaindata cache=16 handles=16
INFO [03-20|14:43:12] Writing custom genesis block
INFO [03-20|14:43:12] Persisted trie from memory database nodes=0 size=0.00B time=0s gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [03-20|14:43:12] Successfully wrote genesis state database=lightchaindata hash=ed1fb6…1760ca
初始化成功后,会在数据目录 chain 中生成 geth 和 keystore 两个文件夹,此时目录结构如下:
D:\Blockchain\Geth
├── chain
│ ├── geth
│ │ ├── chaindata
│ │ │ ├── 000001.log
│ │ │ ├── CURRENT
│ │ │ ├── LOCK
│ │ │ ├── LOG
│ │ │ └── MANIFEST-000000
│ │ └── lightchaindata
│ │ ├── 000001.log
│ │ ├── CURRENT
│ │ ├── LOCK
│ │ ├── LOG
│ │ └── MANIFEST-000000
│ └── keystore
├── genesis.json
└── geth.exe
其中 geth/chaindata 中存放的是区块数据,keystore 中存放的是账户数据。
三、启动私有链节点
3.1 第一次启动
初始化完成后,就有了一条自己的私有链,之后就可以启动自己的私有链节点并做一些操作,输入以下命令即可启动节点:
geth --identity "TestNode" --rpc --rpcport "8545" --rpccorsdomain "*" --datadir "chain" --port "30303" --rpcapi "db,eth,net,web3" --networkid 95518 console
上面命令的主体是 geth console,表示启动节点并进入交互式控制台。
各选项含义如下:
- –identity:区块链的标示,随便填写,用于标示目前网络的名字;
- –rpc:表示开启 HTTP-RPC 服务, 启动rpc通信,可以进行智能合约的部署和调试 ;
- –rpcport:指定 HTTP-RPC 服务监听端口号(默认为 8545);
- –datadir:设置当前区块链网络数据存放的位置 ;
- –port:指定和其他节点连接所用的端口号(默认为 30303);
- –nodiscover:关闭节点发现机制,防止加入有同样初始配置的陌生节点, 禁止被网络中其它节点发现,需要手动添加该节点到网络。
- -rpcaddr –rpc接口的地址
- -rpcapi 设置rpc的范围,暂时开启eth,web3,personal足够
- -networkid 设置当前区块链的网络ID,是一个数字,可以随便写
- -maxpeers 最大节点数量
- -rpccorsdomain 限制rpc访问源的ip,代表不限制 mine 允许挖矿
以上部分解释来自 windows系统下搭建以太坊私有链环境
运行上面的命令后,就启动了区块链节点并进入了该节点的控制台:
Welcome to the Geth JavaScript console!
instance: Geth/TestNode/v1.8.2-stable-b8b9f7f4/windows-amd64/go1.9.2
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.2 后续的启动
后续的启动,可以使用简单一些的命令,不必指定端口号、目录等;(console表示将日志输出到控制台中)
geth -datadir "chain" console
来自 区块链教程—以太坊(一):Windows搭建以太坊私有链_慕课手记
四、控制台操作
进入以太坊 Javascript Console 后,就可以使用里面的内置对象做一些操作,这些内置对象提供的功能很丰富,比如查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。
常用命令有:
- personal.newAccount():创建账户;
- personal.unlockAccount():解锁账户;
- eth.accounts:枚举系统中的账户;
- eth.getBalance():查看账户余额,返回值的单位是 Wei(Wei 是以太坊中最小货币面额单位,类似比特币中的聪,1 ether = 10^18 Wei);
- eth.blockNumber:列出区块总数;
- eth.getTransaction():获取交易;
- eth.getBlock():获取区块;
- miner.start():开始挖矿;
- miner.stop():停止挖矿;
- web3.fromWei():Wei 换算成以太币;
- web3.toWei():以太币换算成 Wei;
- txpool.status:交易池中的状态;
- admin.addPeer():连接到其他节点;
这些命令支持 Tab 键自动补全,具体用法如下。
1. 创建账户
personal.newAccount()
Passphrase:
Repeat passphrase:
"0xfe4b566a620caf0558ba649b7d6554b801ef8dee"
> INFO [03-20|16:48:00] Etherbase automatically configured address=0xFE4b566A620Caf0558BA649b7d6554B801EF8deE
查看刚刚创建的账户:
eth.accounts
["0xfe4b566a620caf0558ba649b7d6554b801ef8dee"]
2. 查看账户余额
eth.getBalance(eth.accounts[0])
3. 启动&停止挖矿
启动挖矿:
miner.start(1)
其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。
停止挖矿,在 console 中输入:
miner.stop()
挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做 coinbase,默认情况下 coinbase 是本地账户中的第一个账户,可以通过 miner.setEtherbase() 将其他账户设置成 coinbase。
4. 发送交易
目前,账户 0 已经挖到了 3 个块的奖励,账户 1 的余额还是0:
eth.getBalance(eth.accounts[0])
15000000000000000000
eth.getBalance(eth.accounts[1])
0
我们要从账户 0 向账户 1 转账,所以要先解锁账户 0,才能发起交易:personal.unlockAccount(eth.accounts[0])
Unlock account 0x3443ffb2a5ce3f4b80080791e0fde16a3fac2802
Passphrase:
true
发送交易,账户 0 -> 账户 1:amount = web3.toWei(5,'ether')
"5000000000000000000"
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
INFO [09-12|07:38:12] Submitted transaction fullhash=0x9f5e61f3d686f793e2df6378d1633d7a9d1df8ec8c597441e1355112d102a6ce recipient=0x02bee2a1582bbf58c42bbdfe7b8db4685d4d4c62
"0x9f5e61f3d686f793e2df6378d1633d7a9d1df8ec8c597441e1355112d102a6ce"
此时如果没有挖矿,用 txpool.status 命令可以看到本地交易池中有一个待确认的交易,可以使用 eth.getBlock("pending", true).transactions 查看当前待确认交易。
使用 miner.start() 命令开始挖矿:
miner.start(1);admin.sleepBlocks(1);miner.stop();
新区块挖出后,挖矿结束,查看账户 1 的余额,已经收到了账户 0 的以太币:web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
5
5. 查看交易和区块
查看当前区块总数:
eth.blockNumber
4
通过交易 Hash 查看交易(Hash 值包含在上面交易返回值中):eth.getTransaction("0x9f5e61f3d686f793e2df6378d1633d7a9d1df8ec8c597441e1355112d102a6ce")
{
blockHash: "0xdc1fb4469bf4613821c303891a71ff0d1f5af9af8c10efdd8bcd8b518533ee7d",
blockNumber: 4,
from: "0x3443ffb2a5ce3f4b80080791e0fde16a3fac2802",
gas: 90000,
gasPrice: 18000000000,
hash: "0x9f5e61f3d686f793e2df6378d1633d7a9d1df8ec8c597441e1355112d102a6ce",
input: "0x",
nonce: 0,
r: "0x4214d2d8d92efc3aafb515d2413ecd45ab3695d9bcc30d9c7c06932de829e064",
s: "0x42822033225a2ef662b9b448576e0271b9958e1f4ec912c259e01c84bd1f6681",
to: "0x02bee2a1582bbf58c42bbdfe7b8db4685d4d4c62",
transactionIndex: 0,
v: "0x824",
value: 5000000000000000000
}
通过区块号查看区块:eth.getBlock(4)
{
difficulty: 131072,
extraData: "0xd783010607846765746887676f312e382e31856c696e7578",
gasLimit: 3153874,
gasUsed: 21000,
hash: "0xdc1fb4469bf4613821c303891a71ff0d1f5af9af8c10efdd8bcd8b518533ee7d",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x3443ffb2a5ce3f4b80080791e0fde16a3fac2802",
mixHash: "0x6df88079cf4fbfae98ad7588926fa30becddf4b8b55f93f0380d82ce0533338c",
nonce: "0x39455ee908666993",
number: 4,
parentHash: "0x14fe27755d6fcc704f6b7018d5dc8193f702d89f2c7807bf6f0e402a2b0a29d9",
receiptsRoot: "0xfcb5b5cc322998562d96339418d08ad8e7c5dd87935f9a3321e040344e3fd095",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 651,
stateRoot: "0xdd4a0ce76c7e0ff149853dce5bb4f99592fb1bc3c5e87eb07518a0235ffacd8c",
timestamp: 1505202063,
totalDifficulty: 525312,
transactions: ["0x9f5e61f3d686f793e2df6378d1633d7a9d1df8ec8c597441e1355112d102a6ce"],
transactionsRoot: "0xbb909845183e037b15d24fe9ad1805fd00350ae04841aa774be6af96e76fdbf9",
uncles: []
}
6.连接到其他节点
可以通过 admin.addPeer() 方法连接到其他节点,两个节点要要指定相同的 chainID。
假设有两个节点:节点一和节点二,chainID 都是 1024,通过下面的步骤就可以从节点一连接到节点二。
首先要知道节点二的 enode 信息,在节点二的 JavaScript console 中执行下面的命令查看 enode 信息:
admin.nodeInfo.enode
"enode://d465bcbd5c34da7f4b8e00cbf9dd18e7e2c38fbd6642b7435f340c7d5168947ff2b822146e1dc1b07e02f7c15d5ca09249a92f1d0caa34587c9b2743172259ee@[::]:30303"
然后在节点一的 JavaScript console 中执行 admin.addPeer(),就可以连接到节点二:admin.addPeer("enode://d465bcbd5c34da7f4b8e00cbf9dd18e7e2c38fbd6642b7435f340c7d5168947ff2b822146e1dc1b07e02f7c15d5ca09249a92f1d0caa34587c9b2743172259ee@127.0.0.1:30304")
addPeer() 的参数就是节点二的 enode 信息,注意要把 enode 中的 [::] 替换成节点二的 IP 地址。连接成功后,节点二就会开始同步节点一的区块,同步完成后,任意一个节点开始挖矿,另一个节点会自动同步区块,向任意一个节点发送交易,另一个节点也会收到该笔交易。
通过 admin.peers 可以查看连接到的其他节点信息,通过 net.peerCount 可以查看已连接到的节点数量。
除了上面的方法,也可以在启动节点的时候指定 --bootnodes 选项连接到其他节点。
以上参考 以太坊私有链搭建指南 | G2EX