diff --git a/core/chain_makers.go b/core/chain_makers.go index 591272a26e..f097939aa6 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -254,6 +254,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} b.header = makeHeader(chainreader, config, parent, gap, statedb, b.engine) + err := ApplyUpgrades(config, &parent.Header().Time, b, statedb) + if err != nil { + return nil, nil, fmt.Errorf("failed to configure precompiles %v", err) + } + // Execute any user modifications to the block if gen != nil { gen(i, b) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index f6e4e439d1..efca0b7755 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -656,6 +656,11 @@ func (api *baseAPI) traceBlock(ctx context.Context, block *types.Block, config * } defer release() + err = core.ApplyUpgrades(api.backend.ChainConfig(), &parent.Header().Time, block, statedb) + if err != nil { + return nil, fmt.Errorf("failed to configure precompiles in block tracing %v", err) + } + // JS tracers have high overhead. In this case run a parallel // process that generates states in one thread and traces txes // in separate worker threads. diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 856e9236f1..eec18f5d5a 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -50,6 +50,7 @@ import ( "github.com/ava-labs/subnet-evm/ethdb" "github.com/ava-labs/subnet-evm/internal/ethapi" "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/rpc" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -914,3 +915,107 @@ func TestTraceChain(t *testing.T) { } } } + +func TestTraceBlockPrecompileActivation(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(3) + copyConfig := *params.TestChainConfig + genesis := &core.Genesis{ + Config: ©Config, + Alloc: core.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + accounts[2].addr: {Balance: big.NewInt(params.Ether)}, + }, + } + // assumes gap is 10 sec, means 3 block away + activateAllowlistBlock := 3 + activateAllowListTime := uint64(activateAllowlistBlock * 10) + activateTxAllowListConfig := params.PrecompileUpgrade{ + Config: txallowlist.NewConfig(&activateAllowListTime, []common.Address{accounts[0].addr}, nil, nil), + } + + deactivateAllowlistBlock := activateAllowlistBlock + 3 + deactivateAllowListTime := uint64(deactivateAllowlistBlock) * 10 + deactivateTxAllowListConfig := params.PrecompileUpgrade{ + Config: txallowlist.NewDisableConfig(&deactivateAllowListTime), + } + + genesis.Config.PrecompileUpgrades = []params.PrecompileUpgrade{ + activateTxAllowListConfig, + deactivateTxAllowListConfig, + } + genBlocks := 10 + signer := types.HomesteadSigner{} + backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + b.AddTx(tx) + }) + defer backend.chain.Stop() + api := NewAPI(backend) + + testSuite := []struct { + blockNumber rpc.BlockNumber + config *TraceConfig + want string + expectErr error + }{ + // Trace head block + { + blockNumber: rpc.BlockNumber(genBlocks), + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + // Trace block before activation + { + blockNumber: rpc.BlockNumber(activateAllowlistBlock - 1), + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + // Trace block activation + { + blockNumber: rpc.BlockNumber(activateAllowlistBlock), + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + // Trace block after activation + { + blockNumber: rpc.BlockNumber(activateAllowlistBlock + 1), + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + // Trace block deactivation + { + blockNumber: rpc.BlockNumber(deactivateAllowlistBlock), + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + // Trace block after deactivation + { + blockNumber: rpc.BlockNumber(deactivateAllowlistBlock + 1), + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + } + for i, tc := range testSuite { + result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config) + if tc.expectErr != nil { + if err == nil { + t.Errorf("test %d, want error %v", i, tc.expectErr) + continue + } + if !reflect.DeepEqual(err, tc.expectErr) { + t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err) + } + continue + } + if err != nil { + t.Errorf("test %d, want no error, have %v", i, err) + continue + } + have, _ := json.Marshal(result) + want := tc.want + if string(have) != want { + t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want) + } + } +} diff --git a/plugin/evm/block.go b/plugin/evm/block.go index 681d44e067..7220eeb2a2 100644 --- a/plugin/evm/block.go +++ b/plugin/evm/block.go @@ -121,7 +121,7 @@ func (b *Block) handlePrecompileAccept(rules *params.Rules, sharedMemoryWriter * if !ok { continue } - if err := accepter.Accept(acceptCtx, log.TxHash, logIdx, log.Topics, log.Data); err != nil { + if err := accepter.Accept(acceptCtx, log.BlockHash, log.BlockNumber, log.TxHash, logIdx, log.Topics, log.Data); err != nil { return err } } diff --git a/plugin/evm/block_test.go b/plugin/evm/block_test.go index 9793074213..d00193c76b 100644 --- a/plugin/evm/block_test.go +++ b/plugin/evm/block_test.go @@ -69,6 +69,8 @@ func TestHandlePrecompileAccept(t *testing.T) { gomock.InOrder( mockAccepter.EXPECT().Accept( gomock.Not(gomock.Nil()), // acceptCtx + ethBlock.Hash(), // blockHash + ethBlock.NumberU64(), // blockNumber ethBlock.Transactions()[txIndex].Hash(), // txHash 0, // logIndex receipt.Logs[0].Topics, // topics @@ -76,6 +78,8 @@ func TestHandlePrecompileAccept(t *testing.T) { ), mockAccepter.EXPECT().Accept( gomock.Not(gomock.Nil()), // acceptCtx + ethBlock.Hash(), // blockHash + ethBlock.NumberU64(), // blockNumber ethBlock.Transactions()[txIndex].Hash(), // txHash 2, // logIndex receipt.Logs[2].Topics, // topics diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 9696860438..52d4dda2ab 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -75,7 +75,7 @@ type AcceptContext struct { // will not maintain backwards compatibility of this interface and your code should not // rely on this. Designed for use only by precompiles that ship with subnet-evm. type Accepter interface { - Accept(acceptCtx *AcceptContext, txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error + Accept(acceptCtx *AcceptContext, blockHash common.Hash, blockNumber uint64, txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error } // ChainContext defines an interface that provides information to a stateful precompile diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index 28e26f9e33..df9c6ab031 100644 --- a/precompile/precompileconfig/mocks.go +++ b/precompile/precompileconfig/mocks.go @@ -246,15 +246,15 @@ func (m *MockAccepter) EXPECT() *MockAccepterMockRecorder { } // Accept mocks base method. -func (m *MockAccepter) Accept(arg0 *AcceptContext, arg1 common.Hash, arg2 int, arg3 []common.Hash, arg4 []byte) error { +func (m *MockAccepter) Accept(arg0 *AcceptContext, arg1 common.Hash, arg2 uint64, arg3 common.Hash, arg4 int, arg5 []common.Hash, arg6 []byte) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Accept", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "Accept", arg0, arg1, arg2, arg3, arg4, arg5, arg6) ret0, _ := ret[0].(error) return ret0 } // Accept indicates an expected call of Accept. -func (mr *MockAccepterMockRecorder) Accept(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockAccepterMockRecorder) Accept(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockAccepter)(nil).Accept), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockAccepter)(nil).Accept), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } diff --git a/x/warp/config.go b/x/warp/config.go index d39c2551f9..60cd8336e7 100644 --- a/x/warp/config.go +++ b/x/warp/config.go @@ -105,12 +105,20 @@ func (c *Config) Equal(s precompileconfig.Config) bool { return equals && c.QuorumNumerator == other.QuorumNumerator } -func (c *Config) Accept(acceptCtx *precompileconfig.AcceptContext, txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error { +func (c *Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash common.Hash, blockNumber uint64, txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error { unsignedMessage, err := UnpackSendWarpEventDataToMessage(logData) if err != nil { return fmt.Errorf("failed to parse warp log data into unsigned message (TxHash: %s, LogIndex: %d): %w", txHash, logIndex, err) } - log.Info("Accepted warp unsigned message", "txHash", txHash, "logIndex", logIndex, "logData", common.Bytes2Hex(logData)) + log.Info( + "Accepted warp unsigned message", + "blockHash", blockHash, + "blockNumber", blockNumber, + "txHash", txHash, + "logIndex", logIndex, + "logData", common.Bytes2Hex(logData), + "warpMessageID", unsignedMessage.ID(), + ) if err := acceptCtx.Warp.AddMessage(unsignedMessage); err != nil { return fmt.Errorf("failed to add warp message during accept (TxHash: %s, LogIndex: %d): %w", txHash, logIndex, err) }