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  }