github.com/aakash4dev/cometbft@v0.38.2/spec/mempool/quint/Ledger.qnt (about) 1 // -*- mode: Bluespec; -*- 2 // A Ledger is a replicated log of blocks. 3 // The specification below considers that 4 // - replication is made with consensus (see Consensus.qnt) 5 // - idempotence is not granted (that is, two log entries might have transactions in common). 6 7 module Ledger{ 8 9 import Spells.* from "Spells" 10 import System.* from "System" 11 import Consensus.* from "Consensus" 12 13 type LedgerState = { 14 log: List[ConsensusState], 15 } 16 17 pure def newLedger(P: Set[Process]): LedgerState = { 18 log: List(newConsensus(P)) 19 } 20 21 //// helpers 22 23 // index of the first null entry 24 pure def height(state: LedgerState): int = { 25 length(state.log.select(s => s.hasDecision())) 26 } 27 28 pure def heightOf(state: LedgerState, p: Process): int = { 29 length(state.log.select(s => s.hasDecided(p))) 30 } 31 32 pure def entry(state: LedgerState, p: Process, i: int): Set[Tx] = { 33 state.log[i].decisionOf(p) 34 } 35 36 pure def lastEntry(state: LedgerState, p: Process): Set[Tx] = { 37 entry(state, p, state.heightOf(p)-1) 38 } 39 40 pure def getSubmittedFor(state: LedgerState, p: Process): Set[Tx] = { 41 0.to(state.heightOf(p)).fold(Set(), (s, i) => s.union(state.log[i].proposalOf(p))) 42 } 43 44 pure def getCommittedFor(state: LedgerState, p: Process): Set[Tx] = { 45 0.to(state.heightOf(p)).fold(Set(), (s, i) => s.union(state.log[i].decisionOf(p))) 46 } 47 48 pure def isSubmitted(state: LedgerState, t: Tx): bool = { 49 0.to(state.height()).exists(h => state.log[h].isProposed(t)) 50 } 51 52 pure def isCommitted(state: LedgerState, t: Tx): bool = { 53 if (state.height()==0) false else 0.to(state.height()-1).exists(i => state.log[i].isDecided(t)) 54 } 55 56 pure def isCommittedFor(state: LedgerState, p: Process, t: Tx): bool = { 57 if (state.heightOf(p)==0) false else 0.to(state.heightOf(p)-1).exists(h => state.log[h].isDecided(t)) 58 } 59 60 //// preconditions 61 62 pure def maySubmit(state: LedgerState, p: Process, txs: Set[Tx]): bool = { 63 state.log[state.heightOf(p)].mayPropose(p, txs) 64 } 65 66 pure def mayCommit(state: LedgerState, p: Process): bool = { 67 state.log[state.heightOf(p)].mayDecide(p) 68 } 69 70 //// transitions 71 72 pure def submit(state: LedgerState, p: Process, txs: Set[Tx]): LedgerState = { 73 val currentConsensus = state.log[heightOf(state,p)] 74 val nextLog = state.log.replaceAt(heightOf(state,p), currentConsensus.propose(p, txs)) 75 {log: nextLog, ...state} 76 } 77 78 pure def commit(state: LedgerState, p: Process): LedgerState = { 79 val currentConsensus = state.log[state.heightOf(p)] 80 val nextLog = if (state.heightOf(p) == state.height()) { 81 state.log.append(newConsensus(processes(state.log[0]))) 82 } else { 83 state.log 84 } 85 {log: nextLog.replaceAt(heightOf(state,p), currentConsensus.decide(p)), ...state} 86 } 87 88 //// state machine 89 90 var _ledger: LedgerState 91 92 action ledgerInit = all { 93 _ledger' = newLedger(PROCESSES) 94 } 95 96 action ledgerDoSubmit = all { 97 nondet p = oneOf(PROCESSES) 98 nondet txs = oneOf(nonEmptyPowerset(TXS)) 99 all { 100 require(_ledger.maySubmit(p, txs)), 101 _ledger' = _ledger.submit(p, txs) 102 } 103 } 104 105 action ledgerDoCommit = all { 106 nondet p = oneOf(PROCESSES) 107 all { 108 require(_ledger.mayCommit(p)), 109 _ledger' = _ledger.commit(p) 110 } 111 } 112 113 action ledgerStep = any { 114 ledgerDoSubmit, 115 ledgerDoCommit 116 } 117 118 //// invariants 119 120 // Validity: every non-null entry is submitted. 121 pure def ledgerValidityInv(state: LedgerState): bool = { 122 0.to(state.height()).forall(h => consensusValidityInv(state.log[h])) 123 } 124 125 // Total Order: for any two processes, entries are prefix one from another. 126 pure def ledgerOrdertInv(state: LedgerState): bool = { 127 0.to(state.height()-1).forall(h => consensusAgreementInv(state.log[h])) 128 } 129 130 // Stability: for every process, its entry always grows. 131 // temporal ledgerStabilityInv = { 132 // and { 133 // _ledger.height() <= next(_ledger.height()), 134 // 0.to(_ledger.height()).forall(h => _ledger.log[h].isSupersededBy(next(_ledger.log[h]))) 135 // } 136 // } 137 // FIXME. cannot be verified yet 138 139 val ledgerInvariant = { 140 and { 141 ledgerValidityInv(_ledger), 142 ledgerOrdertInv(_ledger) 143 } 144 } 145 146 //// tests 147 148 run submitTwiceErrorTest = { 149 nondet p = oneOf(PROCESSES) 150 nondet txs = oneOf(nonEmptyPowerset(TXS)) 151 ledgerInit 152 .then( 153 all { 154 _ledger.submit(p, txs).maySubmit(p, txs), 155 _ledger'=_ledger 156 } 157 ) 158 .fail() 159 } 160 161 run commitNonSubmittedErrorTest = { 162 nondet p = oneOf(PROCESSES) 163 ledgerInit 164 .then( 165 all { 166 _ledger.mayCommit(p), 167 _ledger'=_ledger 168 } 169 ) 170 .fail() 171 } 172 173 run commitSubmittedSuccessTest = { 174 nondet p = oneOf(PROCESSES) 175 nondet q = oneOf(PROCESSES) 176 nondet txs0 = oneOf(nonEmptyPowerset(TXS)) 177 nondet txs1 = oneOf(nonEmptyPowerset(TXS)) 178 ledgerInit 179 .then(_ledger' = _ledger.submit(p, txs0).submit(q, txs1)) 180 .then( 181 all { 182 _ledger.mayCommit(p) and heightOf(_ledger.commit(p), p)==1, 183 _ledger'=_ledger 184 } 185 ) 186 } 187 188 }