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