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

-------------------- MODULE LCVerificationApi_003_draft --------------------------
(**
* The common interface of the light client verification and detection.
*)
EXTENDS Integers, FiniteSets
\* the parameters of Light Client
CONSTANTS
TRUSTING_PERIOD,
(* the period within which the validators are trusted *)
CLOCK_DRIFT,
(* the assumed precision of the clock *)
REAL_CLOCK_DRIFT,
(* the actual clock drift, which under normal circumstances should not
be larger than CLOCK_DRIFT (otherwise, there will be a bug) *)
FAULTY_RATIO
(* a pair <<a, b>> that limits that ratio of faulty validator in the blockchain
from above (exclusive). Tendermint security model prescribes 1 / 3. *)
VARIABLES
localClock (* current time as measured by the light client *)
(* the header is still within the trusting period *)
InTrustingPeriodLocal(header) ==
\* note that the assumption about the drift reduces the period of trust
localClock < header.time + TRUSTING_PERIOD - CLOCK_DRIFT
(* the header is still within the trusting period, even if the clock can go backwards *)
InTrustingPeriodLocalSurely(header) ==
\* note that the assumption about the drift reduces the period of trust
localClock < header.time + TRUSTING_PERIOD - 2 * CLOCK_DRIFT
(* ensure that the local clock does not drift far away from the global clock *)
IsLocalClockWithinDrift(local, global) ==
/\ global - REAL_CLOCK_DRIFT <= local
/\ local <= global + REAL_CLOCK_DRIFT
(**
* Check that the commits in an untrusted block form 1/3 of the next validators
* in a trusted header.
*)
SignedByOneThirdOfTrusted(trusted, untrusted) ==
LET TP == Cardinality(trusted.header.NextVS)
SP == Cardinality(untrusted.Commits \intersect trusted.header.NextVS)
IN
3 * SP > TP
(**
The first part of the precondition of ValidAndVerified, which does not take
the current time into account.
[LCV-FUNC-VALID.1::TLA-PRE-UNTIMED.1]
*)
ValidAndVerifiedPreUntimed(trusted, untrusted) ==
LET thdr == trusted.header
uhdr == untrusted.header
IN
/\ thdr.height < uhdr.height
\* the trusted block has been created earlier
/\ thdr.time < uhdr.time
/\ untrusted.Commits \subseteq uhdr.VS
/\ LET TP == Cardinality(uhdr.VS)
SP == Cardinality(untrusted.Commits)
IN
3 * SP > 2 * TP
/\ thdr.height + 1 = uhdr.height => thdr.NextVS = uhdr.VS
(* As we do not have explicit hashes we ignore these three checks of the English spec:
1. "trusted.Commit is a commit is for the header trusted.Header,
i.e. it contains the correct hash of the header".
2. untrusted.Validators = hash(untrusted.Header.Validators)
3. untrusted.NextValidators = hash(untrusted.Header.NextValidators)
*)
(**
Check the precondition of ValidAndVerified, including the time checks.
[LCV-FUNC-VALID.1::TLA-PRE.1]
*)
ValidAndVerifiedPre(trusted, untrusted, checkFuture) ==
LET thdr == trusted.header
uhdr == untrusted.header
IN
/\ InTrustingPeriodLocal(thdr)
\* The untrusted block is not from the future (modulo clock drift).
\* Do the check, if it is required.
/\ checkFuture => uhdr.time < localClock + CLOCK_DRIFT
/\ ValidAndVerifiedPreUntimed(trusted, untrusted)
(**
Check, whether an untrusted block is valid and verifiable w.r.t. a trusted header.
This test does take current time into account, but only looks at the block structure.
[LCV-FUNC-VALID.1::TLA-UNTIMED.1]
*)
ValidAndVerifiedUntimed(trusted, untrusted) ==
IF ~ValidAndVerifiedPreUntimed(trusted, untrusted)
THEN "INVALID"
ELSE IF untrusted.header.height = trusted.header.height + 1
\/ SignedByOneThirdOfTrusted(trusted, untrusted)
THEN "SUCCESS"
ELSE "NOT_ENOUGH_TRUST"
(**
Check, whether an untrusted block is valid and verifiable w.r.t. a trusted header.
[LCV-FUNC-VALID.1::TLA.1]
*)
ValidAndVerified(trusted, untrusted, checkFuture) ==
IF ~ValidAndVerifiedPre(trusted, untrusted, checkFuture)
THEN "INVALID"
ELSE IF ~InTrustingPeriodLocal(untrusted.header)
(* We leave the following test for the documentation purposes.
The implementation should do this test, as signature verification may be slow.
In the TLA+ specification, ValidAndVerified happens in no time.
*)
THEN "FAILED_TRUSTING_PERIOD"
ELSE IF untrusted.header.height = trusted.header.height + 1
\/ SignedByOneThirdOfTrusted(trusted, untrusted)
THEN "SUCCESS"
ELSE "NOT_ENOUGH_TRUST"
(**
The invariant of the light store that is not related to the blockchain
*)
LightStoreInv(fetchedLightBlocks, lightBlockStatus) ==
\A lh, rh \in DOMAIN fetchedLightBlocks:
\* for every pair of stored headers that have been verified
\/ lh >= rh
\/ lightBlockStatus[lh] /= "StateVerified"
\/ lightBlockStatus[rh] /= "StateVerified"
\* either there is a header between them
\/ \E mh \in DOMAIN fetchedLightBlocks:
lh < mh /\ mh < rh /\ lightBlockStatus[mh] = "StateVerified"
\* or the left header is outside the trusting period, so no guarantees
\/ LET lhdr == fetchedLightBlocks[lh]
rhdr == fetchedLightBlocks[rh]
IN
\* we can verify the right one using the left one
"SUCCESS" = ValidAndVerifiedUntimed(lhdr, rhdr)
(**
Correctness states that all the obtained headers are exactly like in the blockchain.
It is always the case that every verified header in LightStore was generated by
an instance of Tendermint consensus.
[LCV-DIST-SAFE.1::CORRECTNESS-INV.1]
*)
CorrectnessInv(blockchain, fetchedLightBlocks, lightBlockStatus) ==
\A h \in DOMAIN fetchedLightBlocks:
lightBlockStatus[h] = "StateVerified" =>
fetchedLightBlocks[h].header = blockchain[h]
(**
* When the light client terminates, there are no failed blocks.
* (Otherwise, someone lied to us.)
*)
NoFailedBlocksOnSuccessInv(fetchedLightBlocks, lightBlockStatus) ==
\A h \in DOMAIN fetchedLightBlocks:
lightBlockStatus[h] /= "StateFailed"
(**
The expected post-condition of VerifyToTarget.
*)
VerifyToTargetPost(blockchain, isPeerCorrect,
fetchedLightBlocks, lightBlockStatus,
trustedHeight, targetHeight, finalState) ==
LET trustedHeader == fetchedLightBlocks[trustedHeight].header IN
\* The light client is not lying us on the trusted block.
\* It is straightforward to detect.
/\ lightBlockStatus[trustedHeight] = "StateVerified"
/\ trustedHeight \in DOMAIN fetchedLightBlocks
/\ trustedHeader = blockchain[trustedHeight]
\* the invariants we have found in the light client verification
\* there is a problem with trusting period
/\ isPeerCorrect
=> CorrectnessInv(blockchain, fetchedLightBlocks, lightBlockStatus)
\* a correct peer should fail the light client,
\* if the trusted block is in the trusting period
/\ isPeerCorrect /\ InTrustingPeriodLocalSurely(trustedHeader)
=> finalState = "finishedSuccess"
/\ finalState = "finishedSuccess" =>
/\ lightBlockStatus[targetHeight] = "StateVerified"
/\ targetHeight \in DOMAIN fetchedLightBlocks
/\ NoFailedBlocksOnSuccessInv(fetchedLightBlocks, lightBlockStatus)
/\ LightStoreInv(fetchedLightBlocks, lightBlockStatus)
==================================================================================