以太坊PoA共识引擎算法介绍(2)

PoA共识引擎算法实现分析

clique中一些概念和定义

  • EPOCH_LENGTH : epoch长度是30000个block, 每次进入新的epoch,前面的投票都被清空,重新开始记录,这里的投票是指加入或移除signer
  • BLOCK_PERIOD : 出块时间, 默认是15s
  • UNCLE_HASH : 总是 Keccak256(RLP([])) ,因为没有uncle
  • SIGNER_COUNT : 每个block都有一个signers的数量
  • SIGNER_LIMIT : 等于 (SIGNER_COUNT / 2) + 1 . 每个singer只能签名连续SIGNER_LIMIT个block中的1个
    • 比如有5个signer:ABCDE, 对4个block进行签名, 不允许签名者为ABAC, 因为A在连续3个block中签名了2次
  • NONCE_AUTH : 表示投票类型是加入新的signer; 值= 0xffffffffffffffff
  • NONCE_DROP : 表示投票类型是踢除旧的的signer; 值= 0x0000000000000000
  • EXTRA_VANITY : 代表block头中Extra字段中的保留字段长度: 32字节
  • EXTRA_SEAL : 代表block头中Extra字段中的存储签名数据的长度: 65字节
  • IN-TURN/OUT-OF-TURN : 每个block都有一个in-turn的signer, 其他signers是out-of-turn, in-turn的signer的权重大一些, 出块的时间会快一点, 这样可以保证该高度的block被in-turn的signer挖到的概率很大.

clique中最重要的两个数据结构:

  • 共识引擎的结构:
    type Clique struct {
        config *params.CliqueConfig // 系统配置参数
        db ethdb.Database // 数据库: 用于存取检查点快照
        recents *lru.ARCCache //保存最近block的快照, 加速reorgs
        signatures *lru.ARCCache //保存最近block的签名, 加速挖矿
        proposals map[common.Address]bool //当前signer提出的proposals列表
        signer common.Address // signer地址
        signFn SignerFn // 签名函数
        lock sync.RWMutex // 读写锁
    }
  • snapshot的结构:
    type Snapshot struct {
        config *params.CliqueConfig // 系统配置参数
        sigcache *lru.ARCCache // 保存最近block的签名缓存,加速ecrecover
        Number uint64 // 创建快照时的block号
        Hash common.Hash // 创建快照时的block hash
        Signers map[common.Address]struct{} // 此刻的授权的signers
        Recents map[uint64]common.Address // 最近的一组signers, key=blockNumber
        Votes []*Vote // 按时间顺序排列的投票列表
        Tally map[common.Address]Tally // 当前的投票计数,以避免重新计算
    }

除了这两个结构, 对block头的部分字段进行了复用定义, ethereum的block头定义:

    type Header struct {
        ParentHash common.Hash 
        UncleHash common.Hash 
        Coinbase common.Address 
        Root common.Hash 
        TxHash common.Hash 
        ReceiptHash common.Hash 
        Bloom Bloom 
        Difficulty *big.Int 
        Number *big.Int 
        GasLimit *big.Int 
        GasUsed *big.Int 
        Time *big.Int 
        Extra []byte 
        MixDigest common.Hash 
        Nonce BlockNonce 
    }
  • 创世块中的Extra字段包括:
    • 32字节的前缀(extraVanity)
    • 所有signer的地址
    • 65字节的后缀(extraSeal): 保存signer的签名
  • 其他block的Extra字段只包括extraVanity和extraSeal
  • Time字段表示产生block的时间间隔是:blockPeriod(15s)
  • Nonce字段表示进行一个投票: 添加( nonceAuthVote: 0xffffffffffffffff )或者移除( nonceDropVote: 0x0000000000000000 )一个signer
  • Coinbase字段存放 被投票 的地址
    • 举个栗子: signerA的一个投票:加入signerB, 那么Coinbase存放B的地址
  • Difficulty字段的值: 1-是 本block的签名者 (in turn), 2- 非本block的签名者 (out of turn)

下面对比较重要的函数详细分析实现流程

Snapshot.apply(headers)

创建一个新的授权signers的快照, 将从上一个snapshot开始的区块头中的proposals更新到最新的snapshot上

  1. 对入参headers进行完整性检查: 因为可能传入多个区块头, block号必须连续
  2. 遍历所有的header, 如果block号刚好处于epoch的起始(number%Epoch == 0),将snapshot中的Votes和Tally复位( 丢弃历史全部数据 )
  3. 对于每一个header,从签名中恢复得到 signer
  4. 如果该signer在snap.Recents中, 说明 最近已经有过签名 , 不允许再次签名, 直接 返回 结束
  5. 记录 该signer是该block的签名者: snap.Recents[number] = signer
  6. 统计header.Coinbase的投票数,如果 超过signers总数的50%
  7. 执行加入或移除操作
  8. 删除snap.Recents中的一个signer记录: key=number- (uint64(len(snap.Signers)/2 + 1)), 表示释放该signer,下次可以对block进行签名了
  9. 清空被移除的Coinbase的投票
  10. 移除snap.Votes中该Conibase的所有投票记录
  11. 移除snap.Tally中该Conibase的所有投票数记录

共识引擎clique的初始化

Ethereum.StartMining 中,如果Ethereum.engine配置为clique.Clique, 根据当前节点的矿工地址(默认是acounts[0]), 配置clique的 签名者 : clique.Authorize(eb, wallet.SignHash) ,其中 签名函数 是SignHash,对给定的hash进行签名.

获取给定时间点的一个快照 Clique.snapshot

  • 先查找Clique.recents中是否有缓存, 有的话就返回该snapshot
  • 在查找持久化存储中是否有缓存, 有的话就返回该snapshot
  • 如果是创世块
    1. 从Extra中取出所有的signers
    2. newSnapshot(Clique.config, Clique.signatures, 0, genesis.Hash(), signers)
    • signatures是最近的签名快照
    • signers是所有的初始signers
    1. 把snapshot加入到Clique.recents中, 并持久化到db中
  • 其他普通块
    • 沿着父块hash一直往回找是否有snapshot, 如果没找到就记录该区块头
    • 如果找到最近的snapshot, 将前面记录的headers 都 applay 到该snapshot上
    • 保存该最新的snapshot到缓存Clique.recents中, 并持久化到db中

Clique.Prepare(chain , header)

Prepare是共识引擎接口之一. 该函数配置header中共识相关的参数(Cionbase, Difficulty, Extra, MixDigest, Time)

  • 对于非epoch的block( number % Epoch != 0 ):
  1. 得到Clique.proposals中的投票数据(例:A加入C, B踢除D)
  2. 根据snapshot的signers分析投票数否有效(例: C原先没有在signers中, 加入投票有效, D原先在signers中,踢除投票有效)
  3. 从被投票的地址列表(C,D)中, 随机选择一个地址 ,作为该header的Coinbase,设置Nonce为加入( 0xffffffffffffffff )或者踢除( 0x0000000000000000 )
  4. Clique.signer 如果是本轮的签名者(in-turn), 设置header.Difficulty = diffInTurn(1), 否则就是diffNoTurn(2)
  5. 配置header.Extra的数据为[ extraVanity + snap中的全部signers + extraSeal ]
  6. MixDigest需要配置为nil
  7. 配置时间戳:Time为父块的时间+15s

重点: Clique.Seal(chain, block , stop)

Seal也是共识引擎接口之一. 该函数用clique.signer对block的进行签名. 在pow]算法中, 该函数进行hash运算来解"难题".

  • 如果signer没有在snapshot的signers中,不允许对block进行签名
  • 如果不是本block的签名者,延时一定的时间(随机)后再签名, 如果是本block的签名者, 立即签名.
  • 签名结果放在Extra的extraSeal的65字节中

Clique.VerifySeal(chain, header)

VerifySeal也是共识引擎接口之一.

  1. 从header的签名中恢复账户地址,改地址要求在snapshot的signers中
  2. 检查header中的Difficulty是否匹配(in turn或out of turn)

Clique.Finalize

Finalize也是共识引擎接口之一. 该函数生成一个block, 没有叔块处理,也没有奖励机制

  1. header.Root : 状态根保持原状
  2. header.UncleHash : 为nil
  3. types.NewBlock(header, txs, nil, receipts) : 封装并返回最终的block

API.Propose(addr, auth)

添加一个proposal: 调用者对addr的投票, auth表示加入还是踢出

API.Discard(addr)

删除一个proposal

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 1. 以太坊中PoA产生的背景 如果你想用以太坊搭建一个联盟/私有链, 并要求该链交易成本更低甚至没有, 交易延时...
    shi_qinfeng阅读 17,804评论 12 18
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 导语:计算机硬件在飞速发展,数据规模在急速膨胀,但是数据库仍然使用是十年以前的架构体系,WiredTiger 尝试...
    isgiker阅读 3,404评论 0 7
  • 译者序 本文最初是我应以太坊中文社区(Ethfans.org)之邀做的翻译稿,原文取自以太坊社区的 shardin...
    风静縠纹平阅读 6,536评论 0 6
  • 前言: 10月24日开始,集智AI学园陆续推送“人工智能简史”系列文章,讲述人工智能从孕育、诞生到坎坷成长的百年历...
    Jake_张江阅读 2,027评论 0 2