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 }