github.com/MetalBlockchain/metalgo@v1.11.9/vms/rpcchainvm/vm_client.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package rpcchainvm
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  	"time"
    13  
    14  	"github.com/prometheus/client_golang/prometheus"
    15  	"go.uber.org/zap"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/health"
    18  	"google.golang.org/protobuf/types/known/emptypb"
    19  
    20  	"github.com/MetalBlockchain/metalgo/api/keystore/gkeystore"
    21  	"github.com/MetalBlockchain/metalgo/api/metrics"
    22  	"github.com/MetalBlockchain/metalgo/chains/atomic/gsharedmemory"
    23  	"github.com/MetalBlockchain/metalgo/database"
    24  	"github.com/MetalBlockchain/metalgo/database/rpcdb"
    25  	"github.com/MetalBlockchain/metalgo/ids"
    26  	"github.com/MetalBlockchain/metalgo/ids/galiasreader"
    27  	"github.com/MetalBlockchain/metalgo/snow"
    28  	"github.com/MetalBlockchain/metalgo/snow/choices"
    29  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman"
    30  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    31  	"github.com/MetalBlockchain/metalgo/snow/engine/common/appsender"
    32  	"github.com/MetalBlockchain/metalgo/snow/engine/snowman/block"
    33  	"github.com/MetalBlockchain/metalgo/snow/validators/gvalidators"
    34  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    35  	"github.com/MetalBlockchain/metalgo/utils/resource"
    36  	"github.com/MetalBlockchain/metalgo/utils/units"
    37  	"github.com/MetalBlockchain/metalgo/utils/wrappers"
    38  	"github.com/MetalBlockchain/metalgo/version"
    39  	"github.com/MetalBlockchain/metalgo/vms/components/chain"
    40  	"github.com/MetalBlockchain/metalgo/vms/platformvm/warp/gwarp"
    41  	"github.com/MetalBlockchain/metalgo/vms/rpcchainvm/ghttp"
    42  	"github.com/MetalBlockchain/metalgo/vms/rpcchainvm/grpcutils"
    43  	"github.com/MetalBlockchain/metalgo/vms/rpcchainvm/messenger"
    44  	"github.com/MetalBlockchain/metalgo/vms/rpcchainvm/runtime"
    45  
    46  	aliasreaderpb "github.com/MetalBlockchain/metalgo/proto/pb/aliasreader"
    47  	appsenderpb "github.com/MetalBlockchain/metalgo/proto/pb/appsender"
    48  	httppb "github.com/MetalBlockchain/metalgo/proto/pb/http"
    49  	keystorepb "github.com/MetalBlockchain/metalgo/proto/pb/keystore"
    50  	messengerpb "github.com/MetalBlockchain/metalgo/proto/pb/messenger"
    51  	rpcdbpb "github.com/MetalBlockchain/metalgo/proto/pb/rpcdb"
    52  	sharedmemorypb "github.com/MetalBlockchain/metalgo/proto/pb/sharedmemory"
    53  	validatorstatepb "github.com/MetalBlockchain/metalgo/proto/pb/validatorstate"
    54  	vmpb "github.com/MetalBlockchain/metalgo/proto/pb/vm"
    55  	warppb "github.com/MetalBlockchain/metalgo/proto/pb/warp"
    56  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
    57  	dto "github.com/prometheus/client_model/go"
    58  	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    59  )
    60  
    61  // TODO: Enable these to be configured by the user
    62  const (
    63  	decidedCacheSize    = 64 * units.MiB
    64  	missingCacheSize    = 2048
    65  	unverifiedCacheSize = 64 * units.MiB
    66  	bytesToIDCacheSize  = 64 * units.MiB
    67  )
    68  
    69  var (
    70  	errUnsupportedFXs                       = errors.New("unsupported feature extensions")
    71  	errBatchedParseBlockWrongNumberOfBlocks = errors.New("BatchedParseBlock returned different number of blocks than expected")
    72  
    73  	_ block.ChainVM                      = (*VMClient)(nil)
    74  	_ block.BuildBlockWithContextChainVM = (*VMClient)(nil)
    75  	_ block.BatchedChainVM               = (*VMClient)(nil)
    76  	_ block.StateSyncableVM              = (*VMClient)(nil)
    77  	_ prometheus.Gatherer                = (*VMClient)(nil)
    78  
    79  	_ snowman.Block           = (*blockClient)(nil)
    80  	_ block.WithVerifyContext = (*blockClient)(nil)
    81  
    82  	_ block.StateSummary = (*summaryClient)(nil)
    83  )
    84  
    85  // VMClient is an implementation of a VM that talks over RPC.
    86  type VMClient struct {
    87  	*chain.State
    88  	client         vmpb.VMClient
    89  	runtime        runtime.Stopper
    90  	pid            int
    91  	processTracker resource.ProcessTracker
    92  
    93  	messenger            *messenger.Server
    94  	keystore             *gkeystore.Server
    95  	sharedMemory         *gsharedmemory.Server
    96  	bcLookup             *galiasreader.Server
    97  	appSender            *appsender.Server
    98  	validatorStateServer *gvalidators.Server
    99  	warpSignerServer     *gwarp.Server
   100  
   101  	serverCloser grpcutils.ServerCloser
   102  	conns        []*grpc.ClientConn
   103  
   104  	grpcServerMetrics *grpc_prometheus.ServerMetrics
   105  }
   106  
   107  // NewClient returns a VM connected to a remote VM
   108  func NewClient(clientConn *grpc.ClientConn) *VMClient {
   109  	return &VMClient{
   110  		client: vmpb.NewVMClient(clientConn),
   111  		conns:  []*grpc.ClientConn{clientConn},
   112  	}
   113  }
   114  
   115  // SetProcess gives ownership of the server process to the client.
   116  func (vm *VMClient) SetProcess(runtime runtime.Stopper, pid int, processTracker resource.ProcessTracker) {
   117  	vm.runtime = runtime
   118  	vm.processTracker = processTracker
   119  	vm.pid = pid
   120  	processTracker.TrackProcess(vm.pid)
   121  }
   122  
   123  func (vm *VMClient) Initialize(
   124  	ctx context.Context,
   125  	chainCtx *snow.Context,
   126  	db database.Database,
   127  	genesisBytes []byte,
   128  	upgradeBytes []byte,
   129  	configBytes []byte,
   130  	toEngine chan<- common.Message,
   131  	fxs []*common.Fx,
   132  	appSender common.AppSender,
   133  ) error {
   134  	if len(fxs) != 0 {
   135  		return errUnsupportedFXs
   136  	}
   137  
   138  	// Register metrics
   139  	serverReg, err := metrics.MakeAndRegister(
   140  		chainCtx.Metrics,
   141  		"rpcchainvm",
   142  	)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	vm.grpcServerMetrics = grpc_prometheus.NewServerMetrics()
   147  	if err := serverReg.Register(vm.grpcServerMetrics); err != nil {
   148  		return err
   149  	}
   150  
   151  	if err := chainCtx.Metrics.Register("plugin", vm); err != nil {
   152  		return err
   153  	}
   154  
   155  	// Initialize the database
   156  	dbServerListener, err := grpcutils.NewListener()
   157  	if err != nil {
   158  		return err
   159  	}
   160  	dbServerAddr := dbServerListener.Addr().String()
   161  
   162  	go grpcutils.Serve(dbServerListener, vm.newDBServer(db))
   163  	chainCtx.Log.Info("grpc: serving database",
   164  		zap.String("address", dbServerAddr),
   165  	)
   166  
   167  	vm.messenger = messenger.NewServer(toEngine)
   168  	vm.keystore = gkeystore.NewServer(chainCtx.Keystore)
   169  	vm.sharedMemory = gsharedmemory.NewServer(chainCtx.SharedMemory, db)
   170  	vm.bcLookup = galiasreader.NewServer(chainCtx.BCLookup)
   171  	vm.appSender = appsender.NewServer(appSender)
   172  	vm.validatorStateServer = gvalidators.NewServer(chainCtx.ValidatorState)
   173  	vm.warpSignerServer = gwarp.NewServer(chainCtx.WarpSigner)
   174  
   175  	serverListener, err := grpcutils.NewListener()
   176  	if err != nil {
   177  		return err
   178  	}
   179  	serverAddr := serverListener.Addr().String()
   180  
   181  	go grpcutils.Serve(serverListener, vm.newInitServer())
   182  	chainCtx.Log.Info("grpc: serving vm services",
   183  		zap.String("address", serverAddr),
   184  	)
   185  
   186  	resp, err := vm.client.Initialize(ctx, &vmpb.InitializeRequest{
   187  		NetworkId:    chainCtx.NetworkID,
   188  		SubnetId:     chainCtx.SubnetID[:],
   189  		ChainId:      chainCtx.ChainID[:],
   190  		NodeId:       chainCtx.NodeID.Bytes(),
   191  		PublicKey:    bls.PublicKeyToCompressedBytes(chainCtx.PublicKey),
   192  		XChainId:     chainCtx.XChainID[:],
   193  		CChainId:     chainCtx.CChainID[:],
   194  		AvaxAssetId:  chainCtx.AVAXAssetID[:],
   195  		ChainDataDir: chainCtx.ChainDataDir,
   196  		GenesisBytes: genesisBytes,
   197  		UpgradeBytes: upgradeBytes,
   198  		ConfigBytes:  configBytes,
   199  		DbServerAddr: dbServerAddr,
   200  		ServerAddr:   serverAddr,
   201  	})
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	id, err := ids.ToID(resp.LastAcceptedId)
   207  	if err != nil {
   208  		return err
   209  	}
   210  	parentID, err := ids.ToID(resp.LastAcceptedParentId)
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	time, err := grpcutils.TimestampAsTime(resp.Timestamp)
   216  	if err != nil {
   217  		return err
   218  	}
   219  
   220  	// We don't need to check whether this is a block.WithVerifyContext because
   221  	// we'll never Verify this block.
   222  	lastAcceptedBlk := &blockClient{
   223  		vm:       vm,
   224  		id:       id,
   225  		parentID: parentID,
   226  		status:   choices.Accepted,
   227  		bytes:    resp.Bytes,
   228  		height:   resp.Height,
   229  		time:     time,
   230  	}
   231  
   232  	vm.State, err = chain.NewMeteredState(
   233  		serverReg,
   234  		&chain.Config{
   235  			DecidedCacheSize:      decidedCacheSize,
   236  			MissingCacheSize:      missingCacheSize,
   237  			UnverifiedCacheSize:   unverifiedCacheSize,
   238  			BytesToIDCacheSize:    bytesToIDCacheSize,
   239  			LastAcceptedBlock:     lastAcceptedBlk,
   240  			GetBlock:              vm.getBlock,
   241  			UnmarshalBlock:        vm.parseBlock,
   242  			BatchedUnmarshalBlock: vm.batchedParseBlock,
   243  			BuildBlock:            vm.buildBlock,
   244  			BuildBlockWithContext: vm.buildBlockWithContext,
   245  		},
   246  	)
   247  	return err
   248  }
   249  
   250  func (vm *VMClient) newDBServer(db database.Database) *grpc.Server {
   251  	server := grpcutils.NewServer(
   252  		grpcutils.WithUnaryInterceptor(vm.grpcServerMetrics.UnaryServerInterceptor()),
   253  		grpcutils.WithStreamInterceptor(vm.grpcServerMetrics.StreamServerInterceptor()),
   254  	)
   255  
   256  	// See https://github.com/grpc/grpc/blob/master/doc/health-checking.md
   257  	grpcHealth := health.NewServer()
   258  	grpcHealth.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
   259  
   260  	vm.serverCloser.Add(server)
   261  
   262  	// Register services
   263  	rpcdbpb.RegisterDatabaseServer(server, rpcdb.NewServer(db))
   264  	healthpb.RegisterHealthServer(server, grpcHealth)
   265  
   266  	// Ensure metric counters are zeroed on restart
   267  	grpc_prometheus.Register(server)
   268  
   269  	return server
   270  }
   271  
   272  func (vm *VMClient) newInitServer() *grpc.Server {
   273  	server := grpcutils.NewServer(
   274  		grpcutils.WithUnaryInterceptor(vm.grpcServerMetrics.UnaryServerInterceptor()),
   275  		grpcutils.WithStreamInterceptor(vm.grpcServerMetrics.StreamServerInterceptor()),
   276  	)
   277  
   278  	// See https://github.com/grpc/grpc/blob/master/doc/health-checking.md
   279  	grpcHealth := health.NewServer()
   280  	grpcHealth.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
   281  
   282  	vm.serverCloser.Add(server)
   283  
   284  	// Register services
   285  	messengerpb.RegisterMessengerServer(server, vm.messenger)
   286  	keystorepb.RegisterKeystoreServer(server, vm.keystore)
   287  	sharedmemorypb.RegisterSharedMemoryServer(server, vm.sharedMemory)
   288  	aliasreaderpb.RegisterAliasReaderServer(server, vm.bcLookup)
   289  	appsenderpb.RegisterAppSenderServer(server, vm.appSender)
   290  	healthpb.RegisterHealthServer(server, grpcHealth)
   291  	validatorstatepb.RegisterValidatorStateServer(server, vm.validatorStateServer)
   292  	warppb.RegisterSignerServer(server, vm.warpSignerServer)
   293  
   294  	// Ensure metric counters are zeroed on restart
   295  	grpc_prometheus.Register(server)
   296  
   297  	return server
   298  }
   299  
   300  func (vm *VMClient) SetState(ctx context.Context, state snow.State) error {
   301  	resp, err := vm.client.SetState(ctx, &vmpb.SetStateRequest{
   302  		State: vmpb.State(state),
   303  	})
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	id, err := ids.ToID(resp.LastAcceptedId)
   309  	if err != nil {
   310  		return err
   311  	}
   312  
   313  	parentID, err := ids.ToID(resp.LastAcceptedParentId)
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	time, err := grpcutils.TimestampAsTime(resp.Timestamp)
   319  	if err != nil {
   320  		return err
   321  	}
   322  
   323  	// We don't need to check whether this is a block.WithVerifyContext because
   324  	// we'll never Verify this block.
   325  	return vm.State.SetLastAcceptedBlock(&blockClient{
   326  		vm:       vm,
   327  		id:       id,
   328  		parentID: parentID,
   329  		status:   choices.Accepted,
   330  		bytes:    resp.Bytes,
   331  		height:   resp.Height,
   332  		time:     time,
   333  	})
   334  }
   335  
   336  func (vm *VMClient) Shutdown(ctx context.Context) error {
   337  	errs := wrappers.Errs{}
   338  	_, err := vm.client.Shutdown(ctx, &emptypb.Empty{})
   339  	errs.Add(err)
   340  
   341  	vm.serverCloser.Stop()
   342  	for _, conn := range vm.conns {
   343  		errs.Add(conn.Close())
   344  	}
   345  
   346  	vm.runtime.Stop(ctx)
   347  
   348  	vm.processTracker.UntrackProcess(vm.pid)
   349  	return errs.Err
   350  }
   351  
   352  func (vm *VMClient) CreateHandlers(ctx context.Context) (map[string]http.Handler, error) {
   353  	resp, err := vm.client.CreateHandlers(ctx, &emptypb.Empty{})
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  
   358  	handlers := make(map[string]http.Handler, len(resp.Handlers))
   359  	for _, handler := range resp.Handlers {
   360  		clientConn, err := grpcutils.Dial(handler.ServerAddr)
   361  		if err != nil {
   362  			return nil, err
   363  		}
   364  
   365  		vm.conns = append(vm.conns, clientConn)
   366  		handlers[handler.Prefix] = ghttp.NewClient(httppb.NewHTTPClient(clientConn))
   367  	}
   368  	return handlers, nil
   369  }
   370  
   371  func (vm *VMClient) Connected(ctx context.Context, nodeID ids.NodeID, nodeVersion *version.Application) error {
   372  	_, err := vm.client.Connected(ctx, &vmpb.ConnectedRequest{
   373  		NodeId: nodeID.Bytes(),
   374  		Name:   nodeVersion.Name,
   375  		Major:  uint32(nodeVersion.Major),
   376  		Minor:  uint32(nodeVersion.Minor),
   377  		Patch:  uint32(nodeVersion.Patch),
   378  	})
   379  	return err
   380  }
   381  
   382  func (vm *VMClient) Disconnected(ctx context.Context, nodeID ids.NodeID) error {
   383  	_, err := vm.client.Disconnected(ctx, &vmpb.DisconnectedRequest{
   384  		NodeId: nodeID.Bytes(),
   385  	})
   386  	return err
   387  }
   388  
   389  // If the underlying VM doesn't actually implement this method, its [BuildBlock]
   390  // method will be called instead.
   391  func (vm *VMClient) buildBlockWithContext(ctx context.Context, blockCtx *block.Context) (snowman.Block, error) {
   392  	resp, err := vm.client.BuildBlock(ctx, &vmpb.BuildBlockRequest{
   393  		PChainHeight: &blockCtx.PChainHeight,
   394  	})
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  	return vm.newBlockFromBuildBlock(resp)
   399  }
   400  
   401  func (vm *VMClient) buildBlock(ctx context.Context) (snowman.Block, error) {
   402  	resp, err := vm.client.BuildBlock(ctx, &vmpb.BuildBlockRequest{})
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  	return vm.newBlockFromBuildBlock(resp)
   407  }
   408  
   409  func (vm *VMClient) parseBlock(ctx context.Context, bytes []byte) (snowman.Block, error) {
   410  	resp, err := vm.client.ParseBlock(ctx, &vmpb.ParseBlockRequest{
   411  		Bytes: bytes,
   412  	})
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  
   417  	id, err := ids.ToID(resp.Id)
   418  	if err != nil {
   419  		return nil, err
   420  	}
   421  
   422  	parentID, err := ids.ToID(resp.ParentId)
   423  	if err != nil {
   424  		return nil, err
   425  	}
   426  
   427  	status := choices.Status(resp.Status)
   428  	if err := status.Valid(); err != nil {
   429  		return nil, err
   430  	}
   431  
   432  	time, err := grpcutils.TimestampAsTime(resp.Timestamp)
   433  	if err != nil {
   434  		return nil, err
   435  	}
   436  	return &blockClient{
   437  		vm:                  vm,
   438  		id:                  id,
   439  		parentID:            parentID,
   440  		status:              status,
   441  		bytes:               bytes,
   442  		height:              resp.Height,
   443  		time:                time,
   444  		shouldVerifyWithCtx: resp.VerifyWithContext,
   445  	}, nil
   446  }
   447  
   448  func (vm *VMClient) getBlock(ctx context.Context, blkID ids.ID) (snowman.Block, error) {
   449  	resp, err := vm.client.GetBlock(ctx, &vmpb.GetBlockRequest{
   450  		Id: blkID[:],
   451  	})
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	if errEnum := resp.Err; errEnum != vmpb.Error_ERROR_UNSPECIFIED {
   456  		return nil, errEnumToError[errEnum]
   457  	}
   458  
   459  	parentID, err := ids.ToID(resp.ParentId)
   460  	if err != nil {
   461  		return nil, err
   462  	}
   463  
   464  	status := choices.Status(resp.Status)
   465  	if err := status.Valid(); err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	time, err := grpcutils.TimestampAsTime(resp.Timestamp)
   470  	return &blockClient{
   471  		vm:                  vm,
   472  		id:                  blkID,
   473  		parentID:            parentID,
   474  		status:              status,
   475  		bytes:               resp.Bytes,
   476  		height:              resp.Height,
   477  		time:                time,
   478  		shouldVerifyWithCtx: resp.VerifyWithContext,
   479  	}, err
   480  }
   481  
   482  func (vm *VMClient) SetPreference(ctx context.Context, blkID ids.ID) error {
   483  	_, err := vm.client.SetPreference(ctx, &vmpb.SetPreferenceRequest{
   484  		Id: blkID[:],
   485  	})
   486  	return err
   487  }
   488  
   489  func (vm *VMClient) HealthCheck(ctx context.Context) (interface{}, error) {
   490  	// HealthCheck is a special case, where we want to fail fast instead of block.
   491  	failFast := grpc.WaitForReady(false)
   492  	health, err := vm.client.Health(ctx, &emptypb.Empty{}, failFast)
   493  	if err != nil {
   494  		return nil, fmt.Errorf("health check failed: %w", err)
   495  	}
   496  
   497  	return json.RawMessage(health.Details), nil
   498  }
   499  
   500  func (vm *VMClient) Version(ctx context.Context) (string, error) {
   501  	resp, err := vm.client.Version(ctx, &emptypb.Empty{})
   502  	if err != nil {
   503  		return "", err
   504  	}
   505  	return resp.Version, nil
   506  }
   507  
   508  func (vm *VMClient) CrossChainAppRequest(ctx context.Context, chainID ids.ID, requestID uint32, deadline time.Time, request []byte) error {
   509  	_, err := vm.client.CrossChainAppRequest(
   510  		ctx,
   511  		&vmpb.CrossChainAppRequestMsg{
   512  			ChainId:   chainID[:],
   513  			RequestId: requestID,
   514  			Deadline:  grpcutils.TimestampFromTime(deadline),
   515  			Request:   request,
   516  		},
   517  	)
   518  	return err
   519  }
   520  
   521  func (vm *VMClient) CrossChainAppRequestFailed(ctx context.Context, chainID ids.ID, requestID uint32, appErr *common.AppError) error {
   522  	msg := &vmpb.CrossChainAppRequestFailedMsg{
   523  		ChainId:      chainID[:],
   524  		RequestId:    requestID,
   525  		ErrorCode:    appErr.Code,
   526  		ErrorMessage: appErr.Message,
   527  	}
   528  
   529  	_, err := vm.client.CrossChainAppRequestFailed(ctx, msg)
   530  	return err
   531  }
   532  
   533  func (vm *VMClient) CrossChainAppResponse(ctx context.Context, chainID ids.ID, requestID uint32, response []byte) error {
   534  	_, err := vm.client.CrossChainAppResponse(
   535  		ctx,
   536  		&vmpb.CrossChainAppResponseMsg{
   537  			ChainId:   chainID[:],
   538  			RequestId: requestID,
   539  			Response:  response,
   540  		},
   541  	)
   542  	return err
   543  }
   544  
   545  func (vm *VMClient) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, request []byte) error {
   546  	_, err := vm.client.AppRequest(
   547  		ctx,
   548  		&vmpb.AppRequestMsg{
   549  			NodeId:    nodeID.Bytes(),
   550  			RequestId: requestID,
   551  			Request:   request,
   552  			Deadline:  grpcutils.TimestampFromTime(deadline),
   553  		},
   554  	)
   555  	return err
   556  }
   557  
   558  func (vm *VMClient) AppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error {
   559  	_, err := vm.client.AppResponse(
   560  		ctx,
   561  		&vmpb.AppResponseMsg{
   562  			NodeId:    nodeID.Bytes(),
   563  			RequestId: requestID,
   564  			Response:  response,
   565  		},
   566  	)
   567  	return err
   568  }
   569  
   570  func (vm *VMClient) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *common.AppError) error {
   571  	msg := &vmpb.AppRequestFailedMsg{
   572  		NodeId:       nodeID.Bytes(),
   573  		RequestId:    requestID,
   574  		ErrorCode:    appErr.Code,
   575  		ErrorMessage: appErr.Message,
   576  	}
   577  
   578  	_, err := vm.client.AppRequestFailed(ctx, msg)
   579  	return err
   580  }
   581  
   582  func (vm *VMClient) AppGossip(ctx context.Context, nodeID ids.NodeID, msg []byte) error {
   583  	_, err := vm.client.AppGossip(
   584  		ctx,
   585  		&vmpb.AppGossipMsg{
   586  			NodeId: nodeID.Bytes(),
   587  			Msg:    msg,
   588  		},
   589  	)
   590  	return err
   591  }
   592  
   593  func (vm *VMClient) Gather() ([]*dto.MetricFamily, error) {
   594  	resp, err := vm.client.Gather(context.Background(), &emptypb.Empty{})
   595  	if err != nil {
   596  		return nil, err
   597  	}
   598  	return resp.MetricFamilies, nil
   599  }
   600  
   601  func (vm *VMClient) GetAncestors(
   602  	ctx context.Context,
   603  	blkID ids.ID,
   604  	maxBlocksNum int,
   605  	maxBlocksSize int,
   606  	maxBlocksRetrivalTime time.Duration,
   607  ) ([][]byte, error) {
   608  	resp, err := vm.client.GetAncestors(ctx, &vmpb.GetAncestorsRequest{
   609  		BlkId:                 blkID[:],
   610  		MaxBlocksNum:          int32(maxBlocksNum),
   611  		MaxBlocksSize:         int32(maxBlocksSize),
   612  		MaxBlocksRetrivalTime: int64(maxBlocksRetrivalTime),
   613  	})
   614  	if err != nil {
   615  		return nil, err
   616  	}
   617  	return resp.BlksBytes, nil
   618  }
   619  
   620  func (vm *VMClient) batchedParseBlock(ctx context.Context, blksBytes [][]byte) ([]snowman.Block, error) {
   621  	resp, err := vm.client.BatchedParseBlock(ctx, &vmpb.BatchedParseBlockRequest{
   622  		Request: blksBytes,
   623  	})
   624  	if err != nil {
   625  		return nil, err
   626  	}
   627  	if len(blksBytes) != len(resp.Response) {
   628  		return nil, errBatchedParseBlockWrongNumberOfBlocks
   629  	}
   630  
   631  	res := make([]snowman.Block, 0, len(blksBytes))
   632  	for idx, blkResp := range resp.Response {
   633  		id, err := ids.ToID(blkResp.Id)
   634  		if err != nil {
   635  			return nil, err
   636  		}
   637  
   638  		parentID, err := ids.ToID(blkResp.ParentId)
   639  		if err != nil {
   640  			return nil, err
   641  		}
   642  
   643  		status := choices.Status(blkResp.Status)
   644  		if err := status.Valid(); err != nil {
   645  			return nil, err
   646  		}
   647  
   648  		time, err := grpcutils.TimestampAsTime(blkResp.Timestamp)
   649  		if err != nil {
   650  			return nil, err
   651  		}
   652  
   653  		res = append(res, &blockClient{
   654  			vm:                  vm,
   655  			id:                  id,
   656  			parentID:            parentID,
   657  			status:              status,
   658  			bytes:               blksBytes[idx],
   659  			height:              blkResp.Height,
   660  			time:                time,
   661  			shouldVerifyWithCtx: blkResp.VerifyWithContext,
   662  		})
   663  	}
   664  
   665  	return res, nil
   666  }
   667  
   668  func (vm *VMClient) GetBlockIDAtHeight(ctx context.Context, height uint64) (ids.ID, error) {
   669  	resp, err := vm.client.GetBlockIDAtHeight(
   670  		ctx,
   671  		&vmpb.GetBlockIDAtHeightRequest{Height: height},
   672  	)
   673  	if err != nil {
   674  		return ids.Empty, err
   675  	}
   676  	if errEnum := resp.Err; errEnum != vmpb.Error_ERROR_UNSPECIFIED {
   677  		return ids.Empty, errEnumToError[errEnum]
   678  	}
   679  	return ids.ToID(resp.BlkId)
   680  }
   681  
   682  func (vm *VMClient) StateSyncEnabled(ctx context.Context) (bool, error) {
   683  	resp, err := vm.client.StateSyncEnabled(ctx, &emptypb.Empty{})
   684  	if err != nil {
   685  		return false, err
   686  	}
   687  	err = errEnumToError[resp.Err]
   688  	if err == block.ErrStateSyncableVMNotImplemented {
   689  		return false, nil
   690  	}
   691  	return resp.Enabled, err
   692  }
   693  
   694  func (vm *VMClient) GetOngoingSyncStateSummary(ctx context.Context) (block.StateSummary, error) {
   695  	resp, err := vm.client.GetOngoingSyncStateSummary(ctx, &emptypb.Empty{})
   696  	if err != nil {
   697  		return nil, err
   698  	}
   699  	if errEnum := resp.Err; errEnum != vmpb.Error_ERROR_UNSPECIFIED {
   700  		return nil, errEnumToError[errEnum]
   701  	}
   702  
   703  	summaryID, err := ids.ToID(resp.Id)
   704  	return &summaryClient{
   705  		vm:     vm,
   706  		id:     summaryID,
   707  		height: resp.Height,
   708  		bytes:  resp.Bytes,
   709  	}, err
   710  }
   711  
   712  func (vm *VMClient) GetLastStateSummary(ctx context.Context) (block.StateSummary, error) {
   713  	resp, err := vm.client.GetLastStateSummary(ctx, &emptypb.Empty{})
   714  	if err != nil {
   715  		return nil, err
   716  	}
   717  	if errEnum := resp.Err; errEnum != vmpb.Error_ERROR_UNSPECIFIED {
   718  		return nil, errEnumToError[errEnum]
   719  	}
   720  
   721  	summaryID, err := ids.ToID(resp.Id)
   722  	return &summaryClient{
   723  		vm:     vm,
   724  		id:     summaryID,
   725  		height: resp.Height,
   726  		bytes:  resp.Bytes,
   727  	}, err
   728  }
   729  
   730  func (vm *VMClient) ParseStateSummary(ctx context.Context, summaryBytes []byte) (block.StateSummary, error) {
   731  	resp, err := vm.client.ParseStateSummary(
   732  		ctx,
   733  		&vmpb.ParseStateSummaryRequest{
   734  			Bytes: summaryBytes,
   735  		},
   736  	)
   737  	if err != nil {
   738  		return nil, err
   739  	}
   740  	if errEnum := resp.Err; errEnum != vmpb.Error_ERROR_UNSPECIFIED {
   741  		return nil, errEnumToError[errEnum]
   742  	}
   743  
   744  	summaryID, err := ids.ToID(resp.Id)
   745  	return &summaryClient{
   746  		vm:     vm,
   747  		id:     summaryID,
   748  		height: resp.Height,
   749  		bytes:  summaryBytes,
   750  	}, err
   751  }
   752  
   753  func (vm *VMClient) GetStateSummary(ctx context.Context, summaryHeight uint64) (block.StateSummary, error) {
   754  	resp, err := vm.client.GetStateSummary(
   755  		ctx,
   756  		&vmpb.GetStateSummaryRequest{
   757  			Height: summaryHeight,
   758  		},
   759  	)
   760  	if err != nil {
   761  		return nil, err
   762  	}
   763  	if errEnum := resp.Err; errEnum != vmpb.Error_ERROR_UNSPECIFIED {
   764  		return nil, errEnumToError[errEnum]
   765  	}
   766  
   767  	summaryID, err := ids.ToID(resp.Id)
   768  	return &summaryClient{
   769  		vm:     vm,
   770  		id:     summaryID,
   771  		height: summaryHeight,
   772  		bytes:  resp.Bytes,
   773  	}, err
   774  }
   775  
   776  func (vm *VMClient) newBlockFromBuildBlock(resp *vmpb.BuildBlockResponse) (*blockClient, error) {
   777  	id, err := ids.ToID(resp.Id)
   778  	if err != nil {
   779  		return nil, err
   780  	}
   781  
   782  	parentID, err := ids.ToID(resp.ParentId)
   783  	if err != nil {
   784  		return nil, err
   785  	}
   786  
   787  	time, err := grpcutils.TimestampAsTime(resp.Timestamp)
   788  	return &blockClient{
   789  		vm:                  vm,
   790  		id:                  id,
   791  		parentID:            parentID,
   792  		status:              choices.Processing,
   793  		bytes:               resp.Bytes,
   794  		height:              resp.Height,
   795  		time:                time,
   796  		shouldVerifyWithCtx: resp.VerifyWithContext,
   797  	}, err
   798  }
   799  
   800  type blockClient struct {
   801  	vm *VMClient
   802  
   803  	id                  ids.ID
   804  	parentID            ids.ID
   805  	status              choices.Status
   806  	bytes               []byte
   807  	height              uint64
   808  	time                time.Time
   809  	shouldVerifyWithCtx bool
   810  }
   811  
   812  func (b *blockClient) ID() ids.ID {
   813  	return b.id
   814  }
   815  
   816  func (b *blockClient) Accept(ctx context.Context) error {
   817  	b.status = choices.Accepted
   818  	_, err := b.vm.client.BlockAccept(ctx, &vmpb.BlockAcceptRequest{
   819  		Id: b.id[:],
   820  	})
   821  	return err
   822  }
   823  
   824  func (b *blockClient) Reject(ctx context.Context) error {
   825  	b.status = choices.Rejected
   826  	_, err := b.vm.client.BlockReject(ctx, &vmpb.BlockRejectRequest{
   827  		Id: b.id[:],
   828  	})
   829  	return err
   830  }
   831  
   832  func (b *blockClient) Status() choices.Status {
   833  	return b.status
   834  }
   835  
   836  func (b *blockClient) Parent() ids.ID {
   837  	return b.parentID
   838  }
   839  
   840  func (b *blockClient) Verify(ctx context.Context) error {
   841  	resp, err := b.vm.client.BlockVerify(ctx, &vmpb.BlockVerifyRequest{
   842  		Bytes: b.bytes,
   843  	})
   844  	if err != nil {
   845  		return err
   846  	}
   847  
   848  	b.time, err = grpcutils.TimestampAsTime(resp.Timestamp)
   849  	return err
   850  }
   851  
   852  func (b *blockClient) Bytes() []byte {
   853  	return b.bytes
   854  }
   855  
   856  func (b *blockClient) Height() uint64 {
   857  	return b.height
   858  }
   859  
   860  func (b *blockClient) Timestamp() time.Time {
   861  	return b.time
   862  }
   863  
   864  func (b *blockClient) ShouldVerifyWithContext(context.Context) (bool, error) {
   865  	return b.shouldVerifyWithCtx, nil
   866  }
   867  
   868  func (b *blockClient) VerifyWithContext(ctx context.Context, blockCtx *block.Context) error {
   869  	resp, err := b.vm.client.BlockVerify(ctx, &vmpb.BlockVerifyRequest{
   870  		Bytes:        b.bytes,
   871  		PChainHeight: &blockCtx.PChainHeight,
   872  	})
   873  	if err != nil {
   874  		return err
   875  	}
   876  
   877  	b.time, err = grpcutils.TimestampAsTime(resp.Timestamp)
   878  	return err
   879  }
   880  
   881  type summaryClient struct {
   882  	vm *VMClient
   883  
   884  	id     ids.ID
   885  	height uint64
   886  	bytes  []byte
   887  }
   888  
   889  func (s *summaryClient) ID() ids.ID {
   890  	return s.id
   891  }
   892  
   893  func (s *summaryClient) Height() uint64 {
   894  	return s.height
   895  }
   896  
   897  func (s *summaryClient) Bytes() []byte {
   898  	return s.bytes
   899  }
   900  
   901  func (s *summaryClient) Accept(ctx context.Context) (block.StateSyncMode, error) {
   902  	resp, err := s.vm.client.StateSummaryAccept(
   903  		ctx,
   904  		&vmpb.StateSummaryAcceptRequest{
   905  			Bytes: s.bytes,
   906  		},
   907  	)
   908  	if err != nil {
   909  		return block.StateSyncSkipped, err
   910  	}
   911  	return block.StateSyncMode(resp.Mode), errEnumToError[resp.Err]
   912  }