以太坊学习笔记(三)——搭建以太坊私链
以太坊私链的搭建可以直接通过下载程序进行安装,也可以通过编译源码安装,本文介绍通过编译源码进行安装。
编译源码
1.准备环境
我们下载的是go语言的源码,首先需要正确的安装go语言环境,如何正确安装go语言环境,大家可以去网上找教程。
2.下载源码
可以通过go命令行来下载
go get github.com/ethereum/go-ethereum
也可以通过git工具或直接去官方git https://github.com/ethereum/go-ethereum 载源码,源码一般放在go的源码文件下。
3.编译源码
//进入源码文件夹
cd /你的路径/go-ethereum
make geth 或者 make all
编译成功如下图所示: 编译成功提示:在安装编译中可能会报“exec: "gcc": executable file not found in %PATH%”错误,是因为没有GCC环境导致的,搜索报错信息就能找到解决的办法。
GCC下载
4.配置环境变量
从编译成功的截图我们可以看到,如果要启动geth需要到相应的路径下执行命令,所以我们需要配置环境变量
vi ~/.bash_profile
# 打开配置文件,把以下内容添加到文件中
export GETH="$GOPATH/src/github.com/ethereum/go-ethereum/build"
export PATH="$PATH:$GETH/bin"
检查是否安装成功
geth --help
输出如下内容,则表示安装成功了。
安装成功
搭建私链
1.创建私链数据存放目录
//原则上可以在任何地方创建文件夹,但尽量不要在需要管理员权限的路径下创建文件夹,避免不必要的麻烦
mkdir /文件路径/ethprivatechain
//data用于存放账户和区块数据
mkdir /文件路径/ethprivatechain/data
2.进入刚刚创建的文件夹,使用命令启动私链
> geth --datadir data --nodiscover console
启动成功
geth默认端口为8545和30303,mac下可以使用如下命令查看
lsof -i :30303
3.创建两个账户
方便后期测试转账,所以创建两个账户
> personal.newAccount("111111")
//出现的一串字符,为创建的账户地址
"0x16dd83d69d8908109da5ce386a924a2b0fdbe80e"
> personal.newAccount("123456")
"0xaa33b3a596af305e9618777b921570dd1a25215f"
4.查询余额
> eth.getBalance(eth.accounts[0])
//查询结果
0
5.退出geth控制台
> exit
//输出信息
INFO [09-04|15:13:19.802] IPC endpoint closed endpoint=/Users/cyril/Desktop/ethprivatechain/data/geth.ipc
INFO [09-04|15:13:19.803] Blockchain manager stopped
INFO [09-04|15:13:19.803] Stopping Ethereum protocol
INFO [09-04|15:13:19.803] Ethereum protocol stopped
INFO [09-04|15:13:19.803] Transaction pool stopped
INFO [09-04|15:13:19.806] Database closed database=/Users/cyril/Desktop/ethprivatechain/data/geth/chaindata
创建创世区块
1.在ethprivatechain下新建genesis.json
{
"alloc": {
"0x16dd83d69d8908109da5ce386a924a2b0fdbe80e": {
"balance": "100000000000000000000000000"
}
},
"config":{
"chainId":100,
"homesteadBlock":0,
"eip155Block":0,
"eip158Block":0
},
"nonce":"0x0000000000000042",
"mixhash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x2000",
"coinbase":"0xaa33b3a596af305e9618777b921570dd1a25215f",
"timestamp": "0x00",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "",
"gasLimit":"0xffffffff"
}
各参数字段的含义:
- alloc: 用来预置账号以及账号的以太币数量,因为私链挖矿比较容易,所以我们也可以不预置有币的账号,需要的时候自己创建即可。
- chainId:链的ID,以太坊公链是1,我们要与其不同,以免冲突
- nonce: nonce就是一个64位随机数,用于挖矿。
- mixhash:与nonce配合用于挖矿,由上一个区块的一部分生成的hash。
- difficulty: 设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度
- coinbase: 矿工的账号,随便填
- timestamp: 设置创世块的时间戳
- parentHash: 上一个区块的hash值,因为是创世块,所以这个值是0
- extraData: 附加信息,随便填,可以填你的个性信息
- gasLimit: 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大。
2.初始化创世区块
> geth --datadir data init genesis.json
//输出信息
INFO [09-04|15:35:46.767] Maximum peer count ETH=25 LES=0 total=25
INFO [09-04|15:35:46.775] Allocated cache and file handles database=/Users/cyril/Desktop/ethprivatechain/data/geth/chaindata cache=16 handles=16
INFO [09-04|15:35:46.777] Writing custom genesis block
INFO [09-04|15:35:46.777] Persisted trie from memory database nodes=1 size=151.00B time=117.527µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [09-04|15:35:46.778] Successfully wrote genesis state database=chaindata hash=0e8bb0…a5a328
INFO [09-04|15:35:46.778] Allocated cache and file handles database=/Users/cyril/Desktop/ethprivatechain/data/geth/lightchaindata cache=16 handles=16
INFO [09-04|15:35:46.780] Writing custom genesis block
INFO [09-04|15:35:46.780] Persisted trie from memory database nodes=1 size=151.00B time=91.399µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [09-04|15:35:46.780] Successfully wrote genesis state database=lightchaindata hash=0e8bb0…a5a328
提示:
如果在执行上面的命令报错
先删除原来的创世块geth removedb --datadir data
再初始化创世区块geth --datadir data init genesis.json
初始化成功后,会在数据目录 data 中生成 geth 和 keystore 两个文件夹,此时目录结构如下:
tree
//输出结果
.
├── data
│ ├── geth
│ │ ├── LOCK
│ │ ├── chaindata
│ │ │ ├── 000001.log
│ │ │ ├── CURRENT
│ │ │ ├── LOCK
│ │ │ ├── LOG
│ │ │ └── MANIFEST-000000
│ │ ├── lightchaindata
│ │ │ ├── 000001.log
│ │ │ ├── CURRENT
│ │ │ ├── LOCK
│ │ │ ├── LOG
│ │ │ └── MANIFEST-000000
│ │ ├── nodekey
│ │ └── transactions.rlp
│ ├── history
│ └── keystore
│ ├── UTC--2018-09-04T06-55-11.166220891Z--16dd83d69d8908109da5ce386a924a2b0fdbe80e
│ └── UTC--2018-09-04T06-56-21.455326358Z--aa33b3a596af305e9618777b921570dd1a25215f
└── genesis.json
其中 geth/chaindata
中存放的是区块数据,keystore
中存放的是账户数据。
3.进入geth控制台
> geth --datadir data --networkid 100 console
//输出信息
INFO [09-04|16:08:23.164] Maximum peer count ETH=25 LES=0 total=25
INFO [09-04|16:08:23.173] Starting peer-to-peer node instance=Geth/v1.8.16-unstable/darwin-amd64/go1.10.2
INFO [09-04|16:08:23.173] Allocated cache and file handles database=/Users/cyril/Desktop/ethprivatechain/data/geth/chaindata cache=768 handles=128
INFO [09-04|16:08:23.189] Initialised chain configuration config="{ChainID: 100 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Constantinople: <nil> Engine: unknown}"
INFO [09-04|16:08:23.189] Disk storage enabled for ethash caches dir=/Users/cyril/Desktop/ethprivatechain/data/geth/ethash count=3
INFO [09-04|16:08:23.189] Disk storage enabled for ethash DAGs dir=/Users/cyril/.ethash count=2
INFO [09-04|16:08:23.189] Initialising Ethereum protocol versions="[63 62]" network=100
INFO [09-04|16:08:23.191] Loaded most recent local header number=0 hash=0e8bb0…a5a328 td=8192
INFO [09-04|16:08:23.191] Loaded most recent local full block number=0 hash=0e8bb0…a5a328 td=8192
INFO [09-04|16:08:23.191] Loaded most recent local fast block number=0 hash=0e8bb0…a5a328 td=8192
INFO [09-04|16:08:23.192] Loaded local transaction journal transactions=0 dropped=0
INFO [09-04|16:08:23.193] Regenerated local transaction journal transactions=0 accounts=0
INFO [09-04|16:08:23.194] Starting P2P networking
INFO [09-04|16:08:23.223] Mapped network port proto=udp extport=30303 intport=30303 interface=NAT-PMP(192.168.31.1)
INFO [09-04|16:08:23.230] UDP listener up self=enode://9c5d2c3e34f13543af0da28c10a6566d2be0eeff1e73a450e26ab94347cbd96ce096dd27573b3960e94458989c72d57fb0160687bc981fcfed0f24c5738a90de@192.168.114.11:30303
INFO [09-04|16:08:23.230] RLPx listener up self=enode://9c5d2c3e34f13543af0da28c10a6566d2be0eeff1e73a450e26ab94347cbd96ce096dd27573b3960e94458989c72d57fb0160687bc981fcfed0f24c5738a90de@192.168.114.11:30303
INFO [09-04|16:08:23.233] IPC endpoint opened url=/Users/cyril/Desktop/ethprivatechain/data/geth.ipc
INFO [09-04|16:08:23.244] Mapped network port proto=tcp extport=30303 intport=30303 interface=NAT-PMP(192.168.31.1)
Welcome to the Geth JavaScript console!
instance: Geth/v1.8.16-unstable/darwin-amd64/go1.10.2
INFO [09-04|16:08:23.335] Etherbase automatically configured address=0x16dD83D69D8908109Da5ce386A924A2b0FdbE80e
coinbase: 0x16dd83d69d8908109da5ce386a924a2b0fdbe80e
at block: 0 (Thu, 01 Jan 1970 08:00:00 CST)
datadir: /Users/cyril/Desktop/ethprivatechain/data
modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
提示:
networkid为上面设置的100
4.转账交易
解锁账户
> personal.unlockAccount(eth.accounts[0], "111111")
//输出信息
true
发送交易
> amount = web3.toWei(10,'ether')
"10000000000000000000"
> eth.sendTransaction({from:eth.accounts[0], to:eth.accounts[1], value:amount})
INFO [09-04|16:16:53.161] Setting new local account address=0x16dD83D69D8908109Da5ce386A924A2b0FdbE80e
INFO [09-04|16:16:53.161] Submitted transaction fullhash=0x0d681856f3115696460abb8b124425be38e130f7f651c805c04b6ec2ec247784 recipient=0xaA33b3a596Af305e9618777b921570DD1a25215f
"0x0d681856f3115696460abb8b124425be38e130f7f651c805c04b6ec2ec247784"
此时查询账户余额没发生变化,需要进行挖矿确认打包交易
> eth.getBalance(eth.accounts[1])
0
5.启动&停止挖矿
通过miner.start()
启动挖矿
> miner.start(5)
INFO [09-04|16:20:29.602] Updated mining threads threads=5
INFO [09-04|16:20:29.603] Transaction pool price threshold updated price=1000000000
start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。
如下信息表示挖到一个潜在的区块:
INFO [09-04|16:23:44.403] Generating DAG in progress epoch=0 percentage=98 elapsed=3m14.115s
INFO [09-04|16:23:47.190] Generating DAG in progress epoch=0 percentage=99 elapsed=3m16.901s
INFO [09-04|16:23:47.193] Generated ethash verification cache epoch=0 elapsed=3m16.904s
INFO [09-04|16:23:53.057] Successfully sealed new block number=1 sealhash=7d40a9…15a0b8 hash=254822…506ae1 elapsed=3m23.446s
INFO [09-04|16:23:53.058] 🔨 mined potential block number=1 hash=254822…506ae1
INFO [09-04|16:23:53.058] Commit new mining work number=2 sealhash=039ae4…231dbf uncles=0 txs=0 gas=0 fees=0 elapsed=172.398µs
INFO [09-04|16:23:54.818] Successfully sealed new block number=2 sealhash=039ae4…231dbf hash=71c750…4afcfc elapsed=1.759s
下面的输出信息表示区块已经上链:
INFO [09-04|16:24:06.431] 🔗 block reached canonical chain number=1 hash=254822…506ae1
挖矿完成之后查询账户余额
> eth.getBalance(eth.accounts[1])
10000000000000000000
我们可以看到转账的10eth已经到账
通过miner.stop()停止挖矿
> miner.stop()
挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:
> eth.coinbase
"0x16dd83d69d8908109da5ce386a924a2b0fdbe80e"
我们可以通过miner.setEtherbase()
将其他账户设置成coinbase
> miner.setEtherbase(eth.accounts[1])
true
> eth.coinbase
"0xaa33b3a596af305e9618777b921570dd1a25215f"
重新启动挖矿,查看eth.accounts[1]
是否获得以太币
> miner.start(5)
//等待几秒后
> miner.stop()
查询账户余额:
> web3.fromWei(eth.getBalance(eth.accounts[0]), 'ether')
100000185
> web3.fromWei(eth.getBalance(eth.accounts[1]), 'ether')
75
账户0和1都比之前的以太币多,说明挖矿和miner.setEtherbase()
设置成功
6.查看交易信息
查看当前区块总数:
> eth.blockNumber
52
通过区块号查看区块信息:
> eth.getBlock(0)
{
difficulty: 8192,
extraData: "0x",
gasLimit: 4294967295,
gasUsed: 0,
hash: "0x0e8bb0047f80e98089fd81dd4e077af8e43ccee0e1c8e1a7439b8e5b95a5a328",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0xaa33b3a596af305e9618777b921570dd1a25215f",
mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000042",
number: 0,
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 507,
stateRoot: "0x62815262f2efc4e22af8ac64adc029ba4f38a74969ec68de7b0714771ca06190",
timestamp: 0,
totalDifficulty: 8192,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
通过交易hash查看交易,hash可以通过上面的输出信息获取:
> eth.getTransaction("0x0e8bb0047f80e98089fd81dd4e077af8e43ccee0e1c8e1a7439b8e5b95a5a328")
null
控制台操作命令介绍
控制台内置了一些用来操作以太坊的 Javascript 对象,可以直接使用这些对象。
- eth:操作区块链相关的方法;
- net:查看p2p网络状态的方法;
- admin:与管理节点相关的方法;
- miner:启动&停止挖矿的方法;
- personal:管理账户的方法;
- txpool:查看交易内存池的方法;
- web3:包含了以上对象,还包含一些单位换算的方法。
常用命令:
- 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():停止挖矿;
- eth.coinbase:挖矿奖励的账户
- web3.fromWei():Wei 换算成以太币;
- web3.toWei():以太币换算成 Wei;
- txpool.status:交易池中的状态;
- admin.addPeer():连接到其他节点;