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  }