Skip to content

Commit 0658712

Browse files
authored
core: check if sender is EOA (ethereum#23303)
This adds a check to verify that a sender-account does not have code, which means that the codehash is either `emptyCodeHash` _OR_ not present. The latter occurs IFF the sender did not previously exist, a situation which can only occur with zero cost gasprices.
1 parent d3e3a46 commit 0658712

File tree

6 files changed

+91
-16
lines changed

6 files changed

+91
-16
lines changed

core/error.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,7 @@ var (
8787
// ErrFeeCapTooLow is returned if the transaction fee cap is less than the
8888
// the base fee of the block.
8989
ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee")
90+
91+
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
92+
ErrSenderNoEOA = errors.New("sender not an eoa")
9093
)

core/state_processor_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func TestStateProcessorErrors(t *testing.T) {
195195
}
196196
}
197197

198-
// One final error is ErrTxTypeNotSupported. For this, we need an older chain
198+
// ErrTxTypeNotSupported, For this, we need an older chain
199199
{
200200
var (
201201
db = rawdb.NewMemoryDatabase()
@@ -244,6 +244,46 @@ func TestStateProcessorErrors(t *testing.T) {
244244
}
245245
}
246246
}
247+
248+
// ErrSenderNoEOA, for this we need the sender to have contract code
249+
{
250+
var (
251+
db = rawdb.NewMemoryDatabase()
252+
gspec = &Genesis{
253+
Config: config,
254+
Alloc: GenesisAlloc{
255+
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
256+
Balance: big.NewInt(1000000000000000000), // 1 ether
257+
Nonce: 0,
258+
Code: common.FromHex("0xB0B0FACE"),
259+
},
260+
},
261+
}
262+
genesis = gspec.MustCommit(db)
263+
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
264+
)
265+
defer blockchain.Stop()
266+
for i, tt := range []struct {
267+
txs []*types.Transaction
268+
want string
269+
}{
270+
{ // ErrSenderNoEOA
271+
txs: []*types.Transaction{
272+
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
273+
},
274+
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
275+
},
276+
} {
277+
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
278+
_, err := blockchain.InsertChain(types.Blocks{block})
279+
if err == nil {
280+
t.Fatal("block imported without errors")
281+
}
282+
if have, want := err.Error(), tt.want; have != want {
283+
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
284+
}
285+
}
286+
}
247287
}
248288

249289
// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be

core/state_transition.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ import (
2525
cmath "github.com/ethereum/go-ethereum/common/math"
2626
"github.com/ethereum/go-ethereum/core/types"
2727
"github.com/ethereum/go-ethereum/core/vm"
28+
"github.com/ethereum/go-ethereum/crypto"
2829
"github.com/ethereum/go-ethereum/params"
2930
)
3031

32+
var emptyCodeHash = crypto.Keccak256Hash(nil)
33+
3134
/*
3235
The State Transitioning Model
3336
@@ -220,6 +223,11 @@ func (st *StateTransition) preCheck() error {
220223
st.msg.From().Hex(), msgNonce, stNonce)
221224
}
222225
}
226+
// Make sure the sender is an EOA
227+
if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
228+
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
229+
st.msg.From().Hex(), codeHash)
230+
}
223231
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
224232
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
225233
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)

eth/tracers/api_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,24 +417,24 @@ func TestOverriddenTraceCall(t *testing.T) {
417417
},
418418
},
419419
}
420-
for _, testspec := range testSuite {
420+
for i, testspec := range testSuite {
421421
result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
422422
if testspec.expectErr != nil {
423423
if err == nil {
424-
t.Errorf("Expect error %v, get nothing", testspec.expectErr)
424+
t.Errorf("test %d: want error %v, have nothing", i, testspec.expectErr)
425425
continue
426426
}
427427
if !errors.Is(err, testspec.expectErr) {
428-
t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err)
428+
t.Errorf("test %d: error mismatch, want %v, have %v", i, testspec.expectErr, err)
429429
}
430430
} else {
431431
if err != nil {
432-
t.Errorf("Expect no error, get %v", err)
432+
t.Errorf("test %d: want no error, have %v", i, err)
433433
continue
434434
}
435435
ret := new(callTrace)
436436
if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil {
437-
t.Fatalf("failed to unmarshal trace result: %v", err)
437+
t.Fatalf("test %d: failed to unmarshal trace result: %v", i, err)
438438
}
439439
if !jsonEqual(ret, testspec.expect) {
440440
// uncomment this for easier debugging

tests/init_test.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ import (
3434
)
3535

3636
var (
37-
baseDir = filepath.Join(".", "testdata")
38-
blockTestDir = filepath.Join(baseDir, "BlockchainTests")
39-
stateTestDir = filepath.Join(baseDir, "GeneralStateTests")
40-
legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests")
37+
baseDir = filepath.Join(".", "testdata")
38+
blockTestDir = filepath.Join(baseDir, "BlockchainTests")
39+
stateTestDir = filepath.Join(baseDir, "GeneralStateTests")
40+
//legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests")
4141
transactionTestDir = filepath.Join(baseDir, "TransactionTests")
4242
vmTestDir = filepath.Join(baseDir, "VMTests")
4343
rlpTestDir = filepath.Join(baseDir, "RLPTests")
@@ -89,10 +89,11 @@ func findLine(data []byte, offset int64) (line int) {
8989

9090
// testMatcher controls skipping and chain config assignment to tests.
9191
type testMatcher struct {
92-
configpat []testConfig
93-
failpat []testFailure
94-
skiploadpat []*regexp.Regexp
95-
slowpat []*regexp.Regexp
92+
configpat []testConfig
93+
failpat []testFailure
94+
skiploadpat []*regexp.Regexp
95+
slowpat []*regexp.Regexp
96+
runonlylistpat *regexp.Regexp
9697
}
9798

9899
type testConfig struct {
@@ -123,6 +124,10 @@ func (tm *testMatcher) fails(pattern string, reason string) {
123124
tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
124125
}
125126

127+
func (tm *testMatcher) runonly(pattern string) {
128+
tm.runonlylistpat = regexp.MustCompile(pattern)
129+
}
130+
126131
// config defines chain config for tests matching the pattern.
127132
func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
128133
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
212217
if r, _ := tm.findSkip(name); r != "" {
213218
t.Skip(r)
214219
}
220+
if tm.runonlylistpat != nil {
221+
if !tm.runonlylistpat.MatchString(name) {
222+
t.Skip("Skipped by runonly")
223+
}
224+
}
215225
t.Parallel()
216226

217227
// Load the file as map[string]<testType>.
@@ -265,3 +275,14 @@ func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value
265275
m.MapIndex(reflect.ValueOf(key)),
266276
})
267277
}
278+
279+
func TestMatcherRunonlylist(t *testing.T) {
280+
t.Parallel()
281+
tm := new(testMatcher)
282+
tm.runonly("invalid*")
283+
tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) {
284+
if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" {
285+
t.Fatalf("invalid test found: %s != invalidRLPTest.json", name)
286+
}
287+
})
288+
}

tests/state_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func TestState(t *testing.T) {
4545

4646
// Uses 1GB RAM per tested fork
4747
st.skipLoad(`^stStaticCall/static_Call1MB`)
48-
48+
// Un-skip this when https://github.com/ethereum/tests/issues/908 is closed
49+
st.skipLoad(`^stQuadraticComplexityTest/QuadraticComplexitySolidity_CallDataCopy`)
4950
// Broken tests:
5051
// Expected failures:
5152
//st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/0`, "bug in test")
@@ -58,7 +59,9 @@ func TestState(t *testing.T) {
5859
// For Istanbul, older tests were moved into LegacyTests
5960
for _, dir := range []string{
6061
stateTestDir,
61-
legacyStateTestDir,
62+
// legacy state tests are disabled, due to them not being
63+
// regenerated for the no-sender-eoa change.
64+
//legacyStateTestDir,
6265
} {
6366
st.walk(t, dir, func(t *testing.T, name string, test *StateTest) {
6467
for _, subtest := range test.Subtests() {

0 commit comments

Comments
 (0)