----------------------- MODULE Isolation_001_draft ---------------------------- (** * The specification of the attackers isolation at full node, * when it has received an evidence from the light client. * We check that the isolation spec produces a set of validators * that have more than 1/3 of the voting power. * * It follows the English specification: * * https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/attacks/isolate-attackers_001_draft.md * * The assumptions made in this specification: * * - the voting power of every validator is 1 * (add more validators, if you need more validators) * * - Tendermint security model is violated * (there are Byzantine validators who signed a conflicting block) * * Igor Konnov, Zarko Milosevic, Josef Widder, Informal Systems, 2020 *) EXTENDS Integers, FiniteSets, Apalache \* algorithm parameters CONSTANTS AllNodes, (* a set of all nodes that can act as validators (correct and faulty) *) COMMON_HEIGHT, (* an index of the block header that two peers agree upon *) CONFLICT_HEIGHT, (* an index of the block header that two peers disagree upon *) TRUSTING_PERIOD, (* the period within which the validators are trusted *) FAULTY_RATIO (* a pair <> that limits that ratio of faulty validator in the blockchain from above (exclusive). Tendermint security model prescribes 1 / 3. *) VARIABLES blockchain, (* the chain at the full node *) refClock, (* the reference clock at the full node *) Faulty, (* the set of faulty validators *) conflictingBlock, (* an evidence that two peers reported conflicting blocks *) state, (* the state of the attack isolation machine at the full node *) attackers (* the set of the identified attackers *) vars == <> \* instantiate the chain at the full node ULTIMATE_HEIGHT == CONFLICT_HEIGHT + 1 BC == INSTANCE Blockchain_003_draft \* use the light client API TRUSTING_HEIGHT == COMMON_HEIGHT TARGET_HEIGHT == CONFLICT_HEIGHT LC == INSTANCE LCVerificationApi_003_draft WITH localClock <- refClock, REAL_CLOCK_DRIFT <- 0, CLOCK_DRIFT <- 0 \* old-style type annotations in apalache a <: b == a \* [LCAI-NONVALID-OUTPUT.1::TLA.1] ViolatesValidity(header1, header2) == \/ header1.VS /= header2.VS \/ header1.NextVS /= header2.NextVS \/ header1.height /= header2.height \/ header1.time /= header2.time (* The English specification also checks the fields that we do not have at this level of abstraction: - header1.ConsensusHash != header2.ConsensusHash or - header1.AppHash != header2.AppHash or - header1.LastResultsHash header2 != ev.LastResultsHash *) Init == /\ state := "init" \* Pick an arbitrary blockchain from 1 to COMMON_HEIGHT + 1. /\ BC!InitToHeight(FAULTY_RATIO) \* initializes blockchain, Faulty, and refClock /\ attackers := {} <: {STRING} \* attackers are unknown \* Receive an arbitrary evidence. \* Instantiate the light block fields one by one, \* to avoid combinatorial explosion of records. /\ \E time \in Int: \E VS, NextVS, lastCommit, Commits \in SUBSET AllNodes: LET conflicting == [ Commits |-> Commits, header |-> [height |-> CONFLICT_HEIGHT, time |-> time, VS |-> VS, NextVS |-> NextVS, lastCommit |-> lastCommit] ] IN LET refBlock == [ header |-> blockchain[COMMON_HEIGHT], Commits |-> blockchain[COMMON_HEIGHT + 1].lastCommit ] IN /\ "SUCCESS" = LC!ValidAndVerifiedUntimed(refBlock, conflicting) \* More than third of next validators in the common reference block \* is faulty. That is a precondition for a fork. /\ 3 * Cardinality(Faulty \intersect refBlock.header.NextVS) > Cardinality(refBlock.header.NextVS) \* correct validators cannot sign an invalid block /\ ViolatesValidity(conflicting.header, refBlock.header) => conflicting.Commits \subseteq Faulty /\ conflictingBlock := conflicting \* This is a specification of isolateMisbehavingProcesses. \* \* [LCAI-FUNC-MAIN.1::TLA.1] Next == /\ state = "init" \* Extract the rounds from the reference block and the conflicting block. \* In this specification, we just pick rounds non-deterministically. \* The English specification calls RoundOf on the blocks. /\ \E referenceRound, evidenceRound \in Int: /\ referenceRound >= 0 /\ evidenceRound >= 0 /\ LET reference == blockchain[CONFLICT_HEIGHT] referenceCommit == blockchain[CONFLICT_HEIGHT + 1].lastCommit evidenceHeader == conflictingBlock.header evidenceCommit == conflictingBlock.Commits IN IF ViolatesValidity(reference, evidenceHeader) THEN /\ attackers' := blockchain[COMMON_HEIGHT].NextVS \intersect evidenceCommit /\ state' := "Lunatic" ELSE IF referenceRound = evidenceRound THEN /\ attackers' := referenceCommit \intersect evidenceCommit /\ state' := "Equivocation" ELSE \* This property is shown in property \* Accountability of TendermintAcc3.tla /\ state' := "Amnesia" /\ \E Attackers \in SUBSET (Faulty \intersect reference.VS): /\ 3 * Cardinality(Attackers) > Cardinality(reference.VS) /\ attackers' := Attackers /\ blockchain' := blockchain /\ refClock' := refClock /\ Faulty' := Faulty /\ conflictingBlock' := conflictingBlock (********************************** INVARIANTS *******************************) \* This invariant ensure that the attackers have \* more than 1/3 of the voting power \* \* [LCAI-INV-Output.1::TLA-DETECTION-COMPLETENESS.1] DetectionCompleteness == state /= "init" => 3 * Cardinality(attackers) > Cardinality(blockchain[CONFLICT_HEIGHT].VS) \* This invariant ensures that only the faulty validators are detected \* \* [LCAI-INV-Output.1::TLA-DETECTION-ACCURACY.1] DetectionAccuracy == attackers \subseteq Faulty ==============================================================================