github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/protocol_state/kvstore/kvstore_storage.go (about)

     1  package kvstore
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/model/flow"
     7  	"github.com/onflow/flow-go/state/protocol"
     8  	"github.com/onflow/flow-go/state/protocol/protocol_state"
     9  	"github.com/onflow/flow-go/storage"
    10  	"github.com/onflow/flow-go/storage/badger/transaction"
    11  )
    12  
    13  // ProtocolKVStore persists different snapshots of key-value stores [KV-stores]. Here, we augment
    14  // the low-level primitives provided by `storage.ProtocolKVStore` with logic for encoding and
    15  // decoding the state snapshots into abstract representation `protocol_state.KVStoreAPI`.
    16  //
    17  // TODO (optional): include a cache of the _decoded_ Protocol States, so we don't decode+encode on each consensus view (hot-path)
    18  type ProtocolKVStore struct {
    19  	storage.ProtocolKVStore
    20  }
    21  
    22  var _ protocol_state.ProtocolKVStore = (*ProtocolKVStore)(nil)
    23  
    24  // NewProtocolKVStore instantiates a ProtocolKVStore for querying & storing deserialized `protocol_state.KVStoreAPIs`.
    25  // At this abstraction level, we can only handle protocol state snapshots, whose data models are supported by the current
    26  // software version. There might be serialized snapshots with legacy versions in the database, that are not supported
    27  // anymore by this software version and can only be retrieved as versioned binary blobs via `storage.ProtocolKVStore`.
    28  func NewProtocolKVStore(protocolStateSnapshots storage.ProtocolKVStore) *ProtocolKVStore {
    29  	return &ProtocolKVStore{
    30  		ProtocolKVStore: protocolStateSnapshots,
    31  	}
    32  }
    33  
    34  // StoreTx returns an anonymous function (intended to be executed as part of a badger transaction), which persists
    35  // the given KV-store snapshot as part of a DB tx. Per convention, all implementations of `protocol.KVStoreReader`
    36  // must support encoding their state into a version and data blob.
    37  // Expected errors of the returned anonymous function:
    38  //   - storage.ErrAlreadyExists if a KV-store snapshot with the given id is already stored.
    39  func (p *ProtocolKVStore) StoreTx(stateID flow.Identifier, kvStore protocol.KVStoreReader) func(*transaction.Tx) error {
    40  	version, data, err := kvStore.VersionedEncode()
    41  	if err != nil {
    42  		return func(*transaction.Tx) error {
    43  			return fmt.Errorf("failed to VersionedEncode protocol state: %w", err)
    44  		}
    45  	}
    46  	return p.ProtocolKVStore.StoreTx(stateID, &flow.PSKeyValueStoreData{
    47  		Version: version,
    48  		Data:    data,
    49  	})
    50  }
    51  
    52  // ByID retrieves the KV store snapshot with the given ID.
    53  // Expected errors during normal operations:
    54  //   - storage.ErrNotFound if no snapshot with the given Identifier is known.
    55  //   - ErrUnsupportedVersion if input version is not supported
    56  func (p *ProtocolKVStore) ByID(protocolStateID flow.Identifier) (protocol_state.KVStoreAPI, error) {
    57  	versionedData, err := p.ProtocolKVStore.ByID(protocolStateID)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("could not query KV store with ID %x: %w", protocolStateID, err)
    60  	}
    61  	kvStore, err := VersionedDecode(versionedData.Version, versionedData.Data)
    62  	if err != nil {
    63  		return nil, fmt.Errorf("could not decode protocol state (version=%d) with ID %x: %w", versionedData.Version, protocolStateID, err)
    64  	}
    65  	return kvStore, err
    66  }
    67  
    68  // ByBlockID retrieves the kv-store snapshot that the block with the given ID proposes.
    69  // CAUTION: this store snapshot requires confirmation by a QC and will only become active at the child block,
    70  // _after_ validating the QC. Protocol convention:
    71  //   - Consider block B, whose ingestion might potentially lead to an updated KV store state.
    72  //     For example, the state changes if we seal some execution results emitting specific service events.
    73  //   - For the key `blockID`, we use the identity of block B which _proposes_ this updated KV store. As value,
    74  //     the hash of the resulting state at the end of processing B is to be used.
    75  //   - CAUTION: The updated state requires confirmation by a QC and will only become active at the child block,
    76  //     _after_ validating the QC.
    77  //
    78  // Expected errors during normal operations:
    79  //   - storage.ErrNotFound if no snapshot has been indexed for the given block.
    80  //   - ErrUnsupportedVersion if input version is not supported
    81  func (p *ProtocolKVStore) ByBlockID(blockID flow.Identifier) (protocol_state.KVStoreAPI, error) {
    82  	versionedData, err := p.ProtocolKVStore.ByBlockID(blockID)
    83  	if err != nil {
    84  		return nil, fmt.Errorf("could not query KV store at block (%x): %w", blockID, err)
    85  	}
    86  	kvStore, err := VersionedDecode(versionedData.Version, versionedData.Data)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("could not decode protocol state (version=%d) at block (%x): %w", versionedData.Version, blockID, err)
    89  	}
    90  	return kvStore, err
    91  }