You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
7.3 KiB

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