github.com/onflow/flow-go@v0.33.17/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/state/protocol/snapshots" 18 "github.com/onflow/flow-go/storage" 19 ) 20 21 type backendNetwork struct { 22 state protocol.State 23 chainID flow.ChainID 24 headers storage.Headers 25 snapshotHistoryLimit int 26 } 27 28 /* 29 NetworkAPI func 30 31 The observer and access nodes need to be able to handle GetNetworkParameters 32 and GetLatestProtocolStateSnapshot RPCs so this logic was split into 33 the backendNetwork so that we can ignore the rest of the backend logic 34 */ 35 func NewNetworkAPI( 36 state protocol.State, 37 chainID flow.ChainID, 38 headers storage.Headers, 39 snapshotHistoryLimit int, 40 ) *backendNetwork { 41 return &backendNetwork{ 42 state: state, 43 chainID: chainID, 44 headers: headers, 45 snapshotHistoryLimit: snapshotHistoryLimit, 46 } 47 } 48 49 func (b *backendNetwork) GetNetworkParameters(_ context.Context) access.NetworkParameters { 50 return access.NetworkParameters{ 51 ChainID: b.chainID, 52 } 53 } 54 55 func (b *backendNetwork) GetNodeVersionInfo(ctx context.Context) (*access.NodeVersionInfo, error) { 56 stateParams := b.state.Params() 57 sporkId, err := stateParams.SporkID() 58 if err != nil { 59 return nil, status.Errorf(codes.Internal, "failed to read spork ID: %v", err) 60 } 61 62 protocolVersion, err := stateParams.ProtocolVersion() 63 if err != nil { 64 return nil, status.Errorf(codes.Internal, "failed to read protocol version: %v", err) 65 } 66 67 return &access.NodeVersionInfo{ 68 Semver: build.Version(), 69 Commit: build.Commit(), 70 SporkId: sporkId, 71 ProtocolVersion: uint64(protocolVersion), 72 }, nil 73 } 74 75 // GetLatestProtocolStateSnapshot returns the latest finalized snapshot 76 func (b *backendNetwork) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, error) { 77 snapshot := b.state.Final() 78 79 validSnapshot, err := snapshots.GetClosestDynamicBootstrapSnapshot(b.state, snapshot, b.snapshotHistoryLimit) 80 if err != nil { 81 return nil, err 82 } 83 84 data, err := convert.SnapshotToBytes(validSnapshot) 85 if err != nil { 86 return nil, status.Errorf(codes.Internal, "failed to convert snapshot to bytes: %v", err) 87 } 88 89 return data, nil 90 } 91 92 // GetProtocolStateSnapshotByBlockID returns serializable Snapshot for a block, by blockID. 93 // The requested block must be finalized, otherwise an error is returned. 94 // Expected errors during normal operation: 95 // - status.Error[codes.NotFound] - No block with the given ID was found 96 // - status.Error[codes.InvalidArgument] - Block ID will never have a valid snapshot: 97 // 1. A block was found, but it is not finalized and is below the finalized height, so it will never be finalized. 98 // 2. A block was found, however its sealing segment spans an epoch phase transition, yielding an invalid snapshot. 99 // - status.Error[codes.FailedPrecondition] - A block was found, but it is not finalized and is above the finalized height. 100 // The block may or may not be finalized in the future; the client can retry later. 101 func (b *backendNetwork) GetProtocolStateSnapshotByBlockID(_ context.Context, blockID flow.Identifier) ([]byte, error) { 102 snapshot := b.state.AtBlockID(blockID) 103 snapshotHeadByBlockId, err := snapshot.Head() 104 if err != nil { 105 if errors.Is(err, state.ErrUnknownSnapshotReference) { 106 return nil, status.Errorf(codes.NotFound, "failed to get a valid snapshot: block not found") 107 } 108 109 return nil, status.Errorf(codes.Internal, "could not get header by blockID: %v", err) 110 } 111 112 // Because there is no index from block ID to finalized height, we separately look up the finalized 113 // block ID by the height of the queried block, then compare the queried ID to the finalized ID. 114 // If they match, then the queried block must be finalized. 115 blockIDFinalizedAtHeight, err := b.headers.BlockIDByHeight(snapshotHeadByBlockId.Height) 116 if err != nil { 117 if errors.Is(err, storage.ErrNotFound) { 118 // The block exists, but no block has been finalized at its height. Therefore, this block 119 // may be finalized in the future, and the client can retry. 120 return nil, status.Errorf(codes.FailedPrecondition, 121 "failed to retrieve snapshot for block with height %d: block not finalized and is above finalized height", 122 snapshotHeadByBlockId.Height) 123 } 124 return nil, status.Errorf(codes.Internal, "failed to lookup block id by height %d", snapshotHeadByBlockId.Height) 125 } 126 127 if blockIDFinalizedAtHeight != blockID { 128 // A different block than what was queried has been finalized at this height. 129 // Therefore, the queried block will never be finalized. 130 return nil, status.Errorf(codes.InvalidArgument, 131 "failed to retrieve snapshot for block: block not finalized and is below finalized height") 132 } 133 134 validSnapshot, err := snapshots.GetDynamicBootstrapSnapshot(b.state, snapshot) 135 if err != nil { 136 if errors.Is(err, snapshots.ErrSnapshotPhaseMismatch) { 137 return nil, status.Errorf(codes.InvalidArgument, 138 "failed to retrieve snapshot for block, try again with different block: "+ 139 "%v", err) 140 } 141 return nil, status.Errorf(codes.Internal, "failed to get a valid snapshot: %v", err) 142 } 143 144 data, err := convert.SnapshotToBytes(validSnapshot) 145 if err != nil { 146 return nil, status.Errorf(codes.Internal, "failed to convert snapshot to bytes: %v", err) 147 } 148 149 return data, nil 150 } 151 152 // GetProtocolStateSnapshotByHeight returns serializable Snapshot by block height. 153 // The block must be finalized (otherwise the by-height query is ambiguous). 154 // Expected errors during normal operation: 155 // - status.Error[codes.NotFound] - No block with the given height was found. 156 // The block height may or may not be finalized in the future; the client can retry later. 157 // - status.Error[codes.InvalidArgument] - A block was found, however its sealing segment spans an epoch phase transition, 158 // yielding an invalid snapshot. Therefore we will never return a snapshot for this block height. 159 func (b *backendNetwork) GetProtocolStateSnapshotByHeight(_ context.Context, blockHeight uint64) ([]byte, error) { 160 snapshot := b.state.AtHeight(blockHeight) 161 _, err := snapshot.Head() 162 if err != nil { 163 if errors.Is(err, state.ErrUnknownSnapshotReference) { 164 return nil, status.Errorf(codes.NotFound, "failed to find snapshot: %v", err) 165 } 166 167 return nil, status.Errorf(codes.Internal, "failed to get a valid snapshot: %v", err) 168 } 169 170 validSnapshot, err := snapshots.GetDynamicBootstrapSnapshot(b.state, snapshot) 171 if err != nil { 172 if errors.Is(err, snapshots.ErrSnapshotPhaseMismatch) { 173 return nil, status.Errorf(codes.InvalidArgument, 174 "failed to retrieve snapshot for block, try again with different block: "+ 175 "%v", err) 176 } 177 return nil, status.Errorf(codes.Internal, "failed to get a valid snapshot: %v", err) 178 } 179 180 data, err := convert.SnapshotToBytes(validSnapshot) 181 if err != nil { 182 return nil, status.Errorf(codes.Internal, "failed to convert snapshot to bytes: %v", err) 183 } 184 185 return data, nil 186 }