-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Light Client model-based testing #529
Changes from all commits
d9c6b5e
0ac86a3
e435ae3
f0e7599
b361b51
08450a7
04ab1aa
f7b3165
cb478f2
d65a72e
a720487
49ef36d
b80159c
6a3831b
e36ba00
368975c
da11fc1
8ff0f93
4715e18
d223773
ec2ba4c
0df4320
e9a099f
f04a163
b860976
119088c
9932a1d
249a24f
0ff0b99
2f03aef
0f9f187
ae847f3
43706ca
3c4d6cf
e8a897e
3074bde
f440ed4
377d6f2
4a0a27a
3a7d486
af36c98
d432816
6efc959
99e51a4
67c1b1b
2673138
f6e5b91
64b10e0
ba4349d
380c33b
aac7970
d049a07
e6ae06c
c1cb954
f63a7e0
e2fbe20
6e3c83a
f271177
389de68
47875aa
935f785
dbee1c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,7 +75,7 @@ LBT == [header |-> BT, Commits |-> {NT}] | |
|
||
(* the header is still within the trusting period *) | ||
InTrustingPeriod(header) == | ||
now <= header.time + TRUSTING_PERIOD | ||
now < header.time + TRUSTING_PERIOD | ||
|
||
(* | ||
Given a function pVotingPower \in D -> Powers for some D \subseteq AllNodes | ||
|
@@ -110,7 +110,7 @@ FaultAssumption(pFaultyNodes, pNow, pBlockchain) == | |
(* Can a block be produced by a correct peer, or an authenticated Byzantine peer *) | ||
IsLightBlockAllowedByDigitalSignatures(ht, block) == | ||
\/ block.header = blockchain[ht] \* signed by correct and faulty (maybe) | ||
\/ block.Commits \subseteq Faulty /\ block.header.height = ht \* signed only by faulty | ||
\/ block.Commits \subseteq Faulty /\ block.header.height = ht /\ block.header.time > 0 \* signed only by faulty | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess faulty processes may be allowed to write any time, also negative. Or is the time stored in some unsigned domain in the implementation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is indeed stored and serialized/deserialized as an unsigned integer, so signed integers do no make sense and only add confusion, I am afraid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess in the English spec we implicitly also assume positive times. Perhaps we should clarify it there explicitly. |
||
|
||
(* | ||
Initialize the blockchain to the ultimate height right in the initial states. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,8 +29,28 @@ VARIABLES | |
lightBlockStatus, (* a function from heights to block statuses *) | ||
latestVerified (* the latest verified block *) | ||
|
||
(* the variables of the lite client *) | ||
lcvars == <<state, nextHeight, fetchedLightBlocks, lightBlockStatus, latestVerified>> | ||
(* the variables of the lite client *) | ||
lcvars == <<state, nextHeight, fetchedLightBlocks, lightBlockStatus, latestVerified>> | ||
|
||
(* The light client history, which is the function mapping states (0 .. nprobes) to the record with fields: | ||
- verified: the latest verified block in this state | ||
- current: the block that is being checked in this state | ||
- now: the time point in this state | ||
- verdict: the light client verdict for the `current` block in this state | ||
*) | ||
VARIABLE | ||
history | ||
|
||
InitHistory(verified, current, now, verdict) == | ||
LET first == [ verified |-> verified, current |-> current, now |-> now, verdict |-> verdict ] | ||
IN history = [ n \in {0} |-> first ] | ||
|
||
NextHistory(verified, current, now, verdict) == | ||
LET last == [ verified |-> verified, current |-> current, now |-> now, verdict |-> verdict ] | ||
np == nprobes+1 | ||
IN history' = [ n \in DOMAIN history \union {np} |-> | ||
IF n = np THEN last ELSE history[n]] | ||
|
||
|
||
(******************* Blockchain instance ***********************************) | ||
|
||
|
@@ -74,7 +94,7 @@ ValidAndVerifiedPre(trusted, untrusted) == | |
/\ BC!InTrustingPeriod(thdr) | ||
/\ thdr.height < uhdr.height | ||
\* the trusted block has been created earlier (no drift here) | ||
/\ thdr.time <= uhdr.time | ||
/\ thdr.time < uhdr.time | ||
/\ untrusted.Commits \subseteq uhdr.VS | ||
/\ LET TP == Cardinality(uhdr.VS) | ||
SP == Cardinality(untrusted.Commits) | ||
|
@@ -106,7 +126,7 @@ SignedByOneThirdOfTrusted(trusted, untrusted) == | |
*) | ||
ValidAndVerified(trusted, untrusted) == | ||
IF ~ValidAndVerifiedPre(trusted, untrusted) | ||
THEN "FAILED_VERIFICATION" | ||
THEN "INVALID" | ||
ELSE IF ~BC!InTrustingPeriod(untrusted.header) | ||
(* We leave the following test for the documentation purposes. | ||
The implementation should do this test, as signature verification may be slow. | ||
|
@@ -115,8 +135,8 @@ ValidAndVerified(trusted, untrusted) == | |
THEN "FAILED_TRUSTING_PERIOD" | ||
ELSE IF untrusted.header.height = trusted.header.height + 1 | ||
\/ SignedByOneThirdOfTrusted(trusted, untrusted) | ||
THEN "OK" | ||
ELSE "CANNOT_VERIFY" | ||
THEN "SUCCESS" | ||
ELSE "NOT_ENOUGH_TRUST" | ||
Comment on lines
+138
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think OK and CANNOT_VERIFY are used in the English spec. Are SUCCESS and NOT_ENOUGH_TRUST used in the implementation? If yes, we should adapt I guess the English spec. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've taken the verdicts from the implementation. There is another issue I've got, namely that according to the TLA+ constraints the verdict FAILED_TRUSTING_PERIOD cannot ever be produced. At least it's impossible to obtain a test with such verdict from Apalache, and my quick manual check on the TLA+ constraints tells me it's indeed logically impossible. Could you please verify that? In case it's true, it may be makes sense to remove such a verdict. And I would actually update the English spec as well, as I find the implementation names a bit more informative There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will have a look with @konnov There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this PR is closed; let's discuss on another PR: 546 |
||
|
||
(* | ||
Initial states of the light client. | ||
|
@@ -135,6 +155,7 @@ LCInit == | |
/\ lightBlockStatus = [h \in {TRUSTED_HEIGHT} |-> "StateVerified"] | ||
\* the latest verified block the the trusted block | ||
/\ latestVerified = trustedLightBlock | ||
/\ InitHistory(trustedLightBlock, trustedLightBlock, now, "SUCCESS") | ||
|
||
\* block should contain a copy of the block from the reference chain, with a matching commit | ||
CopyLightBlockFromChain(block, height) == | ||
|
@@ -207,8 +228,9 @@ VerifyToTargetLoop == | |
/\ nprobes' = nprobes + 1 | ||
\* Verify the current block | ||
/\ LET verdict == ValidAndVerified(latestVerified, current) IN | ||
NextHistory(latestVerified, current, now, verdict) /\ | ||
\* Decide whether/how to continue | ||
CASE verdict = "OK" -> | ||
CASE verdict = "SUCCESS" -> | ||
/\ lightBlockStatus' = LightStoreUpdateStates(lightBlockStatus, nextHeight, "StateVerified") | ||
/\ latestVerified' = current | ||
/\ state' = | ||
|
@@ -219,7 +241,7 @@ VerifyToTargetLoop == | |
/\ CanScheduleTo(newHeight, current, nextHeight, TARGET_HEIGHT) | ||
/\ nextHeight' = newHeight | ||
|
||
[] verdict = "CANNOT_VERIFY" -> | ||
[] verdict = "NOT_ENOUGH_TRUST" -> | ||
(* | ||
do nothing: the light block current passed validation, but the validator | ||
set is too different to verify it. We keep the state of | ||
|
@@ -244,7 +266,8 @@ VerifyToTargetLoop == | |
VerifyToTargetDone == | ||
/\ latestVerified.header.height >= TARGET_HEIGHT | ||
/\ state' = "finishedSuccess" | ||
/\ UNCHANGED <<nextHeight, nprobes, fetchedLightBlocks, lightBlockStatus, latestVerified>> | ||
/\ UNCHANGED <<nextHeight, nprobes, fetchedLightBlocks, lightBlockStatus, latestVerified>> | ||
/\ UNCHANGED <<history>> | ||
|
||
(********************* Lite client + Blockchain *******************) | ||
Init == | ||
|
@@ -310,7 +333,7 @@ CorrectnessInv == | |
fetchedLightBlocks[h].header = blockchain[h] | ||
|
||
(** | ||
Check that the sequence of the headers in storedLightBlocks satisfies ValidAndVerified = "OK" pairwise | ||
Check that the sequence of the headers in storedLightBlocks satisfies ValidAndVerified = "SUCCESS" pairwise | ||
This property is easily violated, whenever a header cannot be trusted anymore. | ||
*) | ||
StoredHeadersAreVerifiedInv == | ||
|
@@ -322,7 +345,7 @@ StoredHeadersAreVerifiedInv == | |
\/ \E mh \in DOMAIN fetchedLightBlocks: | ||
lh < mh /\ mh < rh | ||
\* or we can verify the right one using the left one | ||
\/ "OK" = ValidAndVerified(fetchedLightBlocks[lh], fetchedLightBlocks[rh]) | ||
\/ "SUCCESS" = ValidAndVerified(fetchedLightBlocks[lh], fetchedLightBlocks[rh]) | ||
|
||
\* An improved version of StoredHeadersAreSound, assuming that a header may be not trusted. | ||
\* This invariant candidate is also violated, | ||
|
@@ -336,7 +359,7 @@ StoredHeadersAreVerifiedOrNotTrustedInv == | |
\/ \E mh \in DOMAIN fetchedLightBlocks: | ||
lh < mh /\ mh < rh | ||
\* or we can verify the right one using the left one | ||
\/ "OK" = ValidAndVerified(fetchedLightBlocks[lh], fetchedLightBlocks[rh]) | ||
\/ "SUCCESS" = ValidAndVerified(fetchedLightBlocks[lh], fetchedLightBlocks[rh]) | ||
\* or the left header is outside the trusting period, so no guarantees | ||
\/ ~BC!InTrustingPeriod(fetchedLightBlocks[lh].header) | ||
|
||
|
@@ -359,7 +382,7 @@ ProofOfChainOfTrustInv == | |
\* or the left header is outside the trusting period, so no guarantees | ||
\/ ~(BC!InTrustingPeriod(fetchedLightBlocks[lh].header)) | ||
\* or we can verify the right one using the left one | ||
\/ "OK" = ValidAndVerified(fetchedLightBlocks[lh], fetchedLightBlocks[rh]) | ||
\/ "SUCCESS" = ValidAndVerified(fetchedLightBlocks[lh], fetchedLightBlocks[rh]) | ||
|
||
(** | ||
* When the light client terminates, there are no failed blocks. (Otherwise, someone lied to us.) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this change come from the implementation or the English spec? Are those consistent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comes from the implementation, as otherwise I get a testing error: spec behavior is different from what we have in the implementation. And the English spec contains this
trusted.Header.Time > now - trustingPeriod
, so I guess this means that the English spec and the implementation agree, but the TLA+ spec is wrong in that respect; could you please verify that it's true?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So now we do model-based testing as conformance test between English spec, TLA+, and the implementation. Amazing 😉