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.

113 lines
4.8 KiB

  1. -------------------------- MODULE Tinychain ----------------------------------
  2. (* A very abstract model of Tendermint blockchain. Its only purpose is to highlight
  3. the relation between validator sets, next validator sets, and last commits.
  4. *)
  5. EXTENDS Integers
  6. \* type annotation
  7. a <: b == a
  8. \* the type of validator sets, e.g., STRING
  9. VST == STRING
  10. \* LastCommit type.
  11. \* It contains:
  12. \* 1) the flag of whether the block id equals to the hash of the previous block, and
  13. \* 2) the set of the validators who have committed the block.
  14. \* In the implementation, blockId is the hash of the previous block, which cannot be forged.
  15. \* We abstract block id into whether it equals to the hash of the previous block or not.
  16. LCT == [blockIdEqRef |-> BOOLEAN, committers |-> VST]
  17. \* Block type.
  18. \* A block contains its height, validator set, next validator set, and last commit.
  19. \* Moreover, it contains the flag that tells us whether the block is equal to the one
  20. \* on the reference chain (this is an abstraction of hash).
  21. BT == [height |-> Int, hashEqRef |-> BOOLEAN, wellFormed |-> BOOLEAN,
  22. VS |-> VST, NextVS |-> VST, lastCommit |-> LCT]
  23. CONSTANTS
  24. (*
  25. A set of abstract values, each value representing a set of validators.
  26. For the purposes of this specification, they can be any values,
  27. e.g., "s1", "s2", etc.
  28. *)
  29. VALIDATOR_SETS,
  30. (* a nil validator set that is outside of VALIDATOR_SETS *)
  31. NIL_VS,
  32. (* The maximal height, up to which the blockchain may grow *)
  33. MAX_HEIGHT
  34. Heights == 1..MAX_HEIGHT
  35. \* the set of all potential commits
  36. Commits == [blockIdEqRef: BOOLEAN, committers: VALIDATOR_SETS]
  37. \* the set of all potential blocks, not necessarily coming from the blockchain
  38. Blocks ==
  39. [height: Heights, hashEqRef: BOOLEAN, wellFormed: BOOLEAN,
  40. VS: VALIDATOR_SETS, NextVS: VALIDATOR_SETS, lastCommit: Commits]
  41. \* Does the chain contain a sound sequence of blocks that could be produced by
  42. \* a 2/3 of faulty validators. This operator can be used to initialise the chain!
  43. \* Since we are abstracting validator sets with VALIDATOR_SETS, which are
  44. \* 2/3 quorums, we just compare committers to those sets. In a more detailed
  45. \* specification, one would write the \subseteq operator instead of equality.
  46. IsCorrectChain(chain) ==
  47. \* restrict the structure of the blocks, to decrease the TLC search space
  48. LET OkCommits == [blockIdEqRef: {TRUE}, committers: VALIDATOR_SETS]
  49. OkBlocks == [height: Heights, hashEqRef: {TRUE}, wellFormed: {TRUE},
  50. VS: VALIDATOR_SETS, NextVS: VALIDATOR_SETS, lastCommit: OkCommits]
  51. IN
  52. /\ chain \in [1..MAX_HEIGHT -> OkBlocks]
  53. /\ \A h \in 1..MAX_HEIGHT:
  54. LET b == chain[h] IN
  55. /\ b.height = h \* the height is correct
  56. /\ h > 1 =>
  57. LET p == chain[h - 1] IN
  58. /\ b.VS = p.NextVS \* the validators propagate from the previous block
  59. /\ b.lastCommit.committers = p.VS \* and they are the committers
  60. \* The basic properties of blocks on the blockchain:
  61. \* They should pass the validity check and they may verify the next block.
  62. \* Does the block pass the consistency check against the next validators of the previous block
  63. IsMatchingValidators(block, nextVS) ==
  64. \* simply check that the validator set is propagated correctly.
  65. \* (the implementation tests hashes and the application state)
  66. block.VS = nextVS
  67. \* Does the block verify the commit (of the next block)
  68. PossibleCommit(block, commit) ==
  69. \* the commits are signed by the block validators
  70. /\ commit.committers = block.VS
  71. \* The block id in the commit matches the block hash (abstract comparison).
  72. \* (The implementation has extensive tests for that.)
  73. \* this is an abstraction of: commit.blockId = hash(block)
  74. \*
  75. \* These are possible scenarios on the concrete hashes:
  76. \*
  77. \* scenario 1: commit.blockId = 10 /\ hash(block) = 10 /\ hash(ref) = 10
  78. \* scenario 2: commit.blockId = 20 /\ hash(block) = 20 /\ block.VS /= ref.VS
  79. \* scenario 3: commit.blockId = 50 /\ hash(block) = 100
  80. \* scenario 4: commit.blockId = 10 /\ hash(block) = 100
  81. \* scenario 5: commit.blockId = 100 /\ hash(block) = 10
  82. /\ commit.blockIdEqRef = block.hashEqRef
  83. \* the following test would be cheating, as we do not have access to the
  84. \* reference chain:
  85. \* /\ commit.blockIdEqRef
  86. \* Basic invariants
  87. \* every block has the validator set that is chosen by its predecessor
  88. ValidBlockInv(chain) ==
  89. \A h \in 2..MAX_HEIGHT:
  90. IsMatchingValidators(chain[h], chain[h - 1].NextVS)
  91. \* last commit of every block is signed by the validators of the predecessor
  92. VerifiedBlockInv(chain) ==
  93. \A h \in 2..MAX_HEIGHT:
  94. PossibleCommit(chain[h - 1], chain[h].lastCommit)
  95. ==================================================================================