github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/utils/debug/remoteView.go (about)

     1  package debug
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/onflow/flow/protobuf/go/flow/execution"
     7  	"google.golang.org/grpc"
     8  	"google.golang.org/grpc/credentials/insecure"
     9  
    10  	"github.com/onflow/flow-go/model/flow"
    11  )
    12  
    13  // RemoteStorageSnapshot provides a storage snapshot connected to a live
    14  // execution node to read the registers.
    15  type RemoteStorageSnapshot struct {
    16  	Cache              registerCache
    17  	BlockID            []byte
    18  	BlockHeader        *flow.Header
    19  	connection         *grpc.ClientConn
    20  	executionAPIclient execution.ExecutionAPIClient
    21  }
    22  
    23  // A RemoteStorageSnapshotOption sets a configuration parameter for the remote
    24  // snapshot
    25  type RemoteStorageSnapshotOption func(*RemoteStorageSnapshot) *RemoteStorageSnapshot
    26  
    27  // WithFileCache sets the output path to store
    28  // register values so can be fetched from a file cache
    29  // it loads the values from the cache upon object construction
    30  func WithCache(cache registerCache) RemoteStorageSnapshotOption {
    31  	return func(snapshot *RemoteStorageSnapshot) *RemoteStorageSnapshot {
    32  		snapshot.Cache = cache
    33  		return snapshot
    34  	}
    35  }
    36  
    37  // WithBlockID sets the blockID for the remote snapshot, if not used
    38  // remote snapshot will use the latest sealed block
    39  func WithBlockID(blockID flow.Identifier) RemoteStorageSnapshotOption {
    40  	return func(snapshot *RemoteStorageSnapshot) *RemoteStorageSnapshot {
    41  		snapshot.BlockID = blockID[:]
    42  		var err error
    43  		snapshot.BlockHeader, err = snapshot.getBlockHeader(blockID)
    44  		if err != nil {
    45  			panic(err)
    46  		}
    47  		return snapshot
    48  	}
    49  }
    50  
    51  func NewRemoteStorageSnapshot(
    52  	grpcAddress string,
    53  	opts ...RemoteStorageSnapshotOption,
    54  ) *RemoteStorageSnapshot {
    55  	conn, err := grpc.Dial(
    56  		grpcAddress,
    57  		grpc.WithTransportCredentials(insecure.NewCredentials()))
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  
    62  	snapshot := &RemoteStorageSnapshot{
    63  		connection:         conn,
    64  		executionAPIclient: execution.NewExecutionAPIClient(conn),
    65  		Cache:              newMemRegisterCache(),
    66  	}
    67  
    68  	snapshot.BlockID, snapshot.BlockHeader, err = snapshot.getLatestBlockID()
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  
    73  	for _, applyOption := range opts {
    74  		snapshot = applyOption(snapshot)
    75  	}
    76  	return snapshot
    77  }
    78  
    79  func (snapshot *RemoteStorageSnapshot) Close() error {
    80  	return snapshot.connection.Close()
    81  }
    82  
    83  func (snapshot *RemoteStorageSnapshot) getLatestBlockID() (
    84  	[]byte,
    85  	*flow.Header,
    86  	error,
    87  ) {
    88  	req := &execution.GetLatestBlockHeaderRequest{
    89  		IsSealed: true,
    90  	}
    91  
    92  	resp, err := snapshot.executionAPIclient.GetLatestBlockHeader(
    93  		context.Background(),
    94  		req)
    95  	if err != nil {
    96  		return nil, nil, err
    97  	}
    98  
    99  	// TODO set chainID and parentID
   100  	header := &flow.Header{
   101  		Height:    resp.Block.Height,
   102  		Timestamp: resp.Block.Timestamp.AsTime(),
   103  	}
   104  
   105  	return resp.Block.Id, header, nil
   106  }
   107  
   108  func (snapshot *RemoteStorageSnapshot) getBlockHeader(
   109  	blockID flow.Identifier,
   110  ) (
   111  	*flow.Header,
   112  	error,
   113  ) {
   114  	req := &execution.GetBlockHeaderByIDRequest{
   115  		Id: blockID[:],
   116  	}
   117  
   118  	resp, err := snapshot.executionAPIclient.GetBlockHeaderByID(
   119  		context.Background(),
   120  		req)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	// TODO set chainID and parentID
   126  	header := &flow.Header{
   127  		Height:    resp.Block.Height,
   128  		Timestamp: resp.Block.Timestamp.AsTime(),
   129  	}
   130  
   131  	return header, nil
   132  }
   133  
   134  func (snapshot *RemoteStorageSnapshot) Get(
   135  	id flow.RegisterID,
   136  ) (
   137  	flow.RegisterValue,
   138  	error,
   139  ) {
   140  	// then check the read cache
   141  	value, found := snapshot.Cache.Get(id.Owner, id.Key)
   142  	if found {
   143  		return value, nil
   144  	}
   145  
   146  	// last use the grpc api the
   147  	req := &execution.GetRegisterAtBlockIDRequest{
   148  		BlockId:       []byte(snapshot.BlockID),
   149  		RegisterOwner: []byte(id.Owner),
   150  		RegisterKey:   []byte(id.Key),
   151  	}
   152  
   153  	// TODO use a proper context for timeouts
   154  	resp, err := snapshot.executionAPIclient.GetRegisterAtBlockID(
   155  		context.Background(),
   156  		req)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	snapshot.Cache.Set(id.Owner, id.Key, resp.Value)
   162  
   163  	// append value to the file cache
   164  
   165  	return resp.Value, nil
   166  }