github.com/koko1123/flow-go-1@v0.29.6/utils/debug/remoteView.go (about)

     1  package debug
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"google.golang.org/grpc"
     8  
     9  	"github.com/onflow/flow/protobuf/go/flow/execution"
    10  
    11  	"github.com/koko1123/flow-go-1/fvm/state"
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  )
    14  
    15  // RemoteView provides a view connected to a live execution node to read the registers
    16  // writen values are kept inside a map
    17  //
    18  // TODO implement register touches
    19  type RemoteView struct {
    20  	Parent             *RemoteView
    21  	Delta              map[string]flow.RegisterValue
    22  	Cache              registerCache
    23  	BlockID            []byte
    24  	BlockHeader        *flow.Header
    25  	connection         *grpc.ClientConn
    26  	executionAPIclient execution.ExecutionAPIClient
    27  }
    28  
    29  // A RemoteViewOption sets a configuration parameter for the remote view
    30  type RemoteViewOption func(view *RemoteView) *RemoteView
    31  
    32  // WithFileCache sets the output path to store
    33  // register values so can be fetched from a file cache
    34  // it loads the values from the cache upon object construction
    35  func WithCache(cache registerCache) RemoteViewOption {
    36  	return func(view *RemoteView) *RemoteView {
    37  		view.Cache = cache
    38  		return view
    39  	}
    40  }
    41  
    42  // WithBlockID sets the blockID for the remote view, if not used
    43  // remote view will use the latest sealed block
    44  func WithBlockID(blockID flow.Identifier) RemoteViewOption {
    45  	return func(view *RemoteView) *RemoteView {
    46  		view.BlockID = blockID[:]
    47  		var err error
    48  		view.BlockHeader, err = view.getBlockHeader(blockID)
    49  		if err != nil {
    50  			panic(err)
    51  		}
    52  		return view
    53  	}
    54  }
    55  
    56  func NewRemoteView(grpcAddress string, opts ...RemoteViewOption) *RemoteView {
    57  	conn, err := grpc.Dial(grpcAddress, grpc.WithInsecure()) //nolint:staticcheck
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  
    62  	view := &RemoteView{
    63  		connection:         conn,
    64  		executionAPIclient: execution.NewExecutionAPIClient(conn),
    65  		Delta:              make(map[string]flow.RegisterValue),
    66  		Cache:              newMemRegisterCache(),
    67  	}
    68  
    69  	view.BlockID, view.BlockHeader, err = view.getLatestBlockID()
    70  	if err != nil {
    71  		panic(err)
    72  	}
    73  
    74  	for _, applyOption := range opts {
    75  		view = applyOption(view)
    76  	}
    77  	return view
    78  }
    79  
    80  func (v *RemoteView) Done() {
    81  	v.connection.Close()
    82  }
    83  
    84  func (v *RemoteView) getLatestBlockID() ([]byte, *flow.Header, error) {
    85  	req := &execution.GetLatestBlockHeaderRequest{
    86  		IsSealed: true,
    87  	}
    88  
    89  	resp, err := v.executionAPIclient.GetLatestBlockHeader(context.Background(), req)
    90  	if err != nil {
    91  		return nil, nil, err
    92  	}
    93  
    94  	// TODO set chainID and parentID
    95  	header := &flow.Header{
    96  		Height:    resp.Block.Height,
    97  		Timestamp: resp.Block.Timestamp.AsTime(),
    98  	}
    99  
   100  	return resp.Block.Id, header, nil
   101  }
   102  
   103  func (v *RemoteView) getBlockHeader(blockID flow.Identifier) (*flow.Header, error) {
   104  	req := &execution.GetBlockHeaderByIDRequest{
   105  		Id: blockID[:],
   106  	}
   107  
   108  	resp, err := v.executionAPIclient.GetBlockHeaderByID(context.Background(), req)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	// TODO set chainID and parentID
   114  	header := &flow.Header{
   115  		Height:    resp.Block.Height,
   116  		Timestamp: resp.Block.Timestamp.AsTime(),
   117  	}
   118  
   119  	return header, nil
   120  }
   121  
   122  func (v *RemoteView) NewChild() state.View {
   123  	return &RemoteView{
   124  		Parent:             v,
   125  		executionAPIclient: v.executionAPIclient,
   126  		connection:         v.connection,
   127  		Cache:              newMemRegisterCache(),
   128  		Delta:              make(map[string][]byte),
   129  	}
   130  }
   131  
   132  func (v *RemoteView) MergeView(o state.View) error {
   133  	var other *RemoteView
   134  	var ok bool
   135  	if other, ok = o.(*RemoteView); !ok {
   136  		return fmt.Errorf("can not merge: view type mismatch (given: %T, expected:RemoteView)", o)
   137  	}
   138  
   139  	for k, value := range other.Delta {
   140  		v.Delta[k] = value
   141  	}
   142  	return nil
   143  }
   144  
   145  func (v *RemoteView) DropDelta() {
   146  	v.Delta = make(map[string]flow.RegisterValue)
   147  }
   148  
   149  func (v *RemoteView) Set(owner, key string, value flow.RegisterValue) error {
   150  	v.Delta[owner+"~"+key] = value
   151  	return nil
   152  }
   153  
   154  func (v *RemoteView) Get(owner, key string) (flow.RegisterValue, error) {
   155  
   156  	// first check the delta
   157  	value, found := v.Delta[owner+"~"+key]
   158  	if found {
   159  		return value, nil
   160  	}
   161  
   162  	// then check the read cache
   163  	value, found = v.Cache.Get(owner, key)
   164  	if found {
   165  		return value, nil
   166  	}
   167  
   168  	// then call the parent (if exist)
   169  	if v.Parent != nil {
   170  		return v.Parent.Get(owner, key)
   171  	}
   172  
   173  	// last use the grpc api the
   174  	req := &execution.GetRegisterAtBlockIDRequest{
   175  		BlockId:       []byte(v.BlockID),
   176  		RegisterOwner: []byte(owner),
   177  		RegisterKey:   []byte(key),
   178  	}
   179  
   180  	// TODO use a proper context for timeouts
   181  	resp, err := v.executionAPIclient.GetRegisterAtBlockID(context.Background(), req)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	v.Cache.Set(owner, key, resp.Value)
   187  
   188  	// append value to the file cache
   189  
   190  	return resp.Value, nil
   191  }
   192  
   193  // returns all the registers that has been touched
   194  func (v *RemoteView) AllRegisters() []flow.RegisterID {
   195  	panic("Not implemented yet")
   196  }
   197  
   198  func (v *RemoteView) RegisterUpdates() ([]flow.RegisterID, []flow.RegisterValue) {
   199  	panic("Not implemented yet")
   200  }
   201  
   202  func (v *RemoteView) Touch(owner, key string) error {
   203  	// no-op for now
   204  	return nil
   205  }
   206  
   207  func (v *RemoteView) Delete(owner, key string) error {
   208  	v.Delta[owner+"~"+key] = nil
   209  	return nil
   210  }