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 }