github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/protocol_state/kvstore.go (about) 1 package protocol_state 2 3 import ( 4 "github.com/onflow/flow-go/model/flow" 5 "github.com/onflow/flow-go/state/protocol" 6 "github.com/onflow/flow-go/storage/badger/transaction" 7 ) 8 9 // This file contains versioned read-write interfaces to the Protocol State's 10 // key-value store and are used by the Protocol State Machine. 11 // 12 // When a key is added or removed, this requires a new protocol state version: 13 // - Create a new versioned model in ./kvstore/models.go (eg. modelv3 if latest model is modelv2) 14 // - Update the protocol.KVStoreReader and KVStoreAPI interfaces to include any new keys 15 16 // KVStoreAPI is the latest interface to the Protocol State key-value store which implements 'Prototype' 17 // pattern for replicating protocol state between versions. 18 // 19 // Caution: 20 // Engineers evolving this interface must ensure that it is backwards-compatible 21 // with all versions of Protocol State Snapshots that can be retrieved from the local 22 // database, which should exactly correspond to the versioned model types defined in 23 // ./kvstore/models.go 24 type KVStoreAPI interface { 25 protocol.KVStoreReader 26 27 // Replicate instantiates a Protocol State Snapshot of the given `protocolVersion`. 28 // We reference to the Protocol State Snapshot, whose `Replicate` method is called 29 // as the 'Parent Snapshot'. 30 // If the `protocolVersion` matches the version of the Parent Snapshot, `Replicate` behaves 31 // exactly like a deep copy. If `protocolVersion` is newer, the data model corresponding 32 // to the newer version is used and values from the Parent Snapshot are replicated into 33 // the new data model. In all cases, the new Snapshot can be mutated without changing the 34 // Parent Snapshot. 35 // 36 // Caution: 37 // Implementors of this function decide on their own how to perform the migration from parent protocol version 38 // to the given `protocolVersion`. It is required that outcome of `Replicate` is a valid KV store model which can be 39 // incorporated in the protocol state without extra operations. 40 // Expected errors during normal operations: 41 // - kvstore.ErrIncompatibleVersionChange if replicating the Parent Snapshot into a Snapshot 42 // with the specified `protocolVersion` is not supported. 43 Replicate(protocolVersion uint64) (KVStoreMutator, error) 44 } 45 46 // KVStoreMutator is the latest read-writer interface to the Protocol State key-value store. 47 // 48 // Caution: 49 // Engineers evolving this interface must ensure that it is backwards-compatible 50 // with all versions of Protocol State Snapshots that can be retrieved from the local 51 // database, which should exactly correspond to the versioned model types defined in 52 // ./kvstore/models.go 53 type KVStoreMutator interface { 54 protocol.KVStoreReader 55 56 // v0/v1 57 58 // SetVersionUpgrade sets the protocol upgrade version. This method is used 59 // to update the Protocol State version when a flow.ProtocolStateVersionUpgrade is processed. 60 // It contains the new version and the view at which it has to be applied. 61 SetVersionUpgrade(version *protocol.ViewBasedActivator[uint64]) 62 63 // SetEpochStateID sets the state ID of the epoch state. 64 // This method is used to commit the epoch state to the KV store when the state of the epoch is updated. 65 SetEpochStateID(stateID flow.Identifier) 66 } 67 68 // OrthogonalStoreStateMachine represents a state machine that exclusively evolves its state P. 69 // The state's specific type P is kept as a generic. Generally, P is the type corresponding 70 // to one specific key in the Key-Value store. 71 // 72 // Orthogonal State Machines: 73 // Orthogonality means that state machines can operate completely independently and work on disjoint 74 // sub-states. By convention, they all consume the same inputs (incl. the ordered sequence of 75 // Service Events sealed in one block). In other words, each state machine has full visibility into 76 // the inputs, but each draws their on independent conclusions (maintain their own exclusive state). 77 // 78 // The Dynamic Protocol State comprises a Key-Value-Store. We loosely associate each key-value-pair 79 // with a dedicated state machine operating exclusively on this key-value pair. A one-to-one 80 // correspondence between key-value-pair and state machine should be the default, but is not strictly 81 // required. However, we strictly require that no key-value-pair is being operated on by *more* than 82 // one state machine. 83 // 84 // The Protocol State is the framework, which orchestrates the orthogonal state machines, feeds them 85 // with inputs, post-processes the outputs and overall manages state machines' life-cycle from block 86 // to block. New key-value pairs and corresponding state machines can easily be added by 87 // - adding a new entry to the Key-Value-Store's data model (file `./kvstore/models.go`) 88 // - implementing the `OrthogonalStoreStateMachine` interface 89 // 90 // For more details see `./Readme.md` 91 // 92 // NOT CONCURRENCY SAFE 93 type OrthogonalStoreStateMachine[P any] interface { 94 95 // Build returns: 96 // - database updates necessary for persisting the updated protocol sub-state and its *dependencies*. 97 // It may contain updates for the sub-state itself and for any dependency that is affected by the update. 98 // Deferred updates must be applied in a transaction to ensure atomicity. 99 // 100 // No errors are expected during normal operations. 101 Build() (*transaction.DeferredBlockPersist, error) 102 103 // EvolveState applies the state change(s) on sub-state P for the candidate block (under construction). 104 // Information that potentially changes the Epoch state (compared to the parent block's state): 105 // - Service Events sealed in the candidate block 106 // - the candidate block's view (already provided at construction time) 107 // 108 // SAFETY REQUIREMENTS: 109 // - The seals for the execution results, from which the `sealedServiceEvents` originate, 110 // must be protocol compliant. 111 // - `sealedServiceEvents` must list the service Events in chronological order. This can be 112 // achieved by arranging the sealed execution results in order of increasing block height. 113 // Within each execution result, the service events are in chronological order. 114 // 115 // CAUTION: 116 // Per convention, the input seals from the block payload have already been confirmed to be protocol compliant. 117 // Hence, the service events in the sealed execution results represent the *honest* execution path. Therefore, 118 // the sealed service events should encode a valid evolution of the protocol state -- provided the system smart 119 // contracts are correct. As we can rule out byzantine attacks as the source of failures, the only remaining 120 // sources of problems can be (a) bugs in the system smart contracts or (b) bugs in the node implementation. 121 // - A service event not representing a valid state transition despite all consistency checks passing is 122 // indicative of case (a) and _should be handled_ internally by the respective state machine. Otherwise, 123 // any bug or unforeseen edge cases in the system smart contracts would in consensus halt, due to errors 124 // while evolving the protocol state. 125 // - Consistency or sanity checks failing within the OrthogonalStoreStateMachine is likely the symptom of an 126 // internal bug in the node software or state corruption, i.e. case (b). This is the only scenario where the 127 // error return of this function is not nil. If such an exception is returned, continuing is not an option. 128 // 129 // No errors are expected during normal operations. 130 EvolveState(sealedServiceEvents []flow.ServiceEvent) error 131 132 // View returns the view associated with this state machine. 133 // The view of the state machine equals the view of the block carrying the respective updates. 134 View() uint64 135 136 // ParentState returns parent state associated with this state machine. 137 ParentState() P 138 } 139 140 // KeyValueStoreStateMachine is a type alias for a state machine that operates on an instance of KVStoreReader. 141 // StateMutator uses this type to store and perform operations on orthogonal state machines. 142 type KeyValueStoreStateMachine = OrthogonalStoreStateMachine[protocol.KVStoreReader] 143 144 // KeyValueStoreStateMachineFactory is an abstract factory interface for creating KeyValueStoreStateMachine instances. 145 // It is used separate creation of state machines from their usage, which allows less coupling and superior testability. 146 // For each concrete type injected in State Mutator a dedicated abstract factory has to be created. 147 type KeyValueStoreStateMachineFactory interface { 148 // Create creates a new instance of an underlying type that operates on KV Store and is created for a specific candidate block. 149 // No errors are expected during normal operations. 150 Create(candidateView uint64, parentID flow.Identifier, parentState protocol.KVStoreReader, mutator KVStoreMutator) (KeyValueStoreStateMachine, error) 151 }