以太坊学习笔记(三)——搭建以太坊私链

以太坊私链的搭建可以直接通过下载程序进行安装,也可以通过编译源码安装,本文介绍通过编译源码进行安装。

编译源码

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下载](https://sourceforge.net/projects/hpc/files/hpc/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():连接到其他节点;

推荐阅读更多精彩内容