以太坊以太坊区块链进阶之路

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

2018-01-16  本文已影响767人  shi_qinfeng

PoA共识引擎算法实现分析

clique中一些概念和定义

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 // 读写锁
    }
    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 
    }

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

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.Prepare(chain , header)

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

  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运算来解"难题".

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

上一篇 下一篇

猜你喜欢

热点阅读