分叉问责
问题陈述
Tendermint 共识算法在所有高度上保证以下规范:- 一致性 — 任意两个正确的全节点不会做出不同的决定。
- 有效性 — 被决定的区块满足预定义谓词 valid()。
- 终止性 — 所有正确的全节点最终都会做出决定。
故障验证者的不当行为
分叉是故障验证者偏离协议的结果。原则上,即使分叉并未真正发生,这种偏离中的若干种也可以被检测出来:- 双重提议:某个故障提议者在 Tendermint 共识中,对同一高度和同一轮提出两个不同的值(区块)。
-
双重签名:Tendermint 共识要求正确验证者在每一轮中至多只对一个值发送
prevote和precommit。如果某个故障验证者针对同一高度/轮次的不同值发送多个prevote和/或precommit消息,这就是不当行为。 -
狂乱验证者:Tendermint 共识要求正确验证者只对满足 valid(v) 的值 v 发送
prevote和precommit。如果故障验证者在 valid(v)=false 的情况下仍然对 v 发送prevote和precommit,这就是不当行为。
prevote 和 precommit 也可以被用于伪造区块。
-
失忆:Tendermint 共识具有锁定机制。如果某个验证者锁定了某个值 v,那么它之后只能对 v 或 nil 发送
prevote/precommit。如果在仍然持有值 v 的锁时,对另一个不同的值 v’(且不是 nil)发送prevote/precommit消息,这就是不当行为。 -
虚假消息:在 Tendermint 共识中,大多数消息发送指令都受到阈值条件的保护,例如,必须先接收到 2f + 1 条
prevote消息,才能发送precommit。故障验证者可能在尚未收到这些prevote消息的情况下就发送precommit。
两种类型的分叉
- Fork-Full。两个正确的验证者在同一高度上对不同的区块做出决定。由于还需要对下一个验证者集合做出决定,正确验证者可能会被分隔到分叉链的两个不同分支中参与。
- Fork-Light。所有正确的验证者都对高度 h 的同一个区块做出决定,但故障进程(无论是否为验证者)会伪造该高度上的另一个区块,以欺骗用户(使用轻客户端的用户)。
攻击场景
链上攻击
矛盾签名(单轮)
存在多种可能导致分叉的场景。第一种是在同一轮中发生双重签名。- F1. 矛盾签名:故障验证者在给定高度 h 的同一轮 r 中,对不同的值签署多条投票消息(
prevote和/或precommit)。
反复切换
Tendermint 共识实现了锁定机制:如果某个正确验证者 p 收到值 v 的提议,并在第 r 轮收到了针对值 id(v) 的 2f + 1 条prevote,它就会锁定 v 并记住 r。在这种情况下,p 还会发送一条针对 id(v) 的 precommit 消息,这条消息之后可以作为 p 锁定了 v 的证明。
在后续轮次中,p 只会对它之前已经锁定过的值发送 prevote 消息。然而,如果在未来某一轮 r’ > r 中,该进程收到新的提议以及针对另一个不同值 v’ 的 2f + 1 条 prevote,那么锁定值就有可能被改变。在这种情况下,p 可以针对 id(v’) 发送 prevote/precommit。这一算法特性可以被以两种方式利用:
-
F2. 故障式反复切换(失忆):故障验证者在第 r 轮对某个值 id(v) 发送
precommit(即值 v 在第 r 轮被锁定),随后又在更高轮 r’ > r 中,在未先正确解锁值 v 的情况下,对另一个不同的值 id(v’) 发送prevote。在这种情况下,故障进程“忘记了”自己已经锁定值 v,并在后续轮次中对其他值发送prevote。 某些正确验证者可能已经在 r 轮对 v 做出决定,而另一些正确验证者则在 r’ 轮对 v’ 做出决定。此时主链上可能出现分支(Fork-Full)。 -
F3. 正确式反复切换(回到过去):存在一些由(正确)验证者签署的、针对值 id(v) 且属于第 r 轮的
precommit消息。尽管如此,v 并未被决定,所有进程都进入下一轮。随后,正确验证者在某个更高轮 r’ > r 中(正确地)锁定并决定了另一个不同的值 v’。之后正确验证者继续向前推进;主链上不会出现分支。 然而,故障验证者可以利用第 r 轮中正确的precommit消息,再配合事后生成的、针对第 r 轮的故障precommit消息,伪造一个针对某个并未在主链上被决定的值的区块(Fork-Light)。
链下攻击
F1-F3 可能污染全节点(甚至验证者)的状态。因此,被污染的(但在其他方面仍然正确的)全节点可能会向轻客户端传播错误区块。 同样地,即使完全不干扰主链,也可能出现以下情况:-
F4. 幽灵验证者:故障验证者在某些高度上投票(签署
prevote和precommit消息),而在这些高度上它们并不属于(主链上的)验证者集合。 - F5. 狂乱验证者:故障验证者签署投票消息,以支持某个(任意的)应用状态,而该状态不同于由有效状态转换产生的应用状态。
受害者类型
我们考虑三类潜在攻击受害者:- FN:全节点
- LCS:按顺序验证头部的轻客户端
- LCB:基于二分法验证头部的轻客户端
| 攻击 | FN | LCS | LCB |
|---|---|---|---|
| F1 | 直接 | FN | FN |
| F2 | 直接 | FN | FN |
| F3 | 直接 | FN | FN |
| F4 | 直接 | ||
| F5 | 直接 |
详细攻击场景
基于双签的攻击
在基于双签的攻击中,故障验证者会在某个高度的同一轮中对多个投票(prevote 和/或 precommit)进行签名。该攻击既可以针对全节点,也可以针对轻客户端执行。执行该攻击需要至少 1/3 的投票权。场景 1:主链上的双签
验证者:- CA - 一组正确验证者,投票权少于 1/3
- CB - 一组正确验证者,投票权少于 1/3
- CA 和 CB 互不相交
- F - 一组故障验证者,拥有 1/3 或更多投票权
- 一个故障提议者向 CA 提议区块 A
- 一个故障提议者向 CB 提议区块 B
- 集合 CA 和 CB 中的验证者分别对 A 和 B 进行 prevote。
- 集合 F 中的故障验证者同时对 A 和 B 进行 prevote。
- 这些故障 prevote 消息
- 针对 A 的消息,比 B 的消息更早到达 CA
- 针对 B 的消息,比 A 的消息更早到达 CB
- 因此,集合 CA 和 CB 中的正确验证者将分别观察到 超过 2/3 的针对 A 和 B 的 prevote,并分别对 A 和 B 进行 precommit。
- 集合 F 中的故障验证者同时对值 A 和 B 进行 precommit。
- 因此,A 和 B 都会得到超过 2/3 的 commit。
- 在这种情况下,创建不当行为证据很简单,因为同一个故障进程会在同一轮中对不同值签署多条消息。
- 我们必须确保这些不同的消息能够到达某个正确进程(全节点、监控器?),由其提交证据。
- 这是针对全节点层面的攻击(Fork-Full)。
- 它也会扩展到轻客户端,
- 对这两者都需要检测和恢复机制。
场景 2:针对轻客户端的双签(LCS)
验证者:- 一组故障验证者 F,拥有超过 2/3 的投票权。
- 在主链上,F 表现正常
- F 协同签署一个与主链上不同的区块 B。
- 轻客户端获得 B,并信任它,因为它带有超过 2/3 投票权的签名。
反复横跳:基于失忆的攻击
在失忆攻击中,故障验证者会在某一轮 r 中锁定某个值 v,然后在更高轮次中,在没有正确解锁值 v 的情况下为另一个值 v’ 投票。该攻击既可用于全节点,也可用于轻客户端。场景 3:故障至多为 2/3
验证者:- 一组故障验证者 F,拥有至少 1/3 但至多 2/3 的投票权
- 一组正确验证者 C
- 故障验证者通过收集超过 2/3 的 投票权(其中包含正确和故障验证者),在第 r 轮对区块 A 达成 commit(但不在主链上公开)。
- 所有验证者(正确和故障)都进入某个 r’ > r 的轮次。
- C 中某些正确验证者在第 r’ 轮之前没有锁定任何值。
- F 中的故障验证者偏离 Tendermint 共识,忽略它们曾在 r 中锁定 A 的事实,并在 r’ 中提议另一个区块 B。
- 由于 C 中那些未锁定任何值的验证者认为 B 可以接受,它们接受 B 的提议并对区块 B 达成 commit。
场景 4:故障超过 2/3
如果攻击者拥有超过 2/3 的投票权,就可以任意更改应用状态。 验证者:- 一组故障验证者 F1,拥有 1/3 或更多投票权
- 一组故障验证者 F2,拥有少于 1/3 的投票权
- 与场景 3 类似(但不需要正确验证者的消息)
- F1 中的故障验证者在第 r 轮锁定值 A
- 它们在后续轮次中为不同的值签名
- F2 在第 r 轮不锁定 A
- F1 中的验证者可以通过分叉问责机制被检测出来。
- F2 中的验证者无法通过该机制被检测出来。 只有在它们签署了与应用冲突的内容时,才能据此追究它们。否则,它们并没有做任何错误的事。
回到过去
在这类攻击中,故障验证者利用了自己在过去某些轮次中没有签署消息这一事实。由于 Tendermint 运行在异步网络中,我们很难区分这种攻击和延迟消息。这类攻击既可用于全节点,也可用于轻客户端。场景 5
验证者:- C1 - 一组正确验证者,拥有超过 1/3 的投票权
- C2 - 一组正确验证者,拥有 1/3 的投票权
- C1 和 C2 互不相交
- F - 一组故障验证者,拥有少于 1/3 的投票权
- 另一个额外的故障进程 q
- F 和 q 违反了 Cosmos 的故障模型。
- 在高度 h 的某一轮 r 中,C1 对值 A 进行 precommit,
- C2 对 nil 进行 precommit,
- F 不发送任何消息
- q 对 nil 进行 precommit。
- 在某个 r’ > r 的轮次中,F、q 和 C2 对另一个不同于 A 的值 B 达成 commit。
- F 和 fp “回到过去”,并在第 r 轮为值 A 签署 precommit 消息。
- 再加上 C1 的 precommit 消息,这已经足以对值 A 达成 commit。
- 只有一个之前对 nil 做过 precommit 的故障验证者实施了双签,而另外那 1/3 的故障验证者实际上执行了一种攻击,其消息序列与失忆攻击中的一部分完全相同。检测这类攻击最终归结为针对双签和失忆的机制。
幻影验证者
在幻影验证者攻击中,那些不属于当前验证者集合、但仍处于绑定状态的进程(因为攻击发生在其 unbonding period 内)可以通过签署投票消息参与攻击。该攻击既可以针对全节点,也可以针对轻客户端执行。场景 6
验证者:- F — 一组故障验证者,在高度 h + k 的主链上不属于验证者集合
-
存在一个分叉,并且高度 h + k 有两个不同的 header,它们对应不同的验证者集合:
- 主链上的 VS2
- 由 F(以及其他方)签署的伪造 header VS2’
- 轻客户端信任高度 h 的某个 header(以及相应的验证者集合 VS1)。
- 作为二分 header 验证的一部分,它会使用新的验证者集合 VS2’ 来验证高度 h + k 的 header。
- 为了检测这一点,节点需要同时看到伪造的 header 和链上的规范 header。
- 如果满足这一点,那么检测这类攻击很容易,因为它只需要验证某些进程是否在自己并不属于验证者集合的高度上签署了消息。
Lunatic 验证者
Lunatic 验证者会同意为任意应用状态签署 commit 消息。它被用于攻击轻客户端。 注意,检测这种行为需要应用层知识。检测这种行为很可能可以通过 参考发生该高度之前的那个区块来完成。 问题: 我们是否可以说,在这种情况下,验证者在投票之前拒绝检查提议值是否有效?Fork accountability
Problem Statement
Tendermint consensus algorithm guarantees the following specifications for all heights:- agreement — no two correct full nodes decide differently.
- validity — the decided block satisfies the predefined predicate valid().
- termination — all correct full nodes eventually decide,
The Misbehavior of Faulty Validators
Forks are the result of faulty validators deviating from the protocol. In principle several such deviations can be detected without a fork actually occurring:- double proposal: A faulty proposer proposes two different values (blocks) for the same height and the same round in Tendermint consensus.
- double signing: Tendermint consensus forces correct validators to prevote and precommit for at most one value per round. In case a faulty validator sends multiple prevote and/or precommit messages for different values for the same height/round, this is a misbehavior.
- lunatic validator: Tendermint consensus forces correct validators to prevote and precommit only for values v that satisfy valid(v). If faulty validators prevote and precommit for v although valid(v)=false this is misbehavior.
- amnesia: Tendermint consensus has a locking mechanism. If a validator has some value v locked, then it can only prevote/precommit for v or nil. Sending prevote/precomit message for a different value v’ (that is not nil) while holding lock on value v is misbehavior.
- spurious messages: In Tendermint consensus most of the message send instructions are guarded by threshold guards, e.g., one needs to receive 2f + 1 prevote messages to send precommit. Faulty validators may send precommit without having received the prevote messages.
Two types of forks
- Fork-Full. Two correct validators decide on different blocks for the same height. Since also the next validator sets are decided upon, the correct validators may be partitioned to participate in two distinct branches of the forked chain.
- Fork-Light. All correct validators decide on the same block for height h, but faulty processes (validators or not), forge a different block for that height, in order to fool users (who use the light client).
Attack scenarios
On-chain attacks
Equivocation (one round)
There are several scenarios in which forks might happen. The first is double signing within a round.- F1. Equivocation: faulty validators sign multiple vote messages (prevote and/or precommit) for different values during the same round r at a given height h.
Flip-flopping
Tendermint consensus implements a locking mechanism: If a correct validator p receives proposal for value v and 2f + 1 prevotes for a value id(v) in round r, it locks v and remembers r. In this case, p also sends a precommit message for id(v), which later may serve as proof that p locked v. In subsequent rounds, p only sends prevote messages for a value it had previously locked. However, it is possible to change the locked value if in a future round r’ > r, if the process receives proposal and 2f + 1 prevotes for a different value v’. In this case, p could send a prevote/precommit for id(v’). This algorithmic feature can be exploited in two ways:- F2. Faulty Flip-flopping (Amnesia): faulty validators precommit some value id(v) in round r (value v is locked in round r) and then prevote for different value id(v’) in higher round r’ > r without previously correctly unlocking value v. In this case faulty processes “forget” that they have locked value v and prevote some other value in the following rounds. Some correct validators might have decided on v in r, and other correct validators decide on v’ in r’. Here we can have branching on the main chain (Fork-Full).
- F3. Correct Flip-flopping (Back to the past): There are some precommit messages signed by (correct) validators for value id(v) in round r. Still, v is not decided upon, and all processes move on to the next round. Then correct validators (correctly) lock and decide a different value v’ in some round r’ > r. And the correct validators continue; there is no branching on the main chain. However, faulty validators may use the correct precommit messages from round r together with a posteriori generated faulty precommit messages for round r to forge a block for a value that was not decided on the main chain (Fork-Light).
Off-chain attacks
F1-F3 may contaminate the state of full nodes (and even validators). Contaminated (but otherwise correct) full nodes may thus communicate faulty blocks to light clients. Similarly, without actually interfering with the main chain, we can have the following:- F4. Phantom validators: faulty validators vote (sign prevote and precommit messages) in heights in which they are not part of the validator sets (at the main chain).
- F5. Lunatic validator: faulty validator that sign vote messages to support (arbitrary) application state that is different from the application state that resulted from valid state transitions.
Types of victims
We consider three types of potential attack victims:- FN: full node
- LCS: light client with sequential header verification
- LCB: light client with bisection based header verification
| Attack | FN | LCS | LCB |
|---|---|---|---|
| F1 | direct | FN | FN |
| F2 | direct | FN | FN |
| F3 | direct | FN | FN |
| F4 | direct | ||
| F5 | direct |
Detailed Attack Scenarios
Equivocation based attacks
In case of equivocation based attacks, faulty validators sign multiple votes (prevote and/or precommit) in the same round of some height. This attack can be executed on both full nodes and light clients. It requires 1/3 or more of voting power to be executed.Scenario 1: Equivocation on the main chain
Validators:- CA - a set of correct validators with less than 1/3 of the voting power
- CB - a set of correct validators with less than 1/3 of the voting power
- CA and CB are disjoint
- F - a set of faulty validators with 1/3 or more voting power
- A faulty proposer proposes block A to CA
- A faulty proposer proposes block B to CB
- Validators from the set CA and CB prevote for A and B, respectively.
- Faulty validators from the set F prevote both for A and B.
- The faulty prevote messages
- for A arrive at CA long before the B messages
- for B arrive at CB long before the A messages
- Therefore correct validators from set CA and CB will observe more than 2/3 of prevotes for A and B and precommit for A and B, respectively.
- Faulty validators from the set F precommit both values A and B.
- Thus, we have more than 2/3 commits for both A and B.
- Creating evidence of misbehavior is simple in this case as we have multiple messages signed by the same faulty processes for different values in the same round.
- We have to ensure that these different messages reach a correct process (full node, monitor?), which can submit evidence.
- This is an attack on the full node level (Fork-Full).
- It extends also to the light clients,
- For both we need a detection and recovery mechanism.
Scenario 2: Equivocation to a light client (LCS)
Validators:- a set F of faulty validators with more than 2/3 of the voting power.
- for the main chain F behaves nicely
- F coordinates to sign a block B that is different from the one on the main chain.
- the light clients obtains B and trusts at as it is signed by more than 2/3 of the voting power.
Flip-flopping: Amnesia based attacks
In case of amnesia, faulty validators lock some value v in some round r, and then vote for different value v’ in higher rounds without correctly unlocking value v. This attack can be used both on full nodes and light clients.Scenario 3: At most 2/3 of faults
Validators:- a set F of faulty validators with 1/3 or more but at most 2/3 of the voting power
- a set C of correct validators
- Faulty validators commit (without exposing it on the main chain) a block A in round r by collecting more than 2/3 of the voting power (containing correct and faulty validators).
- All validators (correct and faulty) reach a round r’ > r.
- Some correct validators in C do not lock any value before round r’.
- The faulty validators in F deviate from Tendermint consensus by ignoring that they locked A in r, and propose a different block B in r’.
- As the validators in C that have not locked any value find B acceptable, they accept the proposal for B and commit a block B.
Scenario 4: More than 2/3 of faults
In case there is an attack with more than 2/3 of the voting power, an attacker can arbitrarily change application state. Validators:- a set F1 of faulty validators with 1/3 or more of the voting power
- a set F2 of faulty validators with less than 1/3 of the voting power
- Similar to Scenario 3 (however, messages by correct validators are not needed)
- The faulty validators in F1 lock value A in round r
- They sign a different value in follow-up rounds
- F2 does not lock A in round r
- The validators in F1 will be detectable by the fork accountability mechanisms.
- The validators in F2 cannot be detected using this mechanism. Only in case they signed something which conflicts with the application this can be used against them. Otherwise, they do not do anything incorrect.
Back to the past
In this kind of attack, faulty validators take advantage of the fact that they did not sign messages in some of the past rounds. Due to the asynchronous network in which Tendermint operates, we cannot easily differentiate between such an attack and delayed message. This kind of attack can be used at both full nodes and light clients.Scenario 5
Validators:- C1 - a set of correct validators with over 1/3 of the voting power
- C2 - a set of correct validators with 1/3 of the voting power
- C1 and C2 are disjoint
- F - a set of faulty validators with less than 1/3 voting power
- one additional faulty process q
- F and q violate the Cosmos failure model.
- in a round r of height h we have C1 precommitting a value A,
- C2 precommits nil,
- F does not send any message
- q precommits nil.
- In some round r’ > r, F and q and C2 commit some other value B different from A.
- F and fp “go back to the past” and sign precommit message for value A in round r.
- Together with precomit messages of C1 this is sufficient for a commit for value A.
- Only a single faulty validator that previously precommited nil did equivocation, while the other 1/3 of faulty validators actually executed an attack that has exactly the same sequence of messages as part of amnesia attack. Detecting this kind of attack boil down to mechanisms for equivocation and amnesia.
Phantom validators
In case of phantom validators, processes that are not part of the current validator set but are still bonded (as attack happen during their unbonding period) can be part of the attack by signing vote messages. This attack can be executed against both full nodes and light clients.Scenario 6
Validators:- F — a set of faulty validators that are not part of the validator set on the main chain at height h + k
-
There is a fork, and there exist two different headers for height h + k, with different validator sets:
- VS2 on the main chain
- forged header VS2’, signed by F (and others)
- a light client has a trust in a header for height h (and the corresponding validator set VS1).
- As part of bisection header verification, it verifies the header at height h + k with new validator set VS2’.
- To detect this, a node needs to see both, the forged header and the canonical header from the chain.
- If this is the case, detecting these kind of attacks is easy as it just requires verifying if processes are signing messages in heights in which they are not part of the validator set.