OS:macOS amd64
geth version:go-ethereum v1.6.7
IDE:goland
go version:go1.10.1 darwin/amd64
搭建过程这里不细讲,具体可以参考Windows下搭建btcd本地化开发环境。只不过本文使用goland作为IDE。
$ go install ./cmd/geth
命令执行完毕,会在$GOPATH/bin下面生成一个geth可执行文件。
本文采用Ethash引擎,下面是genesis.json文件。为了演示效果,难度可以设置的低一点。
{
"config": {
"chainId": 1314,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x200",
"extraData" : "",
"gasLimit" : "0xffffffff",
"nonce" : "0x0000000000000042",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00",
"alloc": { }
}
打开一个终端,切换到$GOPATH/bin目录下面,执行下面的命令:
$ ./geth --datadir ./geth-data0 init genesis.json
$ ./geth --datadir ./geth-data1 init genesis.json
具体是【Run】——【Edit Configurations…】,下图是一个配置参考图:
程序参数的通用格式是:
--datadir "/Users/data/gobin/geth-dataX" --networkid 1314 --nodiscover --rpcport 600X --port 3000X --ipcpath /Users/data/go/bin/gethX.ipc console
【注1】上面的命令还不完备,但基本够能够满足我们的需求。
【注2】错误的命令行参数:
./geth --datadir "/Users/data/go/bin/geth-data0" --networkid 1314 --nodiscover --rpcport 6001 --port 30001 --ipcpath /Users/data/Library/Ethereum/geth1.ipc console
这个参数传进去,直接连到以太坊主网了。不要加./geth。
对应到本文,我们需要启动2个node,node1负责mining,node2负责同步。当然你可以两个node都mining。
我们node1的程序参数是:
--datadir "/Users/data/go/bin/geth-data0" --networkid 1314 --nodiscover --rpcport 6001 --port 30001 --ipcpath /Users/data/go/bin/geth1.ipc console
我们node2的程序参数是:
--datadir "/Users/data/go/bin/geth-data1" --networkid 1314 --nodiscover --rpcport 6002 --port 30002 --ipcpath /Users/data/go/bin/geth2.ipc console
【注3】因为在一台机器上启多个进程,所以数据目录,rpc端口,以及udp端口都得不一样才行。
点击工具栏中的运行三角图标。运行界面如下图:
接下来,我们创建账户,设置coinbase,开始挖矿。下面是要执行的一些命令:
> personal.newAccount("pwd")
//eth.coinbase 查看是否有coinbase,若存在,则不用在设置了。
> miner.setEtherbase(eth.accounts[0])
> admin.nodeInfo.enode //保存下node1的网络ID
点击工具栏的调试图标。
我们重新开一个终端,执行下面的命令,该命令会启动一个连到node2的交互式JS控制台,便于我们输命令:
$ ./geth attach ipc:/Users/data/go/bin/geth2.ipc
命令执行成功,会进入控制台,我们在其中输入下面的命令用于创建2个地址:
> personal.newAccount("pwd1")
> personal.newAccount("pwd2")
我们也可以重新开一个终端关联到node1。但这里我们直接在控制台上输入下面的命令,用于向node2的eth.accounts[0]中转10ETH:
personal.unlockAccount(eth.accounts[0]) //转账之前先解锁
eth.sendTransaction({from:eth.accounts[0],to:"0xa94d69aaa3f4ba948f10aef1c9e746b9b69685cf",value:web3.toWei(10,"ether")})
其中”0xa94d69aaa3f4ba948f10aef1c9e746b9b69685cf”为node2的eth.accounts[0]。
好,准备工作完备。
现在开始debug,如果我们想知道从node2的console上发送一条交易(从eth.accounts[0]转5ETH到eth.accounts[1]),背后都发生了什么,怎么办?
我们需要在/internal/ethapi/api.go的(s *PublicTransactionPoolAPI) SendTransaction方法上打上断点【注意:打上断点以后,直接debug你会发现程序并没有在断点出停。只有当你在控制台发送一条交易或者本地通过rpc发交易才会触发程序在断点处停掉】。
然后在控制台输入以下命令:
> personal.unlockAccount(eth.accounts[0]) //转账之前先解锁
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(10,"ether")})
回车过后,我们的代码就从Console跳到Debugger。下面是函数的调用堆栈:
【注3】如果我们不想单步调试,直接点击上图左侧的绿色小三角,继续运行程序直到下一个断点。
根据调用堆栈,这下我们就知道在控制台点击回车键之后,都发生了什么。下面的信息是node2的日志里面出现的,其实这条信息的背后还是有一些故事的。
INFO [07-18|18:45:15] Submitted transaction fullhash=0x21d1e3f8d0cc2eada9cbfd7113ea5d5543eb5bd82da4d54db16632afda6be9f6 recipient=0xeed9d944214aed8898c20888ed100adc306714fb
我们想知道node2同步node1时,都发生了什么?怎么办?
我们在/core/blockchain.go的(st *insertStats) report方法上添加断点【注意:打上断点还不够,需要事件去触发程序在断点处停掉】。
接着我们在关联node2的控制台上执行添加peer的命令:
admin.addPeer("enode://fc4eb108d62dc8d9fe1133557eafc106682c3652f5edb222342da9613befefdaeed22cf2883a1cba7ecb765abe92d753e18f73b72a3250e9e7788b4eca89c539@127.0.0.1:30001?discport=0")
回车后,我们就能看到node2的console将打印下面的日志信息,表明开始同步。
INFO [07-18|18:58:37] Block synchronisation started
但“同步”二字背后都经过了哪些逻辑呢?显然,我们刚刚打断点的地方是有参与的,否则程序不会走到那里停掉。
重新启动Debug功能,需重新geth attach,有关geth的attach命令用法,可以参见以太坊实战-attach命令。
上面对debug以太坊geth给出了初步的思路,掌握这些方法有利于我们洞悉以太坊geth的内部工作原理。调试过程辅以日志信息就可以相对清楚的了解geth背后的逻辑。