github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/rpc/backend/backend_network.go (about) 1 package backend 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/onflow/flow-go/state" 8 9 "google.golang.org/grpc/codes" 10 "google.golang.org/grpc/status" 11 12 "github.com/onflow/flow-go/access" 13 "github.com/onflow/flow-go/cmd/build" 14 "github.com/onflow/flow-go/engine/common/rpc/convert" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/state/protocol" 17 "github.com/onflow/flow-go/storage" 18 ) 19 20 type backendNetwork struct { 21 state protocol.State 22 chainID flow.ChainID 23 headers storage.Headers 24 snapshotHistoryLimit int 25 } 26 27 /* 28 NetworkAPI func 29 30 The observer and access nodes need to be able to handle GetNetworkParameters 31 and GetLatestProtocolStateSnapshot RPCs so this logic was split into 32 the backendNetwork so that we can ignore the rest of the backend logic 33 */ 34 func NewNetworkAPI( 35 state protocol.State, 36 chainID flow.ChainID, 37 headers storage.Headers, 38 snapshotHistoryLimit int, 39 ) *backendNetwork { 40 return &backendNetwork{ 41 state: state, 42 chainID: chainID, 43 headers: headers, 44 snapshotHistoryLimit: snapshotHistoryLimit, 45 } 46 } 47 48 func (b *backendNetwork) GetNetworkParameters(_ context.Context) access.NetworkParameters { 49 return access.NetworkParameters{ 50 ChainID: b.chainID, 51 } 52 } 53 54 func (b *backendNetwork) GetNodeVersionInfo(_ context.Context) (*access.NodeVersionInfo, error) { 55 stateParams := b.state.Params() 56 sporkId := stateParams.SporkID() 57 protocolVersion := stateParams.ProtocolVersion() 58 59 return &access.NodeVersionInfo{ 60 Semver: build.Version(), 61 Commit: build.Commit(), 62 SporkId: sporkId, 63 ProtocolVersion: uint64(protocolVersion), 64 }, nil 65 } 66 67 // GetLatestProtocolStateSnapshot returns the latest finalized snapshot. 68 func (b *backendNetwork) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, error) { 69 snapshot := b.state.Final() 70 data, err := convert.SnapshotToBytes(snapshot) 71 if err != nil { 72 return nil, status.Errorf(codes.Internal, "failed to convert snapshot to bytes: %v", err) 73 } 74 75 return data, nil 76 } 77 78 // GetProtocolStateSnapshotByBlockID returns serializable Snapshot for a block, by blockID. 79 // The requested block must be finalized, otherwise an error is returned. 80 // Expected errors during normal operation: 81 // - status.Error[codes.NotFound] - No block with the given ID was found 82 // - status.Error[codes.InvalidArgument] - Block ID is for an orphaned block and will never have a valid snapshot 83 // - status.Error[codes.FailedPrecondition] - A block was found, but it is not finalized and is above the finalized height. 84 // The block may or may not be finalized in the future; the client can retry later. 85 func (b *backendNetwork) GetProtocolStateSnapshotByBlockID(_ context.Context, blockID flow.Identifier) ([]byte, error) { 86 snapshot := b.state.AtBlockID(blockID) 87 snapshotHeadByBlockId, err := snapshot.Head() 88 if err != nil { 89 if errors.Is(err, state.ErrUnknownSnapshotReference) { 90 return nil, status.Errorf(codes.NotFound, "failed to get a valid snapshot: block not found") 91 } 92 return nil, status.Errorf(codes.Internal, "could not get header by blockID: %v", err) 93 } 94 95 // Because there is no index from block ID to finalized height, we separately look up the finalized 96 // block ID by the height of the queried block, then compare the queried ID to the finalized ID. 97 // If they match, then the queried block must be finalized. 98 blockIDFinalizedAtHeight, err := b.headers.BlockIDByHeight(snapshotHeadByBlockId.Height) 99 if err != nil { 100 if errors.Is(err, storage.ErrNotFound) { 101 // The block exists, but no block has been finalized at its height. Therefore, this block 102 // may be finalized in the future, and the client can retry. 103 return nil, status.Errorf(codes.FailedPrecondition, 104 "failed to retrieve snapshot for block with height %d: block not finalized and is above finalized height", 105 snapshotHeadByBlockId.Height) 106 } 107 return nil, status.Errorf(codes.Internal, "failed to lookup block id by height %d", snapshotHeadByBlockId.Height) 108 } 109 110 if blockIDFinalizedAtHeight != blockID { 111 // A different block than what was queried has been finalized at this height. 112 // Therefore, the queried block will never be finalized. 113 return nil, status.Errorf(codes.InvalidArgument, 114 "failed to retrieve snapshot for block: block not finalized and is below finalized height") 115 } 116 117 data, err := convert.SnapshotToBytes(snapshot) 118 if err != nil { 119 return nil, status.Errorf(codes.Internal, "failed to convert snapshot to bytes: %v", err) 120 } 121 return data, nil 122 } 123 124 // GetProtocolStateSnapshotByHeight returns serializable Snapshot by block height. 125 // The block must be finalized (otherwise the by-height query is ambiguous). 126 // Expected errors during normal operation: 127 // - status.Error[codes.NotFound] - No block with the given height was found. 128 // The block height may or may not be finalized in the future; the client can retry later. 129 func (b *backendNetwork) GetProtocolStateSnapshotByHeight(_ context.Context, blockHeight uint64) ([]byte, error) { 130 snapshot := b.state.AtHeight(blockHeight) 131 _, err := snapshot.Head() 132 if err != nil { 133 if errors.Is(err, state.ErrUnknownSnapshotReference) { 134 return nil, status.Errorf(codes.NotFound, "failed to find snapshot: %v", err) 135 } 136 return nil, status.Errorf(codes.Internal, "failed to get a valid snapshot: %v", err) 137 } 138 139 data, err := convert.SnapshotToBytes(snapshot) 140 if err != nil { 141 return nil, status.Errorf(codes.Internal, "failed to convert snapshot to bytes: %v", err) 142 } 143 return data, nil 144 }