Proof of Authority 是直接指定某些节点有记帐权,其他节点通过验证,授权的节点生产Block 轮流记账。
geth >= 1.6ethereum/EIPs#225实现了Clique的共识机制。
由于go-ethereum 使用golang 开发的,下面是通过命令行方式安装的geth1.7,用到了ppa 。
geth & tools 1.6 下载地址: https://ethereum.github.io/go-ethereum/downloads/
找到对应的OS 下载相应版本,一定要下载 geth & tools 的版本,因为后期会使用 geth 1.6 版本中一个创建 Private chain 的工具 puppeth 来建立 Clique Private chain。
将下载的.tar.gz文件提取出来,解压后的目录下全是可执行文件,将所有的可执行文件移动到/usr/bin目录下,如果之前安装过geth,应在/usr/bin/下执行sudo rm geth
然后在解压的目录下执行sudo mv XXX /usr/bin
。
创建4个文件夹,分别命名为node1、node2、signer1、signer2。node是普通节点,用于后期节点间发起交易;在接下来的实验中,signer1设置为创世块指定的授权节点;signer2为后期加入信任名单的授权节点。
### 建立Ethereum 帐号
为每个节点建立一个Ethereum 账户。指令geth --datadir ./data account new
是指使用当下目录下的data
目录当作geth
存放资料的地方,并且创一个新的Account。在刚刚创建的node1, node2, signer1, signer2 目录下分别运行相同指令来创建一个账户,如下所示。
以下是我创建的每个角色的Account address:
node1: 0x0a9e3b2ba71cd3c6b3281bbb2a4ed60eadcd7581
node2: 0x42859e6a0c3bdbc3bf9078e59eab655826a540ee
signer1: 0x5824cd5f32da04ecbdc877200f5ac91b5a0e1a5e
signer2: 0x287ea6d4254cabedd72d2e7fc6a0d1e6abd06eb7
注:这些地址最好是保存在txt文件中,以备后期使用。
Clique 是将授权节点的相关信息放在Block Header中,必须设置创世块才可以让授权机制生效。
Clique是将授权的信息放在extraData中,但信息结构的格式并没有那么直观,所以在此使用geth 1.6提供的建立Private Chain的工具puppeth来建立创世块, puppeth是个互动式的程序,直接启动照着指示输入相关信息。
设置Private chain名称,假定为poa_for_fun,如下:
设置生产出一个Block的时间,在这里设10 秒,可自拟,如下:
授权节点。这里使用上面的Signer1的address,如下:
给账户设置一些ether。这里选node1和signer1的address,可自拟,如下:
Network Id,直接用random;添加genesis 的,留空,如下:
ctrl+c 退出,查看当前目录会看到一个poa_for_fun.json 文件。
使用geth init
指令,分别初始化4 个node 的datadir,比如初始化node1可执行:geth --datadir node1/data init poa_for_fun.json
,如下:
分别在node1, node2目录下,运行如下指令,启动geth:
geth --datadir ./data --networkid 55661 --port 2000 console
这里的参数需要特别注意:
① datadir 先前的步骤已经在每个节点各自的目录下都建立了data 目录。
② networkid geths之间一定都要用同一个值才可以互相通信,比如实验中的55661。
③ port geths之间通信时,监听的一个port,由于四个节点都在本机,所以这里必须都指定不同的值,使用node1对应2000, node2对应2001, signer1对应2002, signer2对应2003。
如果节点是授权打包block的节点,启动时要先unlock account,这样才可以打包交易。启动后会要求输入当时创建account时的passphrase。比如,启动signer1,可运行以下指令:
geth --datadir ./data --networkid 55661 --port 2002 --unlock signer1_address console
启动后,会进入一个console的界面,可以运行一些eth.getBalance()
等指令来操作geth。
启动signer2 ,注意port和address都不一样,并且在signer2的目录下
在node1的启动信息中有一段encode信息,下图已标黄,如下:
由于目前是在私有链上,没有设定启动节点,也没设定static node,各节点启动后是无法相互通信的。所以,geth要连上对方的节点就必须先设置好enode://
,复制刚刚启动node1时出现的enode信息,将[::]替换为127.0.0.1,这样就可以让其他节点加入。
在node2, signer1, signer2 的geth console 界面下分别运行如下指令:
完成后,在node1的geth console输入admin.peers
应该要看到三个节点资讯,如下:(每个id对应其他节点的encode字段信息)
在signer1的console界面,键入miner.start()
,geth就会开始挖矿了。在signer1 的console 会出现正在mining 的信息。
其他节点(node1和node2)则会收到import block 的信息。如下:
在node1的console下,使用指令eth.getBalance("")
来查询账户余额。
使用eth.sendTransaction
指令来把一些ether从node1转到node2。首先,需要将node1的account解锁,然后才能转账,如下所示:
在signer1的console下,运行miner.start()
,在几秒后就会看到一个含有一笔交易的block产出,其他结点也会收到信息:
在Clique 共识机制中是使用Clique提供的API来进行节点管理。
signer2 是一开始未设定在创世块中信任列表的节点,如果这时候让它启动miner,会遇到一个未授权的错误,如下:
必须回到已经在授权名单内的节点的console界面下,将新的节点加入。在signer1 的console 输入指令:
在signer2 的console,运行miner.start()
发现功能未实现。
遇到这种情况,应将signer1和signer2都关闭,重新启动,如下:
重新启动signer1,输入账户密码,进入console界面
重新启动signer2,输入账户密码,进入console界面
在signer2中运行miner.start()
进行挖矿,提示Signed recently, must wait for others
在signer1中运行miner.start()
进行挖矿,提示Signed recently, must wait for others
发现signer1和signer2都提示Signed recently, must wait for others,并且两个终端都没有继续挖矿,猜想可能是signer1和signer2之间没有建立通信,在signer1和signer2中分别运行admin.pees,发现都没有链接节点,如下:
因此在signer1中通过admin.addPeer()添加signer2,使二者可以通信,signer1和signer2都重新继续挖矿;其中signer1生成26号块后,signer2从刚才生成25号块提示Signed recently, must wait for others的地方Imported new chain segment,新导入的就是signer1生成的26号块,随后,signer1和signer2交替签名,依次挖矿,如下所示:
在终端不关闭的情况下,重新启动geth进入console界面,通过admin.peers查看链接信息,发现之前添加的节点信息都存在;但是,关闭终端后,重新打开一个新的终端,启动geth进入console界面,通过admin.peers查看链接信息,会发现是空的,需要重新运行admin.addPeer()添加,这一点一定要注意!!!
通过clique.discard(signer_address)
删除信任节点。通过clique.propose(signer_address,true)
添加信任节点,通过实验发现,无法立即生效,需要一半以上的信任节点的投票[2]。一般要在当前所有的信任节点的终端中都执行clique.propose(),并且在各信任节点的终端都同时挖矿,各自挖出几个块后,新添加的信任节点就可已挖矿了,这只是实验测试可行,具体解决方法需要查看源代码,部分重要的函数已放到博客:http://blog.csdn.net/code_segment/article/details/78150163。
提示Signed recently, must wait for others的原因,通过查看最新版go-ethereum项目的源代码,发现consensus/clique/clique.go 的seal函数如下:
snap.Recents为map[uint64]common.Address,保存最近签名的signer
满足number < limit || seen > number-limit条件即输出Signed recently, must wait for others提示,由于signer之间没有建立通信,所以都卡在 <-stop 语句处
参考:
1、https://medium.com/taipei-ethereum-meetup/%E4%BD%BF%E7%94%A8-go-ethereum-1-6-clique-poa-consensus-%E5%BB%BA%E7%AB%8B-private-chain-1-4d359f28feff
2、https://ethereum.stackexchange.com/questions/15541/how-to-add-new-sealer-in-geth-1-6-proof-of-authority