From 22ef3f6e7a3c2aba4358290f806540ef20bcdd9c Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Fri, 14 Aug 2020 19:51:04 +0200 Subject: [PATCH] light: update ADR 47 with light traces (#5250) * update adr 47 with light traces * implement suggestions * add second downside to alternative approach --- ...047-handling-evidence-from-light-client.md | 132 +++++++++--------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/docs/architecture/adr-047-handling-evidence-from-light-client.md b/docs/architecture/adr-047-handling-evidence-from-light-client.md index f5aaa828c..880ad691b 100644 --- a/docs/architecture/adr-047-handling-evidence-from-light-client.md +++ b/docs/architecture/adr-047-handling-evidence-from-light-client.md @@ -5,99 +5,99 @@ * 24-02-2020: Second version * 13-04-2020: Add PotentialAmnesiaEvidence and a few remarks * 31-07-2020: Remove PhantomValidatorEvidence +* 14-08-2020: Introduce light traces ## Context -If the light client is under attack, either directly -> lunatic -attack (light fork) or indirectly -> full fork, it's supposed to halt and -send evidence of misbehavior to a correct full node. Upon receiving an -evidence, the full node should punish malicious validators (if possible). +The bisection method of header verification used by the light client exposes +itself to a potential attack if any block within the light clients trusted period has +a malicious group of validators with power that exceeds the light clients trust level +(default is 1/3). To improve light client (and overall network) security, the light +client has a detector component that compares the verified header provided by the +primary against witness headers. This ADR outlines the decision that ensues when +the light client detector receives two conflicting headers + +## Alternative Approaches + +One of the key arguments surrounding the decision is whether the processing required +to extract verifiable evidence should be on the light client side or the full node side. +As light client, indicated in it's name, is supposed to stay light, the natural +inclination is to avoid any load and pass it directly to the full node who also has +the advantage of having a full state. It remains possible in future discussions to have the +light client form the evidence types itself. The other minor downsides apart from the +load will be that around half the evidence produced by the light client will be invalid and +that, in the event of modified logic, it's easier and expected that the full node be up +to date than the light clients. ## Decision -When a light client sees two conflicting headers (`H1.Hash() != H2.Hash()`, -`H1.Height == H2.Height`), both having 1/3+ of the voting power of the -currently trusted validator set, it will submit a `ConflictingHeadersEvidence` -to all full nodes it's connected to. Evidence needs to be submitted to all full -nodes since there's no way to determine which full node is correct (honest). +When two headers have different hashes, the light client must first verify via +bisection that the signed header provided by the witness is also valid. + +It then collects all the headers that were fetched as part of bisection into two traces. +One containing the primary's headers and the other containing the witness's headers. +The light client sends the trace to the opposite provider (i.e. the primary's trace +to the witness and the witness's trace to the primary) as the light client is +incapable of deciding which one is malicious. + +Assuming one of the two providers is honest, that full node will then take the trace +and extract out the relevant evidence and propogate it until it ends up on chain. + +*NOTE: We do not send evidence to other connected peers* + +*NOTE: The light client halts then and does not verify with any other witness* + +## Detailed Design + +The traces will have the following data structure: ```go -type ConflictingHeadersEvidence struct { - H1 types.SignedHeader - H2 types.SignedHeader +type ConflictingHeadersTrace struct { + Headers []*types.SignedHeader } ``` -_Remark_: Theoretically, only the header, which differs from what a full node -has, needs to be sent. But sending two headers a) makes evidence easily -verifiable b) simplifies the light client, which does not have query each -witness as to which header it possesses. - -When a full node receives the `ConflictingHeadersEvidence` evidence, it should +When a full node receives a `ConflictingHeadersTrace`, it should a) validate it b) figure out if malicious behaviour is obvious (immediately -slashable) or the fork accountability protocol needs to be started. +slashable) or run the amnesia protocol. ### Validating headers -Check both headers are valid (`ValidateBasic`), have the same height, and -signed by 1/3+ of the validator set that the full node had at height -`H1.Height`. +Check headers are valid (`ValidateBasic`), are in order of ascending height, and do not exceed the `MaxTraceSize`. -- Q: What if light client validator set is not equal to full node's validator - set (i.e. from full node's point of view both headers are not properly signed; - this includes the case where none of the two headers were committed on the - main chain) +### Finding Block Bifurcation - Reject the evidence. It means light client is following a fork, but, hey, at - least it will halt. +The node pulls the block ID's for the respective heights of the trace headers from its own block store. -- Q: Don't we want to punish validators who signed something else even if they - have less or equal than 1/3? +First it checks to see that the first header hash matches its first `BlockID` else it can discard it. - No consensus so far. Ethan said no, Zarko said yes. - https://github.com/tendermint/spec/pull/71#discussion_r374210533 +If the last header hash matches the nodes last `BlockID` then it can also discard it on the assumption that a fork can not remerge and hence this is just a trace of valid headers. -### Figuring out if malicious behaviour is immediately slashable +The node then continues to loop in descending order checking that the headers hash doesn't match it's own blockID for that height. Once it reaches the height that the block ID matches the hash it then sends the common header, the trusted header and the diverged header (common header is needed for lunatic evidence) to determine if the divergence is a real offense to the tendermint protocol or if it is just fabricated. -Let's say H1 was committed from this full node's perspective (see Appendix A). -_If neither of the headers (H1 and H2) were committed from the full node's -perspective, the evidence must be rejected._ +### Figuring out if malicious behaviour -Intersect validator sets of H1 and H2. +The node first examines the case of a lunatic attack: -* if there are signers(H2) that are not part of validators(H1), they misbehaved as -they are signing protocol messages in heights they are not validators => -immediately slashable (#F4). +* The validator set of the common header must have at least 1/3 validator power that signed in the divergedHeaders commit -* if `H1.Round == H2.Round`, and some signers signed different precommit -messages in both commits, then it is an equivocation misbehavior => immediately -slashable (#F1). +* One of the deterministically derived hashes (`ValidatorsHash`, `NextValidatorsHash`, `ConsensusHash`, +`AppHash`, or `LastResultsHash`) of the header must not match: -* if `H1.Round != H2.Round` we need to run full detection procedure => not -immediately slashable. +* We then take every validator that voted for the invalid header and was a validator in the common headers validator set and create `LunaticValidatorEvidence` -* if `ValidatorsHash`, `NextValidatorsHash`, `ConsensusHash`, -`AppHash`, and `LastResultsHash` in H2 are different (incorrect application -state transition), then it is a lunatic misbehavior => immediately slashable (#F5). +If this fails then we examine the case of Equivocation (either duplicate vote or amnesia): -If evidence is not immediately slashable, fork accountability needs to invoked -(ADR does not yet exist). +*This only requires the trustedHeader and the divergedHeader* -It's unclear if we should further break up `ConflictingHeadersEvidence` or -gossip and commit it directly. See -https://github.com/tendermint/tendermint/issues/4182#issuecomment-590339233 +* if `trustedHeader.Round == divergedHeader.Round`, and a validator signed for the block in both headers then DuplicateVoteEvidence can be immediately formed -If we'd go without breaking evidence, all we'll need to do is to strip the -committed header from `ConflictingHeadersEvidence` (H1) and leave only the -uncommitted header (H2): +* if `trustedHeader.Round != divergedHeader.Round` then we form PotentialAmnesiaEvidence as some validators in this set have behaved maliciously and protocol in ADR 56 needs to be run. -```go -type ConflictingHeaderEvidence struct { - H types.SignedHeader -} -``` +*The node does not check that there is a 1/3 overlap between headers as this may not be point of the fork and validator sets may have since changed* -If we'd go with breaking evidence, here are the types we'll need: +If no evidence can be formed from a light trace, it is not a legitimate trace and thus the +connection with the peer should be stopped ### F1. Equivocation @@ -172,7 +172,7 @@ punish those who conducted an amnesia attack. See ADR-056 for the architecture of the handling amnesia attacks. -NOTE: Conflicting headers evidence used to also create PhantomValidatorEvidence +NOTE: Conflicting headers trace used to also create PhantomValidatorEvidence but this has since been removed. Refer to Appendix B. ## Status @@ -183,14 +183,15 @@ Proposed. ### Positive +* Light client has increased secuirty against Lunatic attacks. * Tendermint will be able to detect & punish new types of misbehavior * light clients connected to multiple full nodes can help full nodes notice a fork faster ### Negative -* Accepting `ConflictingHeadersEvidence` from light clients opens up a DDOS -attack vector (same is fair for any RPC endpoint open to public; remember that +* Accepting `ConflictingHeadersEvidence` from light clients opens up a large DDOS +attack vector(same is fair for any RPC endpoint open to public; remember that RPC is not open by default). ### Neutral @@ -198,6 +199,7 @@ RPC is not open by default). ## References * [Fork accountability spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client/accountability.md) +* [ADR 056: Proving amnesia attakcs](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-056-proving-amnesia-attacks.md) ## Appendix A