github.com/aakash4dev/cometbft@v0.38.2/spec/light-client/attacks/LCVerificationApi_003_draft.tla (about) 1 -------------------- MODULE LCVerificationApi_003_draft -------------------------- 2 (** 3 * The common interface of the light client verification and detection. 4 *) 5 EXTENDS Integers, FiniteSets 6 7 \* the parameters of Light Client 8 CONSTANTS 9 TRUSTING_PERIOD, 10 (* the period within which the validators are trusted *) 11 CLOCK_DRIFT, 12 (* the assumed precision of the clock *) 13 REAL_CLOCK_DRIFT, 14 (* the actual clock drift, which under normal circumstances should not 15 be larger than CLOCK_DRIFT (otherwise, there will be a bug) *) 16 FAULTY_RATIO 17 (* a pair <<a, b>> that limits that ratio of faulty validator in the blockchain 18 from above (exclusive). Cosmos security model prescribes 1 / 3. *) 19 20 VARIABLES 21 localClock (* current time as measured by the light client *) 22 23 (* the header is still within the trusting period *) 24 InTrustingPeriodLocal(header) == 25 \* note that the assumption about the drift reduces the period of trust 26 localClock < header.time + TRUSTING_PERIOD - CLOCK_DRIFT 27 28 (* the header is still within the trusting period, even if the clock can go backwards *) 29 InTrustingPeriodLocalSurely(header) == 30 \* note that the assumption about the drift reduces the period of trust 31 localClock < header.time + TRUSTING_PERIOD - 2 * CLOCK_DRIFT 32 33 (* ensure that the local clock does not drift far away from the global clock *) 34 IsLocalClockWithinDrift(local, global) == 35 /\ global - REAL_CLOCK_DRIFT <= local 36 /\ local <= global + REAL_CLOCK_DRIFT 37 38 (** 39 * Check that the commits in an untrusted block form 1/3 of the next validators 40 * in a trusted header. 41 *) 42 SignedByOneThirdOfTrusted(trusted, untrusted) == 43 LET TP == Cardinality(trusted.header.NextVS) 44 SP == Cardinality(untrusted.Commits \intersect trusted.header.NextVS) 45 IN 46 3 * SP > TP 47 48 (** 49 The first part of the precondition of ValidAndVerified, which does not take 50 the current time into account. 51 52 [LCV-FUNC-VALID.1::TLA-PRE-UNTIMED.1] 53 *) 54 ValidAndVerifiedPreUntimed(trusted, untrusted) == 55 LET thdr == trusted.header 56 uhdr == untrusted.header 57 IN 58 /\ thdr.height < uhdr.height 59 \* the trusted block has been created earlier 60 /\ thdr.time < uhdr.time 61 /\ untrusted.Commits \subseteq uhdr.VS 62 /\ LET TP == Cardinality(uhdr.VS) 63 SP == Cardinality(untrusted.Commits) 64 IN 65 3 * SP > 2 * TP 66 /\ thdr.height + 1 = uhdr.height => thdr.NextVS = uhdr.VS 67 (* As we do not have explicit hashes we ignore these three checks of the English spec: 68 69 1. "trusted.Commit is a commit is for the header trusted.Header, 70 i.e. it contains the correct hash of the header". 71 2. untrusted.Validators = hash(untrusted.Header.Validators) 72 3. untrusted.NextValidators = hash(untrusted.Header.NextValidators) 73 *) 74 75 (** 76 Check the precondition of ValidAndVerified, including the time checks. 77 78 [LCV-FUNC-VALID.1::TLA-PRE.1] 79 *) 80 ValidAndVerifiedPre(trusted, untrusted, checkFuture) == 81 LET thdr == trusted.header 82 uhdr == untrusted.header 83 IN 84 /\ InTrustingPeriodLocal(thdr) 85 \* The untrusted block is not from the future (modulo clock drift). 86 \* Do the check, if it is required. 87 /\ checkFuture => uhdr.time < localClock + CLOCK_DRIFT 88 /\ ValidAndVerifiedPreUntimed(trusted, untrusted) 89 90 91 (** 92 Check, whether an untrusted block is valid and verifiable w.r.t. a trusted header. 93 This test does take current time into account, but only looks at the block structure. 94 95 [LCV-FUNC-VALID.1::TLA-UNTIMED.1] 96 *) 97 ValidAndVerifiedUntimed(trusted, untrusted) == 98 IF ~ValidAndVerifiedPreUntimed(trusted, untrusted) 99 THEN "INVALID" 100 ELSE IF untrusted.header.height = trusted.header.height + 1 101 \/ SignedByOneThirdOfTrusted(trusted, untrusted) 102 THEN "SUCCESS" 103 ELSE "NOT_ENOUGH_TRUST" 104 105 (** 106 Check, whether an untrusted block is valid and verifiable w.r.t. a trusted header. 107 108 [LCV-FUNC-VALID.1::TLA.1] 109 *) 110 ValidAndVerified(trusted, untrusted, checkFuture) == 111 IF ~ValidAndVerifiedPre(trusted, untrusted, checkFuture) 112 THEN "INVALID" 113 ELSE IF ~InTrustingPeriodLocal(untrusted.header) 114 (* We leave the following test for the documentation purposes. 115 The implementation should do this test, as signature verification may be slow. 116 In the TLA+ specification, ValidAndVerified happens in no time. 117 *) 118 THEN "FAILED_TRUSTING_PERIOD" 119 ELSE IF untrusted.header.height = trusted.header.height + 1 120 \/ SignedByOneThirdOfTrusted(trusted, untrusted) 121 THEN "SUCCESS" 122 ELSE "NOT_ENOUGH_TRUST" 123 124 125 (** 126 The invariant of the light store that is not related to the blockchain 127 *) 128 LightStoreInv(fetchedLightBlocks, lightBlockStatus) == 129 \A lh, rh \in DOMAIN fetchedLightBlocks: 130 \* for every pair of stored headers that have been verified 131 \/ lh >= rh 132 \/ lightBlockStatus[lh] /= "StateVerified" 133 \/ lightBlockStatus[rh] /= "StateVerified" 134 \* either there is a header between them 135 \/ \E mh \in DOMAIN fetchedLightBlocks: 136 lh < mh /\ mh < rh /\ lightBlockStatus[mh] = "StateVerified" 137 \* or the left header is outside the trusting period, so no guarantees 138 \/ LET lhdr == fetchedLightBlocks[lh] 139 rhdr == fetchedLightBlocks[rh] 140 IN 141 \* we can verify the right one using the left one 142 "SUCCESS" = ValidAndVerifiedUntimed(lhdr, rhdr) 143 144 (** 145 Correctness states that all the obtained headers are exactly like in the blockchain. 146 147 It is always the case that every verified header in LightStore was generated by 148 an instance of Tendermint consensus. 149 150 [LCV-DIST-SAFE.1::CORRECTNESS-INV.1] 151 *) 152 CorrectnessInv(blockchain, fetchedLightBlocks, lightBlockStatus) == 153 \A h \in DOMAIN fetchedLightBlocks: 154 lightBlockStatus[h] = "StateVerified" => 155 fetchedLightBlocks[h].header = blockchain[h] 156 157 (** 158 * When the light client terminates, there are no failed blocks. 159 * (Otherwise, someone lied to us.) 160 *) 161 NoFailedBlocksOnSuccessInv(fetchedLightBlocks, lightBlockStatus) == 162 \A h \in DOMAIN fetchedLightBlocks: 163 lightBlockStatus[h] /= "StateFailed" 164 165 (** 166 The expected post-condition of VerifyToTarget. 167 *) 168 VerifyToTargetPost(blockchain, isPeerCorrect, 169 fetchedLightBlocks, lightBlockStatus, 170 trustedHeight, targetHeight, finalState) == 171 LET trustedHeader == fetchedLightBlocks[trustedHeight].header IN 172 \* The light client is not lying us on the trusted block. 173 \* It is straightforward to detect. 174 /\ lightBlockStatus[trustedHeight] = "StateVerified" 175 /\ trustedHeight \in DOMAIN fetchedLightBlocks 176 /\ trustedHeader = blockchain[trustedHeight] 177 \* the invariants we have found in the light client verification 178 \* there is a problem with trusting period 179 /\ isPeerCorrect 180 => CorrectnessInv(blockchain, fetchedLightBlocks, lightBlockStatus) 181 \* a correct peer should fail the light client, 182 \* if the trusted block is in the trusting period 183 /\ isPeerCorrect /\ InTrustingPeriodLocalSurely(trustedHeader) 184 => finalState = "finishedSuccess" 185 /\ finalState = "finishedSuccess" => 186 /\ lightBlockStatus[targetHeight] = "StateVerified" 187 /\ targetHeight \in DOMAIN fetchedLightBlocks 188 /\ NoFailedBlocksOnSuccessInv(fetchedLightBlocks, lightBlockStatus) 189 /\ LightStoreInv(fetchedLightBlocks, lightBlockStatus) 190 191 192 ==================================================================================