github.com/dim4egster/coreth@v0.10.2/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/binary" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io" 13 "math/big" 14 "os" 15 "path/filepath" 16 "strings" 17 "sync" 18 "time" 19 20 avalanchegoMetrics "github.com/dim4egster/qmallgo/api/metrics" 21 22 "github.com/dim4egster/coreth/consensus/dummy" 23 corethConstants "github.com/dim4egster/coreth/constants" 24 "github.com/dim4egster/coreth/core" 25 "github.com/dim4egster/coreth/core/rawdb" 26 "github.com/dim4egster/coreth/core/state" 27 "github.com/dim4egster/coreth/core/types" 28 "github.com/dim4egster/coreth/eth" 29 "github.com/dim4egster/coreth/eth/ethconfig" 30 "github.com/dim4egster/coreth/ethdb" 31 corethPrometheus "github.com/dim4egster/coreth/metrics/prometheus" 32 "github.com/dim4egster/coreth/miner" 33 "github.com/dim4egster/coreth/node" 34 "github.com/dim4egster/coreth/params" 35 "github.com/dim4egster/coreth/peer" 36 "github.com/dim4egster/coreth/plugin/evm/message" 37 "github.com/dim4egster/coreth/rpc" 38 statesyncclient "github.com/dim4egster/coreth/sync/client" 39 "github.com/dim4egster/coreth/sync/client/stats" 40 "github.com/dim4egster/coreth/sync/handlers" 41 handlerstats "github.com/dim4egster/coreth/sync/handlers/stats" 42 "github.com/dim4egster/coreth/trie" 43 44 "github.com/prometheus/client_golang/prometheus" 45 // Force-load tracer engine to trigger registration 46 // 47 // We must import this package (not referenced elsewhere) so that the native "callTracer" 48 // is added to a map of client-accessible tracers. In geth, this is done 49 // inside of cmd/geth. 50 _ "github.com/dim4egster/coreth/eth/tracers/native" 51 52 "github.com/dim4egster/coreth/metrics" 53 "github.com/ethereum/go-ethereum/common" 54 "github.com/ethereum/go-ethereum/log" 55 "github.com/ethereum/go-ethereum/rlp" 56 57 avalancheRPC "github.com/gorilla/rpc/v2" 58 59 "github.com/dim4egster/qmallgo/cache" 60 "github.com/dim4egster/qmallgo/codec" 61 "github.com/dim4egster/qmallgo/codec/linearcodec" 62 "github.com/dim4egster/qmallgo/database" 63 "github.com/dim4egster/qmallgo/database/manager" 64 "github.com/dim4egster/qmallgo/database/prefixdb" 65 "github.com/dim4egster/qmallgo/database/versiondb" 66 "github.com/dim4egster/qmallgo/ids" 67 "github.com/dim4egster/qmallgo/snow" 68 "github.com/dim4egster/qmallgo/snow/choices" 69 "github.com/dim4egster/qmallgo/snow/consensus/snowman" 70 "github.com/dim4egster/qmallgo/snow/engine/snowman/block" 71 "github.com/dim4egster/qmallgo/utils/constants" 72 "github.com/dim4egster/qmallgo/utils/crypto" 73 "github.com/dim4egster/qmallgo/utils/formatting/address" 74 "github.com/dim4egster/qmallgo/utils/logging" 75 "github.com/dim4egster/qmallgo/utils/math" 76 "github.com/dim4egster/qmallgo/utils/perms" 77 "github.com/dim4egster/qmallgo/utils/profiler" 78 "github.com/dim4egster/qmallgo/utils/timer/mockable" 79 "github.com/dim4egster/qmallgo/utils/units" 80 "github.com/dim4egster/qmallgo/vms/components/avax" 81 "github.com/dim4egster/qmallgo/vms/components/chain" 82 "github.com/dim4egster/qmallgo/vms/secp256k1fx" 83 84 commonEng "github.com/dim4egster/qmallgo/snow/engine/common" 85 86 avalancheJSON "github.com/dim4egster/qmallgo/utils/json" 87 ) 88 89 const ( 90 x2cRateInt64 int64 = 1_000_000_000 91 x2cRateMinus1Int64 int64 = x2cRateInt64 - 1 92 93 // Prefixes for metrics gatherers 94 ethMetricsPrefix = "eth" 95 chainStateMetricsPrefix = "chain_state" 96 ) 97 98 var ( 99 // x2cRate is the conversion rate between the smallest denomination on the X-Chain 100 // 1 nAVAX and the smallest denomination on the C-Chain 1 wei. Where 1 nAVAX = 1 gWei. 101 // This is only required for AVAX because the denomination of 1 AVAX is 9 decimal 102 // places on the X and P chains, but is 18 decimal places within the EVM. 103 x2cRate = big.NewInt(x2cRateInt64) 104 x2cRateMinus1 = big.NewInt(x2cRateMinus1Int64) 105 106 _ block.ChainVM = &VM{} 107 _ block.StateSyncableVM = &VM{} 108 _ block.HeightIndexedChainVM = &VM{} 109 _ statesyncclient.EthBlockParser = &VM{} 110 ) 111 112 const ( 113 // Max time from current time allowed for blocks, before they're considered future blocks 114 // and fail verification 115 maxFutureBlockTime = 10 * time.Second 116 maxUTXOsToFetch = 1024 117 defaultMempoolSize = 4096 118 codecVersion = uint16(0) 119 secpFactoryCacheSize = 1024 120 121 decidedCacheSize = 100 122 missingCacheSize = 50 123 unverifiedCacheSize = 50 124 125 targetAtomicTxsSize = 40 * units.KiB 126 ) 127 128 // Define the API endpoints for the VM 129 const ( 130 avaxEndpoint = "/avax" 131 adminEndpoint = "/admin" 132 ethRPCEndpoint = "/rpc" 133 ethWSEndpoint = "/ws" 134 ) 135 136 var ( 137 // Set last accepted key to be longer than the keys used to store accepted block IDs. 138 lastAcceptedKey = []byte("last_accepted_key") 139 acceptedPrefix = []byte("snowman_accepted") 140 metadataPrefix = []byte("metadata") 141 ethDBPrefix = []byte("ethdb") 142 pruneRejectedBlocksKey = []byte("pruned_rejected_blocks") 143 144 // Prefixes for atomic trie 145 atomicTrieDBPrefix = []byte("atomicTrieDB") 146 atomicTrieMetaDBPrefix = []byte("atomicTrieMetaDB") 147 ) 148 149 var ( 150 errEmptyBlock = errors.New("empty block") 151 errUnsupportedFXs = errors.New("unsupported feature extensions") 152 errInvalidBlock = errors.New("invalid block") 153 errInvalidAddr = errors.New("invalid hex address") 154 errInsufficientAtomicTxFee = errors.New("atomic tx fee too low for atomic mempool") 155 errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo") 156 errNoImportInputs = errors.New("tx has no imported inputs") 157 errInputsNotSortedUnique = errors.New("inputs not sorted and unique") 158 errPublicKeySignatureMismatch = errors.New("signature doesn't match public key") 159 errWrongChainID = errors.New("tx has wrong chain ID") 160 errInsufficientFunds = errors.New("insufficient funds") 161 errNoExportOutputs = errors.New("tx has no export outputs") 162 errOutputsNotSorted = errors.New("tx outputs not sorted") 163 errOutputsNotSortedUnique = errors.New("outputs not sorted and unique") 164 errOverflowExport = errors.New("overflow when computing export amount + txFee") 165 errInvalidNonce = errors.New("invalid nonce") 166 errConflictingAtomicInputs = errors.New("invalid block due to conflicting atomic inputs") 167 errUnclesUnsupported = errors.New("uncles unsupported") 168 errRejectedParent = errors.New("rejected parent") 169 errInsufficientFundsForFee = errors.New("insufficient AVAX funds to pay transaction fee") 170 errNoEVMOutputs = errors.New("tx has no EVM outputs") 171 errNilBaseFeeApricotPhase3 = errors.New("nil base fee is invalid after apricotPhase3") 172 errNilExtDataGasUsedApricotPhase4 = errors.New("nil extDataGasUsed is invalid after apricotPhase4") 173 errNilBlockGasCostApricotPhase4 = errors.New("nil blockGasCost is invalid after apricotPhase4") 174 errConflictingAtomicTx = errors.New("conflicting atomic tx present") 175 errTooManyAtomicTx = errors.New("too many atomic tx") 176 errMissingAtomicTxs = errors.New("cannot build a block with non-empty extra data and zero atomic transactions") 177 errInvalidExtraStateRoot = errors.New("invalid ExtraStateRoot") 178 ) 179 180 var originalStderr *os.File 181 182 // legacyApiNames maps pre geth v1.10.20 api names to their updated counterparts. 183 // used in attachEthService for backward configuration compatibility. 184 var legacyApiNames = map[string]string{ 185 "internal-public-eth": "internal-eth", 186 "internal-public-blockchain": "internal-blockchain", 187 "internal-public-transaction-pool": "internal-transaction", 188 "internal-public-tx-pool": "internal-tx-pool", 189 "internal-public-debug": "internal-debug", 190 "internal-private-debug": "internal-debug", 191 "internal-public-account": "internal-account", 192 "internal-private-personal": "internal-personal", 193 194 "public-eth": "eth", 195 "public-eth-filter": "eth-filter", 196 "private-admin": "admin", 197 "public-debug": "debug", 198 "private-debug": "debug", 199 } 200 201 func init() { 202 // Preserve [os.Stderr] prior to the call in plugin/main.go to plugin.Serve(...). 203 // Preserving the log level allows us to update the root handler while writing to the original 204 // [os.Stderr] that is being piped through to the logger via the rpcchainvm. 205 originalStderr = os.Stderr 206 } 207 208 // VM implements the snowman.ChainVM interface 209 type VM struct { 210 ctx *snow.Context 211 // *chain.State helps to implement the VM interface by wrapping blocks 212 // with an efficient caching layer. 213 *chain.State 214 215 config Config 216 217 chainID *big.Int 218 networkID uint64 219 genesisHash common.Hash 220 chainConfig *params.ChainConfig 221 ethConfig ethconfig.Config 222 223 // pointers to eth constructs 224 eth *eth.Ethereum 225 txPool *core.TxPool 226 blockChain *core.BlockChain 227 miner *miner.Miner 228 229 // [db] is the VM's current database managed by ChainState 230 db *versiondb.Database 231 232 // metadataDB is used to store one off keys. 233 metadataDB database.Database 234 235 // [chaindb] is the database supplied to the Ethereum backend 236 chaindb ethdb.Database 237 238 // [acceptedBlockDB] is the database to store the last accepted 239 // block. 240 acceptedBlockDB database.Database 241 242 toEngine chan<- commonEng.Message 243 244 syntacticBlockValidator BlockValidator 245 246 // [atomicTxRepository] maintains two indexes on accepted atomic txs. 247 // - txID to accepted atomic tx 248 // - block height to list of atomic txs accepted on block at that height 249 atomicTxRepository AtomicTxRepository 250 // [atomicTrie] maintains a merkle forest of [height]=>[atomic txs]. 251 atomicTrie AtomicTrie 252 // [atomicBackend] abstracts verification and processing of atomic transactions 253 atomicBackend AtomicBackend 254 255 builder *blockBuilder 256 257 gossiper Gossiper 258 259 baseCodec codec.Registry 260 codec codec.Manager 261 clock mockable.Clock 262 mempool *Mempool 263 264 shutdownChan chan struct{} 265 shutdownWg sync.WaitGroup 266 267 fx secp256k1fx.Fx 268 secpFactory crypto.FactorySECP256K1R 269 270 // Continuous Profiler 271 profiler profiler.ContinuousProfiler 272 273 peer.Network 274 client peer.NetworkClient 275 networkCodec codec.Manager 276 277 // Metrics 278 multiGatherer avalanchegoMetrics.MultiGatherer 279 280 bootstrapped bool 281 IsPlugin bool 282 283 logger CorethLogger 284 // State sync server and client 285 StateSyncServer 286 StateSyncClient 287 } 288 289 // Codec implements the secp256k1fx interface 290 func (vm *VM) Codec() codec.Manager { return vm.codec } 291 292 // CodecRegistry implements the secp256k1fx interface 293 func (vm *VM) CodecRegistry() codec.Registry { return vm.baseCodec } 294 295 // Clock implements the secp256k1fx interface 296 func (vm *VM) Clock() *mockable.Clock { return &vm.clock } 297 298 // Logger implements the secp256k1fx interface 299 func (vm *VM) Logger() logging.Logger { return vm.ctx.Log } 300 301 /* 302 ****************************************************************************** 303 ********************************* Snowman API ******************************** 304 ****************************************************************************** 305 */ 306 307 // implements SnowmanPlusPlusVM interface 308 func (vm *VM) GetActivationTime() time.Time { 309 return time.Unix(vm.chainConfig.ApricotPhase4BlockTimestamp.Int64(), 0) 310 } 311 312 // Initialize implements the snowman.ChainVM interface 313 func (vm *VM) Initialize( 314 ctx *snow.Context, 315 dbManager manager.Manager, 316 genesisBytes []byte, 317 upgradeBytes []byte, 318 configBytes []byte, 319 toEngine chan<- commonEng.Message, 320 fxs []*commonEng.Fx, 321 appSender commonEng.AppSender, 322 ) error { 323 vm.config.SetDefaults() 324 if len(configBytes) > 0 { 325 if err := json.Unmarshal(configBytes, &vm.config); err != nil { 326 return fmt.Errorf("failed to unmarshal config %s: %w", string(configBytes), err) 327 } 328 } 329 if err := vm.config.Validate(); err != nil { 330 return err 331 } 332 333 vm.ctx = ctx 334 335 // Create logger 336 alias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) 337 if err != nil { 338 alias = vm.ctx.ChainID.String() 339 } 340 341 var writer io.Writer = vm.ctx.Log 342 if vm.IsPlugin { 343 writer = originalStderr 344 } 345 346 corethLogger, err := InitLogger(alias, vm.config.LogLevel, vm.config.LogJSONFormat, writer) 347 if err != nil { 348 return fmt.Errorf("failed to initialize logger due to: %w ", err) 349 } 350 vm.logger = corethLogger 351 352 log.Info("Initializing Coreth VM", "Version", Version, "Config", vm.config) 353 354 if len(fxs) > 0 { 355 return errUnsupportedFXs 356 } 357 358 // Enable debug-level metrics that might impact runtime performance 359 metrics.EnabledExpensive = vm.config.MetricsExpensiveEnabled 360 361 vm.toEngine = toEngine 362 vm.shutdownChan = make(chan struct{}, 1) 363 baseDB := dbManager.Current().Database 364 // Use NewNested rather than New so that the structure of the database 365 // remains the same regardless of the provided baseDB type. 366 vm.chaindb = Database{prefixdb.NewNested(ethDBPrefix, baseDB)} 367 vm.db = versiondb.New(baseDB) 368 vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) 369 vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) 370 g := new(core.Genesis) 371 if err := json.Unmarshal(genesisBytes, g); err != nil { 372 return err 373 } 374 375 var extDataHashes map[common.Hash]common.Hash 376 // Set the chain config for mainnet/fuji chain IDs 377 switch { 378 case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0: 379 g.Config = params.AvalancheMainnetChainConfig 380 extDataHashes = mainnetExtDataHashes 381 case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0: 382 g.Config = params.AvalancheFujiChainConfig 383 extDataHashes = fujiExtDataHashes 384 case g.Config.ChainID.Cmp(params.AvalancheLocalChainID) == 0: 385 g.Config = params.AvalancheLocalChainConfig 386 } 387 vm.syntacticBlockValidator = NewBlockValidator(extDataHashes) 388 389 // Ensure that non-standard commit interval is only allowed for the local network 390 if g.Config.ChainID.Cmp(params.AvalancheLocalChainID) != 0 { 391 if vm.config.CommitInterval != defaultCommitInterval { 392 return fmt.Errorf("cannot start non-local network with commit interval %d", vm.config.CommitInterval) 393 } 394 if vm.config.StateSyncCommitInterval != defaultSyncableCommitInterval { 395 return fmt.Errorf("cannot start non-local network with syncable interval %d", vm.config.StateSyncCommitInterval) 396 } 397 } 398 399 // Free the memory of the extDataHash map that is not used (i.e. if mainnet 400 // config, free fuji) 401 fujiExtDataHashes = nil 402 mainnetExtDataHashes = nil 403 404 vm.chainID = g.Config.ChainID 405 406 vm.ethConfig = ethconfig.NewDefaultConfig() 407 vm.ethConfig.Genesis = g 408 vm.ethConfig.NetworkId = vm.chainID.Uint64() 409 410 // Set minimum price for mining and default gas price oracle value to the min 411 // gas price to prevent so transactions and blocks all use the correct fees 412 vm.ethConfig.RPCGasCap = vm.config.RPCGasCap 413 vm.ethConfig.RPCEVMTimeout = vm.config.APIMaxDuration.Duration 414 vm.ethConfig.RPCTxFeeCap = vm.config.RPCTxFeeCap 415 vm.ethConfig.TxPool.NoLocals = !vm.config.LocalTxsEnabled 416 vm.ethConfig.AllowUnfinalizedQueries = vm.config.AllowUnfinalizedQueries 417 vm.ethConfig.AllowUnprotectedTxs = vm.config.AllowUnprotectedTxs 418 vm.ethConfig.Preimages = vm.config.Preimages 419 vm.ethConfig.Pruning = vm.config.Pruning 420 vm.ethConfig.AcceptorQueueLimit = vm.config.AcceptorQueueLimit 421 vm.ethConfig.PopulateMissingTries = vm.config.PopulateMissingTries 422 vm.ethConfig.PopulateMissingTriesParallelism = vm.config.PopulateMissingTriesParallelism 423 vm.ethConfig.AllowMissingTries = vm.config.AllowMissingTries 424 vm.ethConfig.SnapshotDelayInit = vm.config.StateSyncEnabled 425 vm.ethConfig.SnapshotAsync = vm.config.SnapshotAsync 426 vm.ethConfig.SnapshotVerify = vm.config.SnapshotVerify 427 vm.ethConfig.OfflinePruning = vm.config.OfflinePruning 428 vm.ethConfig.OfflinePruningBloomFilterSize = vm.config.OfflinePruningBloomFilterSize 429 vm.ethConfig.OfflinePruningDataDirectory = vm.config.OfflinePruningDataDirectory 430 vm.ethConfig.CommitInterval = vm.config.CommitInterval 431 432 // Create directory for offline pruning 433 if len(vm.ethConfig.OfflinePruningDataDirectory) != 0 { 434 if err := os.MkdirAll(vm.ethConfig.OfflinePruningDataDirectory, perms.ReadWriteExecute); err != nil { 435 log.Error("failed to create offline pruning data directory", "error", err) 436 return err 437 } 438 } 439 440 vm.genesisHash = vm.ethConfig.Genesis.ToBlock(nil).Hash() 441 442 vm.chainConfig = g.Config 443 vm.networkID = vm.ethConfig.NetworkId 444 vm.secpFactory = crypto.FactorySECP256K1R{Cache: cache.LRU{Size: secpFactoryCacheSize}} 445 446 vm.codec = Codec 447 448 // TODO: read size from settings 449 vm.mempool = NewMempool(ctx.AVAXAssetID, defaultMempoolSize) 450 451 lastAcceptedHash, lastAcceptedHeight, err := vm.readLastAccepted() 452 if err != nil { 453 return err 454 } 455 log.Info(fmt.Sprintf("lastAccepted = %s", lastAcceptedHash)) 456 457 if err := vm.initializeMetrics(); err != nil { 458 return err 459 } 460 461 // initialize peer network 462 vm.networkCodec = message.Codec 463 vm.Network = peer.NewNetwork(appSender, vm.networkCodec, ctx.NodeID, vm.config.MaxOutboundActiveRequests) 464 vm.client = peer.NewNetworkClient(vm.Network) 465 466 if err := vm.initializeChain(lastAcceptedHash); err != nil { 467 return err 468 } 469 // initialize bonus blocks on mainnet 470 var ( 471 bonusBlockHeights map[uint64]ids.ID 472 canonicalBlockHeights []uint64 473 ) 474 if vm.chainID.Cmp(params.AvalancheMainnetChainID) == 0 { 475 bonusBlockHeights = bonusBlockMainnetHeights 476 canonicalBlockHeights = canonicalBlockMainnetHeights 477 } 478 479 // initialize atomic repository 480 vm.atomicTxRepository, err = NewAtomicTxRepository( 481 vm.db, vm.codec, lastAcceptedHeight, 482 bonusBlockHeights, canonicalBlockHeights, 483 vm.getAtomicTxFromPreApricot5BlockByHeight, 484 ) 485 if err != nil { 486 return fmt.Errorf("failed to create atomic repository: %w", err) 487 } 488 vm.atomicBackend, err = NewAtomicBackend( 489 vm.db, vm.ctx.SharedMemory, bonusBlockHeights, vm.atomicTxRepository, lastAcceptedHeight, lastAcceptedHash, vm.config.CommitInterval, 490 ) 491 if err != nil { 492 return fmt.Errorf("failed to create atomic backend: %w", err) 493 } 494 vm.atomicTrie = vm.atomicBackend.AtomicTrie() 495 496 go vm.ctx.Log.RecoverAndPanic(vm.startContinuousProfiler) 497 498 // The Codec explicitly registers the types it requires from the secp256k1fx 499 // so [vm.baseCodec] is a dummy codec use to fulfill the secp256k1fx VM 500 // interface. The fx will register all of its types, which can be safely 501 // ignored by the VM's codec. 502 vm.baseCodec = linearcodec.NewDefault() 503 504 if err := vm.fx.Initialize(vm); err != nil { 505 return err 506 } 507 508 vm.initializeStateSyncServer() 509 return vm.initializeStateSyncClient(lastAcceptedHeight) 510 } 511 512 func (vm *VM) initializeMetrics() error { 513 vm.multiGatherer = avalanchegoMetrics.NewMultiGatherer() 514 // If metrics are enabled, register the default metrics regitry 515 if metrics.Enabled { 516 gatherer := corethPrometheus.Gatherer(metrics.DefaultRegistry) 517 if err := vm.multiGatherer.Register(ethMetricsPrefix, gatherer); err != nil { 518 return err 519 } 520 // Register [multiGatherer] after registerers have been registered to it 521 if err := vm.ctx.Metrics.Register(vm.multiGatherer); err != nil { 522 return err 523 } 524 } 525 return nil 526 } 527 528 func (vm *VM) initializeChain(lastAcceptedHash common.Hash) error { 529 nodecfg := &node.Config{ 530 CorethVersion: Version, 531 KeyStoreDir: vm.config.KeystoreDirectory, 532 ExternalSigner: vm.config.KeystoreExternalSigner, 533 InsecureUnlockAllowed: vm.config.KeystoreInsecureUnlockAllowed, 534 } 535 node, err := node.New(nodecfg) 536 if err != nil { 537 return err 538 } 539 vm.eth, err = eth.New( 540 node, 541 &vm.ethConfig, 542 vm.createConsensusCallbacks(), 543 vm.chaindb, 544 vm.config.EthBackendSettings(), 545 lastAcceptedHash, 546 &vm.clock, 547 ) 548 if err != nil { 549 return err 550 } 551 vm.eth.SetEtherbase(corethConstants.BlackholeAddr) 552 vm.txPool = vm.eth.TxPool() 553 vm.blockChain = vm.eth.BlockChain() 554 vm.miner = vm.eth.Miner() 555 556 // start goroutines to update the tx pool gas minimum gas price when upgrades go into effect 557 vm.handleGasPriceUpdates() 558 559 vm.eth.Start() 560 return vm.initChainState(vm.blockChain.LastAcceptedBlock()) 561 } 562 563 // initializeStateSyncClient initializes the client for performing state sync. 564 // If state sync is disabled, this function will wipe any ongoing summary from 565 // disk to ensure that we do not continue syncing from an invalid snapshot. 566 func (vm *VM) initializeStateSyncClient(lastAcceptedHeight uint64) error { 567 // parse nodeIDs from state sync IDs in vm config 568 var stateSyncIDs []ids.NodeID 569 if vm.config.StateSyncEnabled && len(vm.config.StateSyncIDs) > 0 { 570 nodeIDs := strings.Split(vm.config.StateSyncIDs, ",") 571 stateSyncIDs = make([]ids.NodeID, len(nodeIDs)) 572 for i, nodeIDString := range nodeIDs { 573 nodeID, err := ids.NodeIDFromString(nodeIDString) 574 if err != nil { 575 return fmt.Errorf("failed to parse %s as NodeID: %w", nodeIDString, err) 576 } 577 stateSyncIDs[i] = nodeID 578 } 579 } 580 581 vm.StateSyncClient = NewStateSyncClient(&stateSyncClientConfig{ 582 chain: vm.eth, 583 state: vm.State, 584 client: statesyncclient.NewClient( 585 &statesyncclient.ClientConfig{ 586 NetworkClient: vm.client, 587 Codec: vm.networkCodec, 588 Stats: stats.NewClientSyncerStats(), 589 StateSyncNodeIDs: stateSyncIDs, 590 BlockParser: vm, 591 }, 592 ), 593 enabled: vm.config.StateSyncEnabled, 594 skipResume: vm.config.StateSyncSkipResume, 595 stateSyncMinBlocks: vm.config.StateSyncMinBlocks, 596 lastAcceptedHeight: lastAcceptedHeight, // TODO clean up how this is passed around 597 chaindb: vm.chaindb, 598 metadataDB: vm.metadataDB, 599 acceptedBlockDB: vm.acceptedBlockDB, 600 db: vm.db, 601 atomicBackend: vm.atomicBackend, 602 toEngine: vm.toEngine, 603 }) 604 605 // If StateSync is disabled, clear any ongoing summary so that we will not attempt to resume 606 // sync using a snapshot that has been modified by the node running normal operations. 607 if !vm.config.StateSyncEnabled { 608 return vm.StateSyncClient.StateSyncClearOngoingSummary() 609 } 610 611 return nil 612 } 613 614 // initializeStateSyncServer should be called after [vm.chain] is initialized. 615 func (vm *VM) initializeStateSyncServer() { 616 vm.StateSyncServer = NewStateSyncServer(&stateSyncServerConfig{ 617 Chain: vm.blockChain, 618 AtomicTrie: vm.atomicTrie, 619 SyncableInterval: vm.config.StateSyncCommitInterval, 620 }) 621 622 vm.setAppRequestHandlers() 623 } 624 625 func (vm *VM) initChainState(lastAcceptedBlock *types.Block) error { 626 block, err := vm.newBlock(lastAcceptedBlock) 627 if err != nil { 628 return fmt.Errorf("failed to create block wrapper for the last accepted block: %w", err) 629 } 630 block.status = choices.Accepted 631 632 config := &chain.Config{ 633 DecidedCacheSize: decidedCacheSize, 634 MissingCacheSize: missingCacheSize, 635 UnverifiedCacheSize: unverifiedCacheSize, 636 GetBlockIDAtHeight: vm.GetBlockIDAtHeight, 637 GetBlock: vm.getBlock, 638 UnmarshalBlock: vm.parseBlock, 639 BuildBlock: vm.buildBlock, 640 LastAcceptedBlock: block, 641 } 642 643 // Register chain state metrics 644 chainStateRegisterer := prometheus.NewRegistry() 645 state, err := chain.NewMeteredState(chainStateRegisterer, config) 646 if err != nil { 647 return fmt.Errorf("could not create metered state: %w", err) 648 } 649 vm.State = state 650 651 return vm.multiGatherer.Register(chainStateMetricsPrefix, chainStateRegisterer) 652 } 653 654 func (vm *VM) createConsensusCallbacks() *dummy.ConsensusCallbacks { 655 return &dummy.ConsensusCallbacks{ 656 OnFinalizeAndAssemble: vm.onFinalizeAndAssemble, 657 OnExtraStateChange: vm.onExtraStateChange, 658 } 659 } 660 661 func (vm *VM) preBatchOnFinalizeAndAssemble(header *types.Header, state *state.StateDB, txs []*types.Transaction) ([]byte, *big.Int, *big.Int, error) { 662 for { 663 tx, exists := vm.mempool.NextTx() 664 if !exists { 665 break 666 } 667 // Take a snapshot of [state] before calling verifyTx so that if the transaction fails verification 668 // we can revert to [snapshot]. 669 // Note: snapshot is taken inside the loop because you cannot revert to the same snapshot more than 670 // once. 671 snapshot := state.Snapshot() 672 rules := vm.chainConfig.AvalancheRules(header.Number, new(big.Int).SetUint64(header.Time)) 673 if err := vm.verifyTx(tx, header.ParentHash, header.BaseFee, state, rules); err != nil { 674 // Discard the transaction from the mempool on failed verification. 675 vm.mempool.DiscardCurrentTx(tx.ID()) 676 state.RevertToSnapshot(snapshot) 677 continue 678 } 679 680 atomicTxBytes, err := vm.codec.Marshal(codecVersion, tx) 681 if err != nil { 682 // Discard the transaction from the mempool and error if the transaction 683 // cannot be marshalled. This should never happen. 684 vm.mempool.DiscardCurrentTx(tx.ID()) 685 return nil, nil, nil, fmt.Errorf("failed to marshal atomic transaction %s due to %w", tx.ID(), err) 686 } 687 var contribution, gasUsed *big.Int 688 if rules.IsApricotPhase4 { 689 contribution, gasUsed, err = tx.BlockFeeContribution(rules.IsApricotPhase5, vm.ctx.AVAXAssetID, header.BaseFee) 690 if err != nil { 691 return nil, nil, nil, err 692 } 693 } 694 return atomicTxBytes, contribution, gasUsed, nil 695 } 696 697 if len(txs) == 0 { 698 // this could happen due to the async logic of geth tx pool 699 return nil, nil, nil, errEmptyBlock 700 } 701 702 return nil, nil, nil, nil 703 } 704 705 // assumes that we are in at least Apricot Phase 5. 706 func (vm *VM) postBatchOnFinalizeAndAssemble(header *types.Header, state *state.StateDB, txs []*types.Transaction) ([]byte, *big.Int, *big.Int, error) { 707 var ( 708 batchAtomicTxs []*Tx 709 batchAtomicUTXOs ids.Set 710 batchContribution *big.Int = new(big.Int).Set(common.Big0) 711 batchGasUsed *big.Int = new(big.Int).Set(common.Big0) 712 rules = vm.chainConfig.AvalancheRules(header.Number, new(big.Int).SetUint64(header.Time)) 713 size int 714 ) 715 716 for { 717 tx, exists := vm.mempool.NextTx() 718 if !exists { 719 break 720 } 721 722 // Ensure that adding [tx] to the block will not exceed the block size soft limit. 723 txSize := len(tx.SignedBytes()) 724 if size+txSize > targetAtomicTxsSize { 725 vm.mempool.CancelCurrentTx(tx.ID()) 726 break 727 } 728 729 var ( 730 txGasUsed, txContribution *big.Int 731 err error 732 ) 733 734 // Note: we do not need to check if we are in at least ApricotPhase4 here because 735 // we assume that this function will only be called when the block is in at least 736 // ApricotPhase5. 737 txContribution, txGasUsed, err = tx.BlockFeeContribution(true, vm.ctx.AVAXAssetID, header.BaseFee) 738 if err != nil { 739 return nil, nil, nil, err 740 } 741 // ensure [gasUsed] + [batchGasUsed] doesnt exceed the [atomicGasLimit] 742 if totalGasUsed := new(big.Int).Add(batchGasUsed, txGasUsed); totalGasUsed.Cmp(params.AtomicGasLimit) > 0 { 743 // Send [tx] back to the mempool's tx heap. 744 vm.mempool.CancelCurrentTx(tx.ID()) 745 break 746 } 747 748 if batchAtomicUTXOs.Overlaps(tx.InputUTXOs()) { 749 // Discard the transaction from the mempool since it will fail verification 750 // after this block has been accepted. 751 // Note: if the proposed block is not accepted, the transaction may still be 752 // valid, but we discard it early here based on the assumption that the proposed 753 // block will most likely be accepted. 754 // Discard the transaction from the mempool on failed verification. 755 vm.mempool.DiscardCurrentTx(tx.ID()) 756 continue 757 } 758 759 snapshot := state.Snapshot() 760 if err := vm.verifyTx(tx, header.ParentHash, header.BaseFee, state, rules); err != nil { 761 // Discard the transaction from the mempool and reset the state to [snapshot] 762 // if it fails verification here. 763 // Note: prior to this point, we have not modified [state] so there is no need to 764 // revert to a snapshot if we discard the transaction prior to this point. 765 vm.mempool.DiscardCurrentTx(tx.ID()) 766 state.RevertToSnapshot(snapshot) 767 continue 768 } 769 770 batchAtomicTxs = append(batchAtomicTxs, tx) 771 batchAtomicUTXOs.Union(tx.InputUTXOs()) 772 // Add the [txGasUsed] to the [batchGasUsed] when the [tx] has passed verification 773 batchGasUsed.Add(batchGasUsed, txGasUsed) 774 batchContribution.Add(batchContribution, txContribution) 775 size += txSize 776 } 777 778 // In Clementine the block header must include the atomic trie root. 779 if rules.IsClementine { 780 // Pass common.Hash{} as the current block's hash to the atomic backend, this avoids 781 // pinning changes to the atomic trie in memory, as we are still computing the header 782 // for this block and don't have its hash yet. Here we calculate the root of the atomic 783 // trie to store in the block header. 784 atomicTrieRoot, err := vm.atomicBackend.InsertTxs(common.Hash{}, header.Number.Uint64(), header.ParentHash, batchAtomicTxs) 785 if err != nil { 786 return nil, nil, nil, err 787 } 788 header.ExtraStateRoot = atomicTrieRoot 789 } 790 791 // If there is a non-zero number of transactions, marshal them and return the byte slice 792 // for the block's extra data along with the contribution and gas used. 793 if len(batchAtomicTxs) > 0 { 794 atomicTxBytes, err := vm.codec.Marshal(codecVersion, batchAtomicTxs) 795 if err != nil { 796 // If we fail to marshal the batch of atomic transactions for any reason, 797 // discard the entire set of current transactions. 798 vm.mempool.DiscardCurrentTxs() 799 return nil, nil, nil, fmt.Errorf("failed to marshal batch of atomic transactions due to %w", err) 800 } 801 return atomicTxBytes, batchContribution, batchGasUsed, nil 802 } 803 804 // If there are no regular transactions and there were also no atomic transactions to be included, 805 // then the block is empty and should be considered invalid. 806 if len(txs) == 0 { 807 // this could happen due to the async logic of geth tx pool 808 return nil, nil, nil, errEmptyBlock 809 } 810 811 // If there are no atomic transactions, but there is a non-zero number of regular transactions, then 812 // we return a nil slice with no contribution from the atomic transactions and a nil error. 813 return nil, nil, nil, nil 814 } 815 816 func (vm *VM) onFinalizeAndAssemble(header *types.Header, state *state.StateDB, txs []*types.Transaction) ([]byte, *big.Int, *big.Int, error) { 817 if !vm.chainConfig.IsApricotPhase5(new(big.Int).SetUint64(header.Time)) { 818 return vm.preBatchOnFinalizeAndAssemble(header, state, txs) 819 } 820 return vm.postBatchOnFinalizeAndAssemble(header, state, txs) 821 } 822 823 func (vm *VM) onExtraStateChange(block *types.Block, state *state.StateDB) (*big.Int, *big.Int, error) { 824 var ( 825 batchContribution *big.Int = big.NewInt(0) 826 batchGasUsed *big.Int = big.NewInt(0) 827 header = block.Header() 828 rules = vm.chainConfig.AvalancheRules(header.Number, new(big.Int).SetUint64(header.Time)) 829 ) 830 831 txs, err := ExtractAtomicTxs(block.ExtData(), rules.IsApricotPhase5, vm.codec) 832 if err != nil { 833 return nil, nil, err 834 } 835 836 // If [atomicBackend] is nil, the VM is still initializing and is reprocessing accepted blocks. 837 if vm.atomicBackend != nil { 838 if vm.atomicBackend.IsBonus(block.NumberU64(), block.Hash()) { 839 log.Info("skipping atomic tx verification on bonus block", "block", block.Hash()) 840 } else { 841 // Verify [txs] do not conflict with themselves or ancestor blocks. 842 if err := vm.verifyTxs(txs, block.ParentHash(), block.BaseFee(), block.NumberU64(), rules); err != nil { 843 return nil, nil, err 844 } 845 } 846 // Update the atomic backend with [txs] from this block. 847 atomicRoot, err := vm.atomicBackend.InsertTxs(block.Hash(), block.NumberU64(), block.ParentHash(), txs) 848 if err != nil { 849 return nil, nil, err 850 } 851 if rules.IsClementine { 852 // In Clementine, the atomic trie root should be in ExtraStateRoot. 853 if header.ExtraStateRoot != atomicRoot { 854 return nil, nil, fmt.Errorf( 855 "%w: (expected %s) (got %s)", errInvalidExtraStateRoot, header.ExtraStateRoot, atomicRoot, 856 ) 857 } 858 } 859 } 860 861 // If there are no transactions, we can return early. 862 if len(txs) == 0 { 863 return nil, nil, nil 864 } 865 866 for _, tx := range txs { 867 if err := tx.UnsignedAtomicTx.EVMStateTransfer(vm.ctx, state); err != nil { 868 return nil, nil, err 869 } 870 // If ApricotPhase4 is enabled, calculate the block fee contribution 871 if rules.IsApricotPhase4 { 872 contribution, gasUsed, err := tx.BlockFeeContribution(rules.IsApricotPhase5, vm.ctx.AVAXAssetID, block.BaseFee()) 873 if err != nil { 874 return nil, nil, err 875 } 876 877 batchContribution.Add(batchContribution, contribution) 878 batchGasUsed.Add(batchGasUsed, gasUsed) 879 } 880 881 // If ApricotPhase5 is enabled, enforce that the atomic gas used does not exceed the 882 // atomic gas limit. 883 if rules.IsApricotPhase5 { 884 // Ensure that [tx] does not push [block] above the atomic gas limit. 885 if batchGasUsed.Cmp(params.AtomicGasLimit) == 1 { 886 return nil, nil, fmt.Errorf("atomic gas used (%d) by block (%s), exceeds atomic gas limit (%d)", batchGasUsed, block.Hash().Hex(), params.AtomicGasLimit) 887 } 888 } 889 } 890 return batchContribution, batchGasUsed, nil 891 } 892 893 func (vm *VM) pruneChain() error { 894 if !vm.config.Pruning { 895 return nil 896 } 897 pruned, err := vm.db.Has(pruneRejectedBlocksKey) 898 if err != nil { 899 return fmt.Errorf("failed to check if the VM has pruned rejected blocks: %w", err) 900 } 901 if pruned { 902 return nil 903 } 904 905 lastAcceptedHeight := vm.LastAcceptedBlock().Height() 906 if err := vm.blockChain.RemoveRejectedBlocks(0, lastAcceptedHeight); err != nil { 907 return err 908 } 909 heightBytes := make([]byte, 8) 910 binary.PutUvarint(heightBytes, lastAcceptedHeight) 911 if err := vm.db.Put(pruneRejectedBlocksKey, heightBytes); err != nil { 912 return err 913 } 914 return vm.db.Commit() 915 } 916 917 func (vm *VM) SetState(state snow.State) error { 918 switch state { 919 case snow.StateSyncing: 920 vm.bootstrapped = false 921 return nil 922 case snow.Bootstrapping: 923 vm.bootstrapped = false 924 if err := vm.StateSyncClient.Error(); err != nil { 925 return err 926 } 927 return vm.fx.Bootstrapping() 928 case snow.NormalOp: 929 // Initialize goroutines related to block building once we enter normal operation as there is no need to handle mempool gossip before this point. 930 vm.initBlockBuilding() 931 vm.bootstrapped = true 932 return vm.fx.Bootstrapped() 933 default: 934 return snow.ErrUnknownState 935 } 936 } 937 938 // initBlockBuilding starts goroutines to manage block building 939 func (vm *VM) initBlockBuilding() { 940 // NOTE: gossip network must be initialized first otherwise ETH tx gossip will not work. 941 gossipStats := NewGossipStats() 942 vm.gossiper = vm.createGossiper(gossipStats) 943 vm.builder = vm.NewBlockBuilder(vm.toEngine) 944 vm.builder.awaitSubmittedTxs() 945 vm.Network.SetGossipHandler(NewGossipHandler(vm, gossipStats)) 946 } 947 948 // setAppRequestHandlers sets the request handlers for the VM to serve state sync 949 // requests. 950 func (vm *VM) setAppRequestHandlers() { 951 // Create separate EVM TrieDB (read only) for serving leafs requests. 952 // We create a separate TrieDB here, so that it has a separate cache from the one 953 // used by the node when processing blocks. 954 evmTrieDB := trie.NewDatabaseWithConfig( 955 vm.chaindb, 956 &trie.Config{ 957 Cache: vm.config.StateSyncServerTrieCache, 958 }, 959 ) 960 syncRequestHandler := handlers.NewSyncHandler( 961 vm.blockChain, 962 evmTrieDB, 963 vm.atomicTrie.TrieDB(), 964 vm.networkCodec, 965 handlerstats.NewHandlerStats(metrics.Enabled), 966 ) 967 vm.Network.SetRequestHandler(syncRequestHandler) 968 } 969 970 // Shutdown implements the snowman.ChainVM interface 971 func (vm *VM) Shutdown() error { 972 if vm.ctx == nil { 973 return nil 974 } 975 vm.Network.Shutdown() 976 if err := vm.StateSyncClient.Shutdown(); err != nil { 977 log.Error("error stopping state syncer", "err", err) 978 } 979 close(vm.shutdownChan) 980 vm.eth.Stop() 981 vm.shutdownWg.Wait() 982 return nil 983 } 984 985 // buildBlock builds a block to be wrapped by ChainState 986 func (vm *VM) buildBlock() (snowman.Block, error) { 987 block, err := vm.miner.GenerateBlock() 988 vm.builder.handleGenerateBlock() 989 if err != nil { 990 vm.mempool.CancelCurrentTxs() 991 return nil, err 992 } 993 994 // Note: the status of block is set by ChainState 995 blk, err := vm.newBlock(block) 996 if err != nil { 997 vm.mempool.DiscardCurrentTxs() 998 return nil, err 999 } 1000 if err != nil { 1001 return nil, err 1002 } 1003 1004 // Verify is called on a non-wrapped block here, such that this 1005 // does not add [blk] to the processing blocks map in ChainState. 1006 // 1007 // TODO cache verification since Verify() will be called by the 1008 // consensus engine as well. 1009 // 1010 // Note: this is only called when building a new block, so caching 1011 // verification will only be a significant optimization for nodes 1012 // that produce a large number of blocks. 1013 // We call verify without writes here to avoid generating a reference 1014 // to the blk state root in the triedb when we are going to call verify 1015 // again from the consensus engine with writes enabled. 1016 if err := blk.verify(false /*=writes*/); err != nil { 1017 vm.mempool.CancelCurrentTxs() 1018 return nil, fmt.Errorf("block failed verification due to: %w", err) 1019 } 1020 1021 log.Debug(fmt.Sprintf("Built block %s", blk.ID())) 1022 // Marks the current transactions from the mempool as being successfully issued 1023 // into a block. 1024 vm.mempool.IssueCurrentTxs() 1025 return blk, nil 1026 } 1027 1028 // parseBlock parses [b] into a block to be wrapped by ChainState. 1029 func (vm *VM) parseBlock(b []byte) (snowman.Block, error) { 1030 ethBlock := new(types.Block) 1031 if err := rlp.DecodeBytes(b, ethBlock); err != nil { 1032 return nil, err 1033 } 1034 1035 // Note: the status of block is set by ChainState 1036 block, err := vm.newBlock(ethBlock) 1037 if err != nil { 1038 return nil, err 1039 } 1040 // Performing syntactic verification in ParseBlock allows for 1041 // short-circuiting bad blocks before they are processed by the VM. 1042 if err := block.syntacticVerify(); err != nil { 1043 return nil, fmt.Errorf("syntactic block verification failed: %w", err) 1044 } 1045 return block, nil 1046 } 1047 1048 func (vm *VM) ParseEthBlock(b []byte) (*types.Block, error) { 1049 block, err := vm.parseBlock(b) 1050 if err != nil { 1051 return nil, err 1052 } 1053 1054 return block.(*Block).ethBlock, nil 1055 } 1056 1057 // getBlock attempts to retrieve block [id] from the VM to be wrapped 1058 // by ChainState. 1059 func (vm *VM) getBlock(id ids.ID) (snowman.Block, error) { 1060 ethBlock := vm.blockChain.GetBlockByHash(common.Hash(id)) 1061 // If [ethBlock] is nil, return [database.ErrNotFound] here 1062 // so that the miss is considered cacheable. 1063 if ethBlock == nil { 1064 return nil, database.ErrNotFound 1065 } 1066 // Note: the status of block is set by ChainState 1067 return vm.newBlock(ethBlock) 1068 } 1069 1070 // SetPreference sets what the current tail of the chain is 1071 func (vm *VM) SetPreference(blkID ids.ID) error { 1072 // Since each internal handler used by [vm.State] always returns a block 1073 // with non-nil ethBlock value, GetBlockInternal should never return a 1074 // (*Block) with a nil ethBlock value. 1075 block, err := vm.GetBlockInternal(blkID) 1076 if err != nil { 1077 return fmt.Errorf("failed to set preference to %s: %w", blkID, err) 1078 } 1079 1080 return vm.blockChain.SetPreference(block.(*Block).ethBlock) 1081 } 1082 1083 // VerifyHeightIndex always returns a nil error since the index is maintained by 1084 // vm.blockChain. 1085 func (vm *VM) VerifyHeightIndex() error { 1086 return nil 1087 } 1088 1089 // GetBlockAtHeight implements the HeightIndexedChainVM interface and returns the 1090 // canonical block at [blkHeight]. 1091 // If [blkHeight] is less than the height of the last accepted block, this will return 1092 // the block accepted at that height. Otherwise, it may return a blkID that has not yet 1093 // been accepted. 1094 // Note: the engine assumes that if a block is not found at [blkHeight], then 1095 // [database.ErrNotFound] will be returned. This indicates that the VM has state synced 1096 // and does not have all historical blocks available. 1097 func (vm *VM) GetBlockIDAtHeight(blkHeight uint64) (ids.ID, error) { 1098 ethBlock := vm.blockChain.GetBlockByNumber(blkHeight) 1099 if ethBlock == nil { 1100 return ids.ID{}, database.ErrNotFound 1101 } 1102 1103 return ids.ID(ethBlock.Hash()), nil 1104 } 1105 1106 func (vm *VM) Version() (string, error) { 1107 return Version, nil 1108 } 1109 1110 // NewHandler returns a new Handler for a service where: 1111 // * The handler's functionality is defined by [service] 1112 // [service] should be a gorilla RPC service (see https://www.gorillatoolkit.org/pkg/rpc/v2) 1113 // * The name of the service is [name] 1114 // * The LockOption is the first element of [lockOption] 1115 // By default the LockOption is WriteLock 1116 // [lockOption] should have either 0 or 1 elements. Elements beside the first are ignored. 1117 func newHandler(name string, service interface{}, lockOption ...commonEng.LockOption) (*commonEng.HTTPHandler, error) { 1118 server := avalancheRPC.NewServer() 1119 server.RegisterCodec(avalancheJSON.NewCodec(), "application/json") 1120 server.RegisterCodec(avalancheJSON.NewCodec(), "application/json;charset=UTF-8") 1121 if err := server.RegisterService(service, name); err != nil { 1122 return nil, err 1123 } 1124 1125 var lock commonEng.LockOption = commonEng.WriteLock 1126 if len(lockOption) != 0 { 1127 lock = lockOption[0] 1128 } 1129 return &commonEng.HTTPHandler{LockOptions: lock, Handler: server}, nil 1130 } 1131 1132 // CreateHandlers makes new http handlers that can handle API calls 1133 func (vm *VM) CreateHandlers() (map[string]*commonEng.HTTPHandler, error) { 1134 handler := rpc.NewServer(vm.config.APIMaxDuration.Duration) 1135 enabledAPIs := vm.config.EthAPIs() 1136 if err := attachEthService(handler, vm.eth.APIs(), enabledAPIs); err != nil { 1137 return nil, err 1138 } 1139 1140 primaryAlias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) 1141 if err != nil { 1142 return nil, fmt.Errorf("failed to get primary alias for chain due to %w", err) 1143 } 1144 apis := make(map[string]*commonEng.HTTPHandler) 1145 avaxAPI, err := newHandler("avax", &AvaxAPI{vm}) 1146 if err != nil { 1147 return nil, fmt.Errorf("failed to register service for AVAX API due to %w", err) 1148 } 1149 enabledAPIs = append(enabledAPIs, "avax") 1150 apis[avaxEndpoint] = avaxAPI 1151 1152 if vm.config.CorethAdminAPIEnabled { 1153 adminAPI, err := newHandler("admin", NewAdminService(vm, os.ExpandEnv(fmt.Sprintf("%s_coreth_performance_%s", vm.config.CorethAdminAPIDir, primaryAlias)))) 1154 if err != nil { 1155 return nil, fmt.Errorf("failed to register service for admin API due to %w", err) 1156 } 1157 apis[adminEndpoint] = adminAPI 1158 enabledAPIs = append(enabledAPIs, "coreth-admin") 1159 } 1160 1161 if vm.config.SnowmanAPIEnabled { 1162 if err := handler.RegisterName("snowman", &SnowmanAPI{vm}); err != nil { 1163 return nil, err 1164 } 1165 enabledAPIs = append(enabledAPIs, "snowman") 1166 } 1167 1168 log.Info(fmt.Sprintf("Enabled APIs: %s", strings.Join(enabledAPIs, ", "))) 1169 apis[ethRPCEndpoint] = &commonEng.HTTPHandler{ 1170 LockOptions: commonEng.NoLock, 1171 Handler: handler, 1172 } 1173 apis[ethWSEndpoint] = &commonEng.HTTPHandler{ 1174 LockOptions: commonEng.NoLock, 1175 Handler: handler.WebsocketHandlerWithDuration( 1176 []string{"*"}, 1177 vm.config.APIMaxDuration.Duration, 1178 vm.config.WSCPURefillRate.Duration, 1179 vm.config.WSCPUMaxStored.Duration, 1180 ), 1181 } 1182 1183 return apis, nil 1184 } 1185 1186 // CreateStaticHandlers makes new http handlers that can handle API calls 1187 func (vm *VM) CreateStaticHandlers() (map[string]*commonEng.HTTPHandler, error) { 1188 handler := rpc.NewServer(0) 1189 if err := handler.RegisterName("static", &StaticService{}); err != nil { 1190 return nil, err 1191 } 1192 1193 return map[string]*commonEng.HTTPHandler{ 1194 "/rpc": {LockOptions: commonEng.NoLock, Handler: handler}, 1195 }, nil 1196 } 1197 1198 /* 1199 ****************************************************************************** 1200 *********************************** Helpers ********************************** 1201 ****************************************************************************** 1202 */ 1203 1204 // conflicts returns an error if [inputs] conflicts with any of the atomic inputs contained in [ancestor] 1205 // or any of its ancestor blocks going back to the last accepted block in its ancestry. If [ancestor] is 1206 // accepted, then nil will be returned immediately. 1207 // If the ancestry of [ancestor] cannot be fetched, then [errRejectedParent] may be returned. 1208 func (vm *VM) conflicts(inputs ids.Set, ancestor *Block) error { 1209 for ancestor.Status() != choices.Accepted { 1210 // If any of the atomic transactions in the ancestor conflict with [inputs] 1211 // return an error. 1212 for _, atomicTx := range ancestor.atomicTxs { 1213 if inputs.Overlaps(atomicTx.InputUTXOs()) { 1214 return errConflictingAtomicInputs 1215 } 1216 } 1217 1218 // Move up the chain. 1219 nextAncestorID := ancestor.Parent() 1220 // If the ancestor is unknown, then the parent failed 1221 // verification when it was called. 1222 // If the ancestor is rejected, then this block shouldn't be 1223 // inserted into the canonical chain because the parent is 1224 // will be missing. 1225 // If the ancestor is processing, then the block may have 1226 // been verified. 1227 nextAncestorIntf, err := vm.GetBlockInternal(nextAncestorID) 1228 if err != nil { 1229 return errRejectedParent 1230 } 1231 1232 if blkStatus := nextAncestorIntf.Status(); blkStatus == choices.Unknown || blkStatus == choices.Rejected { 1233 return errRejectedParent 1234 } 1235 nextAncestor, ok := nextAncestorIntf.(*Block) 1236 if !ok { 1237 return fmt.Errorf("ancestor block %s had unexpected type %T", nextAncestor.ID(), nextAncestorIntf) 1238 } 1239 ancestor = nextAncestor 1240 } 1241 1242 return nil 1243 } 1244 1245 // getAtomicTx returns the requested transaction, status, and height. 1246 // If the status is Unknown, then the returned transaction will be nil. 1247 func (vm *VM) getAtomicTx(txID ids.ID) (*Tx, Status, uint64, error) { 1248 if tx, height, err := vm.atomicTxRepository.GetByTxID(txID); err == nil { 1249 return tx, Accepted, height, nil 1250 } else if err != database.ErrNotFound { 1251 return nil, Unknown, 0, err 1252 } 1253 tx, dropped, found := vm.mempool.GetTx(txID) 1254 switch { 1255 case found && dropped: 1256 return tx, Dropped, 0, nil 1257 case found: 1258 return tx, Processing, 0, nil 1259 default: 1260 return nil, Unknown, 0, nil 1261 } 1262 } 1263 1264 // ParseAddress takes in an address and produces the ID of the chain it's for 1265 // the ID of the address 1266 func (vm *VM) ParseAddress(addrStr string) (ids.ID, ids.ShortID, error) { 1267 chainIDAlias, hrp, addrBytes, err := address.Parse(addrStr) 1268 if err != nil { 1269 return ids.ID{}, ids.ShortID{}, err 1270 } 1271 1272 chainID, err := vm.ctx.BCLookup.Lookup(chainIDAlias) 1273 if err != nil { 1274 return ids.ID{}, ids.ShortID{}, err 1275 } 1276 1277 expectedHRP := constants.GetHRP(vm.ctx.NetworkID) 1278 if hrp != expectedHRP { 1279 return ids.ID{}, ids.ShortID{}, fmt.Errorf("expected hrp %q but got %q", 1280 expectedHRP, hrp) 1281 } 1282 1283 addr, err := ids.ToShortID(addrBytes) 1284 if err != nil { 1285 return ids.ID{}, ids.ShortID{}, err 1286 } 1287 return chainID, addr, nil 1288 } 1289 1290 // issueTx verifies [tx] as valid to be issued on top of the currently preferred block 1291 // and then issues [tx] into the mempool if valid. 1292 func (vm *VM) issueTx(tx *Tx, local bool) error { 1293 if err := vm.verifyTxAtTip(tx); err != nil { 1294 if !local { 1295 // unlike local txs, invalid remote txs are recorded as discarded 1296 // so that they won't be requested again 1297 txID := tx.ID() 1298 vm.mempool.discardedTxs.Put(txID, tx) 1299 log.Debug("failed to verify remote tx being issued to the mempool", 1300 "txID", txID, 1301 "err", err, 1302 ) 1303 return nil 1304 } 1305 return err 1306 } 1307 // add to mempool and possibly re-gossip 1308 if err := vm.mempool.AddTx(tx); err != nil { 1309 if !local { 1310 // unlike local txs, invalid remote txs are recorded as discarded 1311 // so that they won't be requested again 1312 txID := tx.ID() 1313 vm.mempool.discardedTxs.Put(tx.ID(), tx) 1314 log.Debug("failed to issue remote tx to mempool", 1315 "txID", txID, 1316 "err", err, 1317 ) 1318 return nil 1319 } 1320 return err 1321 } 1322 // NOTE: Gossiping of the issued [Tx] is handled in [AddTx] 1323 return nil 1324 } 1325 1326 // verifyTxAtTip verifies that [tx] is valid to be issued on top of the currently preferred block 1327 func (vm *VM) verifyTxAtTip(tx *Tx) error { 1328 // Note: we fetch the current block and then the state at that block instead of the current state directly 1329 // since we need the header of the current block below. 1330 preferredBlock := vm.blockChain.CurrentBlock() 1331 preferredState, err := vm.blockChain.StateAt(preferredBlock.Root()) 1332 if err != nil { 1333 return fmt.Errorf("failed to retrieve block state at tip while verifying atomic tx: %w", err) 1334 } 1335 rules := vm.currentRules() 1336 parentHeader := preferredBlock.Header() 1337 var nextBaseFee *big.Int 1338 timestamp := vm.clock.Time().Unix() 1339 bigTimestamp := big.NewInt(timestamp) 1340 if vm.chainConfig.IsApricotPhase3(bigTimestamp) { 1341 _, nextBaseFee, err = dummy.EstimateNextBaseFee(vm.chainConfig, parentHeader, uint64(timestamp)) 1342 if err != nil { 1343 // Return extremely detailed error since CalcBaseFee should never encounter an issue here 1344 return fmt.Errorf("failed to calculate base fee with parent timestamp (%d), parent ExtraData: (0x%x), and current timestamp (%d): %w", parentHeader.Time, parentHeader.Extra, timestamp, err) 1345 } 1346 } 1347 1348 return vm.verifyTx(tx, parentHeader.Hash(), nextBaseFee, preferredState, rules) 1349 } 1350 1351 // verifyTx verifies that [tx] is valid to be issued into a block with parent block [parentHash] 1352 // and validated at [state] using [rules] as the current rule set. 1353 // Note: verifyTx may modify [state]. If [state] needs to be properly maintained, the caller is responsible 1354 // for reverting to the correct snapshot after calling this function. If this function is called with a 1355 // throwaway state, then this is not necessary. 1356 func (vm *VM) verifyTx(tx *Tx, parentHash common.Hash, baseFee *big.Int, state *state.StateDB, rules params.Rules) error { 1357 parentIntf, err := vm.GetBlockInternal(ids.ID(parentHash)) 1358 if err != nil { 1359 return fmt.Errorf("failed to get parent block: %w", err) 1360 } 1361 parent, ok := parentIntf.(*Block) 1362 if !ok { 1363 return fmt.Errorf("parent block %s had unexpected type %T", parentIntf.ID(), parentIntf) 1364 } 1365 if err := tx.UnsignedAtomicTx.SemanticVerify(vm, tx, parent, baseFee, rules); err != nil { 1366 return err 1367 } 1368 return tx.UnsignedAtomicTx.EVMStateTransfer(vm.ctx, state) 1369 } 1370 1371 // verifyTxs verifies that [txs] are valid to be issued into a block with parent block [parentHash] 1372 // using [rules] as the current rule set. 1373 func (vm *VM) verifyTxs(txs []*Tx, parentHash common.Hash, baseFee *big.Int, height uint64, rules params.Rules) error { 1374 // Ensure that the parent was verified and inserted correctly. 1375 if !vm.blockChain.HasBlock(parentHash, height-1) { 1376 return errRejectedParent 1377 } 1378 1379 ancestorID := ids.ID(parentHash) 1380 // If the ancestor is unknown, then the parent failed verification when 1381 // it was called. 1382 // If the ancestor is rejected, then this block shouldn't be inserted 1383 // into the canonical chain because the parent will be missing. 1384 ancestorInf, err := vm.GetBlockInternal(ancestorID) 1385 if err != nil { 1386 return errRejectedParent 1387 } 1388 if blkStatus := ancestorInf.Status(); blkStatus == choices.Unknown || blkStatus == choices.Rejected { 1389 return errRejectedParent 1390 } 1391 ancestor, ok := ancestorInf.(*Block) 1392 if !ok { 1393 return fmt.Errorf("expected parent block %s, to be *Block but is %T", ancestor.ID(), ancestorInf) 1394 } 1395 1396 // Ensure each tx in [txs] doesn't conflict with any other atomic tx in 1397 // a processing ancestor block. 1398 inputs := &ids.Set{} 1399 for _, atomicTx := range txs { 1400 utx := atomicTx.UnsignedAtomicTx 1401 if err := utx.SemanticVerify(vm, atomicTx, ancestor, baseFee, rules); err != nil { 1402 return fmt.Errorf("invalid block due to failed semanatic verify: %w at height %d", err, height) 1403 } 1404 txInputs := utx.InputUTXOs() 1405 if inputs.Overlaps(txInputs) { 1406 return errConflictingAtomicInputs 1407 } 1408 inputs.Union(txInputs) 1409 } 1410 return nil 1411 } 1412 1413 // GetAtomicUTXOs returns the utxos that at least one of the provided addresses is 1414 // referenced in. 1415 func (vm *VM) GetAtomicUTXOs( 1416 chainID ids.ID, 1417 addrs ids.ShortSet, 1418 startAddr ids.ShortID, 1419 startUTXOID ids.ID, 1420 limit int, 1421 ) ([]*avax.UTXO, ids.ShortID, ids.ID, error) { 1422 if limit <= 0 || limit > maxUTXOsToFetch { 1423 limit = maxUTXOsToFetch 1424 } 1425 1426 addrsList := make([][]byte, addrs.Len()) 1427 for i, addr := range addrs.List() { 1428 addrsList[i] = addr.Bytes() 1429 } 1430 1431 allUTXOBytes, lastAddr, lastUTXO, err := vm.ctx.SharedMemory.Indexed( 1432 chainID, 1433 addrsList, 1434 startAddr.Bytes(), 1435 startUTXOID[:], 1436 limit, 1437 ) 1438 if err != nil { 1439 return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error fetching atomic UTXOs: %w", err) 1440 } 1441 1442 lastAddrID, err := ids.ToShortID(lastAddr) 1443 if err != nil { 1444 lastAddrID = ids.ShortEmpty 1445 } 1446 lastUTXOID, err := ids.ToID(lastUTXO) 1447 if err != nil { 1448 lastUTXOID = ids.Empty 1449 } 1450 1451 utxos := make([]*avax.UTXO, len(allUTXOBytes)) 1452 for i, utxoBytes := range allUTXOBytes { 1453 utxo := &avax.UTXO{} 1454 if _, err := vm.codec.Unmarshal(utxoBytes, utxo); err != nil { 1455 return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error parsing UTXO: %w", err) 1456 } 1457 utxos[i] = utxo 1458 } 1459 return utxos, lastAddrID, lastUTXOID, nil 1460 } 1461 1462 // GetSpendableFunds returns a list of EVMInputs and keys (in corresponding 1463 // order) to total [amount] of [assetID] owned by [keys]. 1464 // Note: we return [][]*crypto.PrivateKeySECP256K1R even though each input 1465 // corresponds to a single key, so that the signers can be passed in to 1466 // [tx.Sign] which supports multiple keys on a single input. 1467 func (vm *VM) GetSpendableFunds( 1468 keys []*crypto.PrivateKeySECP256K1R, 1469 assetID ids.ID, 1470 amount uint64, 1471 ) ([]EVMInput, [][]*crypto.PrivateKeySECP256K1R, error) { 1472 // Note: current state uses the state of the preferred block. 1473 state, err := vm.blockChain.State() 1474 if err != nil { 1475 return nil, nil, err 1476 } 1477 inputs := []EVMInput{} 1478 signers := [][]*crypto.PrivateKeySECP256K1R{} 1479 // Note: we assume that each key in [keys] is unique, so that iterating over 1480 // the keys will not produce duplicated nonces in the returned EVMInput slice. 1481 for _, key := range keys { 1482 if amount == 0 { 1483 break 1484 } 1485 addr := GetEthAddress(key) 1486 var balance uint64 1487 if assetID == vm.ctx.AVAXAssetID { 1488 // If the asset is AVAX, we divide by the x2cRate to convert back to the correct 1489 // denomination of AVAX that can be exported. 1490 balance = new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64() 1491 } else { 1492 balance = state.GetBalanceMultiCoin(addr, common.Hash(assetID)).Uint64() 1493 } 1494 if balance == 0 { 1495 continue 1496 } 1497 if amount < balance { 1498 balance = amount 1499 } 1500 nonce, err := vm.GetCurrentNonce(addr) 1501 if err != nil { 1502 return nil, nil, err 1503 } 1504 inputs = append(inputs, EVMInput{ 1505 Address: addr, 1506 Amount: balance, 1507 AssetID: assetID, 1508 Nonce: nonce, 1509 }) 1510 signers = append(signers, []*crypto.PrivateKeySECP256K1R{key}) 1511 amount -= balance 1512 } 1513 1514 if amount > 0 { 1515 return nil, nil, errInsufficientFunds 1516 } 1517 1518 return inputs, signers, nil 1519 } 1520 1521 // GetSpendableAVAXWithFee returns a list of EVMInputs and keys (in corresponding 1522 // order) to total [amount] + [fee] of [AVAX] owned by [keys]. 1523 // This function accounts for the added cost of the additional inputs needed to 1524 // create the transaction and makes sure to skip any keys with a balance that is 1525 // insufficient to cover the additional fee. 1526 // Note: we return [][]*crypto.PrivateKeySECP256K1R even though each input 1527 // corresponds to a single key, so that the signers can be passed in to 1528 // [tx.Sign] which supports multiple keys on a single input. 1529 func (vm *VM) GetSpendableAVAXWithFee( 1530 keys []*crypto.PrivateKeySECP256K1R, 1531 amount uint64, 1532 cost uint64, 1533 baseFee *big.Int, 1534 ) ([]EVMInput, [][]*crypto.PrivateKeySECP256K1R, error) { 1535 // Note: current state uses the state of the preferred block. 1536 state, err := vm.blockChain.State() 1537 if err != nil { 1538 return nil, nil, err 1539 } 1540 1541 initialFee, err := calculateDynamicFee(cost, baseFee) 1542 if err != nil { 1543 return nil, nil, err 1544 } 1545 1546 newAmount, err := math.Add64(amount, initialFee) 1547 if err != nil { 1548 return nil, nil, err 1549 } 1550 amount = newAmount 1551 1552 inputs := []EVMInput{} 1553 signers := [][]*crypto.PrivateKeySECP256K1R{} 1554 // Note: we assume that each key in [keys] is unique, so that iterating over 1555 // the keys will not produce duplicated nonces in the returned EVMInput slice. 1556 for _, key := range keys { 1557 if amount == 0 { 1558 break 1559 } 1560 1561 prevFee, err := calculateDynamicFee(cost, baseFee) 1562 if err != nil { 1563 return nil, nil, err 1564 } 1565 1566 newCost := cost + EVMInputGas 1567 newFee, err := calculateDynamicFee(newCost, baseFee) 1568 if err != nil { 1569 return nil, nil, err 1570 } 1571 1572 additionalFee := newFee - prevFee 1573 1574 addr := GetEthAddress(key) 1575 // Since the asset is AVAX, we divide by the x2cRate to convert back to 1576 // the correct denomination of AVAX that can be exported. 1577 balance := new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64() 1578 // If the balance for [addr] is insufficient to cover the additional cost 1579 // of adding an input to the transaction, skip adding the input altogether 1580 if balance <= additionalFee { 1581 continue 1582 } 1583 1584 // Update the cost for the next iteration 1585 cost = newCost 1586 1587 newAmount, err := math.Add64(amount, additionalFee) 1588 if err != nil { 1589 return nil, nil, err 1590 } 1591 amount = newAmount 1592 1593 // Use the entire [balance] as an input, but if the required [amount] 1594 // is less than the balance, update the [inputAmount] to spend the 1595 // minimum amount to finish the transaction. 1596 inputAmount := balance 1597 if amount < balance { 1598 inputAmount = amount 1599 } 1600 nonce, err := vm.GetCurrentNonce(addr) 1601 if err != nil { 1602 return nil, nil, err 1603 } 1604 inputs = append(inputs, EVMInput{ 1605 Address: addr, 1606 Amount: inputAmount, 1607 AssetID: vm.ctx.AVAXAssetID, 1608 Nonce: nonce, 1609 }) 1610 signers = append(signers, []*crypto.PrivateKeySECP256K1R{key}) 1611 amount -= inputAmount 1612 } 1613 1614 if amount > 0 { 1615 return nil, nil, errInsufficientFunds 1616 } 1617 1618 return inputs, signers, nil 1619 } 1620 1621 // GetCurrentNonce returns the nonce associated with the address at the 1622 // preferred block 1623 func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) { 1624 // Note: current state uses the state of the preferred block. 1625 state, err := vm.blockChain.State() 1626 if err != nil { 1627 return 0, err 1628 } 1629 return state.GetNonce(address), nil 1630 } 1631 1632 // currentRules returns the chain rules for the current block. 1633 func (vm *VM) currentRules() params.Rules { 1634 header := vm.eth.APIBackend.CurrentHeader() 1635 return vm.chainConfig.AvalancheRules(header.Number, big.NewInt(int64(header.Time))) 1636 } 1637 1638 func (vm *VM) startContinuousProfiler() { 1639 // If the profiler directory is empty, return immediately 1640 // without creating or starting a continuous profiler. 1641 if vm.config.ContinuousProfilerDir == "" { 1642 return 1643 } 1644 vm.profiler = profiler.NewContinuous( 1645 filepath.Join(vm.config.ContinuousProfilerDir), 1646 vm.config.ContinuousProfilerFrequency.Duration, 1647 vm.config.ContinuousProfilerMaxFiles, 1648 ) 1649 defer vm.profiler.Shutdown() 1650 1651 vm.shutdownWg.Add(1) 1652 go func() { 1653 defer vm.shutdownWg.Done() 1654 log.Info("Dispatching continuous profiler", "dir", vm.config.ContinuousProfilerDir, "freq", vm.config.ContinuousProfilerFrequency, "maxFiles", vm.config.ContinuousProfilerMaxFiles) 1655 err := vm.profiler.Dispatch() 1656 if err != nil { 1657 log.Error("continuous profiler failed", "err", err) 1658 } 1659 }() 1660 // Wait for shutdownChan to be closed 1661 <-vm.shutdownChan 1662 } 1663 1664 func (vm *VM) estimateBaseFee(ctx context.Context) (*big.Int, error) { 1665 // Get the base fee to use 1666 baseFee, err := vm.eth.APIBackend.EstimateBaseFee(ctx) 1667 if err != nil { 1668 return nil, err 1669 } 1670 if baseFee == nil { 1671 baseFee = initialBaseFee 1672 } else { 1673 // give some breathing room 1674 baseFee.Mul(baseFee, big.NewInt(11)) 1675 baseFee.Div(baseFee, big.NewInt(10)) 1676 } 1677 1678 return baseFee, nil 1679 } 1680 1681 func (vm *VM) getAtomicTxFromPreApricot5BlockByHeight(height uint64) (*Tx, error) { 1682 blk := vm.blockChain.GetBlockByNumber(height) 1683 if blk == nil { 1684 return nil, nil 1685 } 1686 return ExtractAtomicTx(blk.ExtData(), vm.codec) 1687 } 1688 1689 // readLastAccepted reads the last accepted hash from [acceptedBlockDB] and returns the 1690 // last accepted block hash and height by reading directly from [vm.chaindb] instead of relying 1691 // on [chain]. 1692 // Note: assumes chaindb, ethConfig, and genesisHash have been initialized. 1693 func (vm *VM) readLastAccepted() (common.Hash, uint64, error) { 1694 // Attempt to load last accepted block to determine if it is necessary to 1695 // initialize state with the genesis block. 1696 lastAcceptedBytes, lastAcceptedErr := vm.acceptedBlockDB.Get(lastAcceptedKey) 1697 switch { 1698 case lastAcceptedErr == database.ErrNotFound: 1699 // If there is nothing in the database, return the genesis block hash and height 1700 return vm.genesisHash, 0, nil 1701 case lastAcceptedErr != nil: 1702 return common.Hash{}, 0, fmt.Errorf("failed to get last accepted block ID due to: %w", lastAcceptedErr) 1703 case len(lastAcceptedBytes) != common.HashLength: 1704 return common.Hash{}, 0, fmt.Errorf("last accepted bytes should have been length %d, but found %d", common.HashLength, len(lastAcceptedBytes)) 1705 default: 1706 lastAcceptedHash := common.BytesToHash(lastAcceptedBytes) 1707 height := rawdb.ReadHeaderNumber(vm.chaindb, lastAcceptedHash) 1708 if height == nil { 1709 return common.Hash{}, 0, fmt.Errorf("failed to retrieve header number of last accepted block: %s", lastAcceptedHash) 1710 } 1711 return lastAcceptedHash, *height, nil 1712 } 1713 } 1714 1715 // attachEthService registers the backend RPC services provided by Ethereum 1716 // to the provided handler under their assigned namespaces. 1717 func attachEthService(handler *rpc.Server, apis []rpc.API, names []string) error { 1718 enabledServicesSet := make(map[string]struct{}) 1719 for _, ns := range names { 1720 // handle pre geth v1.10.20 api names as aliases for their updated values 1721 // to allow configurations to be backwards compatible. 1722 if newName, isLegacy := legacyApiNames[ns]; isLegacy { 1723 log.Info("deprecated api name referenced in configuration.", "deprecated", ns, "new", newName) 1724 enabledServicesSet[newName] = struct{}{} 1725 continue 1726 } 1727 1728 enabledServicesSet[ns] = struct{}{} 1729 } 1730 1731 apiSet := make(map[string]rpc.API) 1732 for _, api := range apis { 1733 if existingAPI, exists := apiSet[api.Name]; exists { 1734 return fmt.Errorf("duplicated API name: %s, namespaces %s and %s", api.Name, api.Namespace, existingAPI.Namespace) 1735 } 1736 apiSet[api.Name] = api 1737 } 1738 1739 for name := range enabledServicesSet { 1740 api, exists := apiSet[name] 1741 if !exists { 1742 return fmt.Errorf("API service %s not found", name) 1743 } 1744 if err := handler.RegisterName(api.Namespace, api.Service); err != nil { 1745 return err 1746 } 1747 } 1748 1749 return nil 1750 }