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

1. 以太坊中PoA产生的背景

如果你想用以太坊搭建一个联盟/私有链, 并要求该链交易成本更低甚至没有, 交易延时更低,并发更高, 还拥有完全的控制权(意味着被攻击概率更低). 目前以太坊采用PoW或后续的casper能否满足要求?

  • 首先, pow存在51%攻击问题, 恶意挖矿者超过全网算力的51%后基本上就能完全控制整个网络. 由于链无法被更改, 已上链的数据也无法更改, 但恶意挖矿者也可以做一些DoS攻击阻止合法交易上链,考虑到具有相同创世块的旷工都能加入你的网络, 潜在的安全隐患会长期存在.

  • 其次, PoW大量的电力资源消耗也是需要作为后续成本考虑. PoS可以解决部分Pow问题, 比如节约电力,在一定程度上保护了51%的攻击(恶意旷工会被惩罚), 但从控制权和安全考虑还有欠缺, 因为PoS还是允许任何符合条件的旷工加入。

在已经运行的测试网络Ropsten中, 由于pow设定的难度较低,恶意旷工滥用较低的PoW难度并将gaslimit扩大到90亿(正常是470万),发送大量的交易瘫痪了整个网络。而在此之前,攻击者也尝试了多次非常长的重组(reorgs),导致不同客户端之间的分叉,甚至不同的版本。

这些攻击的根本原因是PoW网络的安全性依赖于背后的算力。而从零开始重新启动一个新的testnet将不会解决任何问题,因为攻击者可以一次又一次地进行相同的攻击。 Parity团队决定采取紧急解决方案,回滚大量的块,并设置不允许gaslimit超过某一阈值的软分叉规则。

虽然Parity的解决方案可能在短期内有效, 但是这不是优雅的:Ethereum本身应该具有动态gaslimit限制; 也不可移植:其他客户端需要自己实现新的软分叉逻辑; 并与同步模式不兼容, 也不支持轻客户端; 尽管并不完美,但是Parity的解决方案仍然可行。 一个更长期的替代解决方案是使用PoA共识,相对简单并容易实现.

2. PoA的特点

  • PoA是依靠预设好的授权节点(signers),负责产生block.
  • 可以由已授权的signer选举(投票超过50%)加入新的signer。
  • 即使存在恶意signer,他最多只能攻击连续块(数量是 (SIGNER_COUNT / 2) + 1) 中的1个,期间可以由其他signer投票踢出该恶意signer。
  • 可指定产生block的时间。

3. PoA需要解决的问题

  1. 如何控制挖矿频率,即出块时间
  2. 如何验证某个块的有效性
  3. 如何动态调整授权签名者(signers)列表,并全网动态同步
  4. 如何在signers之间分配挖矿的负载或者叫做挖矿的机会

对应的解决办法如下:

  1. 协议规定采用固定的block出块时间, 区块头中的时间戳间隔为15s
  2. 先看看block同步的方法,从中来分析PoA中验证block的解决办法

有两种同步blockchain的方法

  1. 经典方法是从创世块开始挨个执行所有交易。 这是经过验证的,但是在Ethereum的复杂网络中,计算量非常大。
  2. 另一个是仅下载区块头并验证其有效性,之后可以从网络下载任意的近期状态对最近的区块头进行检查。

显然第二种方法更好. 由于PoA方案的块可以仅由可信任的签名者来创建, 因此,客户端看到的每个块(或块头)可以与可信任签名者列表进行匹配。 要验证该块的有效性就必须得到该块对应的签名者列表, 如果签名者在该列表中带包该块有效. 这里的挑战是如何维护并及时更改的授权签名者列表? 存储在智能合约中?不可行, 因为在快速轻量级同步期间无法访问状态。

因此, 授权签名者列表必须完全包含在块头中 。那么需要改变块头的结构, 引入新的字段来满足投票机制吗? 这也不可行:改变这样的核心数据结构将是开发者的噩梦。

所以授权签名者名单必须完全符合当前的数据模型, 不能改变区块头中的字段,而是 **复用当前可用的字段: Extra字段. **

Extra 是可变长数组, 对它的修改是 非侵入 操作, 比如RLP,hash操作都支持可变长数据. Extra中包含所有签名者列表和当前节点的签名者对该区块头的签名数据(可以恢复出来签名者的地址).

  1. 更新一个动态的签名者列表的方法是复用区块头中的 Coinbase和Nonce字段 ,以创建投票方案:
  • 常规的块中这两个字段置为0
  • 如果签名者希望对授权签名者列表进行更改,则将:
    • Coinbase 设置为被投票的签名者
    • Nonce 设置为 00xff ... f 投票,代表 添加或移除
    • 任何同步的客户端都可以在块处理过程中“统计”投票,并通过投票结果来维护授权签名者列表。

为了避免一个无限的时间来统计投票,我们设置一个投票窗口, 为一个epoch,长度是30000个block。每个epoch的起始清空所有历史的投票, 并作为签名者列表的检查点. 这允许客户端仅基于检查点哈希进行同步,而不必重播在链路上完成的所有投票。

  1. 目前的方案是在所有signer之间轮询出块, 并通过算法保证同一个signer只能签名 (SIGNER_COUNT / 2) + 1) 个block中第一个.

综上, PoA的工作流程如下:

  1. 在创世块中指定一组初始授权的signers, 所有地址 保存在创世块Extra字段中
  2. 启动挖矿后, 该组signers开始对生成的block进行 签名并广播.
  3. 签名结果 保存在区块头的Extra字段中
  4. Extra中更新当前高度已授权的 所有signers的地址 ,因为有新加入或踢出的signer
  5. 每一高度都有一个signer处于IN-TURN状态, 其他signer处于OUT-OF-TURN状态, IN-TURN的signer签名的block会 立即广播 , OUT-OF-TURN的signer签名的block会 延时 一点随机时间后再广播, 保证IN-TURN的签名block有更高的优先级上链
  6. 如果需要加入一个新的signer, signer通过API接口发起一个proposal, 该proposal通过复用区块头 Coinbase(新signer地址)和Nonce("0xffffffffffffffff") 字段广播给其他节点. 所有已授权的signers对该新的signer进行"加入"投票, 如果赞成票超过signers总数的50%, 表示同意加入
  7. 如果需要踢出一个旧的signer, 所有已授权的signers对该旧的signer进行"踢出"投票, 如果赞成票超过signers总数的50%, 表示同意踢出

signer对区块头进行签名

  1. Extra的长度至少65字节以上(签名结果是65字节,即R, S, V, V是0或1)
  2. 对blockHeader中所有字段除了Extra的 后65字节 外进行 RLP编码
  3. 对编码后的数据进行 Keccak256 hash
  4. 签名后的数据(65字节)保存到Extra的 后65字节

授权策略

以下建议的策略将减少网络流量和分叉

  • 如果签名者被允许签署一个块(在授权列表中,但最近没有签名)。
    • 计算下一个块的最优签名时间(父块时间+ BLOCK_PERIOD)。
    • 如果签名人是in-turn,立即进行签名和广播block。
    • 如果签名者是out-of-turn,延迟 rand(SIGNER_COUNT * 500ms) 后再签名并广播

级联投票

当移除一个授权的签名者时,可能会导致其他移除前的投票成立. 例: ABCD4个signer, AB加入E,此时不成立(没有超过50%), 如果ABC移除D, 会自动导致加入E的投票成立(2/3的投票比例)

投票策略

因为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, 所有之前的pending投票都作废, 重新开始统计投票

投票场景举例

  • ABCD, AB先分别踢除CD, C踢除D, 结果是剩下ABC
  • ABCD, AB先分别踢除CD, C踢除D, B又投给C留下的票, 结果是剩下ABC
  • ABCD, AB先分别踢除CD, C踢除D, 即使C投给自己留下的票, 结果是剩下AB
  • ABCDE, ABC先分别加入F(成功,ABCDEF), BCDE踢除F(成功,ABCDE), DE加入F(失败,ABCDE), BCD踢除A(成功, BCDE), B加入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时都会加一点随机延时。这确保了很难发生分叉。

你可能感兴趣的:(以太坊PoA共识引擎算法介绍(1))