diff --git a/core/error.go b/core/error.go index bcbce09ba..33cf874d1 100644 --- a/core/error.go +++ b/core/error.go @@ -87,4 +87,7 @@ var ( // ErrFeeCapTooLow is returned if the transaction fee cap is less than the // the base fee of the block. ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee") + + // ErrSenderNoEOA is returned if the sender of a transaction is a contract. + ErrSenderNoEOA = errors.New("sender not an eoa") ) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index ce6d2bcdd..13a9eb810 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -195,7 +195,7 @@ func TestStateProcessorErrors(t *testing.T) { } } - // One final error is ErrTxTypeNotSupported. For this, we need an older chain + // ErrTxTypeNotSupported, For this, we need an older chain { var ( db = rawdb.NewMemoryDatabase() @@ -244,6 +244,46 @@ func TestStateProcessorErrors(t *testing.T) { } } } + + // ErrSenderNoEOA, for this we need the sender to have contract code + { + var ( + db = rawdb.NewMemoryDatabase() + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + Code: common.FromHex("0xB0B0FACE"), + }, + }, + } + genesis = gspec.MustCommit(db) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ) + defer blockchain.Stop() + for i, tt := range []struct { + txs []*types.Transaction + want string + }{ + { // ErrSenderNoEOA + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), + }, + want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", + }, + } { + block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) + _, err := blockchain.InsertChain(types.Blocks{block}) + if err == nil { + t.Fatal("block imported without errors") + } + if have, want := err.Error(), tt.want; have != want { + t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + } + } + } } // GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be diff --git a/core/state_transition.go b/core/state_transition.go index b5b0ae990..68f3c74a3 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -25,9 +25,12 @@ import ( cmath "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) +var emptyCodeHash = crypto.Keccak256Hash(nil) + /* The State Transitioning Model @@ -220,6 +223,11 @@ func (st *StateTransition) preCheck() error { st.msg.From().Hex(), msgNonce, stNonce) } } + // Make sure the sender is an EOA + if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { + return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, + st.msg.From().Hex(), codeHash) + } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 4a4e5bbc3..9afd59d59 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -417,24 +417,24 @@ func TestOverriddenTraceCall(t *testing.T) { }, }, } - for _, testspec := range testSuite { + for i, testspec := range testSuite { result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) if testspec.expectErr != nil { if err == nil { - t.Errorf("Expect error %v, get nothing", testspec.expectErr) + t.Errorf("test %d: want error %v, have nothing", i, testspec.expectErr) continue } if !errors.Is(err, testspec.expectErr) { - t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) + t.Errorf("test %d: error mismatch, want %v, have %v", i, testspec.expectErr, err) } } else { if err != nil { - t.Errorf("Expect no error, get %v", err) + t.Errorf("test %d: want no error, have %v", i, err) continue } ret := new(callTrace) if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil { - t.Fatalf("failed to unmarshal trace result: %v", err) + t.Fatalf("test %d: failed to unmarshal trace result: %v", i, err) } if !jsonEqual(ret, testspec.expect) { // uncomment this for easier debugging diff --git a/tests/init_test.go b/tests/init_test.go index f1a4ca2cf..969cf7139 100644 --- a/tests/init_test.go +++ b/tests/init_test.go @@ -34,10 +34,10 @@ import ( ) var ( - baseDir = filepath.Join(".", "testdata") - blockTestDir = filepath.Join(baseDir, "BlockchainTests") - stateTestDir = filepath.Join(baseDir, "GeneralStateTests") - legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") + baseDir = filepath.Join(".", "testdata") + blockTestDir = filepath.Join(baseDir, "BlockchainTests") + stateTestDir = filepath.Join(baseDir, "GeneralStateTests") + //legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") transactionTestDir = filepath.Join(baseDir, "TransactionTests") vmTestDir = filepath.Join(baseDir, "VMTests") rlpTestDir = filepath.Join(baseDir, "RLPTests") @@ -89,10 +89,11 @@ func findLine(data []byte, offset int64) (line int) { // testMatcher controls skipping and chain config assignment to tests. type testMatcher struct { - configpat []testConfig - failpat []testFailure - skiploadpat []*regexp.Regexp - slowpat []*regexp.Regexp + configpat []testConfig + failpat []testFailure + skiploadpat []*regexp.Regexp + slowpat []*regexp.Regexp + runonlylistpat *regexp.Regexp } type testConfig struct { @@ -123,6 +124,10 @@ func (tm *testMatcher) fails(pattern string, reason string) { tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason}) } +func (tm *testMatcher) runonly(pattern string) { + tm.runonlylistpat = regexp.MustCompile(pattern) +} + // config defines chain config for tests matching the pattern. func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg}) @@ -212,6 +217,11 @@ func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest inte if r, _ := tm.findSkip(name); r != "" { t.Skip(r) } + if tm.runonlylistpat != nil { + if !tm.runonlylistpat.MatchString(name) { + t.Skip("Skipped by runonly") + } + } t.Parallel() // Load the file as map[string]. @@ -265,3 +275,14 @@ func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value m.MapIndex(reflect.ValueOf(key)), }) } + +func TestMatcherRunonlylist(t *testing.T) { + t.Parallel() + tm := new(testMatcher) + tm.runonly("invalid*") + tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) { + if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" { + t.Fatalf("invalid test found: %s != invalidRLPTest.json", name) + } + }) +} diff --git a/tests/state_test.go b/tests/state_test.go index c2ca0e8d6..242e9712a 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -45,7 +45,8 @@ func TestState(t *testing.T) { // Uses 1GB RAM per tested fork st.skipLoad(`^stStaticCall/static_Call1MB`) - + // Un-skip this when https://github.com/ethereum/tests/issues/908 is closed + st.skipLoad(`^stQuadraticComplexityTest/QuadraticComplexitySolidity_CallDataCopy`) // Broken tests: // Expected failures: //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/0`, "bug in test") @@ -58,7 +59,9 @@ func TestState(t *testing.T) { // For Istanbul, older tests were moved into LegacyTests for _, dir := range []string{ stateTestDir, - legacyStateTestDir, + // legacy state tests are disabled, due to them not being + // regenerated for the no-sender-eoa change. + //legacyStateTestDir, } { st.walk(t, dir, func(t *testing.T, name string, test *StateTest) { for _, subtest := range test.Subtests() {