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  }