clique.go中实现了consensus中的所有接口完成POA算法的实现
Clique.Prepare(chain , header)Prepare是共识引擎接口之一. 该函数配置header中共识相关的参数(Cionbase, Difficulty, Extra, MixDigest, Time)
对于非epoch的block( number % Epoch != 0 ): 得到Clique.proposals中的投票数据(例:A加入C, B踢除D)根据snapshot的signers分析投票数否有效(例: C原先没有在signers中, 加入投票有效, D原先在signers中,踢除投票有效)从被投票的地址列表(C,D)中, 随机选择一个地址 ,作为该header的Coinbase,设置Nonce为加入( 0xffffffffffffffff )或者踢除( 0x0000000000000000 )Clique.signer 如果是本轮的签名者(in-turn), 设置header.Difficulty = diffInTurn(1), 否则就是diffNoTurn(2)配置header.Extra的数据为[ extraVanity + snap中的全部signers + extraSeal ]MixDigest需要配置为nil配置时间戳:Time为父块的时间+15s填充header对象的Coinbase,Nonce,Difficulty,Extra(预留后缀,用于Seal()存入当前块签名者的签名) 共识引擎clique的初始化在 Ethereum.StartMining 中,如果Ethereum.engine配置为clique.Clique, 根据当前节点的矿工地址(默认是acounts[0]), 配置clique的 签名者 : clique.Authorize(eb, wallet.SignHash) ,其中 签名函数 是SignHash,对给定的hash进行签名.
Clique.snapshot(chain,number,hash,parents)快照在指定的时间内检索授权的快照,首先封装了在内存和磁盘中,寻找快照;
如果是在创世区块中则创建一个新的快照;
如果没有区块头的快照,则收集区块向后移,有明确的父就强制到父,没有明确的父到数据库中找
找到快照后,将所有的header的后半部分前移
再通过区块头生成一个新的快照,将当前区块的hash保存到最近的快照中,将生成的快照保存到磁盘上
其中的votes应用存储投票的数量
Snapshot.apply(headers)创建一个新的授权signers的快照, 将从上一个snapshot开始的区块头中的proposals更新到最新的snapshot上
对入参headers进行完整性检查: 因为可能传入多个区块头, block号必须连续遍历所有的header, 如果block号刚好处于epoch的起始(number%Epoch == 0),将snapshot中的Votes和Tally复位( 丢弃历史全部数据 )对于每一个header,从签名中得到 signer如果该signer在snap.Recents中, 说明 最近已经有过签名 , 不允许再次签名, 返回空记录 该signer是该block的签名者: snap.Recents[number] = signer统计header.Coinbase的投票数,如果 超过signers总数的50%执行加入或移除 *** 作删除snap.Recents中的一个signer记录: key=number- (uint64(len(snap.Signers)/2 + 1)), 表示释放该signer,下次可以对block进行签名了清空被移除的Coinbase的投票移除snap.Votes中该Conibase的所有投票记录移除snap.Tally中该Conibase的所有投票数记录 Clique.Seal(chain, block , stop)Seal也是共识引擎接口之一. 该函数用clique.signer对block的进行签名. 实现共识,引擎,尝试使用创建密封块,生成签名到header.Extra的后缀中
可对一个调用过 Finalize()的区块进行授权或封印,成功时返回的区块全部成员齐整,可视为一个正常区块,可被广播到整个网络中,也可以被插入区块链等
不支持密封genesis区块如果signer没有在snapshot的signers中,不允许对block进行签名signer不是本区块的签名者需要延时随机一段时候后再签名,是本区块的签名者则直接签名当前签名者在‘最近签名者’中,则等待下一个epoch不支持0-period的链,不支持空块密封,没有奖励但是能够密封把签名的结果用copy替换保存到区块头Extra字段的extraSeal的65字节中中 Clique.VerifySeal(chain, header)VerifySeal也是共识引擎接口之一.
从header的签名中恢复账户地址,改地址要求在snapshot的signers中检查区块头中的计算难度是否匹配(in turn或out of turn) Clique.FinalizeFinalize也是共识引擎接口之一. 该函数生成一个block, 对新区块“定型”,没有叔块处理,也没有奖励机制
header.Root : 状态根保持原状header.UncleHash : 为niltypes.NewBlock(header, txs, nil, receipts) : 封装并返回最终的block API.Propose(addr, auth)添加一个proposal: 调用者对addr的投票, auth表示加入还是踢出
verifyUncles(chain,block)判断block中的叔伯块是否大于零,由于这个共识中不允许有叔伯块
ecercover(header,sigcache)从签名头中提取以太坊帐户地址
CalcDifficulty(chain,time,parent)返回区块的计算难度,计算难度是难度调整算法
Difficulty字段的值: 1-是 本block的签名者 (in turn), 2- 非本block的签名者 (out of turn)
投票策略因为blockchain可能会小范围重组(small reorgs), 常规的投票机制(cast-and-forget, 投票和忘记)可能不是最佳的,因为包含单个投票的block可能不会在最终的链上,会因为已有最新的block而被抛弃。
一个简单但有效的办法是对signers配置"提议(proposal)".例如 "add 0x...", "drop 0x...", 有多个并发的提议时, 签名代码"随机"选择一个提议注入到该签名者签名的block中,这样多个并发的提议和重组(reorgs)都可以保存在链上.
该列表可能在一定数量的block/epoch 之后过期,提案通过并不意味着它不会被重新调用,因此在提议通过时不应立即丢弃。
加入和踢除新的signer的投票都是立即生效的,参与下一次投票计数加入和踢除都需要 超过当前signer总数的50% 的signer进行投票可以踢除自己(也需要超过50%投票)可以并行投票(A,B交叉对C,D进行投票), 只要最终投票数 *** 作50%再没进入新的epoch对于以一个signer的投票未被通过时,后面有其他signer对该signer的投票也算入判断中进入一个新的epoch, 所有之前的pending投票都作废, 重新开始统计投票不允许反复连续投 (由于有最近签名者的限制,在SIGNER_COUNT / 2 + 1个块内,签名者只能签署一个块) 投票场景举例 ABCD, AB先分别踢除CD, C踢除D, 结果是剩下ABCABCD, AB先分别踢除CD, C踢除D, B又投给C留下的票, 结果是剩下ABCABCD, AB先分别踢除CD, C踢除D, 即使C投给自己留下的票, 结果是剩下ABABCDE, ABC先分别加入F(成功,ABCDEF), BCDE踢除F(成功,ABCDE), DE加入F(失败,ABCDE), BCD踢除A(成功, BCDE), B加入F(由于DE加入F还存在,此时BDE加入F,满足超过50%投票), 结果是剩下BCDEF 4. PoA中的攻击及防御 恶意签名者(Malicious signer). 恶意用户被添加到签名者列表中,或签名者密钥/机器遭到入侵. 解决方案是,N个授权签名人的列表,任一签名者只能对每K个block签名其中的1个。这样尽量减少损害,其余的矿工可以投票踢出恶意用户。审查签名者(Censoring signer). 如果一个签名者(或一组签名者)试图检查block中其他signer的提议(特别是投票踢出他们), 为了解决这个问题,我们将签名者的允许的挖矿频率限制在1/(N/2)。如果他不想被踢出出去, 就必须控制超过50%的signers."垃圾邮件"签名者(Spamming signer). 这些signer在每个他们签名的block中都注入一个新的投票提议.由于节点需要统计所有投票以创建授权签名者列表, 久而久之之后会产生大量垃圾的无用的投票, 导致系统运行变慢.通过epoch的机制,每次进入新的epoch都会丢弃旧的投票并发块(Concurrent blocks). 如果授权签名者的数量为N,我们允许每个签名者签名是1/K,那么在任何时候,至少N-K个签名者都可以成功签名一个block。为了避免这些block竞争( 分叉 ),每个签名者生成一个新block时都会加一点随机延时。这确保了很难发生分叉。欢迎分享,转载请注明来源:内存溢出
评论列表(0条)