github.com/MetalBlockchain/subnet-evm@v0.4.9/plugin/evm/vm_test.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 "crypto/ecdsa" 9 "crypto/rand" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "math/big" 14 "os" 15 "path/filepath" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/MetalBlockchain/subnet-evm/commontype" 21 "github.com/MetalBlockchain/subnet-evm/internal/ethapi" 22 "github.com/MetalBlockchain/subnet-evm/metrics" 23 "github.com/MetalBlockchain/subnet-evm/plugin/evm/message" 24 "github.com/MetalBlockchain/subnet-evm/precompile" 25 "github.com/MetalBlockchain/subnet-evm/trie" 26 "github.com/MetalBlockchain/subnet-evm/vmerrs" 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/common/hexutil" 29 "github.com/ethereum/go-ethereum/crypto" 30 "github.com/ethereum/go-ethereum/log" 31 32 "github.com/stretchr/testify/require" 33 34 "github.com/MetalBlockchain/metalgo/api/keystore" 35 "github.com/MetalBlockchain/metalgo/database/manager" 36 "github.com/MetalBlockchain/metalgo/ids" 37 "github.com/MetalBlockchain/metalgo/snow" 38 "github.com/MetalBlockchain/metalgo/snow/choices" 39 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 40 "github.com/MetalBlockchain/metalgo/snow/validators" 41 metalConstants "github.com/MetalBlockchain/metalgo/utils/constants" 42 "github.com/MetalBlockchain/metalgo/utils/formatting" 43 "github.com/MetalBlockchain/metalgo/utils/logging" 44 "github.com/MetalBlockchain/metalgo/version" 45 "github.com/MetalBlockchain/metalgo/vms/components/chain" 46 47 engCommon "github.com/MetalBlockchain/metalgo/snow/engine/common" 48 49 "github.com/MetalBlockchain/subnet-evm/consensus/dummy" 50 "github.com/MetalBlockchain/subnet-evm/constants" 51 "github.com/MetalBlockchain/subnet-evm/core" 52 "github.com/MetalBlockchain/subnet-evm/core/types" 53 "github.com/MetalBlockchain/subnet-evm/eth" 54 "github.com/MetalBlockchain/subnet-evm/params" 55 "github.com/MetalBlockchain/subnet-evm/rpc" 56 57 "github.com/MetalBlockchain/subnet-evm/accounts/abi" 58 accountKeystore "github.com/MetalBlockchain/subnet-evm/accounts/keystore" 59 ) 60 61 var ( 62 testNetworkID uint32 = 10 63 testCChainID = ids.ID{'c', 'c', 'h', 'a', 'i', 'n', 't', 'e', 's', 't'} 64 testXChainID = ids.ID{'t', 'e', 's', 't', 'x'} 65 testMinGasPrice int64 = 225_000_000_000 66 testKeys []*ecdsa.PrivateKey 67 testEthAddrs []common.Address // testEthAddrs[i] corresponds to testKeys[i] 68 testAvaxAssetID = ids.ID{1, 2, 3} 69 username = "Johns" 70 password = "CjasdjhiPeirbSenfeI13" // #nosec G101 71 // Use chainId: 43111, so that it does not overlap with any Metal ChainIDs, which may have their 72 // config overridden in vm.Initialize. 73 genesisJSONSubnetEVMLateEnablement = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"subnetEVMTimestamp\":50},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" 74 genesisJSONSubnetEVM = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"subnetEVMTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" 75 genesisJSONPreSubnetEVM = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" 76 genesisJSONLatest = genesisJSONSubnetEVM 77 78 firstTxAmount *big.Int 79 genesisBalance *big.Int 80 ) 81 82 func init() { 83 key1, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 84 key2, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 85 testKeys = append(testKeys, key1, key2) 86 addr1 := crypto.PubkeyToAddress(key1.PublicKey) 87 addr2 := crypto.PubkeyToAddress(key2.PublicKey) 88 testEthAddrs = append(testEthAddrs, addr1, addr2) 89 90 firstTxAmount = new(big.Int).Mul(big.NewInt(testMinGasPrice), big.NewInt(21000*100)) 91 genesisBalance = new(big.Int).Mul(big.NewInt(testMinGasPrice), big.NewInt(21000*1000)) 92 } 93 94 // BuildGenesisTest returns the genesis bytes for Subnet EVM VM to be used in testing 95 func buildGenesisTest(t *testing.T, genesisJSON string) []byte { 96 ss := CreateStaticService() 97 98 genesis := &core.Genesis{} 99 if err := json.Unmarshal([]byte(genesisJSON), genesis); err != nil { 100 t.Fatalf("Problem unmarshaling genesis JSON: %s", err) 101 } 102 args := &BuildGenesisArgs{GenesisData: genesis} 103 reply := &BuildGenesisReply{} 104 err := ss.BuildGenesis(nil, args, reply) 105 if err != nil { 106 t.Fatalf("Failed to create test genesis") 107 } 108 genesisBytes, err := formatting.Decode(reply.Encoding, reply.GenesisBytes) 109 if err != nil { 110 t.Fatalf("Failed to decode genesis bytes: %s", err) 111 } 112 return genesisBytes 113 } 114 115 func NewContext() *snow.Context { 116 ctx := snow.DefaultContextTest() 117 ctx.NetworkID = testNetworkID 118 ctx.NodeID = ids.GenerateTestNodeID() 119 ctx.ChainID = testCChainID 120 ctx.AVAXAssetID = testAvaxAssetID 121 ctx.XChainID = testXChainID 122 aliaser := ctx.BCLookup.(ids.Aliaser) 123 _ = aliaser.Alias(testCChainID, "C") 124 _ = aliaser.Alias(testCChainID, testCChainID.String()) 125 _ = aliaser.Alias(testXChainID, "X") 126 _ = aliaser.Alias(testXChainID, testXChainID.String()) 127 ctx.ValidatorState = &validators.TestState{ 128 GetSubnetIDF: func(_ context.Context, chainID ids.ID) (ids.ID, error) { 129 subnetID, ok := map[ids.ID]ids.ID{ 130 metalConstants.PlatformChainID: metalConstants.PrimaryNetworkID, 131 testXChainID: metalConstants.PrimaryNetworkID, 132 testCChainID: metalConstants.PrimaryNetworkID, 133 }[chainID] 134 if !ok { 135 return ids.Empty, errors.New("unknown chain") 136 } 137 return subnetID, nil 138 }, 139 } 140 return ctx 141 } 142 143 type snLookup struct { 144 chainsToSubnet map[ids.ID]ids.ID 145 } 146 147 func (sn *snLookup) SubnetID(chainID ids.ID) (ids.ID, error) { 148 subnetID, ok := sn.chainsToSubnet[chainID] 149 if !ok { 150 return ids.ID{}, errors.New("unknown chain") 151 } 152 return subnetID, nil 153 } 154 155 // If [genesisJSON] is empty, defaults to using [genesisJSONLatest] 156 func setupGenesis(t *testing.T, 157 genesisJSON string, 158 ) (*snow.Context, 159 manager.Manager, 160 []byte, 161 chan engCommon.Message, 162 ) { 163 if len(genesisJSON) == 0 { 164 genesisJSON = genesisJSONLatest 165 } 166 genesisBytes := buildGenesisTest(t, genesisJSON) 167 ctx := NewContext() 168 169 baseDBManager := manager.NewMemDB(&version.Semantic{ 170 Major: 1, 171 Minor: 4, 172 Patch: 5, 173 }) 174 175 // NB: this lock is intentionally left locked when this function returns. 176 // The caller of this function is responsible for unlocking. 177 ctx.Lock.Lock() 178 179 userKeystore := keystore.New(logging.NoLog{}, manager.NewMemDB(&version.Semantic{ 180 Major: 1, 181 Minor: 4, 182 Patch: 5, 183 })) 184 if err := userKeystore.CreateUser(username, password); err != nil { 185 t.Fatal(err) 186 } 187 ctx.Keystore = userKeystore.NewBlockchainKeyStore(ctx.ChainID) 188 189 issuer := make(chan engCommon.Message, 1) 190 prefixedDBManager := baseDBManager.NewPrefixDBManager([]byte{1}) 191 return ctx, prefixedDBManager, genesisBytes, issuer 192 } 193 194 // GenesisVM creates a VM instance with the genesis test bytes and returns 195 // the channel use to send messages to the engine, the VM, database manager, 196 // and sender. 197 // If [genesisJSON] is empty, defaults to using [genesisJSONLatest] 198 func GenesisVM(t *testing.T, 199 finishBootstrapping bool, 200 genesisJSON string, 201 configJSON string, 202 upgradeJSON string, 203 ) (chan engCommon.Message, 204 *VM, manager.Manager, 205 *engCommon.SenderTest, 206 ) { 207 vm := &VM{} 208 ctx, dbManager, genesisBytes, issuer := setupGenesis(t, genesisJSON) 209 appSender := &engCommon.SenderTest{T: t} 210 appSender.CantSendAppGossip = true 211 appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } 212 if err := vm.Initialize( 213 context.Background(), 214 ctx, 215 dbManager, 216 genesisBytes, 217 []byte(upgradeJSON), 218 []byte(configJSON), 219 issuer, 220 []*engCommon.Fx{}, 221 appSender, 222 ); err != nil { 223 t.Fatal(err) 224 } 225 226 if finishBootstrapping { 227 require.NoError(t, vm.SetState(context.Background(), snow.Bootstrapping)) 228 require.NoError(t, vm.SetState(context.Background(), snow.NormalOp)) 229 } 230 231 return issuer, vm, dbManager, appSender 232 } 233 234 func TestVMConfig(t *testing.T) { 235 txFeeCap := float64(11) 236 enabledEthAPIs := []string{"debug"} 237 configJSON := fmt.Sprintf("{\"rpc-tx-fee-cap\": %g,\"eth-apis\": %s}", txFeeCap, fmt.Sprintf("[%q]", enabledEthAPIs[0])) 238 _, vm, _, _ := GenesisVM(t, false, "", configJSON, "") 239 require.Equal(t, vm.config.RPCTxFeeCap, txFeeCap, "Tx Fee Cap should be set") 240 require.Equal(t, vm.config.EthAPIs(), enabledEthAPIs, "EnabledEthAPIs should be set") 241 require.NoError(t, vm.Shutdown(context.Background())) 242 } 243 244 func TestVMConfigDefaults(t *testing.T) { 245 txFeeCap := float64(11) 246 enabledEthAPIs := []string{"debug"} 247 configJSON := fmt.Sprintf("{\"rpc-tx-fee-cap\": %g,\"eth-apis\": %s}", txFeeCap, fmt.Sprintf("[%q]", enabledEthAPIs[0])) 248 _, vm, _, _ := GenesisVM(t, false, "", configJSON, "") 249 250 var vmConfig Config 251 vmConfig.SetDefaults() 252 vmConfig.RPCTxFeeCap = txFeeCap 253 vmConfig.EnabledEthAPIs = enabledEthAPIs 254 require.Equal(t, vmConfig, vm.config, "VM Config should match default with overrides") 255 require.NoError(t, vm.Shutdown(context.Background())) 256 } 257 258 func TestVMNilConfig(t *testing.T) { 259 _, vm, _, _ := GenesisVM(t, false, "", "", "") 260 261 // VM Config should match defaults if no config is passed in 262 var vmConfig Config 263 vmConfig.SetDefaults() 264 require.Equal(t, vmConfig, vm.config, "VM Config should match default config") 265 require.NoError(t, vm.Shutdown(context.Background())) 266 } 267 268 func TestVMContinuousProfiler(t *testing.T) { 269 profilerDir := t.TempDir() 270 profilerFrequency := 500 * time.Millisecond 271 configJSON := fmt.Sprintf("{\"continuous-profiler-dir\": %q,\"continuous-profiler-frequency\": \"500ms\"}", profilerDir) 272 _, vm, _, _ := GenesisVM(t, false, "", configJSON, "") 273 require.Equal(t, vm.config.ContinuousProfilerDir, profilerDir, "profiler dir should be set") 274 require.Equal(t, vm.config.ContinuousProfilerFrequency.Duration, profilerFrequency, "profiler frequency should be set") 275 276 // Sleep for twice the frequency of the profiler to give it time 277 // to generate the first profile. 278 time.Sleep(2 * time.Second) 279 require.NoError(t, vm.Shutdown(context.Background())) 280 281 // Check that the first profile was generated 282 expectedFileName := filepath.Join(profilerDir, "cpu.profile.1") 283 _, err := os.Stat(expectedFileName) 284 require.NoError(t, err, "Expected continuous profiler to generate the first CPU profile at %s", expectedFileName) 285 } 286 287 func TestVMUpgrades(t *testing.T) { 288 genesisTests := []struct { 289 name string 290 genesis string 291 expectedGasPrice *big.Int 292 }{ 293 { 294 name: "Subnet EVM", 295 genesis: genesisJSONSubnetEVM, 296 expectedGasPrice: big.NewInt(0), 297 }, 298 } 299 for _, test := range genesisTests { 300 t.Run(test.name, func(t *testing.T) { 301 _, vm, _, _ := GenesisVM(t, true, test.genesis, "", "") 302 303 if gasPrice := vm.txPool.GasPrice(); gasPrice.Cmp(test.expectedGasPrice) != 0 { 304 t.Fatalf("Expected pool gas price to be %d but found %d", test.expectedGasPrice, gasPrice) 305 } 306 defer func() { 307 shutdownChan := make(chan error, 1) 308 shutdownFunc := func() { 309 err := vm.Shutdown(context.Background()) 310 shutdownChan <- err 311 } 312 313 go shutdownFunc() 314 shutdownTimeout := 50 * time.Millisecond 315 ticker := time.NewTicker(shutdownTimeout) 316 select { 317 case <-ticker.C: 318 t.Fatalf("VM shutdown took longer than timeout: %v", shutdownTimeout) 319 case err := <-shutdownChan: 320 if err != nil { 321 t.Fatalf("Shutdown errored: %s", err) 322 } 323 } 324 }() 325 326 lastAcceptedID, err := vm.LastAccepted(context.Background()) 327 if err != nil { 328 t.Fatal(err) 329 } 330 331 if lastAcceptedID != ids.ID(vm.genesisHash) { 332 t.Fatal("Expected last accepted block to match the genesis block hash") 333 } 334 335 genesisBlk, err := vm.GetBlock(context.Background(), lastAcceptedID) 336 if err != nil { 337 t.Fatalf("Failed to get genesis block due to %s", err) 338 } 339 340 if height := genesisBlk.Height(); height != 0 { 341 t.Fatalf("Expected height of geneiss block to be 0, found: %d", height) 342 } 343 344 if _, err := vm.ParseBlock(context.Background(), genesisBlk.Bytes()); err != nil { 345 t.Fatalf("Failed to parse genesis block due to %s", err) 346 } 347 348 genesisStatus := genesisBlk.Status() 349 if genesisStatus != choices.Accepted { 350 t.Fatalf("expected genesis status to be %s but was %s", choices.Accepted, genesisStatus) 351 } 352 }) 353 } 354 } 355 356 func issueAndAccept(t *testing.T, issuer <-chan engCommon.Message, vm *VM) snowman.Block { 357 t.Helper() 358 <-issuer 359 360 blk, err := vm.BuildBlock(context.Background()) 361 if err != nil { 362 t.Fatal(err) 363 } 364 365 if err := blk.Verify(context.Background()); err != nil { 366 t.Fatal(err) 367 } 368 369 if status := blk.Status(); status != choices.Processing { 370 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 371 } 372 373 if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { 374 t.Fatal(err) 375 } 376 377 if err := blk.Accept(context.Background()); err != nil { 378 t.Fatal(err) 379 } 380 381 return blk 382 } 383 384 func TestSubnetEVMUpgradeRequiredAtGenesis(t *testing.T) { 385 genesisTests := []struct { 386 genesisJSON string 387 configJSON string 388 expectedErr error 389 }{ 390 { 391 // we expect an error when subnet evm upgrade is nil in chain config 392 genesisJSON: genesisJSONPreSubnetEVM, 393 configJSON: "", 394 expectedErr: errSubnetEVMUpgradeNotEnabled, 395 }, 396 { 397 // we expect an error when subnet evm upgrade is not enabled at genesis and at a later block instead 398 genesisJSON: genesisJSONSubnetEVMLateEnablement, 399 configJSON: "", 400 expectedErr: errSubnetEVMUpgradeNotEnabled, 401 }, 402 { 403 // we do not expect an err when skip-subnet-evm-upgrade-check is set to true 404 genesisJSON: genesisJSONPreSubnetEVM, 405 configJSON: "{\"skip-subnet-evm-upgrade-check\": true}", 406 expectedErr: nil, 407 }, 408 { 409 // we do not expect an err when skip-subnet-evm-upgrade-check is set to true 410 genesisJSON: genesisJSONSubnetEVMLateEnablement, 411 configJSON: "{\"skip-subnet-evm-upgrade-check\": true}", 412 expectedErr: nil, 413 }, 414 } 415 416 for _, test := range genesisTests { 417 ctx, dbManager, genesisBytes, issuer := setupGenesis(t, test.genesisJSON) 418 vm := &VM{} 419 err := vm.Initialize( 420 context.Background(), 421 ctx, 422 dbManager, 423 genesisBytes, 424 []byte(""), 425 []byte(test.configJSON), 426 issuer, 427 []*engCommon.Fx{}, 428 nil, 429 ) 430 431 require.ErrorIs(t, err, test.expectedErr) 432 } 433 } 434 435 func TestBuildEthTxBlock(t *testing.T) { 436 // reduce block gas cost 437 issuer, vm, dbManager, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "{\"pruning-enabled\":true}", "") 438 439 defer func() { 440 if err := vm.Shutdown(context.Background()); err != nil { 441 t.Fatal(err) 442 } 443 }() 444 445 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 446 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 447 448 key, err := accountKeystore.NewKey(rand.Reader) 449 if err != nil { 450 t.Fatal(err) 451 } 452 453 tx := types.NewTransaction(uint64(0), key.Address, firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 454 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 455 if err != nil { 456 t.Fatal(err) 457 } 458 errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 459 for i, err := range errs { 460 if err != nil { 461 t.Fatalf("Failed to add tx at index %d: %s", i, err) 462 } 463 } 464 465 blk1 := issueAndAccept(t, issuer, vm) 466 newHead := <-newTxPoolHeadChan 467 if newHead.Head.Hash() != common.Hash(blk1.ID()) { 468 t.Fatalf("Expected new block to match") 469 } 470 471 txs := make([]*types.Transaction, 10) 472 for i := 0; i < 10; i++ { 473 tx := types.NewTransaction(uint64(i), key.Address, big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 474 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), key.PrivateKey) 475 if err != nil { 476 t.Fatal(err) 477 } 478 txs[i] = signedTx 479 } 480 errs = vm.txPool.AddRemotesSync(txs) 481 for i, err := range errs { 482 if err != nil { 483 t.Fatalf("Failed to add tx at index %d: %s", i, err) 484 } 485 } 486 487 vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) 488 blk2 := issueAndAccept(t, issuer, vm) 489 newHead = <-newTxPoolHeadChan 490 if newHead.Head.Hash() != common.Hash(blk2.ID()) { 491 t.Fatalf("Expected new block to match") 492 } 493 494 if status := blk2.Status(); status != choices.Accepted { 495 t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) 496 } 497 498 lastAcceptedID, err := vm.LastAccepted(context.Background()) 499 if err != nil { 500 t.Fatal(err) 501 } 502 if lastAcceptedID != blk2.ID() { 503 t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk2.ID(), lastAcceptedID) 504 } 505 506 ethBlk1 := blk1.(*chain.BlockWrapper).Block.(*Block).ethBlock 507 if ethBlk1Root := ethBlk1.Root(); !vm.blockChain.HasState(ethBlk1Root) { 508 t.Fatalf("Expected blk1 state root to not yet be pruned after blk2 was accepted because of tip buffer") 509 } 510 511 // Clear the cache and ensure that GetBlock returns internal blocks with the correct status 512 vm.State.Flush() 513 blk2Refreshed, err := vm.GetBlockInternal(context.Background(), blk2.ID()) 514 if err != nil { 515 t.Fatal(err) 516 } 517 if status := blk2Refreshed.Status(); status != choices.Accepted { 518 t.Fatalf("Expected refreshed blk2 to be Accepted, but found status: %s", status) 519 } 520 521 blk1RefreshedID := blk2Refreshed.Parent() 522 blk1Refreshed, err := vm.GetBlockInternal(context.Background(), blk1RefreshedID) 523 if err != nil { 524 t.Fatal(err) 525 } 526 if status := blk1Refreshed.Status(); status != choices.Accepted { 527 t.Fatalf("Expected refreshed blk1 to be Accepted, but found status: %s", status) 528 } 529 530 if blk1Refreshed.ID() != blk1.ID() { 531 t.Fatalf("Found unexpected blkID for parent of blk2") 532 } 533 534 restartedVM := &VM{} 535 genesisBytes := buildGenesisTest(t, genesisJSONSubnetEVM) 536 537 if err := restartedVM.Initialize( 538 context.Background(), 539 NewContext(), 540 dbManager, 541 genesisBytes, 542 []byte(""), 543 []byte("{\"pruning-enabled\":true}"), 544 issuer, 545 []*engCommon.Fx{}, 546 nil, 547 ); err != nil { 548 t.Fatal(err) 549 } 550 551 // State root should not have been committed and discarded on restart 552 if ethBlk1Root := ethBlk1.Root(); restartedVM.blockChain.HasState(ethBlk1Root) { 553 t.Fatalf("Expected blk1 state root to be pruned after blk2 was accepted on top of it in pruning mode") 554 } 555 556 // State root should be committed when accepted tip on shutdown 557 ethBlk2 := blk2.(*chain.BlockWrapper).Block.(*Block).ethBlock 558 if ethBlk2Root := ethBlk2.Root(); !restartedVM.blockChain.HasState(ethBlk2Root) { 559 t.Fatalf("Expected blk2 state root to not be pruned after shutdown (last accepted tip should be committed)") 560 } 561 } 562 563 // Regression test to ensure that after accepting block A 564 // then calling SetPreference on block B (when it becomes preferred) 565 // and the head of a longer chain (block D) does not corrupt the 566 // canonical chain. 567 // A 568 // / \ 569 // B C 570 // | 571 // D 572 func TestSetPreferenceRace(t *testing.T) { 573 // Create two VMs which will agree on block A and then 574 // build the two distinct preferred chains above 575 issuer1, vm1, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "{\"pruning-enabled\":true}", "") 576 issuer2, vm2, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "{\"pruning-enabled\":true}", "") 577 578 defer func() { 579 if err := vm1.Shutdown(context.Background()); err != nil { 580 t.Fatal(err) 581 } 582 583 if err := vm2.Shutdown(context.Background()); err != nil { 584 t.Fatal(err) 585 } 586 }() 587 588 newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) 589 vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) 590 newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) 591 vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) 592 593 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 594 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[0]) 595 if err != nil { 596 t.Fatal(err) 597 } 598 599 txErrors := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 600 for i, err := range txErrors { 601 if err != nil { 602 t.Fatalf("Failed to add tx at index %d: %s", i, err) 603 } 604 } 605 606 <-issuer1 607 608 vm1BlkA, err := vm1.BuildBlock(context.Background()) 609 if err != nil { 610 t.Fatalf("Failed to build block with import transaction: %s", err) 611 } 612 613 if err := vm1BlkA.Verify(context.Background()); err != nil { 614 t.Fatalf("Block failed verification on VM1: %s", err) 615 } 616 617 if status := vm1BlkA.Status(); status != choices.Processing { 618 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 619 } 620 621 if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { 622 t.Fatal(err) 623 } 624 625 vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) 626 if err != nil { 627 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 628 } 629 if err := vm2BlkA.Verify(context.Background()); err != nil { 630 t.Fatalf("Block failed verification on VM2: %s", err) 631 } 632 if status := vm2BlkA.Status(); status != choices.Processing { 633 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 634 } 635 if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { 636 t.Fatal(err) 637 } 638 639 if err := vm1BlkA.Accept(context.Background()); err != nil { 640 t.Fatalf("VM1 failed to accept block: %s", err) 641 } 642 if err := vm2BlkA.Accept(context.Background()); err != nil { 643 t.Fatalf("VM2 failed to accept block: %s", err) 644 } 645 646 newHead := <-newTxPoolHeadChan1 647 if newHead.Head.Hash() != common.Hash(vm1BlkA.ID()) { 648 t.Fatalf("Expected new block to match") 649 } 650 newHead = <-newTxPoolHeadChan2 651 if newHead.Head.Hash() != common.Hash(vm2BlkA.ID()) { 652 t.Fatalf("Expected new block to match") 653 } 654 655 // Create list of 10 successive transactions to build block A on vm1 656 // and to be split into two separate blocks on VM2 657 txs := make([]*types.Transaction, 10) 658 for i := 0; i < 10; i++ { 659 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 660 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[1]) 661 if err != nil { 662 t.Fatal(err) 663 } 664 txs[i] = signedTx 665 } 666 667 var errs []error 668 669 // Add the remote transactions, build the block, and set VM1's preference for block A 670 errs = vm1.txPool.AddRemotesSync(txs) 671 for i, err := range errs { 672 if err != nil { 673 t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) 674 } 675 } 676 677 <-issuer1 678 679 vm1BlkB, err := vm1.BuildBlock(context.Background()) 680 if err != nil { 681 t.Fatal(err) 682 } 683 684 if err := vm1BlkB.Verify(context.Background()); err != nil { 685 t.Fatal(err) 686 } 687 688 if status := vm1BlkB.Status(); status != choices.Processing { 689 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 690 } 691 692 if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { 693 t.Fatal(err) 694 } 695 696 // Split the transactions over two blocks, and set VM2's preference to them in sequence 697 // after building each block 698 // Block C 699 errs = vm2.txPool.AddRemotesSync(txs[0:5]) 700 for i, err := range errs { 701 if err != nil { 702 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 703 } 704 } 705 706 <-issuer2 707 vm2BlkC, err := vm2.BuildBlock(context.Background()) 708 if err != nil { 709 t.Fatalf("Failed to build BlkC on VM2: %s", err) 710 } 711 712 if err := vm2BlkC.Verify(context.Background()); err != nil { 713 t.Fatalf("BlkC failed verification on VM2: %s", err) 714 } 715 716 if status := vm2BlkC.Status(); status != choices.Processing { 717 t.Fatalf("Expected status of built block C to be %s, but found %s", choices.Processing, status) 718 } 719 720 if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { 721 t.Fatal(err) 722 } 723 724 newHead = <-newTxPoolHeadChan2 725 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { 726 t.Fatalf("Expected new block to match") 727 } 728 729 // Block D 730 errs = vm2.txPool.AddRemotesSync(txs[5:10]) 731 for i, err := range errs { 732 if err != nil { 733 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 734 } 735 } 736 737 <-issuer2 738 vm2BlkD, err := vm2.BuildBlock(context.Background()) 739 if err != nil { 740 t.Fatalf("Failed to build BlkD on VM2: %s", err) 741 } 742 743 if err := vm2BlkD.Verify(context.Background()); err != nil { 744 t.Fatalf("BlkD failed verification on VM2: %s", err) 745 } 746 747 if status := vm2BlkD.Status(); status != choices.Processing { 748 t.Fatalf("Expected status of built block D to be %s, but found %s", choices.Processing, status) 749 } 750 751 if err := vm2.SetPreference(context.Background(), vm2BlkD.ID()); err != nil { 752 t.Fatal(err) 753 } 754 755 // VM1 receives blkC and blkD from VM1 756 // and happens to call SetPreference on blkD without ever calling SetPreference 757 // on blkC 758 // Here we parse them in reverse order to simulate receiving a chain from the tip 759 // back to the last accepted block as would typically be the case in the consensus 760 // engine 761 vm1BlkD, err := vm1.ParseBlock(context.Background(), vm2BlkD.Bytes()) 762 if err != nil { 763 t.Fatalf("VM1 errored parsing blkD: %s", err) 764 } 765 vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) 766 if err != nil { 767 t.Fatalf("VM1 errored parsing blkC: %s", err) 768 } 769 770 // The blocks must be verified in order. This invariant is maintained 771 // in the consensus engine. 772 if err := vm1BlkC.Verify(context.Background()); err != nil { 773 t.Fatalf("VM1 BlkC failed verification: %s", err) 774 } 775 if err := vm1BlkD.Verify(context.Background()); err != nil { 776 t.Fatalf("VM1 BlkD failed verification: %s", err) 777 } 778 779 // Set VM1's preference to blockD, skipping blockC 780 if err := vm1.SetPreference(context.Background(), vm1BlkD.ID()); err != nil { 781 t.Fatal(err) 782 } 783 784 // Accept the longer chain on both VMs and ensure there are no errors 785 // VM1 Accepts the blocks in order 786 if err := vm1BlkC.Accept(context.Background()); err != nil { 787 t.Fatalf("VM1 BlkC failed on accept: %s", err) 788 } 789 if err := vm1BlkD.Accept(context.Background()); err != nil { 790 t.Fatalf("VM1 BlkC failed on accept: %s", err) 791 } 792 793 // VM2 Accepts the blocks in order 794 if err := vm2BlkC.Accept(context.Background()); err != nil { 795 t.Fatalf("VM2 BlkC failed on accept: %s", err) 796 } 797 if err := vm2BlkD.Accept(context.Background()); err != nil { 798 t.Fatalf("VM2 BlkC failed on accept: %s", err) 799 } 800 801 log.Info("Validating canonical chain") 802 // Verify the Canonical Chain for Both VMs 803 if err := vm2.blockChain.ValidateCanonicalChain(); err != nil { 804 t.Fatalf("VM2 failed canonical chain verification due to: %s", err) 805 } 806 807 if err := vm1.blockChain.ValidateCanonicalChain(); err != nil { 808 t.Fatalf("VM1 failed canonical chain verification due to: %s", err) 809 } 810 } 811 812 // Regression test to ensure that a VM that accepts block A and B 813 // will not attempt to orphan either when verifying blocks C and D 814 // from another VM (which have a common ancestor under the finalized 815 // frontier). 816 // A 817 // / \ 818 // B C 819 // 820 // verifies block B and C, then Accepts block B. Then we test to ensure 821 // that the VM defends against any attempt to set the preference or to 822 // accept block C, which should be an orphaned block at this point and 823 // get rejected. 824 func TestReorgProtection(t *testing.T) { 825 issuer1, vm1, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "{\"pruning-enabled\":false}", "") 826 issuer2, vm2, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "{\"pruning-enabled\":false}", "") 827 828 defer func() { 829 if err := vm1.Shutdown(context.Background()); err != nil { 830 t.Fatal(err) 831 } 832 833 if err := vm2.Shutdown(context.Background()); err != nil { 834 t.Fatal(err) 835 } 836 }() 837 838 newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) 839 vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) 840 newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) 841 vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) 842 843 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 844 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[0]) 845 if err != nil { 846 t.Fatal(err) 847 } 848 849 txErrors := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 850 for i, err := range txErrors { 851 if err != nil { 852 t.Fatalf("Failed to add tx at index %d: %s", i, err) 853 } 854 } 855 856 <-issuer1 857 858 vm1BlkA, err := vm1.BuildBlock(context.Background()) 859 if err != nil { 860 t.Fatalf("Failed to build block with import transaction: %s", err) 861 } 862 863 if err := vm1BlkA.Verify(context.Background()); err != nil { 864 t.Fatalf("Block failed verification on VM1: %s", err) 865 } 866 867 if status := vm1BlkA.Status(); status != choices.Processing { 868 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 869 } 870 871 if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { 872 t.Fatal(err) 873 } 874 875 vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) 876 if err != nil { 877 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 878 } 879 if err := vm2BlkA.Verify(context.Background()); err != nil { 880 t.Fatalf("Block failed verification on VM2: %s", err) 881 } 882 if status := vm2BlkA.Status(); status != choices.Processing { 883 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 884 } 885 if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { 886 t.Fatal(err) 887 } 888 889 if err := vm1BlkA.Accept(context.Background()); err != nil { 890 t.Fatalf("VM1 failed to accept block: %s", err) 891 } 892 if err := vm2BlkA.Accept(context.Background()); err != nil { 893 t.Fatalf("VM2 failed to accept block: %s", err) 894 } 895 896 newHead := <-newTxPoolHeadChan1 897 if newHead.Head.Hash() != common.Hash(vm1BlkA.ID()) { 898 t.Fatalf("Expected new block to match") 899 } 900 newHead = <-newTxPoolHeadChan2 901 if newHead.Head.Hash() != common.Hash(vm2BlkA.ID()) { 902 t.Fatalf("Expected new block to match") 903 } 904 905 // Create list of 10 successive transactions to build block A on vm1 906 // and to be split into two separate blocks on VM2 907 txs := make([]*types.Transaction, 10) 908 for i := 0; i < 10; i++ { 909 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 910 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[1]) 911 if err != nil { 912 t.Fatal(err) 913 } 914 txs[i] = signedTx 915 } 916 917 var errs []error 918 919 // Add the remote transactions, build the block, and set VM1's preference for block A 920 errs = vm1.txPool.AddRemotesSync(txs) 921 for i, err := range errs { 922 if err != nil { 923 t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) 924 } 925 } 926 927 <-issuer1 928 929 vm1BlkB, err := vm1.BuildBlock(context.Background()) 930 if err != nil { 931 t.Fatal(err) 932 } 933 934 if err := vm1BlkB.Verify(context.Background()); err != nil { 935 t.Fatal(err) 936 } 937 938 if status := vm1BlkB.Status(); status != choices.Processing { 939 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 940 } 941 942 if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { 943 t.Fatal(err) 944 } 945 946 // Split the transactions over two blocks, and set VM2's preference to them in sequence 947 // after building each block 948 // Block C 949 errs = vm2.txPool.AddRemotesSync(txs[0:5]) 950 for i, err := range errs { 951 if err != nil { 952 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 953 } 954 } 955 956 <-issuer2 957 vm2BlkC, err := vm2.BuildBlock(context.Background()) 958 if err != nil { 959 t.Fatalf("Failed to build BlkC on VM2: %s", err) 960 } 961 962 if err := vm2BlkC.Verify(context.Background()); err != nil { 963 t.Fatalf("Block failed verification on VM2: %s", err) 964 } 965 if status := vm2BlkC.Status(); status != choices.Processing { 966 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 967 } 968 969 vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) 970 if err != nil { 971 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 972 } 973 974 if err := vm1BlkC.Verify(context.Background()); err != nil { 975 t.Fatalf("Block failed verification on VM1: %s", err) 976 } 977 978 // Accept B, such that block C should get Rejected. 979 if err := vm1BlkB.Accept(context.Background()); err != nil { 980 t.Fatalf("VM1 failed to accept block: %s", err) 981 } 982 983 // The below (setting preference blocks that have a common ancestor 984 // with the preferred chain lower than the last finalized block) 985 // should NEVER happen. However, the VM defends against this 986 // just in case. 987 if err := vm1.SetPreference(context.Background(), vm1BlkC.ID()); !strings.Contains(err.Error(), "cannot orphan finalized block") { 988 t.Fatalf("Unexpected error when setting preference that would trigger reorg: %s", err) 989 } 990 991 if err := vm1BlkC.Accept(context.Background()); !strings.Contains(err.Error(), "expected accepted block to have parent") { 992 t.Fatalf("Unexpected error when setting block at finalized height: %s", err) 993 } 994 } 995 996 // Regression test to ensure that a VM that accepts block C while preferring 997 // block B will trigger a reorg. 998 // A 999 // / \ 1000 // B C 1001 func TestNonCanonicalAccept(t *testing.T) { 1002 issuer1, vm1, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1003 issuer2, vm2, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1004 1005 defer func() { 1006 if err := vm1.Shutdown(context.Background()); err != nil { 1007 t.Fatal(err) 1008 } 1009 1010 if err := vm2.Shutdown(context.Background()); err != nil { 1011 t.Fatal(err) 1012 } 1013 }() 1014 1015 newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) 1016 vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) 1017 newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) 1018 vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) 1019 1020 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1021 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[0]) 1022 if err != nil { 1023 t.Fatal(err) 1024 } 1025 1026 txErrors := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1027 for i, err := range txErrors { 1028 if err != nil { 1029 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1030 } 1031 } 1032 1033 <-issuer1 1034 1035 vm1BlkA, err := vm1.BuildBlock(context.Background()) 1036 if err != nil { 1037 t.Fatalf("Failed to build block with import transaction: %s", err) 1038 } 1039 1040 if err := vm1BlkA.Verify(context.Background()); err != nil { 1041 t.Fatalf("Block failed verification on VM1: %s", err) 1042 } 1043 1044 if status := vm1BlkA.Status(); status != choices.Processing { 1045 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1046 } 1047 1048 if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { 1049 t.Fatal(err) 1050 } 1051 1052 vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) 1053 if err != nil { 1054 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1055 } 1056 if err := vm2BlkA.Verify(context.Background()); err != nil { 1057 t.Fatalf("Block failed verification on VM2: %s", err) 1058 } 1059 if status := vm2BlkA.Status(); status != choices.Processing { 1060 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 1061 } 1062 if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { 1063 t.Fatal(err) 1064 } 1065 1066 if err := vm1BlkA.Accept(context.Background()); err != nil { 1067 t.Fatalf("VM1 failed to accept block: %s", err) 1068 } 1069 if err := vm2BlkA.Accept(context.Background()); err != nil { 1070 t.Fatalf("VM2 failed to accept block: %s", err) 1071 } 1072 1073 newHead := <-newTxPoolHeadChan1 1074 if newHead.Head.Hash() != common.Hash(vm1BlkA.ID()) { 1075 t.Fatalf("Expected new block to match") 1076 } 1077 newHead = <-newTxPoolHeadChan2 1078 if newHead.Head.Hash() != common.Hash(vm2BlkA.ID()) { 1079 t.Fatalf("Expected new block to match") 1080 } 1081 1082 // Create list of 10 successive transactions to build block A on vm1 1083 // and to be split into two separate blocks on VM2 1084 txs := make([]*types.Transaction, 10) 1085 for i := 0; i < 10; i++ { 1086 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 1087 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[1]) 1088 if err != nil { 1089 t.Fatal(err) 1090 } 1091 txs[i] = signedTx 1092 } 1093 1094 var errs []error 1095 1096 // Add the remote transactions, build the block, and set VM1's preference for block A 1097 errs = vm1.txPool.AddRemotesSync(txs) 1098 for i, err := range errs { 1099 if err != nil { 1100 t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) 1101 } 1102 } 1103 1104 <-issuer1 1105 1106 vm1BlkB, err := vm1.BuildBlock(context.Background()) 1107 if err != nil { 1108 t.Fatal(err) 1109 } 1110 1111 if err := vm1BlkB.Verify(context.Background()); err != nil { 1112 t.Fatal(err) 1113 } 1114 1115 if status := vm1BlkB.Status(); status != choices.Processing { 1116 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1117 } 1118 1119 if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { 1120 t.Fatal(err) 1121 } 1122 1123 vm1.blockChain.GetVMConfig().AllowUnfinalizedQueries = true 1124 1125 blkBHeight := vm1BlkB.Height() 1126 blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1127 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { 1128 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) 1129 } 1130 1131 errs = vm2.txPool.AddRemotesSync(txs[0:5]) 1132 for i, err := range errs { 1133 if err != nil { 1134 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1135 } 1136 } 1137 1138 <-issuer2 1139 vm2BlkC, err := vm2.BuildBlock(context.Background()) 1140 if err != nil { 1141 t.Fatalf("Failed to build BlkC on VM2: %s", err) 1142 } 1143 1144 vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) 1145 if err != nil { 1146 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1147 } 1148 1149 if err := vm1BlkC.Verify(context.Background()); err != nil { 1150 t.Fatalf("Block failed verification on VM1: %s", err) 1151 } 1152 1153 if err := vm1BlkC.Accept(context.Background()); err != nil { 1154 t.Fatalf("VM1 failed to accept block: %s", err) 1155 } 1156 1157 blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1158 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { 1159 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkCHash.Hex(), b.Hash().Hex()) 1160 } 1161 } 1162 1163 // Regression test to ensure that a VM that verifies block B, C, then 1164 // D (preferring block B) does not trigger a reorg through the re-verification 1165 // of block C or D. 1166 // A 1167 // / \ 1168 // B C 1169 // | 1170 // D 1171 func TestStickyPreference(t *testing.T) { 1172 issuer1, vm1, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1173 issuer2, vm2, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1174 1175 defer func() { 1176 if err := vm1.Shutdown(context.Background()); err != nil { 1177 t.Fatal(err) 1178 } 1179 1180 if err := vm2.Shutdown(context.Background()); err != nil { 1181 t.Fatal(err) 1182 } 1183 }() 1184 1185 newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) 1186 vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) 1187 newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) 1188 vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) 1189 1190 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1191 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[0]) 1192 if err != nil { 1193 t.Fatal(err) 1194 } 1195 1196 txErrors := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1197 for i, err := range txErrors { 1198 if err != nil { 1199 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1200 } 1201 } 1202 1203 <-issuer1 1204 1205 vm1BlkA, err := vm1.BuildBlock(context.Background()) 1206 if err != nil { 1207 t.Fatalf("Failed to build block with import transaction: %s", err) 1208 } 1209 1210 if err := vm1BlkA.Verify(context.Background()); err != nil { 1211 t.Fatalf("Block failed verification on VM1: %s", err) 1212 } 1213 1214 if status := vm1BlkA.Status(); status != choices.Processing { 1215 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1216 } 1217 1218 if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { 1219 t.Fatal(err) 1220 } 1221 1222 vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) 1223 if err != nil { 1224 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1225 } 1226 if err := vm2BlkA.Verify(context.Background()); err != nil { 1227 t.Fatalf("Block failed verification on VM2: %s", err) 1228 } 1229 if status := vm2BlkA.Status(); status != choices.Processing { 1230 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 1231 } 1232 if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { 1233 t.Fatal(err) 1234 } 1235 1236 if err := vm1BlkA.Accept(context.Background()); err != nil { 1237 t.Fatalf("VM1 failed to accept block: %s", err) 1238 } 1239 if err := vm2BlkA.Accept(context.Background()); err != nil { 1240 t.Fatalf("VM2 failed to accept block: %s", err) 1241 } 1242 1243 newHead := <-newTxPoolHeadChan1 1244 if newHead.Head.Hash() != common.Hash(vm1BlkA.ID()) { 1245 t.Fatalf("Expected new block to match") 1246 } 1247 newHead = <-newTxPoolHeadChan2 1248 if newHead.Head.Hash() != common.Hash(vm2BlkA.ID()) { 1249 t.Fatalf("Expected new block to match") 1250 } 1251 1252 // Create list of 10 successive transactions to build block A on vm1 1253 // and to be split into two separate blocks on VM2 1254 txs := make([]*types.Transaction, 10) 1255 for i := 0; i < 10; i++ { 1256 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 1257 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[1]) 1258 if err != nil { 1259 t.Fatal(err) 1260 } 1261 txs[i] = signedTx 1262 } 1263 1264 var errs []error 1265 1266 // Add the remote transactions, build the block, and set VM1's preference for block A 1267 errs = vm1.txPool.AddRemotesSync(txs) 1268 for i, err := range errs { 1269 if err != nil { 1270 t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) 1271 } 1272 } 1273 1274 <-issuer1 1275 1276 vm1BlkB, err := vm1.BuildBlock(context.Background()) 1277 if err != nil { 1278 t.Fatal(err) 1279 } 1280 1281 if err := vm1BlkB.Verify(context.Background()); err != nil { 1282 t.Fatal(err) 1283 } 1284 1285 if status := vm1BlkB.Status(); status != choices.Processing { 1286 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1287 } 1288 1289 if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { 1290 t.Fatal(err) 1291 } 1292 1293 vm1.blockChain.GetVMConfig().AllowUnfinalizedQueries = true 1294 1295 blkBHeight := vm1BlkB.Height() 1296 blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1297 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { 1298 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) 1299 } 1300 1301 errs = vm2.txPool.AddRemotesSync(txs[0:5]) 1302 for i, err := range errs { 1303 if err != nil { 1304 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1305 } 1306 } 1307 1308 <-issuer2 1309 vm2BlkC, err := vm2.BuildBlock(context.Background()) 1310 if err != nil { 1311 t.Fatalf("Failed to build BlkC on VM2: %s", err) 1312 } 1313 1314 if err := vm2BlkC.Verify(context.Background()); err != nil { 1315 t.Fatalf("BlkC failed verification on VM2: %s", err) 1316 } 1317 1318 if status := vm2BlkC.Status(); status != choices.Processing { 1319 t.Fatalf("Expected status of built block C to be %s, but found %s", choices.Processing, status) 1320 } 1321 1322 if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { 1323 t.Fatal(err) 1324 } 1325 1326 newHead = <-newTxPoolHeadChan2 1327 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { 1328 t.Fatalf("Expected new block to match") 1329 } 1330 1331 errs = vm2.txPool.AddRemotesSync(txs[5:]) 1332 for i, err := range errs { 1333 if err != nil { 1334 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1335 } 1336 } 1337 1338 <-issuer2 1339 vm2BlkD, err := vm2.BuildBlock(context.Background()) 1340 if err != nil { 1341 t.Fatalf("Failed to build BlkD on VM2: %s", err) 1342 } 1343 1344 // Parse blocks produced in vm2 1345 vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) 1346 if err != nil { 1347 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1348 } 1349 blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1350 1351 vm1BlkD, err := vm1.ParseBlock(context.Background(), vm2BlkD.Bytes()) 1352 if err != nil { 1353 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1354 } 1355 blkDHeight := vm1BlkD.Height() 1356 blkDHash := vm1BlkD.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1357 1358 // Should be no-ops 1359 if err := vm1BlkC.Verify(context.Background()); err != nil { 1360 t.Fatalf("Block failed verification on VM1: %s", err) 1361 } 1362 if err := vm1BlkD.Verify(context.Background()); err != nil { 1363 t.Fatalf("Block failed verification on VM1: %s", err) 1364 } 1365 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { 1366 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) 1367 } 1368 if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b != nil { 1369 t.Fatalf("expected block at %d to be nil but got %s", blkDHeight, b.Hash().Hex()) 1370 } 1371 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkBHash { 1372 t.Fatalf("expected current block to have hash %s but got %s", blkBHash.Hex(), b.Hash().Hex()) 1373 } 1374 1375 // Should still be no-ops on re-verify 1376 if err := vm1BlkC.Verify(context.Background()); err != nil { 1377 t.Fatalf("Block failed verification on VM1: %s", err) 1378 } 1379 if err := vm1BlkD.Verify(context.Background()); err != nil { 1380 t.Fatalf("Block failed verification on VM1: %s", err) 1381 } 1382 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { 1383 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) 1384 } 1385 if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b != nil { 1386 t.Fatalf("expected block at %d to be nil but got %s", blkDHeight, b.Hash().Hex()) 1387 } 1388 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkBHash { 1389 t.Fatalf("expected current block to have hash %s but got %s", blkBHash.Hex(), b.Hash().Hex()) 1390 } 1391 1392 // Should be queryable after setting preference to side chain 1393 if err := vm1.SetPreference(context.Background(), vm1BlkD.ID()); err != nil { 1394 t.Fatal(err) 1395 } 1396 1397 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { 1398 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkCHash.Hex(), b.Hash().Hex()) 1399 } 1400 if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b.Hash() != blkDHash { 1401 t.Fatalf("expected block at %d to have hash %s but got %s", blkDHeight, blkDHash.Hex(), b.Hash().Hex()) 1402 } 1403 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkDHash { 1404 t.Fatalf("expected current block to have hash %s but got %s", blkDHash.Hex(), b.Hash().Hex()) 1405 } 1406 1407 // Attempt to accept out of order 1408 if err := vm1BlkD.Accept(context.Background()); !strings.Contains(err.Error(), "expected accepted block to have parent") { 1409 t.Fatalf("unexpected error when accepting out of order block: %s", err) 1410 } 1411 1412 // Accept in order 1413 if err := vm1BlkC.Accept(context.Background()); err != nil { 1414 t.Fatalf("Block failed verification on VM1: %s", err) 1415 } 1416 if err := vm1BlkD.Accept(context.Background()); err != nil { 1417 t.Fatalf("Block failed acceptance on VM1: %s", err) 1418 } 1419 1420 // Ensure queryable after accepting 1421 if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { 1422 t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkCHash.Hex(), b.Hash().Hex()) 1423 } 1424 if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b.Hash() != blkDHash { 1425 t.Fatalf("expected block at %d to have hash %s but got %s", blkDHeight, blkDHash.Hex(), b.Hash().Hex()) 1426 } 1427 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkDHash { 1428 t.Fatalf("expected current block to have hash %s but got %s", blkDHash.Hex(), b.Hash().Hex()) 1429 } 1430 } 1431 1432 // Regression test to ensure that a VM that prefers block B is able to parse 1433 // block C but unable to parse block D because it names B as an uncle, which 1434 // are not supported. 1435 // A 1436 // / \ 1437 // B C 1438 // | 1439 // D 1440 func TestUncleBlock(t *testing.T) { 1441 issuer1, vm1, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1442 issuer2, vm2, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1443 1444 defer func() { 1445 if err := vm1.Shutdown(context.Background()); err != nil { 1446 t.Fatal(err) 1447 } 1448 if err := vm2.Shutdown(context.Background()); err != nil { 1449 t.Fatal(err) 1450 } 1451 }() 1452 1453 newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) 1454 vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) 1455 newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) 1456 vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) 1457 1458 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1459 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[0]) 1460 if err != nil { 1461 t.Fatal(err) 1462 } 1463 1464 txErrors := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1465 for i, err := range txErrors { 1466 if err != nil { 1467 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1468 } 1469 } 1470 1471 <-issuer1 1472 1473 vm1BlkA, err := vm1.BuildBlock(context.Background()) 1474 if err != nil { 1475 t.Fatalf("Failed to build block with import transaction: %s", err) 1476 } 1477 1478 if err := vm1BlkA.Verify(context.Background()); err != nil { 1479 t.Fatalf("Block failed verification on VM1: %s", err) 1480 } 1481 1482 if status := vm1BlkA.Status(); status != choices.Processing { 1483 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1484 } 1485 1486 if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { 1487 t.Fatal(err) 1488 } 1489 1490 vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) 1491 if err != nil { 1492 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1493 } 1494 if err := vm2BlkA.Verify(context.Background()); err != nil { 1495 t.Fatalf("Block failed verification on VM2: %s", err) 1496 } 1497 if status := vm2BlkA.Status(); status != choices.Processing { 1498 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 1499 } 1500 if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { 1501 t.Fatal(err) 1502 } 1503 1504 if err := vm1BlkA.Accept(context.Background()); err != nil { 1505 t.Fatalf("VM1 failed to accept block: %s", err) 1506 } 1507 if err := vm2BlkA.Accept(context.Background()); err != nil { 1508 t.Fatalf("VM2 failed to accept block: %s", err) 1509 } 1510 1511 newHead := <-newTxPoolHeadChan1 1512 if newHead.Head.Hash() != common.Hash(vm1BlkA.ID()) { 1513 t.Fatalf("Expected new block to match") 1514 } 1515 newHead = <-newTxPoolHeadChan2 1516 if newHead.Head.Hash() != common.Hash(vm2BlkA.ID()) { 1517 t.Fatalf("Expected new block to match") 1518 } 1519 1520 txs := make([]*types.Transaction, 10) 1521 for i := 0; i < 10; i++ { 1522 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 1523 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[1]) 1524 if err != nil { 1525 t.Fatal(err) 1526 } 1527 txs[i] = signedTx 1528 } 1529 1530 var errs []error 1531 1532 errs = vm1.txPool.AddRemotesSync(txs) 1533 for i, err := range errs { 1534 if err != nil { 1535 t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) 1536 } 1537 } 1538 1539 <-issuer1 1540 1541 vm1BlkB, err := vm1.BuildBlock(context.Background()) 1542 if err != nil { 1543 t.Fatal(err) 1544 } 1545 1546 if err := vm1BlkB.Verify(context.Background()); err != nil { 1547 t.Fatal(err) 1548 } 1549 1550 if status := vm1BlkB.Status(); status != choices.Processing { 1551 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1552 } 1553 1554 if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { 1555 t.Fatal(err) 1556 } 1557 1558 errs = vm2.txPool.AddRemotesSync(txs[0:5]) 1559 for i, err := range errs { 1560 if err != nil { 1561 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1562 } 1563 } 1564 1565 <-issuer2 1566 vm2BlkC, err := vm2.BuildBlock(context.Background()) 1567 if err != nil { 1568 t.Fatalf("Failed to build BlkC on VM2: %s", err) 1569 } 1570 1571 if err := vm2BlkC.Verify(context.Background()); err != nil { 1572 t.Fatalf("BlkC failed verification on VM2: %s", err) 1573 } 1574 1575 if status := vm2BlkC.Status(); status != choices.Processing { 1576 t.Fatalf("Expected status of built block C to be %s, but found %s", choices.Processing, status) 1577 } 1578 1579 if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { 1580 t.Fatal(err) 1581 } 1582 1583 newHead = <-newTxPoolHeadChan2 1584 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { 1585 t.Fatalf("Expected new block to match") 1586 } 1587 1588 errs = vm2.txPool.AddRemotesSync(txs[5:10]) 1589 for i, err := range errs { 1590 if err != nil { 1591 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1592 } 1593 } 1594 1595 <-issuer2 1596 vm2BlkD, err := vm2.BuildBlock(context.Background()) 1597 if err != nil { 1598 t.Fatalf("Failed to build BlkD on VM2: %s", err) 1599 } 1600 1601 // Create uncle block from blkD 1602 blkDEthBlock := vm2BlkD.(*chain.BlockWrapper).Block.(*Block).ethBlock 1603 uncles := []*types.Header{vm1BlkB.(*chain.BlockWrapper).Block.(*Block).ethBlock.Header()} 1604 uncleBlockHeader := types.CopyHeader(blkDEthBlock.Header()) 1605 uncleBlockHeader.UncleHash = types.CalcUncleHash(uncles) 1606 1607 uncleEthBlock := types.NewBlock( 1608 uncleBlockHeader, 1609 blkDEthBlock.Transactions(), 1610 uncles, 1611 nil, 1612 new(trie.Trie), 1613 ) 1614 uncleBlock := vm2.newBlock(uncleEthBlock) 1615 1616 if err := uncleBlock.Verify(context.Background()); !errors.Is(err, errUnclesUnsupported) { 1617 t.Fatalf("VM2 should have failed with %q but got %q", errUnclesUnsupported, err.Error()) 1618 } 1619 if _, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()); err != nil { 1620 t.Fatalf("VM1 errored parsing blkC: %s", err) 1621 } 1622 if _, err := vm1.ParseBlock(context.Background(), uncleBlock.Bytes()); !errors.Is(err, errUnclesUnsupported) { 1623 t.Fatalf("VM1 should have failed with %q but got %q", errUnclesUnsupported, err.Error()) 1624 } 1625 } 1626 1627 // Regression test to ensure that a VM that is not able to parse a block that 1628 // contains no transactions. 1629 func TestEmptyBlock(t *testing.T) { 1630 issuer, vm, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1631 1632 defer func() { 1633 if err := vm.Shutdown(context.Background()); err != nil { 1634 t.Fatal(err) 1635 } 1636 }() 1637 1638 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1639 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 1640 if err != nil { 1641 t.Fatal(err) 1642 } 1643 1644 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1645 for i, err := range txErrors { 1646 if err != nil { 1647 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1648 } 1649 } 1650 1651 <-issuer 1652 1653 blk, err := vm.BuildBlock(context.Background()) 1654 if err != nil { 1655 t.Fatalf("Failed to build block with import transaction: %s", err) 1656 } 1657 1658 // Create empty block from blkA 1659 ethBlock := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 1660 1661 emptyEthBlock := types.NewBlock( 1662 types.CopyHeader(ethBlock.Header()), 1663 nil, 1664 nil, 1665 nil, 1666 new(trie.Trie), 1667 ) 1668 1669 emptyBlock := vm.newBlock(emptyEthBlock) 1670 1671 if _, err := vm.ParseBlock(context.Background(), emptyBlock.Bytes()); !errors.Is(err, errEmptyBlock) { 1672 t.Fatalf("VM should have failed with errEmptyBlock but got %s", err.Error()) 1673 } 1674 if err := emptyBlock.Verify(context.Background()); !errors.Is(err, errEmptyBlock) { 1675 t.Fatalf("block should have failed verification with errEmptyBlock but got %s", err.Error()) 1676 } 1677 } 1678 1679 // Regression test to ensure that a VM that verifies block B, C, then 1680 // D (preferring block B) reorgs when C and then D are accepted. 1681 // A 1682 // / \ 1683 // B C 1684 // | 1685 // D 1686 func TestAcceptReorg(t *testing.T) { 1687 issuer1, vm1, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1688 issuer2, vm2, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1689 1690 defer func() { 1691 if err := vm1.Shutdown(context.Background()); err != nil { 1692 t.Fatal(err) 1693 } 1694 1695 if err := vm2.Shutdown(context.Background()); err != nil { 1696 t.Fatal(err) 1697 } 1698 }() 1699 1700 newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) 1701 vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) 1702 newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) 1703 vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) 1704 1705 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1706 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[0]) 1707 if err != nil { 1708 t.Fatal(err) 1709 } 1710 1711 txErrors := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1712 for i, err := range txErrors { 1713 if err != nil { 1714 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1715 } 1716 } 1717 1718 <-issuer1 1719 1720 vm1BlkA, err := vm1.BuildBlock(context.Background()) 1721 if err != nil { 1722 t.Fatalf("Failed to build block with import transaction: %s", err) 1723 } 1724 1725 if err := vm1BlkA.Verify(context.Background()); err != nil { 1726 t.Fatalf("Block failed verification on VM1: %s", err) 1727 } 1728 1729 if status := vm1BlkA.Status(); status != choices.Processing { 1730 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1731 } 1732 1733 if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { 1734 t.Fatal(err) 1735 } 1736 1737 vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) 1738 if err != nil { 1739 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1740 } 1741 if err := vm2BlkA.Verify(context.Background()); err != nil { 1742 t.Fatalf("Block failed verification on VM2: %s", err) 1743 } 1744 if status := vm2BlkA.Status(); status != choices.Processing { 1745 t.Fatalf("Expected status of block on VM2 to be %s, but found %s", choices.Processing, status) 1746 } 1747 if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { 1748 t.Fatal(err) 1749 } 1750 1751 if err := vm1BlkA.Accept(context.Background()); err != nil { 1752 t.Fatalf("VM1 failed to accept block: %s", err) 1753 } 1754 if err := vm2BlkA.Accept(context.Background()); err != nil { 1755 t.Fatalf("VM2 failed to accept block: %s", err) 1756 } 1757 1758 newHead := <-newTxPoolHeadChan1 1759 if newHead.Head.Hash() != common.Hash(vm1BlkA.ID()) { 1760 t.Fatalf("Expected new block to match") 1761 } 1762 newHead = <-newTxPoolHeadChan2 1763 if newHead.Head.Hash() != common.Hash(vm2BlkA.ID()) { 1764 t.Fatalf("Expected new block to match") 1765 } 1766 1767 // Create list of 10 successive transactions to build block A on vm1 1768 // and to be split into two separate blocks on VM2 1769 txs := make([]*types.Transaction, 10) 1770 for i := 0; i < 10; i++ { 1771 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) 1772 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), testKeys[1]) 1773 if err != nil { 1774 t.Fatal(err) 1775 } 1776 txs[i] = signedTx 1777 } 1778 1779 // Add the remote transactions, build the block, and set VM1's preference 1780 // for block B 1781 errs := vm1.txPool.AddRemotesSync(txs) 1782 for i, err := range errs { 1783 if err != nil { 1784 t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) 1785 } 1786 } 1787 1788 <-issuer1 1789 1790 vm1BlkB, err := vm1.BuildBlock(context.Background()) 1791 if err != nil { 1792 t.Fatal(err) 1793 } 1794 1795 if err := vm1BlkB.Verify(context.Background()); err != nil { 1796 t.Fatal(err) 1797 } 1798 1799 if status := vm1BlkB.Status(); status != choices.Processing { 1800 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1801 } 1802 1803 if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { 1804 t.Fatal(err) 1805 } 1806 1807 errs = vm2.txPool.AddRemotesSync(txs[0:5]) 1808 for i, err := range errs { 1809 if err != nil { 1810 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1811 } 1812 } 1813 1814 <-issuer2 1815 1816 vm2BlkC, err := vm2.BuildBlock(context.Background()) 1817 if err != nil { 1818 t.Fatalf("Failed to build BlkC on VM2: %s", err) 1819 } 1820 1821 if err := vm2BlkC.Verify(context.Background()); err != nil { 1822 t.Fatalf("BlkC failed verification on VM2: %s", err) 1823 } 1824 1825 if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { 1826 t.Fatal(err) 1827 } 1828 1829 newHead = <-newTxPoolHeadChan2 1830 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { 1831 t.Fatalf("Expected new block to match") 1832 } 1833 1834 errs = vm2.txPool.AddRemotesSync(txs[5:]) 1835 for i, err := range errs { 1836 if err != nil { 1837 t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) 1838 } 1839 } 1840 1841 <-issuer2 1842 1843 vm2BlkD, err := vm2.BuildBlock(context.Background()) 1844 if err != nil { 1845 t.Fatalf("Failed to build BlkD on VM2: %s", err) 1846 } 1847 1848 // Parse blocks produced in vm2 1849 vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) 1850 if err != nil { 1851 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1852 } 1853 1854 vm1BlkD, err := vm1.ParseBlock(context.Background(), vm2BlkD.Bytes()) 1855 if err != nil { 1856 t.Fatalf("Unexpected error parsing block from vm2: %s", err) 1857 } 1858 1859 if err := vm1BlkC.Verify(context.Background()); err != nil { 1860 t.Fatalf("Block failed verification on VM1: %s", err) 1861 } 1862 if err := vm1BlkD.Verify(context.Background()); err != nil { 1863 t.Fatalf("Block failed verification on VM1: %s", err) 1864 } 1865 1866 blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1867 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkBHash { 1868 t.Fatalf("expected current block to have hash %s but got %s", blkBHash.Hex(), b.Hash().Hex()) 1869 } 1870 1871 if err := vm1BlkC.Accept(context.Background()); err != nil { 1872 t.Fatal(err) 1873 } 1874 1875 blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1876 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkCHash { 1877 t.Fatalf("expected current block to have hash %s but got %s", blkCHash.Hex(), b.Hash().Hex()) 1878 } 1879 if err := vm1BlkB.Reject(context.Background()); err != nil { 1880 t.Fatal(err) 1881 } 1882 1883 if err := vm1BlkD.Accept(context.Background()); err != nil { 1884 t.Fatal(err) 1885 } 1886 blkDHash := vm1BlkD.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1887 if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkDHash { 1888 t.Fatalf("expected current block to have hash %s but got %s", blkDHash.Hex(), b.Hash().Hex()) 1889 } 1890 } 1891 1892 func TestFutureBlock(t *testing.T) { 1893 issuer, vm, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1894 1895 defer func() { 1896 if err := vm.Shutdown(context.Background()); err != nil { 1897 t.Fatal(err) 1898 } 1899 }() 1900 1901 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1902 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 1903 if err != nil { 1904 t.Fatal(err) 1905 } 1906 1907 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1908 for i, err := range txErrors { 1909 if err != nil { 1910 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1911 } 1912 } 1913 1914 <-issuer 1915 1916 blkA, err := vm.BuildBlock(context.Background()) 1917 if err != nil { 1918 t.Fatalf("Failed to build block with import transaction: %s", err) 1919 } 1920 1921 // Create empty block from blkA 1922 internalBlkA := blkA.(*chain.BlockWrapper).Block.(*Block) 1923 modifiedHeader := types.CopyHeader(internalBlkA.ethBlock.Header()) 1924 // Set the VM's clock to the time of the produced block 1925 vm.clock.Set(time.Unix(int64(modifiedHeader.Time), 0)) 1926 // Set the modified time to exceed the allowed future time 1927 modifiedTime := modifiedHeader.Time + uint64(maxFutureBlockTime.Seconds()+1) 1928 modifiedHeader.Time = modifiedTime 1929 modifiedBlock := types.NewBlock( 1930 modifiedHeader, 1931 internalBlkA.ethBlock.Transactions(), 1932 nil, 1933 nil, 1934 new(trie.Trie), 1935 ) 1936 1937 futureBlock := vm.newBlock(modifiedBlock) 1938 1939 if err := futureBlock.Verify(context.Background()); err == nil { 1940 t.Fatal("Future block should have failed verification due to block timestamp too far in the future") 1941 } else if !strings.Contains(err.Error(), "block timestamp is too far in the future") { 1942 t.Fatalf("Expected error to be block timestamp too far in the future but found %s", err) 1943 } 1944 } 1945 1946 func TestLastAcceptedBlockNumberAllow(t *testing.T) { 1947 issuer, vm, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 1948 1949 defer func() { 1950 if err := vm.Shutdown(context.Background()); err != nil { 1951 t.Fatal(err) 1952 } 1953 }() 1954 1955 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 1956 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 1957 if err != nil { 1958 t.Fatal(err) 1959 } 1960 1961 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 1962 for i, err := range txErrors { 1963 if err != nil { 1964 t.Fatalf("Failed to add tx at index %d: %s", i, err) 1965 } 1966 } 1967 1968 <-issuer 1969 1970 blk, err := vm.BuildBlock(context.Background()) 1971 if err != nil { 1972 t.Fatalf("Failed to build block with import transaction: %s", err) 1973 } 1974 1975 if err := blk.Verify(context.Background()); err != nil { 1976 t.Fatalf("Block failed verification on VM: %s", err) 1977 } 1978 1979 if status := blk.Status(); status != choices.Processing { 1980 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 1981 } 1982 1983 if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { 1984 t.Fatal(err) 1985 } 1986 1987 blkHeight := blk.Height() 1988 blkHash := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock.Hash() 1989 1990 vm.blockChain.GetVMConfig().AllowUnfinalizedQueries = true 1991 1992 ctx := context.Background() 1993 b, err := vm.eth.APIBackend.BlockByNumber(ctx, rpc.BlockNumber(blkHeight)) 1994 if err != nil { 1995 t.Fatal(err) 1996 } 1997 if b.Hash() != blkHash { 1998 t.Fatalf("expected block at %d to have hash %s but got %s", blkHeight, blkHash.Hex(), b.Hash().Hex()) 1999 } 2000 2001 vm.blockChain.GetVMConfig().AllowUnfinalizedQueries = false 2002 2003 _, err = vm.eth.APIBackend.BlockByNumber(ctx, rpc.BlockNumber(blkHeight)) 2004 if !errors.Is(err, eth.ErrUnfinalizedData) { 2005 t.Fatalf("expected ErrUnfinalizedData but got %s", err.Error()) 2006 } 2007 2008 if err := blk.Accept(context.Background()); err != nil { 2009 t.Fatalf("VM failed to accept block: %s", err) 2010 } 2011 2012 if b := vm.blockChain.GetBlockByNumber(blkHeight); b.Hash() != blkHash { 2013 t.Fatalf("expected block at %d to have hash %s but got %s", blkHeight, blkHash.Hex(), b.Hash().Hex()) 2014 } 2015 } 2016 2017 func TestConfigureLogLevel(t *testing.T) { 2018 configTests := []struct { 2019 name string 2020 logConfig string 2021 genesisJSON, upgradeJSON string 2022 expectedErr string 2023 }{ 2024 { 2025 name: "Log level info", 2026 logConfig: "{\"log-level\": \"info\"}", 2027 genesisJSON: genesisJSONSubnetEVM, 2028 upgradeJSON: "", 2029 expectedErr: "", 2030 }, 2031 } 2032 for _, test := range configTests { 2033 t.Run(test.name, func(t *testing.T) { 2034 vm := &VM{} 2035 ctx, dbManager, genesisBytes, issuer := setupGenesis(t, test.genesisJSON) 2036 appSender := &engCommon.SenderTest{T: t} 2037 appSender.CantSendAppGossip = true 2038 appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } 2039 err := vm.Initialize( 2040 context.Background(), 2041 ctx, 2042 dbManager, 2043 genesisBytes, 2044 []byte(""), 2045 []byte(test.logConfig), 2046 issuer, 2047 []*engCommon.Fx{}, 2048 appSender, 2049 ) 2050 if len(test.expectedErr) == 0 && err != nil { 2051 t.Fatal(err) 2052 } else if len(test.expectedErr) > 0 { 2053 if err == nil { 2054 t.Fatalf("initialize should have failed due to %s", test.expectedErr) 2055 } else if !strings.Contains(err.Error(), test.expectedErr) { 2056 t.Fatalf("Expected initialize to fail due to %s, but failed with %s", test.expectedErr, err.Error()) 2057 } 2058 } 2059 2060 // If the VM was not initialized, do not attept to shut it down 2061 if err == nil { 2062 shutdownChan := make(chan error, 1) 2063 shutdownFunc := func() { 2064 err := vm.Shutdown(context.Background()) 2065 shutdownChan <- err 2066 } 2067 go shutdownFunc() 2068 2069 shutdownTimeout := 50 * time.Millisecond 2070 ticker := time.NewTicker(shutdownTimeout) 2071 select { 2072 case <-ticker.C: 2073 t.Fatalf("VM shutdown took longer than timeout: %v", shutdownTimeout) 2074 case err := <-shutdownChan: 2075 if err != nil { 2076 t.Fatalf("Shutdown errored: %s", err) 2077 } 2078 } 2079 } 2080 }) 2081 } 2082 } 2083 2084 // Regression test to ensure we can build blocks if we are starting with the 2085 // Subnet EVM ruleset in genesis. 2086 func TestBuildSubnetEVMBlock(t *testing.T) { 2087 issuer, vm, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 2088 2089 defer func() { 2090 if err := vm.Shutdown(context.Background()); err != nil { 2091 t.Fatal(err) 2092 } 2093 }() 2094 2095 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2096 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2097 2098 tx := types.NewTransaction(uint64(0), testEthAddrs[1], new(big.Int).Mul(firstTxAmount, big.NewInt(4)), 21000, big.NewInt(testMinGasPrice*3), nil) 2099 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2100 if err != nil { 2101 t.Fatal(err) 2102 } 2103 2104 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2105 for i, err := range txErrors { 2106 if err != nil { 2107 t.Fatalf("Failed to add tx at index %d: %s", i, err) 2108 } 2109 } 2110 2111 blk := issueAndAccept(t, issuer, vm) 2112 newHead := <-newTxPoolHeadChan 2113 if newHead.Head.Hash() != common.Hash(blk.ID()) { 2114 t.Fatalf("Expected new block to match") 2115 } 2116 2117 txs := make([]*types.Transaction, 10) 2118 for i := 0; i < 10; i++ { 2119 tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice*3), nil) 2120 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2121 if err != nil { 2122 t.Fatal(err) 2123 } 2124 txs[i] = signedTx 2125 } 2126 errs := vm.txPool.AddRemotes(txs) 2127 for i, err := range errs { 2128 if err != nil { 2129 t.Fatalf("Failed to add tx at index %d: %s", i, err) 2130 } 2131 } 2132 2133 blk = issueAndAccept(t, issuer, vm) 2134 ethBlk := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2135 if ethBlk.BlockGasCost() == nil || ethBlk.BlockGasCost().Cmp(big.NewInt(100)) < 0 { 2136 t.Fatalf("expected blockGasCost to be at least 100 but got %d", ethBlk.BlockGasCost()) 2137 } 2138 minRequiredTip, err := dummy.MinRequiredTip(vm.chainConfig, ethBlk.Header()) 2139 if err != nil { 2140 t.Fatal(err) 2141 } 2142 if minRequiredTip == nil || minRequiredTip.Cmp(big.NewInt(0.05*params.GWei)) < 0 { 2143 t.Fatalf("expected minRequiredTip to be at least 0.05 gwei but got %d", minRequiredTip) 2144 } 2145 2146 if status := blk.Status(); status != choices.Accepted { 2147 t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) 2148 } 2149 2150 lastAcceptedID, err := vm.LastAccepted(context.Background()) 2151 if err != nil { 2152 t.Fatal(err) 2153 } 2154 if lastAcceptedID != blk.ID() { 2155 t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) 2156 } 2157 2158 // Confirm all txs are present 2159 ethBlkTxs := vm.blockChain.GetBlockByNumber(2).Transactions() 2160 for i, tx := range txs { 2161 if len(ethBlkTxs) <= i { 2162 t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs)) 2163 } 2164 if ethBlkTxs[i].Hash() != tx.Hash() { 2165 t.Fatalf("expected tx at index %d to have hash: %x but has: %x", i, txs[i].Hash(), tx.Hash()) 2166 } 2167 } 2168 } 2169 2170 func TestBuildAllowListActivationBlock(t *testing.T) { 2171 genesis := &core.Genesis{} 2172 if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { 2173 t.Fatal(err) 2174 } 2175 genesis.Config.ContractDeployerAllowListConfig = precompile.NewContractDeployerAllowListConfig(big.NewInt(time.Now().Unix()), testEthAddrs, nil) 2176 2177 genesisJSON, err := genesis.MarshalJSON() 2178 if err != nil { 2179 t.Fatal(err) 2180 } 2181 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", "") 2182 2183 defer func() { 2184 if err := vm.Shutdown(context.Background()); err != nil { 2185 t.Fatal(err) 2186 } 2187 }() 2188 2189 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2190 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2191 2192 genesisState, err := vm.blockChain.StateAt(vm.blockChain.Genesis().Root()) 2193 if err != nil { 2194 t.Fatal(err) 2195 } 2196 role := precompile.GetContractDeployerAllowListStatus(genesisState, testEthAddrs[0]) 2197 if role != precompile.AllowListNoRole { 2198 t.Fatalf("Expected allow list status to be set to no role: %s, but found: %s", precompile.AllowListNoRole, role) 2199 } 2200 2201 // Send basic transaction to construct a simple block and confirm that the precompile state configuration in the worker behaves correctly. 2202 tx := types.NewTransaction(uint64(0), testEthAddrs[1], new(big.Int).Mul(firstTxAmount, big.NewInt(4)), 21000, big.NewInt(testMinGasPrice*3), nil) 2203 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2204 if err != nil { 2205 t.Fatal(err) 2206 } 2207 2208 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2209 for i, err := range txErrors { 2210 if err != nil { 2211 t.Fatalf("Failed to add tx at index %d: %s", i, err) 2212 } 2213 } 2214 2215 blk := issueAndAccept(t, issuer, vm) 2216 newHead := <-newTxPoolHeadChan 2217 if newHead.Head.Hash() != common.Hash(blk.ID()) { 2218 t.Fatalf("Expected new block to match") 2219 } 2220 2221 // Verify that the allow list config activation was handled correctly in the first block. 2222 blkState, err := vm.blockChain.StateAt(blk.(*chain.BlockWrapper).Block.(*Block).ethBlock.Root()) 2223 if err != nil { 2224 t.Fatal(err) 2225 } 2226 role = precompile.GetContractDeployerAllowListStatus(blkState, testEthAddrs[0]) 2227 if role != precompile.AllowListAdmin { 2228 t.Fatalf("Expected allow list status to be set to Admin: %s, but found: %s", precompile.AllowListAdmin, role) 2229 } 2230 } 2231 2232 // Test that the tx allow list allows whitelisted transactions and blocks non-whitelisted addresses 2233 func TestTxAllowListSuccessfulTx(t *testing.T) { 2234 // Setup chain params 2235 genesis := &core.Genesis{} 2236 if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { 2237 t.Fatal(err) 2238 } 2239 genesis.Config.TxAllowListConfig = precompile.NewTxAllowListConfig(big.NewInt(0), testEthAddrs[0:1], nil) 2240 genesisJSON, err := genesis.MarshalJSON() 2241 if err != nil { 2242 t.Fatal(err) 2243 } 2244 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", "") 2245 2246 defer func() { 2247 if err := vm.Shutdown(context.Background()); err != nil { 2248 t.Fatal(err) 2249 } 2250 }() 2251 2252 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2253 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2254 2255 genesisState, err := vm.blockChain.StateAt(vm.blockChain.Genesis().Root()) 2256 if err != nil { 2257 t.Fatal(err) 2258 } 2259 2260 // Check that address 0 is whitelisted and address 1 is not 2261 role := precompile.GetTxAllowListStatus(genesisState, testEthAddrs[0]) 2262 if role != precompile.AllowListAdmin { 2263 t.Fatalf("Expected allow list status to be set to admin: %s, but found: %s", precompile.AllowListAdmin, role) 2264 } 2265 role = precompile.GetTxAllowListStatus(genesisState, testEthAddrs[1]) 2266 if role != precompile.AllowListNoRole { 2267 t.Fatalf("Expected allow list status to be set to no role: %s, but found: %s", precompile.AllowListNoRole, role) 2268 } 2269 2270 // Submit a successful transaction 2271 tx0 := types.NewTransaction(uint64(0), testEthAddrs[0], big.NewInt(1), 21000, big.NewInt(testMinGasPrice), nil) 2272 signedTx0, err := types.SignTx(tx0, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2273 require.NoError(t, err) 2274 2275 errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0}) 2276 if err := errs[0]; err != nil { 2277 t.Fatalf("Failed to add tx at index: %s", err) 2278 } 2279 2280 // Submit a rejected transaction, should throw an error 2281 tx1 := types.NewTransaction(uint64(0), testEthAddrs[1], big.NewInt(2), 21000, big.NewInt(testMinGasPrice), nil) 2282 signedTx1, err := types.SignTx(tx1, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2283 if err != nil { 2284 t.Fatal(err) 2285 } 2286 2287 errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) 2288 if err := errs[0]; !errors.Is(err, precompile.ErrSenderAddressNotAllowListed) { 2289 t.Fatalf("expected ErrSenderAddressNotAllowListed, got: %s", err) 2290 } 2291 2292 blk := issueAndAccept(t, issuer, vm) 2293 2294 // Verify that the constructed block only has the whitelisted tx 2295 block := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2296 2297 txs := block.Transactions() 2298 2299 if txs.Len() != 1 { 2300 t.Fatalf("Expected number of txs to be %d, but found %d", 1, txs.Len()) 2301 } 2302 2303 require.Equal(t, signedTx0.Hash(), txs[0].Hash()) 2304 } 2305 2306 // Test that the tx allow list allows whitelisted transactions and blocks non-whitelisted addresses 2307 // and the allowlist is removed after the precompile is disabled. 2308 func TestTxAllowListDisablePrecompile(t *testing.T) { 2309 // Setup chain params 2310 genesis := &core.Genesis{} 2311 if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { 2312 t.Fatal(err) 2313 } 2314 enableAllowListTimestamp := time.Unix(0, 0) // enable at genesis 2315 genesis.Config.TxAllowListConfig = precompile.NewTxAllowListConfig(big.NewInt(enableAllowListTimestamp.Unix()), testEthAddrs[0:1], nil) 2316 genesisJSON, err := genesis.MarshalJSON() 2317 if err != nil { 2318 t.Fatal(err) 2319 } 2320 2321 // arbitrary choice ahead of enableAllowListTimestamp 2322 disableAllowListTimestamp := enableAllowListTimestamp.Add(10 * time.Hour) 2323 // configure a network upgrade to remove the allowlist 2324 upgradeConfig := fmt.Sprintf(` 2325 { 2326 "precompileUpgrades": [ 2327 { 2328 "txAllowListConfig": { 2329 "blockTimestamp": %d, 2330 "disable": true 2331 } 2332 } 2333 ] 2334 } 2335 `, disableAllowListTimestamp.Unix()) 2336 2337 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", upgradeConfig) 2338 2339 vm.clock.Set(disableAllowListTimestamp) // upgrade takes effect after a block is issued, so we can set vm's clock here. 2340 2341 defer func() { 2342 if err := vm.Shutdown(context.Background()); err != nil { 2343 t.Fatal(err) 2344 } 2345 }() 2346 2347 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2348 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2349 2350 genesisState, err := vm.blockChain.StateAt(vm.blockChain.Genesis().Root()) 2351 if err != nil { 2352 t.Fatal(err) 2353 } 2354 2355 // Check that address 0 is whitelisted and address 1 is not 2356 role := precompile.GetTxAllowListStatus(genesisState, testEthAddrs[0]) 2357 if role != precompile.AllowListAdmin { 2358 t.Fatalf("Expected allow list status to be set to admin: %s, but found: %s", precompile.AllowListAdmin, role) 2359 } 2360 role = precompile.GetTxAllowListStatus(genesisState, testEthAddrs[1]) 2361 if role != precompile.AllowListNoRole { 2362 t.Fatalf("Expected allow list status to be set to no role: %s, but found: %s", precompile.AllowListNoRole, role) 2363 } 2364 2365 // Submit a successful transaction 2366 tx0 := types.NewTransaction(uint64(0), testEthAddrs[0], big.NewInt(1), 21000, big.NewInt(testMinGasPrice), nil) 2367 signedTx0, err := types.SignTx(tx0, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2368 require.NoError(t, err) 2369 2370 errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0}) 2371 if err := errs[0]; err != nil { 2372 t.Fatalf("Failed to add tx at index: %s", err) 2373 } 2374 2375 // Submit a rejected transaction, should throw an error 2376 tx1 := types.NewTransaction(uint64(0), testEthAddrs[1], big.NewInt(2), 21000, big.NewInt(testMinGasPrice), nil) 2377 signedTx1, err := types.SignTx(tx1, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2378 if err != nil { 2379 t.Fatal(err) 2380 } 2381 2382 errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) 2383 if err := errs[0]; !errors.Is(err, precompile.ErrSenderAddressNotAllowListed) { 2384 t.Fatalf("expected ErrSenderAddressNotAllowListed, got: %s", err) 2385 } 2386 2387 blk := issueAndAccept(t, issuer, vm) 2388 2389 // Verify that the constructed block only has the whitelisted tx 2390 block := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2391 txs := block.Transactions() 2392 if txs.Len() != 1 { 2393 t.Fatalf("Expected number of txs to be %d, but found %d", 1, txs.Len()) 2394 } 2395 require.Equal(t, signedTx0.Hash(), txs[0].Hash()) 2396 2397 // verify the issued block is after the network upgrade 2398 require.True(t, block.Timestamp().Cmp(big.NewInt(disableAllowListTimestamp.Unix())) >= 0) 2399 2400 <-newTxPoolHeadChan // wait for new head in tx pool 2401 2402 // retry the rejected Tx, which should now succeed 2403 errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) 2404 if err := errs[0]; err != nil { 2405 t.Fatalf("Failed to add tx at index: %s", err) 2406 } 2407 2408 vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) // add 2 seconds for gas fee to adjust 2409 blk = issueAndAccept(t, issuer, vm) 2410 2411 // Verify that the constructed block only has the previously rejected tx 2412 block = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2413 txs = block.Transactions() 2414 if txs.Len() != 1 { 2415 t.Fatalf("Expected number of txs to be %d, but found %d", 1, txs.Len()) 2416 } 2417 require.Equal(t, signedTx1.Hash(), txs[0].Hash()) 2418 } 2419 2420 // Test that the fee manager changes fee configuration 2421 func TestFeeManagerChangeFee(t *testing.T) { 2422 // Setup chain params 2423 genesis := &core.Genesis{} 2424 if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { 2425 t.Fatal(err) 2426 } 2427 genesis.Config.FeeManagerConfig = precompile.NewFeeManagerConfig(big.NewInt(0), testEthAddrs[0:1], nil, nil) 2428 2429 // set a lower fee config now 2430 testLowFeeConfig := commontype.FeeConfig{ 2431 GasLimit: big.NewInt(8_000_000), 2432 TargetBlockRate: 5, // in seconds 2433 2434 MinBaseFee: big.NewInt(5_000_000_000), 2435 TargetGas: big.NewInt(18_000_000), 2436 BaseFeeChangeDenominator: big.NewInt(3396), 2437 2438 MinBlockGasCost: big.NewInt(0), 2439 MaxBlockGasCost: big.NewInt(4_000_000), 2440 BlockGasCostStep: big.NewInt(500_000), 2441 } 2442 2443 genesis.Config.FeeConfig = testLowFeeConfig 2444 genesisJSON, err := genesis.MarshalJSON() 2445 if err != nil { 2446 t.Fatal(err) 2447 } 2448 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", "") 2449 2450 defer func() { 2451 if err := vm.Shutdown(context.Background()); err != nil { 2452 t.Fatal(err) 2453 } 2454 }() 2455 2456 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2457 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2458 2459 genesisState, err := vm.blockChain.StateAt(vm.blockChain.Genesis().Root()) 2460 if err != nil { 2461 t.Fatal(err) 2462 } 2463 2464 // Check that address 0 is whitelisted and address 1 is not 2465 role := precompile.GetFeeConfigManagerStatus(genesisState, testEthAddrs[0]) 2466 if role != precompile.AllowListAdmin { 2467 t.Fatalf("Expected fee manager list status to be set to admin: %s, but found: %s", precompile.FeeConfigManagerAddress, role) 2468 } 2469 role = precompile.GetFeeConfigManagerStatus(genesisState, testEthAddrs[1]) 2470 if role != precompile.AllowListNoRole { 2471 t.Fatalf("Expected fee manager list status to be set to no role: %s, but found: %s", precompile.FeeConfigManagerAddress, role) 2472 } 2473 // Contract is initialized but no preconfig is given, reader should return genesis fee config 2474 feeConfig, lastChangedAt, err := vm.blockChain.GetFeeConfigAt(vm.blockChain.Genesis().Header()) 2475 require.NoError(t, err) 2476 require.EqualValues(t, feeConfig, testLowFeeConfig) 2477 require.Zero(t, vm.blockChain.CurrentBlock().Number().Cmp(lastChangedAt)) 2478 2479 // set a different fee config now 2480 testHighFeeConfig := testLowFeeConfig 2481 testHighFeeConfig.MinBaseFee = big.NewInt(28_000_000_000) 2482 2483 data, err := precompile.PackSetFeeConfig(testHighFeeConfig) 2484 require.NoError(t, err) 2485 2486 tx := types.NewTx(&types.DynamicFeeTx{ 2487 ChainID: genesis.Config.ChainID, 2488 Nonce: uint64(0), 2489 To: &precompile.FeeConfigManagerAddress, 2490 Gas: testLowFeeConfig.GasLimit.Uint64(), 2491 Value: common.Big0, 2492 GasFeeCap: testLowFeeConfig.MinBaseFee, // give low fee, it should work since we still haven't applied high fees 2493 GasTipCap: common.Big0, 2494 Data: data, 2495 }) 2496 2497 signedTx, err := types.SignTx(tx, types.LatestSigner(genesis.Config), testKeys[0]) 2498 if err != nil { 2499 t.Fatal(err) 2500 } 2501 2502 errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2503 if err := errs[0]; err != nil { 2504 t.Fatalf("Failed to add tx at index: %s", err) 2505 } 2506 2507 blk := issueAndAccept(t, issuer, vm) 2508 newHead := <-newTxPoolHeadChan 2509 if newHead.Head.Hash() != common.Hash(blk.ID()) { 2510 t.Fatalf("Expected new block to match") 2511 } 2512 2513 block := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2514 2515 // Contract is initialized but no state is given, reader should return genesis fee config 2516 feeConfig, lastChangedAt, err = vm.blockChain.GetFeeConfigAt(block.Header()) 2517 require.NoError(t, err) 2518 require.EqualValues(t, testHighFeeConfig, feeConfig) 2519 require.EqualValues(t, vm.blockChain.CurrentBlock().Number(), lastChangedAt) 2520 2521 // should fail, with same params since fee is higher now 2522 tx2 := types.NewTx(&types.DynamicFeeTx{ 2523 ChainID: genesis.Config.ChainID, 2524 Nonce: uint64(1), 2525 To: &precompile.FeeConfigManagerAddress, 2526 Gas: genesis.Config.FeeConfig.GasLimit.Uint64(), 2527 Value: common.Big0, 2528 GasFeeCap: testLowFeeConfig.MinBaseFee, // this is too low for applied config, should fail 2529 GasTipCap: common.Big0, 2530 Data: data, 2531 }) 2532 2533 signedTx2, err := types.SignTx(tx2, types.LatestSigner(genesis.Config), testKeys[0]) 2534 if err != nil { 2535 t.Fatal(err) 2536 } 2537 2538 err = vm.txPool.AddRemote(signedTx2) 2539 require.ErrorIs(t, err, core.ErrUnderpriced) 2540 } 2541 2542 // Test Allow Fee Recipients is disabled and, etherbase must be blackhole address 2543 func TestAllowFeeRecipientDisabled(t *testing.T) { 2544 genesis := &core.Genesis{} 2545 if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { 2546 t.Fatal(err) 2547 } 2548 genesis.Config.AllowFeeRecipients = false // set to false initially 2549 genesisJSON, err := genesis.MarshalJSON() 2550 if err != nil { 2551 t.Fatal(err) 2552 } 2553 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", "") 2554 2555 vm.miner.SetEtherbase(common.HexToAddress("0x0123456789")) // set non-blackhole address by force 2556 defer func() { 2557 if err := vm.Shutdown(context.Background()); err != nil { 2558 t.Fatal(err) 2559 } 2560 }() 2561 2562 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2563 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2564 2565 tx := types.NewTransaction(uint64(0), testEthAddrs[1], new(big.Int).Mul(firstTxAmount, big.NewInt(4)), 21000, big.NewInt(testMinGasPrice*3), nil) 2566 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2567 if err != nil { 2568 t.Fatal(err) 2569 } 2570 2571 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2572 for i, err := range txErrors { 2573 if err != nil { 2574 t.Fatalf("Failed to add tx at index %d: %s", i, err) 2575 } 2576 } 2577 2578 <-issuer 2579 2580 blk, err := vm.BuildBlock(context.Background()) 2581 require.NoError(t, err) // this won't return an error since miner will set the etherbase to blackhole address 2582 2583 ethBlock := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2584 require.Equal(t, constants.BlackholeAddr, ethBlock.Coinbase()) 2585 2586 // Create empty block from blk 2587 internalBlk := blk.(*chain.BlockWrapper).Block.(*Block) 2588 modifiedHeader := types.CopyHeader(internalBlk.ethBlock.Header()) 2589 modifiedHeader.Coinbase = common.HexToAddress("0x0123456789") // set non-blackhole address by force 2590 modifiedBlock := types.NewBlock( 2591 modifiedHeader, 2592 internalBlk.ethBlock.Transactions(), 2593 nil, 2594 nil, 2595 new(trie.Trie), 2596 ) 2597 2598 modifiedBlk := vm.newBlock(modifiedBlock) 2599 2600 require.ErrorIs(t, modifiedBlk.Verify(context.Background()), vmerrs.ErrInvalidCoinbase) 2601 } 2602 2603 func TestAllowFeeRecipientEnabled(t *testing.T) { 2604 genesis := &core.Genesis{} 2605 if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { 2606 t.Fatal(err) 2607 } 2608 genesis.Config.AllowFeeRecipients = true 2609 genesisJSON, err := genesis.MarshalJSON() 2610 if err != nil { 2611 t.Fatal(err) 2612 } 2613 2614 etherBase := common.HexToAddress("0x0123456789") 2615 c := Config{} 2616 c.SetDefaults() 2617 c.FeeRecipient = etherBase.String() 2618 configJSON, err := json.Marshal(c) 2619 if err != nil { 2620 t.Fatal(err) 2621 } 2622 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), string(configJSON), "") 2623 2624 defer func() { 2625 if err := vm.Shutdown(context.Background()); err != nil { 2626 t.Fatal(err) 2627 } 2628 }() 2629 2630 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2631 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2632 2633 tx := types.NewTransaction(uint64(0), testEthAddrs[1], new(big.Int).Mul(firstTxAmount, big.NewInt(4)), 21000, big.NewInt(testMinGasPrice*3), nil) 2634 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2635 if err != nil { 2636 t.Fatal(err) 2637 } 2638 2639 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2640 for i, err := range txErrors { 2641 if err != nil { 2642 t.Fatalf("Failed to add tx at index %d: %s", i, err) 2643 } 2644 } 2645 2646 blk := issueAndAccept(t, issuer, vm) 2647 newHead := <-newTxPoolHeadChan 2648 if newHead.Head.Hash() != common.Hash(blk.ID()) { 2649 t.Fatalf("Expected new block to match") 2650 } 2651 ethBlock := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2652 require.Equal(t, etherBase, ethBlock.Coinbase()) 2653 // Verify that etherBase has received fees 2654 blkState, err := vm.blockChain.StateAt(ethBlock.Root()) 2655 if err != nil { 2656 t.Fatal(err) 2657 } 2658 2659 balance := blkState.GetBalance(etherBase) 2660 require.Equal(t, 1, balance.Cmp(common.Big0)) 2661 } 2662 2663 func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) { 2664 genesis := &core.Genesis{} 2665 require.NoError(t, genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM))) 2666 2667 genesis.Config.RewardManagerConfig = precompile.NewRewardManagerConfig(common.Big0, testEthAddrs[0:1], nil, nil) 2668 genesis.Config.AllowFeeRecipients = true // enable this in genesis to test if this is recognized by the reward manager 2669 genesisJSON, err := genesis.MarshalJSON() 2670 require.NoError(t, err) 2671 2672 etherBase := common.HexToAddress("0x0123456789") // give custom ether base 2673 c := Config{} 2674 c.SetDefaults() 2675 c.FeeRecipient = etherBase.String() 2676 configJSON, err := json.Marshal(c) 2677 require.NoError(t, err) 2678 2679 // arbitrary choice ahead of enableAllowListTimestamp 2680 // configure a network upgrade to remove the reward manager 2681 disableTime := time.Now().Add(10 * time.Hour) 2682 2683 // configure a network upgrade to remove the allowlist 2684 upgradeConfig := fmt.Sprintf(` 2685 { 2686 "precompileUpgrades": [ 2687 { 2688 "rewardManagerConfig": { 2689 "blockTimestamp": %d, 2690 "disable": true 2691 } 2692 } 2693 ] 2694 } 2695 `, disableTime.Unix()) 2696 2697 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), string(configJSON), upgradeConfig) 2698 2699 defer func() { 2700 err := vm.Shutdown(context.Background()) 2701 require.NoError(t, err) 2702 }() 2703 2704 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2705 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2706 2707 testAddr := common.HexToAddress("0x9999991111") 2708 data, err := precompile.PackSetRewardAddress(testAddr) 2709 require.NoError(t, err) 2710 2711 gas := 21000 + 240 + precompile.SetRewardAddressGasCost // 21000 for tx, 240 for tx data 2712 2713 tx := types.NewTransaction(uint64(0), precompile.RewardManagerAddress, big.NewInt(1), gas, big.NewInt(testMinGasPrice), data) 2714 2715 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2716 require.NoError(t, err) 2717 2718 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2719 for _, err := range txErrors { 2720 require.NoError(t, err) 2721 } 2722 2723 blk := issueAndAccept(t, issuer, vm) 2724 newHead := <-newTxPoolHeadChan 2725 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2726 ethBlock := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2727 require.Equal(t, etherBase, ethBlock.Coinbase()) // reward address is activated at this block so this is fine 2728 2729 tx1 := types.NewTransaction(uint64(0), testEthAddrs[0], big.NewInt(2), 21000, big.NewInt(testMinGasPrice*3), nil) 2730 signedTx1, err := types.SignTx(tx1, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2731 require.NoError(t, err) 2732 2733 txErrors = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) 2734 for _, err := range txErrors { 2735 require.NoError(t, err) 2736 } 2737 2738 blk = issueAndAccept(t, issuer, vm) 2739 newHead = <-newTxPoolHeadChan 2740 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2741 ethBlock = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2742 require.Equal(t, testAddr, ethBlock.Coinbase()) // reward address was activated at previous block 2743 // Verify that etherBase has received fees 2744 blkState, err := vm.blockChain.StateAt(ethBlock.Root()) 2745 require.NoError(t, err) 2746 2747 balance := blkState.GetBalance(testAddr) 2748 require.Equal(t, 1, balance.Cmp(common.Big0)) 2749 2750 // Test Case: Disable reward manager 2751 // This should revert back to enabling fee recipients 2752 previousBalance := blkState.GetBalance(etherBase) 2753 2754 // issue a new block to trigger the upgrade 2755 vm.clock.Set(disableTime) // upgrade takes effect after a block is issued, so we can set vm's clock here. 2756 tx2 := types.NewTransaction(uint64(1), testEthAddrs[0], big.NewInt(2), 21000, big.NewInt(testMinGasPrice), nil) 2757 signedTx2, err := types.SignTx(tx2, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2758 require.NoError(t, err) 2759 2760 txErrors = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx2}) 2761 for _, err := range txErrors { 2762 require.NoError(t, err) 2763 } 2764 2765 blk = issueAndAccept(t, issuer, vm) 2766 newHead = <-newTxPoolHeadChan 2767 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2768 ethBlock = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2769 // Reward manager deactivated at this block, so we expect the parent state 2770 // to determine the coinbase for this block before full deactivation in the 2771 // next block. 2772 require.Equal(t, testAddr, ethBlock.Coinbase()) 2773 require.True(t, ethBlock.Timestamp().Cmp(big.NewInt(disableTime.Unix())) >= 0) 2774 2775 vm.clock.Set(vm.clock.Time().Add(3 * time.Hour)) // let time pass to decrease gas price 2776 // issue another block to verify that the reward manager is disabled 2777 tx2 = types.NewTransaction(uint64(2), testEthAddrs[0], big.NewInt(2), 21000, big.NewInt(testMinGasPrice), nil) 2778 signedTx2, err = types.SignTx(tx2, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2779 require.NoError(t, err) 2780 2781 txErrors = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx2}) 2782 for _, err := range txErrors { 2783 require.NoError(t, err) 2784 } 2785 2786 blk = issueAndAccept(t, issuer, vm) 2787 newHead = <-newTxPoolHeadChan 2788 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2789 ethBlock = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2790 // reward manager was disabled at previous block 2791 // so this block should revert back to enabling fee recipients 2792 require.Equal(t, etherBase, ethBlock.Coinbase()) 2793 require.True(t, ethBlock.Timestamp().Cmp(big.NewInt(disableTime.Unix())) >= 0) 2794 2795 // Verify that Blackhole has received fees 2796 blkState, err = vm.blockChain.StateAt(ethBlock.Root()) 2797 require.NoError(t, err) 2798 2799 balance = blkState.GetBalance(etherBase) 2800 require.Equal(t, 1, balance.Cmp(previousBalance)) 2801 } 2802 2803 func TestRewardManagerPrecompileAllowFeeRecipients(t *testing.T) { 2804 genesis := &core.Genesis{} 2805 require.NoError(t, genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM))) 2806 2807 genesis.Config.RewardManagerConfig = precompile.NewRewardManagerConfig(common.Big0, testEthAddrs[0:1], nil, nil) 2808 genesis.Config.AllowFeeRecipients = false // disable this in genesis 2809 genesisJSON, err := genesis.MarshalJSON() 2810 require.NoError(t, err) 2811 etherBase := common.HexToAddress("0x0123456789") // give custom ether base 2812 c := Config{} 2813 c.SetDefaults() 2814 c.FeeRecipient = etherBase.String() 2815 configJSON, err := json.Marshal(c) 2816 require.NoError(t, err) 2817 // configure a network upgrade to remove the reward manager 2818 // arbitrary choice ahead of enableAllowListTimestamp 2819 // configure a network upgrade to remove the reward manager 2820 disableTime := time.Now().Add(10 * time.Hour) 2821 2822 // configure a network upgrade to remove the allowlist 2823 upgradeConfig := fmt.Sprintf(` 2824 { 2825 "precompileUpgrades": [ 2826 { 2827 "rewardManagerConfig": { 2828 "blockTimestamp": %d, 2829 "disable": true 2830 } 2831 } 2832 ] 2833 } 2834 `, disableTime.Unix()) 2835 issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), string(configJSON), upgradeConfig) 2836 2837 defer func() { 2838 require.NoError(t, vm.Shutdown(context.Background())) 2839 }() 2840 2841 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2842 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2843 2844 data, err := precompile.PackAllowFeeRecipients() 2845 require.NoError(t, err) 2846 2847 gas := 21000 + 240 + precompile.AllowFeeRecipientsGasCost // 21000 for tx, 240 for tx data 2848 2849 tx := types.NewTransaction(uint64(0), precompile.RewardManagerAddress, big.NewInt(1), gas, big.NewInt(testMinGasPrice), data) 2850 2851 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2852 require.NoError(t, err) 2853 2854 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2855 for _, err := range txErrors { 2856 require.NoError(t, err) 2857 } 2858 2859 blk := issueAndAccept(t, issuer, vm) 2860 newHead := <-newTxPoolHeadChan 2861 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2862 ethBlock := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2863 require.Equal(t, constants.BlackholeAddr, ethBlock.Coinbase()) // reward address is activated at this block so this is fine 2864 2865 tx1 := types.NewTransaction(uint64(0), testEthAddrs[0], big.NewInt(2), 21000, big.NewInt(testMinGasPrice*3), nil) 2866 signedTx1, err := types.SignTx(tx1, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2867 require.NoError(t, err) 2868 2869 txErrors = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) 2870 for _, err := range txErrors { 2871 require.NoError(t, err) 2872 } 2873 2874 blk = issueAndAccept(t, issuer, vm) 2875 newHead = <-newTxPoolHeadChan 2876 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2877 ethBlock = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2878 require.Equal(t, etherBase, ethBlock.Coinbase()) // reward address was activated at previous block 2879 // Verify that etherBase has received fees 2880 blkState, err := vm.blockChain.StateAt(ethBlock.Root()) 2881 require.NoError(t, err) 2882 2883 balance := blkState.GetBalance(etherBase) 2884 require.Equal(t, 1, balance.Cmp(common.Big0)) 2885 2886 // Test Case: Disable reward manager 2887 // This should revert back to burning fees 2888 previousBalance := blkState.GetBalance(constants.BlackholeAddr) 2889 2890 vm.clock.Set(disableTime) // upgrade takes effect after a block is issued, so we can set vm's clock here. 2891 tx2 := types.NewTransaction(uint64(1), testEthAddrs[0], big.NewInt(2), 21000, big.NewInt(testMinGasPrice), nil) 2892 signedTx2, err := types.SignTx(tx2, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2893 require.NoError(t, err) 2894 2895 txErrors = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx2}) 2896 for _, err := range txErrors { 2897 require.NoError(t, err) 2898 } 2899 2900 blk = issueAndAccept(t, issuer, vm) 2901 newHead = <-newTxPoolHeadChan 2902 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2903 ethBlock = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2904 require.Equal(t, etherBase, ethBlock.Coinbase()) // reward address was activated at previous block 2905 require.True(t, ethBlock.Timestamp().Cmp(big.NewInt(disableTime.Unix())) >= 0) 2906 2907 vm.clock.Set(vm.clock.Time().Add(3 * time.Hour)) // let time pass so that gas price is reduced 2908 tx2 = types.NewTransaction(uint64(2), testEthAddrs[0], big.NewInt(2), 21000, big.NewInt(testMinGasPrice), nil) 2909 signedTx2, err = types.SignTx(tx2, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[1]) 2910 require.NoError(t, err) 2911 2912 txErrors = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx2}) 2913 for _, err := range txErrors { 2914 require.NoError(t, err) 2915 } 2916 2917 blk = issueAndAccept(t, issuer, vm) 2918 newHead = <-newTxPoolHeadChan 2919 require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID())) 2920 ethBlock = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock 2921 require.Equal(t, constants.BlackholeAddr, ethBlock.Coinbase()) // reward address was activated at previous block 2922 require.True(t, ethBlock.Timestamp().Cmp(big.NewInt(disableTime.Unix())) >= 0) 2923 2924 // Verify that Blackhole has received fees 2925 blkState, err = vm.blockChain.StateAt(ethBlock.Root()) 2926 require.NoError(t, err) 2927 2928 balance = blkState.GetBalance(constants.BlackholeAddr) 2929 require.Equal(t, 1, balance.Cmp(previousBalance)) 2930 } 2931 2932 func TestSkipChainConfigCheckCompatible(t *testing.T) { 2933 // The most recent network upgrade in Subnet-EVM is SubnetEVM itself, which cannot be disabled for this test since it results in 2934 // disabling dynamic fees and causes a panic since some code assumes that this is enabled. 2935 // TODO update this test when there is a future network upgrade that can be skipped in the config. 2936 t.Skip("no skippable upgrades") 2937 // Hack: registering metrics uses global variables, so we need to disable metrics here so that we can initialize the VM twice. 2938 metrics.Enabled = false 2939 defer func() { metrics.Enabled = true }() 2940 2941 issuer, vm, dbManager, appSender := GenesisVM(t, true, genesisJSONPreSubnetEVM, "{\"pruning-enabled\":true}", "") 2942 2943 defer func() { 2944 if err := vm.Shutdown(context.Background()); err != nil { 2945 t.Fatal(err) 2946 } 2947 }() 2948 2949 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 2950 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 2951 2952 key, err := accountKeystore.NewKey(rand.Reader) 2953 if err != nil { 2954 t.Fatal(err) 2955 } 2956 2957 tx := types.NewTransaction(uint64(0), key.Address, firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 2958 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 2959 if err != nil { 2960 t.Fatal(err) 2961 } 2962 errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 2963 for i, err := range errs { 2964 if err != nil { 2965 t.Fatalf("Failed to add tx at index %d: %s", i, err) 2966 } 2967 } 2968 2969 blk := issueAndAccept(t, issuer, vm) 2970 newHead := <-newTxPoolHeadChan 2971 if newHead.Head.Hash() != common.Hash(blk.ID()) { 2972 t.Fatalf("Expected new block to match") 2973 } 2974 2975 reinitVM := &VM{} 2976 // use the block's timestamp instead of 0 since rewind to genesis 2977 // is hardcoded to be allowed in core/genesis.go. 2978 genesisWithUpgrade := &core.Genesis{} 2979 require.NoError(t, json.Unmarshal([]byte(genesisJSONPreSubnetEVM), genesisWithUpgrade)) 2980 genesisWithUpgrade.Config.SubnetEVMTimestamp = big.NewInt(blk.Timestamp().Unix()) 2981 genesisWithUpgradeBytes, err := json.Marshal(genesisWithUpgrade) 2982 require.NoError(t, err) 2983 2984 // this will not be allowed 2985 err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, []byte{}, issuer, []*engCommon.Fx{}, appSender) 2986 require.ErrorContains(t, err, "mismatching SubnetEVM fork block timestamp in database") 2987 2988 // try again with skip-upgrade-check 2989 config := []byte("{\"skip-upgrade-check\": true}") 2990 err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, config, issuer, []*engCommon.Fx{}, appSender) 2991 require.NoError(t, err) 2992 require.NoError(t, reinitVM.Shutdown(context.Background())) 2993 } 2994 2995 func TestCrossChainMessagestoVM(t *testing.T) { 2996 crossChainCodec := message.CrossChainCodec 2997 require := require.New(t) 2998 2999 // the following is based on this contract: 3000 // contract T { 3001 // event received(address sender, uint amount, bytes memo); 3002 // event receivedAddr(address sender); 3003 // 3004 // function receive(bytes calldata memo) external payable returns (string memory res) { 3005 // emit received(msg.sender, msg.value, memo); 3006 // emit receivedAddr(msg.sender); 3007 // return "hello world"; 3008 // } 3009 // } 3010 3011 const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` 3012 const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]` 3013 parsed, err := abi.JSON(strings.NewReader(abiJSON)) 3014 require.NoErrorf(err, "could not parse abi: %v") 3015 3016 calledSendCrossChainAppResponseFn := false 3017 issuer, vm, _, appSender := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 3018 3019 defer func() { 3020 err := vm.Shutdown(context.Background()) 3021 require.NoError(err) 3022 }() 3023 3024 appSender.SendCrossChainAppResponseF = func(ctx context.Context, respondingChainID ids.ID, requestID uint32, responseBytes []byte) { 3025 calledSendCrossChainAppResponseFn = true 3026 3027 var response message.EthCallResponse 3028 if _, err = crossChainCodec.Unmarshal(responseBytes, &response); err != nil { 3029 require.NoErrorf(err, "unexpected error during unmarshal: %w") 3030 } 3031 3032 result := core.ExecutionResult{} 3033 err = json.Unmarshal(response.ExecutionResult, &result) 3034 require.NoError(err) 3035 require.NotNil(result.ReturnData) 3036 3037 finalResult, err := parsed.Unpack("receive", result.ReturnData) 3038 require.NoError(err) 3039 require.NotNil(finalResult) 3040 require.Equal("hello world", finalResult[0]) 3041 } 3042 3043 newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) 3044 vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) 3045 3046 tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) 3047 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 3048 require.NoError(err) 3049 3050 txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) 3051 for _, err := range txErrors { 3052 require.NoError(err) 3053 } 3054 3055 <-issuer 3056 3057 blk1, err := vm.BuildBlock(context.Background()) 3058 require.NoError(err) 3059 3060 err = blk1.Verify(context.Background()) 3061 require.NoError(err) 3062 3063 if status := blk1.Status(); status != choices.Processing { 3064 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 3065 } 3066 3067 err = vm.SetPreference(context.Background(), blk1.ID()) 3068 require.NoError(err) 3069 3070 err = blk1.Accept(context.Background()) 3071 require.NoError(err) 3072 3073 newHead := <-newTxPoolHeadChan 3074 if newHead.Head.Hash() != common.Hash(blk1.ID()) { 3075 t.Fatalf("Expected new block to match") 3076 } 3077 3078 if status := blk1.Status(); status != choices.Accepted { 3079 t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) 3080 } 3081 3082 lastAcceptedID, err := vm.LastAccepted(context.Background()) 3083 require.NoError(err) 3084 3085 if lastAcceptedID != blk1.ID() { 3086 t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk1.ID(), lastAcceptedID) 3087 } 3088 3089 contractTx := types.NewContractCreation(1, common.Big0, 200000, big.NewInt(testMinGasPrice), common.FromHex(abiBin)) 3090 contractSignedTx, err := types.SignTx(contractTx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) 3091 require.NoError(err) 3092 3093 errs := vm.txPool.AddRemotesSync([]*types.Transaction{contractSignedTx}) 3094 for _, err := range errs { 3095 require.NoError(err) 3096 } 3097 testAddr := testEthAddrs[0] 3098 contractAddress := crypto.CreateAddress(testAddr, 1) 3099 3100 <-issuer 3101 3102 blk2, err := vm.BuildBlock(context.Background()) 3103 require.NoError(err) 3104 3105 err = blk2.Verify(context.Background()) 3106 require.NoError(err) 3107 3108 if status := blk2.Status(); status != choices.Processing { 3109 t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) 3110 } 3111 3112 err = vm.SetPreference(context.Background(), blk2.ID()) 3113 require.NoError(err) 3114 3115 err = blk2.Accept(context.Background()) 3116 require.NoError(err) 3117 3118 newHead = <-newTxPoolHeadChan 3119 if newHead.Head.Hash() != common.Hash(blk2.ID()) { 3120 t.Fatalf("Expected new block to match") 3121 } 3122 3123 if status := blk2.Status(); status != choices.Accepted { 3124 t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) 3125 } 3126 3127 lastAcceptedID, err = vm.LastAccepted(context.Background()) 3128 require.NoError(err) 3129 3130 if lastAcceptedID != blk2.ID() { 3131 t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk2.ID(), lastAcceptedID) 3132 } 3133 3134 input, err := parsed.Pack("receive", []byte("X")) 3135 require.NoError(err) 3136 3137 data := hexutil.Bytes(input) 3138 3139 requestArgs, err := json.Marshal(ðapi.TransactionArgs{ 3140 To: &contractAddress, 3141 Data: &data, 3142 }) 3143 require.NoError(err) 3144 3145 var ethCallRequest message.CrossChainRequest = message.EthCallRequest{ 3146 RequestArgs: requestArgs, 3147 } 3148 3149 crossChainRequest, err := crossChainCodec.Marshal(message.Version, ðCallRequest) 3150 require.NoError(err) 3151 3152 requestingChainID := ids.ID(common.BytesToHash([]byte{1, 2, 3, 4, 5})) 3153 3154 // we need all items in the acceptor queue to be processed before we process a cross chain request 3155 vm.blockChain.DrainAcceptorQueue() 3156 err = vm.Network.CrossChainAppRequest(context.Background(), requestingChainID, 1, time.Now().Add(60*time.Second), crossChainRequest) 3157 require.NoError(err) 3158 require.True(calledSendCrossChainAppResponseFn, "sendCrossChainAppResponseFn was not called") 3159 } 3160 3161 func TestSignatureRequestsToVM(t *testing.T) { 3162 _, vm, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") 3163 3164 defer func() { 3165 err := vm.Shutdown(context.Background()) 3166 require.NoError(t, err) 3167 }() 3168 3169 // Generate a SignatureRequest for an unknown message 3170 var signatureRequest message.Request = message.SignatureRequest{ 3171 MessageID: ids.GenerateTestID(), 3172 } 3173 3174 requestBytes, err := message.Codec.Marshal(message.Version, &signatureRequest) 3175 require.NoError(t, err) 3176 3177 // Currently with warp not being initialized we just need to make sure the NoopSignatureRequestHandler does not 3178 // panic/crash when sent a SignatureRequest. 3179 // TODO: We will need to update the test when warp is initialized to check for expected response. 3180 err = vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, time.Now().Add(60*time.Second), requestBytes) 3181 require.NoError(t, err) 3182 }