-
-
owned this note
-
Published
Linked with GitHub
# FFG+GHOST filtering "viable" branches
Yan recently pointed out a potential issue with FFG+GHOST as previously specified. The issues lies in the difference between (a) a branch of the tree that has the latest justified block in it __vs__ (b) a branch of the tree that _sees_ the latest justified block as justified.
If validators are to use the state at the head of the fork choice to cast their votes, they must only consider branches that have the latest justification/finalization info correctly in the state. If they consider _all_ branches that include the latest justified block, some such branches might induce voting on out-of-date/incorrect information.
WIP implementation of fix in spec repo: https://github.com/ethereum/eth2.0-specs/pull/1495
## Scenario
The following is an attack scenario that can break liveness, but there are less explicitly nefarious scenarios that also lead to poor outcomes.
_Setup_
* There is some ancestor block A
* Then B, child of A, is created
* Then C1, child of B, gets created, and B gets justified via the inclusion of votes A->B (2/3 of validators) in C1
* A few validators attest B -> C1, but C1 does not get justified
_Attack_
* B<-C2<-D2 get created as a separate chain, but does NOT include enough votes to justify B
* \>1/6 of validators defect to this new chain, voting on A->C2 along with 1/3 of honest validators that didn't vote on A->B
* Honest validators from the other chain now see D2 as head. If they use the state of D2 as the info to cast vote, it would suggest A->D2 which surrounds B->C1. Such a vote is slashable and thus this type of scenario can break plausible liveness.
## Fix
When running the fork choice, filter out branches that do not contain correct latest justified in head state. So, with the new fork choice in the above scenario, the honest validators see C1 as head and cannot be convinced by minority attacker to switch.
## Argument for why fix doesn't break other things
The fix only affects the fork choice, and the fix can only change the fork choice in unexpected cases, and only when the client discovers a new block that contains evidence justifying a new block. This general class of situation already exists. For example, a weird situation that this fix would enable is if A is 65% justified and a chain built off of A through B1 gets built, but then an attacker publishes another 2%, completing the justification, along with a new block B2. However, a parallel situation now exists, where if B1 gets to 65% justification but then a chain built on B2 starts winning, and suddenly the attacker publishes the last remaining 2% fully justifying B1.
## Review by Ryuya
TL;DR: The fix seems reasonable.
Rationale:
This issue is because Casper FFG adopts the locking paradigm of BFT in chain-based consensus, i.e., unlike Tendermint or Hotstuff, blocks which could not collect the 2f + 1 votes for the previous checkpoint (C2 and D2 in the document) continue to exist in the chain, rather than being discarded. ("Chain-based BFT" is a much more ambitious than the "chained/pipelined BFT" in academic papers.)
From this point of view, the proposed fix seems a straightforward way to re-define the locking/unlocking rule into chain-based BFT.
Unlocking rules exist to force validators to vote on the highest justified checkpoint to synchronize them, so in FFG, the unlocking rule (= definition of the start point of the fork-choice) should not count a chain (C2's chain) which does not allow validators vote on the highest justified checkpoint.
I think this fix is compatible with the fix on the bouncing attack, which does not make use of the inconsistency of locking/unlocking.