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  }