github.com/MetalBlockchain/subnet-evm@v0.4.9/plugin/evm/vm.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package evm 5 6 import ( 7 "context" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "os" 12 "path/filepath" 13 "strings" 14 "sync" 15 "time" 16 17 avalanchegoMetrics "github.com/MetalBlockchain/metalgo/api/metrics" 18 "github.com/prometheus/client_golang/prometheus" 19 20 "github.com/MetalBlockchain/subnet-evm/commontype" 21 "github.com/MetalBlockchain/subnet-evm/constants" 22 "github.com/MetalBlockchain/subnet-evm/core" 23 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 24 "github.com/MetalBlockchain/subnet-evm/core/types" 25 "github.com/MetalBlockchain/subnet-evm/eth" 26 "github.com/MetalBlockchain/subnet-evm/eth/ethconfig" 27 "github.com/MetalBlockchain/subnet-evm/metrics" 28 subnetEVMPrometheus "github.com/MetalBlockchain/subnet-evm/metrics/prometheus" 29 "github.com/MetalBlockchain/subnet-evm/miner" 30 "github.com/MetalBlockchain/subnet-evm/node" 31 "github.com/MetalBlockchain/subnet-evm/params" 32 "github.com/MetalBlockchain/subnet-evm/peer" 33 "github.com/MetalBlockchain/subnet-evm/plugin/evm/message" 34 "github.com/MetalBlockchain/subnet-evm/rpc" 35 statesyncclient "github.com/MetalBlockchain/subnet-evm/sync/client" 36 "github.com/MetalBlockchain/subnet-evm/sync/client/stats" 37 "github.com/MetalBlockchain/subnet-evm/trie" 38 "github.com/MetalBlockchain/subnet-evm/warp" 39 40 // Force-load tracer engine to trigger registration 41 // 42 // We must import this package (not referenced elsewhere) so that the native "callTracer" 43 // is added to a map of client-accessible tracers. In geth, this is done 44 // inside of cmd/geth. 45 _ "github.com/MetalBlockchain/subnet-evm/eth/tracers/native" 46 47 "github.com/ethereum/go-ethereum/common" 48 "github.com/ethereum/go-ethereum/log" 49 "github.com/ethereum/go-ethereum/rlp" 50 51 avalancheRPC "github.com/gorilla/rpc/v2" 52 53 "github.com/MetalBlockchain/metalgo/codec" 54 "github.com/MetalBlockchain/metalgo/database" 55 "github.com/MetalBlockchain/metalgo/database/manager" 56 "github.com/MetalBlockchain/metalgo/database/prefixdb" 57 "github.com/MetalBlockchain/metalgo/database/versiondb" 58 "github.com/MetalBlockchain/metalgo/ids" 59 "github.com/MetalBlockchain/metalgo/snow" 60 "github.com/MetalBlockchain/metalgo/snow/choices" 61 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 62 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 63 cjson "github.com/MetalBlockchain/metalgo/utils/json" 64 "github.com/MetalBlockchain/metalgo/utils/perms" 65 "github.com/MetalBlockchain/metalgo/utils/profiler" 66 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 67 "github.com/MetalBlockchain/metalgo/vms/components/chain" 68 69 commonEng "github.com/MetalBlockchain/metalgo/snow/engine/common" 70 71 avalancheJSON "github.com/MetalBlockchain/metalgo/utils/json" 72 ) 73 74 var ( 75 _ block.ChainVM = &VM{} 76 _ block.HeightIndexedChainVM = &VM{} 77 ) 78 79 const ( 80 // Max time from current time allowed for blocks, before they're considered future blocks 81 // and fail verification 82 maxFutureBlockTime = 10 * time.Second 83 84 decidedCacheSize = 100 85 missingCacheSize = 50 86 unverifiedCacheSize = 50 87 warpSignatureCacheSize = 500 88 89 // Prefixes for metrics gatherers 90 ethMetricsPrefix = "eth" 91 chainStateMetricsPrefix = "chain_state" 92 ) 93 94 // Define the API endpoints for the VM 95 const ( 96 adminEndpoint = "/admin" 97 ethRPCEndpoint = "/rpc" 98 ethWSEndpoint = "/ws" 99 ) 100 101 var ( 102 // Set last accepted key to be longer than the keys used to store accepted block IDs. 103 lastAcceptedKey = []byte("last_accepted_key") 104 acceptedPrefix = []byte("snowman_accepted") 105 metadataPrefix = []byte("metadata") 106 warpPrefix = []byte("warp") 107 ethDBPrefix = []byte("ethdb") 108 ) 109 110 var ( 111 errEmptyBlock = errors.New("empty block") 112 errUnsupportedFXs = errors.New("unsupported feature extensions") 113 errInvalidBlock = errors.New("invalid block") 114 errInvalidNonce = errors.New("invalid nonce") 115 errUnclesUnsupported = errors.New("uncles unsupported") 116 errNilBaseFeeSubnetEVM = errors.New("nil base fee is invalid after subnetEVM") 117 errNilBlockGasCostSubnetEVM = errors.New("nil blockGasCost is invalid after subnetEVM") 118 errSubnetEVMUpgradeNotEnabled = errors.New("SubnetEVM upgrade is not enabled in genesis") 119 ) 120 121 var originalStderr *os.File 122 123 // legacyApiNames maps pre geth v1.10.20 api names to their updated counterparts. 124 // used in attachEthService for backward configuration compatibility. 125 var legacyApiNames = map[string]string{ 126 "internal-public-eth": "internal-eth", 127 "internal-public-blockchain": "internal-blockchain", 128 "internal-public-transaction-pool": "internal-transaction", 129 "internal-public-tx-pool": "internal-tx-pool", 130 "internal-public-debug": "internal-debug", 131 "internal-private-debug": "internal-debug", 132 "internal-public-account": "internal-account", 133 "internal-private-personal": "internal-personal", 134 135 "public-eth": "eth", 136 "public-eth-filter": "eth-filter", 137 "private-admin": "admin", 138 "public-debug": "debug", 139 "private-debug": "debug", 140 } 141 142 func init() { 143 // Preserve [os.Stderr] prior to the call in plugin/main.go to plugin.Serve(...). 144 // Preserving the log level allows us to update the root handler while writing to the original 145 // [os.Stderr] that is being piped through to the logger via the rpcchainvm. 146 originalStderr = os.Stderr 147 } 148 149 // VM implements the snowman.ChainVM interface 150 type VM struct { 151 ctx *snow.Context 152 // *chain.State helps to implement the VM interface by wrapping blocks 153 // with an efficient caching layer. 154 *chain.State 155 156 config Config 157 158 networkID uint64 159 genesisHash common.Hash 160 chainConfig *params.ChainConfig 161 ethConfig ethconfig.Config 162 163 // pointers to eth constructs 164 eth *eth.Ethereum 165 txPool *core.TxPool 166 blockChain *core.BlockChain 167 miner *miner.Miner 168 169 // [db] is the VM's current database managed by ChainState 170 db *versiondb.Database 171 172 // metadataDB is used to store one off keys. 173 metadataDB database.Database 174 175 // [chaindb] is the database supplied to the Ethereum backend 176 chaindb Database 177 178 // [acceptedBlockDB] is the database to store the last accepted 179 // block. 180 acceptedBlockDB database.Database 181 182 // [warpDB] is used to store warp message signatures 183 // set to a prefixDB with the prefix [warpPrefix] 184 warpDB database.Database 185 186 toEngine chan<- commonEng.Message 187 188 syntacticBlockValidator BlockValidator 189 190 builder *blockBuilder 191 192 gossiper Gossiper 193 194 clock mockable.Clock 195 196 shutdownChan chan struct{} 197 shutdownWg sync.WaitGroup 198 199 // Continuous Profiler 200 profiler profiler.ContinuousProfiler 201 202 peer.Network 203 client peer.NetworkClient 204 networkCodec codec.Manager 205 206 // Metrics 207 multiGatherer avalanchegoMetrics.MultiGatherer 208 209 bootstrapped bool 210 211 logger SubnetEVMLogger 212 // State sync server and client 213 StateSyncServer 214 StateSyncClient 215 216 // Avalanche Warp Messaging backend 217 // Used to serve BLS signatures of warp messages over RPC 218 warpBackend warp.WarpBackend 219 } 220 221 /* 222 ****************************************************************************** 223 ********************************* Snowman API ******************************** 224 ****************************************************************************** 225 */ 226 227 // implements SnowmanPlusPlusVM interface 228 func (vm *VM) GetActivationTime() time.Time { 229 return time.Unix(vm.chainConfig.SubnetEVMTimestamp.Int64(), 0) 230 } 231 232 // Initialize implements the snowman.ChainVM interface 233 func (vm *VM) Initialize( 234 _ context.Context, 235 chainCtx *snow.Context, 236 dbManager manager.Manager, 237 genesisBytes []byte, 238 upgradeBytes []byte, 239 configBytes []byte, 240 toEngine chan<- commonEng.Message, 241 fxs []*commonEng.Fx, 242 appSender commonEng.AppSender, 243 ) error { 244 vm.config.SetDefaults() 245 if len(configBytes) > 0 { 246 if err := json.Unmarshal(configBytes, &vm.config); err != nil { 247 return fmt.Errorf("failed to unmarshal config %s: %w", string(configBytes), err) 248 } 249 } 250 if err := vm.config.Validate(); err != nil { 251 return err 252 } 253 254 vm.ctx = chainCtx 255 256 // Create logger 257 alias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) 258 if err != nil { 259 // fallback to ChainID string instead of erroring 260 alias = vm.ctx.ChainID.String() 261 } 262 263 subnetEVMLogger, err := InitLogger(alias, vm.config.LogLevel, vm.config.LogJSONFormat, originalStderr) 264 if err != nil { 265 return fmt.Errorf("failed to initialize logger due to: %w ", err) 266 } 267 vm.logger = subnetEVMLogger 268 269 log.Info("Initializing Subnet EVM VM", "Version", Version, "Config", vm.config) 270 271 if len(fxs) > 0 { 272 return errUnsupportedFXs 273 } 274 275 // Enable debug-level metrics that might impact runtime performance 276 metrics.EnabledExpensive = vm.config.MetricsExpensiveEnabled 277 278 vm.toEngine = toEngine 279 vm.shutdownChan = make(chan struct{}, 1) 280 baseDB := dbManager.Current().Database 281 // Use NewNested rather than New so that the structure of the database 282 // remains the same regardless of the provided baseDB type. 283 vm.chaindb = Database{prefixdb.NewNested(ethDBPrefix, baseDB)} 284 vm.db = versiondb.New(baseDB) 285 vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) 286 vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) 287 vm.warpDB = prefixdb.New(warpPrefix, vm.db) 288 289 if vm.config.InspectDatabase { 290 start := time.Now() 291 log.Info("Starting database inspection") 292 if err := rawdb.InspectDatabase(vm.chaindb, nil, nil); err != nil { 293 return err 294 } 295 log.Info("Completed database inspection", "elapsed", time.Since(start)) 296 } 297 298 g := new(core.Genesis) 299 if err := json.Unmarshal(genesisBytes, g); err != nil { 300 return err 301 } 302 303 if g.Config == nil { 304 g.Config = params.SubnetEVMDefaultChainConfig 305 } 306 307 // Load airdrop file if provided 308 if vm.config.AirdropFile != "" { 309 g.AirdropData, err = os.ReadFile(vm.config.AirdropFile) 310 if err != nil { 311 return fmt.Errorf("could not read airdrop file '%s': %w", vm.config.AirdropFile, err) 312 } 313 } 314 // Set the Avalanche Context on the ChainConfig 315 g.Config.AvalancheContext = params.AvalancheContext{ 316 SnowCtx: chainCtx, 317 } 318 vm.syntacticBlockValidator = NewBlockValidator() 319 320 if g.Config.FeeConfig == commontype.EmptyFeeConfig { 321 log.Warn("No fee config given in genesis, setting default fee config", "DefaultFeeConfig", params.DefaultFeeConfig) 322 g.Config.FeeConfig = params.DefaultFeeConfig 323 } 324 325 vm.ethConfig = ethconfig.NewDefaultConfig() 326 vm.ethConfig.Genesis = g 327 vm.ethConfig.NetworkId = g.Config.ChainID.Uint64() 328 329 // Set minimum price for mining and default gas price oracle value to the min 330 // gas price to prevent so transactions and blocks all use the correct fees 331 vm.ethConfig.RPCGasCap = vm.config.RPCGasCap 332 vm.ethConfig.RPCEVMTimeout = vm.config.APIMaxDuration.Duration 333 vm.ethConfig.RPCTxFeeCap = vm.config.RPCTxFeeCap 334 335 vm.ethConfig.TxPool.Locals = vm.config.PriorityRegossipAddresses 336 vm.ethConfig.TxPool.NoLocals = !vm.config.LocalTxsEnabled 337 vm.ethConfig.TxPool.Journal = vm.config.TxPoolJournal 338 vm.ethConfig.TxPool.Rejournal = vm.config.TxPoolRejournal.Duration 339 vm.ethConfig.TxPool.PriceLimit = vm.config.TxPoolPriceLimit 340 vm.ethConfig.TxPool.PriceBump = vm.config.TxPoolPriceBump 341 vm.ethConfig.TxPool.AccountSlots = vm.config.TxPoolAccountSlots 342 vm.ethConfig.TxPool.GlobalSlots = vm.config.TxPoolGlobalSlots 343 vm.ethConfig.TxPool.AccountQueue = vm.config.TxPoolAccountQueue 344 vm.ethConfig.TxPool.GlobalQueue = vm.config.TxPoolGlobalQueue 345 346 vm.ethConfig.AllowUnfinalizedQueries = vm.config.AllowUnfinalizedQueries 347 vm.ethConfig.AllowUnprotectedTxs = vm.config.AllowUnprotectedTxs 348 vm.ethConfig.AllowUnprotectedTxHashes = vm.config.AllowUnprotectedTxHashes 349 vm.ethConfig.Preimages = vm.config.Preimages 350 vm.ethConfig.Pruning = vm.config.Pruning 351 vm.ethConfig.TrieCleanCache = vm.config.TrieCleanCache 352 vm.ethConfig.TrieCleanJournal = vm.config.TrieCleanJournal 353 vm.ethConfig.TrieCleanRejournal = vm.config.TrieCleanRejournal.Duration 354 vm.ethConfig.TrieDirtyCache = vm.config.TrieDirtyCache 355 vm.ethConfig.TrieDirtyCommitTarget = vm.config.TrieDirtyCommitTarget 356 vm.ethConfig.SnapshotCache = vm.config.SnapshotCache 357 vm.ethConfig.AcceptorQueueLimit = vm.config.AcceptorQueueLimit 358 vm.ethConfig.PopulateMissingTries = vm.config.PopulateMissingTries 359 vm.ethConfig.PopulateMissingTriesParallelism = vm.config.PopulateMissingTriesParallelism 360 vm.ethConfig.AllowMissingTries = vm.config.AllowMissingTries 361 vm.ethConfig.SnapshotDelayInit = vm.config.StateSyncEnabled 362 vm.ethConfig.SnapshotAsync = vm.config.SnapshotAsync 363 vm.ethConfig.SnapshotVerify = vm.config.SnapshotVerify 364 vm.ethConfig.OfflinePruning = vm.config.OfflinePruning 365 vm.ethConfig.OfflinePruningBloomFilterSize = vm.config.OfflinePruningBloomFilterSize 366 vm.ethConfig.OfflinePruningDataDirectory = vm.config.OfflinePruningDataDirectory 367 vm.ethConfig.CommitInterval = vm.config.CommitInterval 368 vm.ethConfig.SkipUpgradeCheck = vm.config.SkipUpgradeCheck 369 vm.ethConfig.AcceptedCacheSize = vm.config.AcceptedCacheSize 370 vm.ethConfig.TxLookupLimit = vm.config.TxLookupLimit 371 372 // Create directory for offline pruning 373 if len(vm.ethConfig.OfflinePruningDataDirectory) != 0 { 374 if err := os.MkdirAll(vm.ethConfig.OfflinePruningDataDirectory, perms.ReadWriteExecute); err != nil { 375 log.Error("failed to create offline pruning data directory", "error", err) 376 return err 377 } 378 } 379 380 // Handle custom fee recipient 381 if common.IsHexAddress(vm.config.FeeRecipient) { 382 address := common.HexToAddress(vm.config.FeeRecipient) 383 log.Info("Setting fee recipient", "address", address) 384 vm.ethConfig.Miner.Etherbase = address 385 } else { 386 log.Warn("Config has not specified any coinbase address. Defaulting to the blackhole address.") 387 vm.ethConfig.Miner.Etherbase = constants.BlackholeAddr 388 } 389 390 vm.chainConfig = g.Config 391 vm.networkID = vm.ethConfig.NetworkId 392 393 if !vm.config.SkipSubnetEVMUpgradeCheck { 394 // check that subnetEVM upgrade is enabled from genesis before upgradeBytes 395 if !vm.chainConfig.IsSubnetEVM(common.Big0) { 396 return errSubnetEVMUpgradeNotEnabled 397 } 398 } 399 400 // Apply upgradeBytes (if any) by unmarshalling them into [chainConfig.UpgradeConfig]. 401 // Initializing the chain will verify upgradeBytes are compatible with existing values. 402 if len(upgradeBytes) > 0 { 403 var upgradeConfig params.UpgradeConfig 404 if err := json.Unmarshal(upgradeBytes, &upgradeConfig); err != nil { 405 return fmt.Errorf("failed to parse upgrade bytes: %w", err) 406 } 407 vm.chainConfig.UpgradeConfig = upgradeConfig 408 } 409 410 // create genesisHash after applying upgradeBytes in case 411 // upgradeBytes modifies genesis. 412 vm.genesisHash = vm.ethConfig.Genesis.ToBlock(nil).Hash() 413 414 lastAcceptedHash, lastAcceptedHeight, err := vm.readLastAccepted() 415 if err != nil { 416 return err 417 } 418 log.Info("reading accepted block db", "lastAcceptedHash", lastAcceptedHash) 419 420 if err := vm.initializeMetrics(); err != nil { 421 return err 422 } 423 424 // initialize peer network 425 vm.networkCodec = message.Codec 426 vm.Network = peer.NewNetwork(appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) 427 vm.client = peer.NewNetworkClient(vm.Network) 428 429 // initialize warp backend 430 vm.warpBackend = warp.NewWarpBackend(vm.ctx, vm.warpDB, warpSignatureCacheSize) 431 432 if err := vm.initializeChain(lastAcceptedHash, vm.ethConfig); err != nil { 433 return err 434 } 435 436 go vm.ctx.Log.RecoverAndPanic(vm.startContinuousProfiler) 437 438 vm.initializeStateSyncServer() 439 return vm.initializeStateSyncClient(lastAcceptedHeight) 440 } 441 442 func (vm *VM) initializeMetrics() error { 443 vm.multiGatherer = avalanchegoMetrics.NewMultiGatherer() 444 // If metrics are enabled, register the default metrics regitry 445 if metrics.Enabled { 446 gatherer := subnetEVMPrometheus.Gatherer(metrics.DefaultRegistry) 447 if err := vm.multiGatherer.Register(ethMetricsPrefix, gatherer); err != nil { 448 return err 449 } 450 // Register [multiGatherer] after registerers have been registered to it 451 if err := vm.ctx.Metrics.Register(vm.multiGatherer); err != nil { 452 return err 453 } 454 } 455 return nil 456 } 457 458 func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig.Config) error { 459 nodecfg := &node.Config{ 460 SubnetEVMVersion: Version, 461 KeyStoreDir: vm.config.KeystoreDirectory, 462 ExternalSigner: vm.config.KeystoreExternalSigner, 463 InsecureUnlockAllowed: vm.config.KeystoreInsecureUnlockAllowed, 464 } 465 node, err := node.New(nodecfg) 466 if err != nil { 467 return err 468 } 469 vm.eth, err = eth.New( 470 node, 471 &vm.ethConfig, 472 vm.chaindb, 473 vm.config.EthBackendSettings(), 474 lastAcceptedHash, 475 &vm.clock, 476 ) 477 if err != nil { 478 return err 479 } 480 vm.eth.SetEtherbase(ethConfig.Miner.Etherbase) 481 vm.txPool = vm.eth.TxPool() 482 vm.blockChain = vm.eth.BlockChain() 483 vm.miner = vm.eth.Miner() 484 485 // start goroutines to update the tx pool gas minimum gas price when upgrades go into effect 486 vm.handleGasPriceUpdates() 487 488 vm.eth.Start() 489 return vm.initChainState(vm.blockChain.LastAcceptedBlock()) 490 } 491 492 // initializeStateSyncClient initializes the client for performing state sync. 493 // If state sync is disabled, this function will wipe any ongoing summary from 494 // disk to ensure that we do not continue syncing from an invalid snapshot. 495 func (vm *VM) initializeStateSyncClient(lastAcceptedHeight uint64) error { 496 // parse nodeIDs from state sync IDs in vm config 497 var stateSyncIDs []ids.NodeID 498 if vm.config.StateSyncEnabled && len(vm.config.StateSyncIDs) > 0 { 499 nodeIDs := strings.Split(vm.config.StateSyncIDs, ",") 500 stateSyncIDs = make([]ids.NodeID, len(nodeIDs)) 501 for i, nodeIDString := range nodeIDs { 502 nodeID, err := ids.NodeIDFromString(nodeIDString) 503 if err != nil { 504 return fmt.Errorf("failed to parse %s as NodeID: %w", nodeIDString, err) 505 } 506 stateSyncIDs[i] = nodeID 507 } 508 } 509 510 vm.StateSyncClient = NewStateSyncClient(&stateSyncClientConfig{ 511 chain: vm.eth, 512 state: vm.State, 513 client: statesyncclient.NewClient( 514 &statesyncclient.ClientConfig{ 515 NetworkClient: vm.client, 516 Codec: vm.networkCodec, 517 Stats: stats.NewClientSyncerStats(), 518 StateSyncNodeIDs: stateSyncIDs, 519 BlockParser: vm, 520 }, 521 ), 522 enabled: vm.config.StateSyncEnabled, 523 skipResume: vm.config.StateSyncSkipResume, 524 stateSyncMinBlocks: vm.config.StateSyncMinBlocks, 525 lastAcceptedHeight: lastAcceptedHeight, // TODO clean up how this is passed around 526 chaindb: vm.chaindb, 527 metadataDB: vm.metadataDB, 528 acceptedBlockDB: vm.acceptedBlockDB, 529 db: vm.db, 530 toEngine: vm.toEngine, 531 }) 532 533 // If StateSync is disabled, clear any ongoing summary so that we will not attempt to resume 534 // sync using a snapshot that has been modified by the node running normal operations. 535 if !vm.config.StateSyncEnabled { 536 return vm.StateSyncClient.StateSyncClearOngoingSummary() 537 } 538 539 return nil 540 } 541 542 // initializeStateSyncServer should be called after [vm.chain] is initialized. 543 func (vm *VM) initializeStateSyncServer() { 544 vm.StateSyncServer = NewStateSyncServer(&stateSyncServerConfig{ 545 Chain: vm.blockChain, 546 SyncableInterval: vm.config.StateSyncCommitInterval, 547 }) 548 549 vm.setAppRequestHandlers() 550 vm.setCrossChainAppRequestHandler() 551 } 552 553 func (vm *VM) initChainState(lastAcceptedBlock *types.Block) error { 554 block := vm.newBlock(lastAcceptedBlock) 555 block.status = choices.Accepted 556 557 config := &chain.Config{ 558 DecidedCacheSize: decidedCacheSize, 559 MissingCacheSize: missingCacheSize, 560 UnverifiedCacheSize: unverifiedCacheSize, 561 GetBlockIDAtHeight: vm.GetBlockIDAtHeight, 562 GetBlock: vm.getBlock, 563 UnmarshalBlock: vm.parseBlock, 564 BuildBlock: vm.buildBlock, 565 LastAcceptedBlock: block, 566 } 567 568 // Register chain state metrics 569 chainStateRegisterer := prometheus.NewRegistry() 570 state, err := chain.NewMeteredState(chainStateRegisterer, config) 571 if err != nil { 572 return fmt.Errorf("could not create metered state: %w", err) 573 } 574 vm.State = state 575 576 return vm.multiGatherer.Register(chainStateMetricsPrefix, chainStateRegisterer) 577 } 578 579 func (vm *VM) SetState(_ context.Context, state snow.State) error { 580 switch state { 581 case snow.StateSyncing: 582 vm.bootstrapped = false 583 return nil 584 case snow.Bootstrapping: 585 vm.bootstrapped = false 586 if err := vm.StateSyncClient.Error(); err != nil { 587 return err 588 } 589 return nil 590 case snow.NormalOp: 591 // Initialize gossip handling once we enter normal operation as there is no need to handle mempool gossip before this point. 592 vm.initBlockBuilding() 593 vm.bootstrapped = true 594 return nil 595 default: 596 return snow.ErrUnknownState 597 } 598 } 599 600 // initBlockBuilding starts goroutines to manage block building 601 func (vm *VM) initBlockBuilding() { 602 // NOTE: gossip network must be initialized first otherwise ETH tx gossip will not work. 603 gossipStats := NewGossipStats() 604 vm.gossiper = vm.createGossiper(gossipStats) 605 vm.builder = vm.NewBlockBuilder(vm.toEngine) 606 vm.builder.awaitSubmittedTxs() 607 vm.Network.SetGossipHandler(NewGossipHandler(vm, gossipStats)) 608 } 609 610 // setAppRequestHandlers sets the request handlers for the VM to serve state sync 611 // requests. 612 func (vm *VM) setAppRequestHandlers() { 613 // Create separate EVM TrieDB (read only) for serving leafs requests. 614 // We create a separate TrieDB here, so that it has a separate cache from the one 615 // used by the node when processing blocks. 616 evmTrieDB := trie.NewDatabaseWithConfig( 617 vm.chaindb, 618 &trie.Config{ 619 Cache: vm.config.StateSyncServerTrieCache, 620 }, 621 ) 622 623 networkHandler := newNetworkHandler(vm.blockChain, evmTrieDB, vm.networkCodec) 624 vm.Network.SetRequestHandler(networkHandler) 625 } 626 627 // setCrossChainAppRequestHandler sets the request handlers for the VM to serve cross chain 628 // requests. 629 func (vm *VM) setCrossChainAppRequestHandler() { 630 crossChainRequestHandler := message.NewCrossChainHandler(vm.eth.APIBackend, message.CrossChainCodec) 631 vm.Network.SetCrossChainRequestHandler(crossChainRequestHandler) 632 } 633 634 // Shutdown implements the snowman.ChainVM interface 635 func (vm *VM) Shutdown(context.Context) error { 636 if vm.ctx == nil { 637 return nil 638 } 639 vm.Network.Shutdown() 640 if err := vm.StateSyncClient.Shutdown(); err != nil { 641 log.Error("error stopping state syncer", "err", err) 642 } 643 close(vm.shutdownChan) 644 vm.eth.Stop() 645 vm.shutdownWg.Wait() 646 return nil 647 } 648 649 // buildBlock builds a block to be wrapped by ChainState 650 func (vm *VM) buildBlock(context.Context) (snowman.Block, error) { 651 block, err := vm.miner.GenerateBlock() 652 vm.builder.handleGenerateBlock() 653 if err != nil { 654 return nil, err 655 } 656 657 // Note: the status of block is set by ChainState 658 blk := vm.newBlock(block) 659 660 // Verify is called on a non-wrapped block here, such that this 661 // does not add [blk] to the processing blocks map in ChainState. 662 // 663 // TODO cache verification since Verify() will be called by the 664 // consensus engine as well. 665 // 666 // Note: this is only called when building a new block, so caching 667 // verification will only be a significant optimization for nodes 668 // that produce a large number of blocks. 669 // We call verify without writes here to avoid generating a reference 670 // to the blk state root in the triedb when we are going to call verify 671 // again from the consensus engine with writes enabled. 672 if err := blk.verify(false /*=writes*/); err != nil { 673 return nil, fmt.Errorf("block failed verification due to: %w", err) 674 } 675 676 log.Debug(fmt.Sprintf("Built block %s", blk.ID())) 677 // Marks the current transactions from the mempool as being successfully issued 678 // into a block. 679 return blk, nil 680 } 681 682 // parseBlock parses [b] into a block to be wrapped by ChainState. 683 func (vm *VM) parseBlock(_ context.Context, b []byte) (snowman.Block, error) { 684 ethBlock := new(types.Block) 685 if err := rlp.DecodeBytes(b, ethBlock); err != nil { 686 return nil, err 687 } 688 689 // Note: the status of block is set by ChainState 690 block := vm.newBlock(ethBlock) 691 // Performing syntactic verification in ParseBlock allows for 692 // short-circuiting bad blocks before they are processed by the VM. 693 if err := block.syntacticVerify(); err != nil { 694 return nil, fmt.Errorf("syntactic block verification failed: %w", err) 695 } 696 return block, nil 697 } 698 699 func (vm *VM) ParseEthBlock(b []byte) (*types.Block, error) { 700 block, err := vm.parseBlock(context.TODO(), b) 701 if err != nil { 702 return nil, err 703 } 704 705 return block.(*Block).ethBlock, nil 706 } 707 708 // getBlock attempts to retrieve block [id] from the VM to be wrapped 709 // by ChainState. 710 func (vm *VM) getBlock(_ context.Context, id ids.ID) (snowman.Block, error) { 711 ethBlock := vm.blockChain.GetBlockByHash(common.Hash(id)) 712 // If [ethBlock] is nil, return [database.ErrNotFound] here 713 // so that the miss is considered cacheable. 714 if ethBlock == nil { 715 return nil, database.ErrNotFound 716 } 717 // Note: the status of block is set by ChainState 718 return vm.newBlock(ethBlock), nil 719 } 720 721 // SetPreference sets what the current tail of the chain is 722 func (vm *VM) SetPreference(ctx context.Context, blkID ids.ID) error { 723 // Since each internal handler used by [vm.State] always returns a block 724 // with non-nil ethBlock value, GetBlockInternal should never return a 725 // (*Block) with a nil ethBlock value. 726 block, err := vm.GetBlockInternal(ctx, blkID) 727 if err != nil { 728 return fmt.Errorf("failed to set preference to %s: %w", blkID, err) 729 } 730 731 return vm.blockChain.SetPreference(block.(*Block).ethBlock) 732 } 733 734 // VerifyHeightIndex always returns a nil error since the index is maintained by 735 // vm.blockChain. 736 func (vm *VM) VerifyHeightIndex(context.Context) error { 737 return nil 738 } 739 740 // GetBlockAtHeight implements the HeightIndexedChainVM interface and returns the 741 // canonical block at [blkHeight]. 742 // If [blkHeight] is less than the height of the last accepted block, this will return 743 // the block accepted at that height. Otherwise, it may return a blkID that has not yet 744 // been accepted. 745 // Note: the engine assumes that if a block is not found at [blkHeight], then 746 // [database.ErrNotFound] will be returned. This indicates that the VM has state synced 747 // and does not have all historical blocks available. 748 func (vm *VM) GetBlockIDAtHeight(_ context.Context, blkHeight uint64) (ids.ID, error) { 749 ethBlock := vm.blockChain.GetBlockByNumber(blkHeight) 750 if ethBlock == nil { 751 return ids.ID{}, database.ErrNotFound 752 } 753 754 return ids.ID(ethBlock.Hash()), nil 755 } 756 757 func (vm *VM) Version(context.Context) (string, error) { 758 return Version, nil 759 } 760 761 // NewHandler returns a new Handler for a service where: 762 // - The handler's functionality is defined by [service] 763 // [service] should be a gorilla RPC service (see https://www.gorillatoolkit.org/pkg/rpc/v2) 764 // - The name of the service is [name] 765 // - The LockOption is the first element of [lockOption] 766 // By default the LockOption is WriteLock 767 // [lockOption] should have either 0 or 1 elements. Elements beside the first are ignored. 768 func newHandler(name string, service interface{}, lockOption ...commonEng.LockOption) (*commonEng.HTTPHandler, error) { 769 server := avalancheRPC.NewServer() 770 server.RegisterCodec(avalancheJSON.NewCodec(), "application/json") 771 server.RegisterCodec(avalancheJSON.NewCodec(), "application/json;charset=UTF-8") 772 if err := server.RegisterService(service, name); err != nil { 773 return nil, err 774 } 775 776 var lock commonEng.LockOption = commonEng.WriteLock 777 if len(lockOption) != 0 { 778 lock = lockOption[0] 779 } 780 return &commonEng.HTTPHandler{LockOptions: lock, Handler: server}, nil 781 } 782 783 // CreateHandlers makes new http handlers that can handle API calls 784 func (vm *VM) CreateHandlers(context.Context) (map[string]*commonEng.HTTPHandler, error) { 785 handler := rpc.NewServer(vm.config.APIMaxDuration.Duration) 786 enabledAPIs := vm.config.EthAPIs() 787 if err := attachEthService(handler, vm.eth.APIs(), enabledAPIs); err != nil { 788 return nil, err 789 } 790 791 primaryAlias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) 792 if err != nil { 793 return nil, fmt.Errorf("failed to get primary alias for chain due to %w", err) 794 } 795 apis := make(map[string]*commonEng.HTTPHandler) 796 if vm.config.AdminAPIEnabled { 797 adminAPI, err := newHandler("admin", NewAdminService(vm, os.ExpandEnv(fmt.Sprintf("%s_subnet_evm_performance_%s", vm.config.AdminAPIDir, primaryAlias)))) 798 if err != nil { 799 return nil, fmt.Errorf("failed to register service for admin API due to %w", err) 800 } 801 apis[adminEndpoint] = adminAPI 802 enabledAPIs = append(enabledAPIs, "subnet-evm-admin") 803 } 804 805 if vm.config.SnowmanAPIEnabled { 806 if err := handler.RegisterName("snowman", &SnowmanAPI{vm}); err != nil { 807 return nil, err 808 } 809 enabledAPIs = append(enabledAPIs, "snowman") 810 } 811 812 if vm.config.WarpAPIEnabled { 813 if err := handler.RegisterName("warp", &warp.WarpAPI{Backend: vm.warpBackend}); err != nil { 814 return nil, err 815 } 816 enabledAPIs = append(enabledAPIs, "warp") 817 } 818 819 log.Info(fmt.Sprintf("Enabled APIs: %s", strings.Join(enabledAPIs, ", "))) 820 apis[ethRPCEndpoint] = &commonEng.HTTPHandler{ 821 LockOptions: commonEng.NoLock, 822 Handler: handler, 823 } 824 apis[ethWSEndpoint] = &commonEng.HTTPHandler{ 825 LockOptions: commonEng.NoLock, 826 Handler: handler.WebsocketHandlerWithDuration( 827 []string{"*"}, 828 vm.config.APIMaxDuration.Duration, 829 vm.config.WSCPURefillRate.Duration, 830 vm.config.WSCPUMaxStored.Duration, 831 ), 832 } 833 834 return apis, nil 835 } 836 837 // CreateStaticHandlers makes new http handlers that can handle API calls 838 func (vm *VM) CreateStaticHandlers(context.Context) (map[string]*commonEng.HTTPHandler, error) { 839 server := avalancheRPC.NewServer() 840 codec := cjson.NewCodec() 841 server.RegisterCodec(codec, "application/json") 842 server.RegisterCodec(codec, "application/json;charset=UTF-8") 843 serviceName := "subnetevm" 844 if err := server.RegisterService(&StaticService{}, serviceName); err != nil { 845 return nil, err 846 } 847 848 return map[string]*commonEng.HTTPHandler{ 849 "/rpc": {LockOptions: commonEng.NoLock, Handler: server}, 850 }, nil 851 } 852 853 /* 854 ****************************************************************************** 855 *********************************** Helpers ********************************** 856 ****************************************************************************** 857 */ 858 859 // GetCurrentNonce returns the nonce associated with the address at the 860 // preferred block 861 func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) { 862 // Note: current state uses the state of the preferred block. 863 state, err := vm.blockChain.State() 864 if err != nil { 865 return 0, err 866 } 867 return state.GetNonce(address), nil 868 } 869 870 func (vm *VM) startContinuousProfiler() { 871 // If the profiler directory is empty, return immediately 872 // without creating or starting a continuous profiler. 873 if vm.config.ContinuousProfilerDir == "" { 874 return 875 } 876 vm.profiler = profiler.NewContinuous( 877 filepath.Join(vm.config.ContinuousProfilerDir), 878 vm.config.ContinuousProfilerFrequency.Duration, 879 vm.config.ContinuousProfilerMaxFiles, 880 ) 881 defer vm.profiler.Shutdown() 882 883 vm.shutdownWg.Add(1) 884 go func() { 885 defer vm.shutdownWg.Done() 886 log.Info("Dispatching continuous profiler", "dir", vm.config.ContinuousProfilerDir, "freq", vm.config.ContinuousProfilerFrequency, "maxFiles", vm.config.ContinuousProfilerMaxFiles) 887 err := vm.profiler.Dispatch() 888 if err != nil { 889 log.Error("continuous profiler failed", "err", err) 890 } 891 }() 892 // Wait for shutdownChan to be closed 893 <-vm.shutdownChan 894 } 895 896 // readLastAccepted reads the last accepted hash from [acceptedBlockDB] and returns the 897 // last accepted block hash and height by reading directly from [vm.chaindb] instead of relying 898 // on [chain]. 899 // Note: assumes chaindb, ethConfig, and genesisHash have been initialized. 900 func (vm *VM) readLastAccepted() (common.Hash, uint64, error) { 901 // Attempt to load last accepted block to determine if it is necessary to 902 // initialize state with the genesis block. 903 lastAcceptedBytes, lastAcceptedErr := vm.acceptedBlockDB.Get(lastAcceptedKey) 904 switch { 905 case lastAcceptedErr == database.ErrNotFound: 906 // If there is nothing in the database, return the genesis block hash and height 907 return vm.genesisHash, 0, nil 908 case lastAcceptedErr != nil: 909 return common.Hash{}, 0, fmt.Errorf("failed to get last accepted block ID due to: %w", lastAcceptedErr) 910 case len(lastAcceptedBytes) != common.HashLength: 911 return common.Hash{}, 0, fmt.Errorf("last accepted bytes should have been length %d, but found %d", common.HashLength, len(lastAcceptedBytes)) 912 default: 913 lastAcceptedHash := common.BytesToHash(lastAcceptedBytes) 914 height := rawdb.ReadHeaderNumber(vm.chaindb, lastAcceptedHash) 915 if height == nil { 916 return common.Hash{}, 0, fmt.Errorf("failed to retrieve header number of last accepted block: %s", lastAcceptedHash) 917 } 918 return lastAcceptedHash, *height, nil 919 } 920 } 921 922 // attachEthService registers the backend RPC services provided by Ethereum 923 // to the provided handler under their assigned namespaces. 924 func attachEthService(handler *rpc.Server, apis []rpc.API, names []string) error { 925 enabledServicesSet := make(map[string]struct{}) 926 for _, ns := range names { 927 // handle pre geth v1.10.20 api names as aliases for their updated values 928 // to allow configurations to be backwards compatible. 929 if newName, isLegacy := legacyApiNames[ns]; isLegacy { 930 log.Info("deprecated api name referenced in configuration.", "deprecated", ns, "new", newName) 931 enabledServicesSet[newName] = struct{}{} 932 continue 933 } 934 935 enabledServicesSet[ns] = struct{}{} 936 } 937 938 apiSet := make(map[string]rpc.API) 939 for _, api := range apis { 940 if existingAPI, exists := apiSet[api.Name]; exists { 941 return fmt.Errorf("duplicated API name: %s, namespaces %s and %s", api.Name, api.Namespace, existingAPI.Namespace) 942 } 943 apiSet[api.Name] = api 944 } 945 946 for name := range enabledServicesSet { 947 api, exists := apiSet[name] 948 if !exists { 949 return fmt.Errorf("API service %s not found", name) 950 } 951 if err := handler.RegisterName(api.Namespace, api.Service); err != nil { 952 return err 953 } 954 } 955 956 return nil 957 }