core: check if sender is EOA (#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.
This commit is contained in:
		
							parent
							
								
									d3e3a460ec
								
							
						
					
					
						commit
						0658712f65
					
				| @ -87,4 +87,7 @@ var ( | |||||||
| 	// ErrFeeCapTooLow is returned if the transaction fee cap is less than the
 | 	// ErrFeeCapTooLow is returned if the transaction fee cap is less than the
 | ||||||
| 	// the base fee of the block.
 | 	// the base fee of the block.
 | ||||||
| 	ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee") | 	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") | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -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 ( | 		var ( | ||||||
| 			db    = rawdb.NewMemoryDatabase() | 			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
 | // GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
 | ||||||
|  | |||||||
| @ -25,9 +25,12 @@ import ( | |||||||
| 	cmath "github.com/ethereum/go-ethereum/common/math" | 	cmath "github.com/ethereum/go-ethereum/common/math" | ||||||
| 	"github.com/ethereum/go-ethereum/core/types" | 	"github.com/ethereum/go-ethereum/core/types" | ||||||
| 	"github.com/ethereum/go-ethereum/core/vm" | 	"github.com/ethereum/go-ethereum/core/vm" | ||||||
|  | 	"github.com/ethereum/go-ethereum/crypto" | ||||||
| 	"github.com/ethereum/go-ethereum/params" | 	"github.com/ethereum/go-ethereum/params" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | var emptyCodeHash = crypto.Keccak256Hash(nil) | ||||||
|  | 
 | ||||||
| /* | /* | ||||||
| The State Transitioning Model | The State Transitioning Model | ||||||
| 
 | 
 | ||||||
| @ -220,6 +223,11 @@ func (st *StateTransition) preCheck() error { | |||||||
| 				st.msg.From().Hex(), msgNonce, stNonce) | 				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)
 | 	// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
 | ||||||
| 	if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { | 	if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { | ||||||
| 		// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
 | 		// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
 | ||||||
|  | |||||||
| @ -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) | 		result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) | ||||||
| 		if testspec.expectErr != nil { | 		if testspec.expectErr != nil { | ||||||
| 			if err == 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 | 				continue | ||||||
| 			} | 			} | ||||||
| 			if !errors.Is(err, testspec.expectErr) { | 			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 { | 		} else { | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				t.Errorf("Expect no error, get %v", err) | 				t.Errorf("test %d: want no error, have %v", i, err) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			ret := new(callTrace) | 			ret := new(callTrace) | ||||||
| 			if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil { | 			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) { | 			if !jsonEqual(ret, testspec.expect) { | ||||||
| 				// uncomment this for easier debugging
 | 				// uncomment this for easier debugging
 | ||||||
|  | |||||||
| @ -34,10 +34,10 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	baseDir            = filepath.Join(".", "testdata") | 	baseDir      = filepath.Join(".", "testdata") | ||||||
| 	blockTestDir       = filepath.Join(baseDir, "BlockchainTests") | 	blockTestDir = filepath.Join(baseDir, "BlockchainTests") | ||||||
| 	stateTestDir       = filepath.Join(baseDir, "GeneralStateTests") | 	stateTestDir = filepath.Join(baseDir, "GeneralStateTests") | ||||||
| 	legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") | 	//legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests")
 | ||||||
| 	transactionTestDir = filepath.Join(baseDir, "TransactionTests") | 	transactionTestDir = filepath.Join(baseDir, "TransactionTests") | ||||||
| 	vmTestDir          = filepath.Join(baseDir, "VMTests") | 	vmTestDir          = filepath.Join(baseDir, "VMTests") | ||||||
| 	rlpTestDir         = filepath.Join(baseDir, "RLPTests") | 	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.
 | // testMatcher controls skipping and chain config assignment to tests.
 | ||||||
| type testMatcher struct { | type testMatcher struct { | ||||||
| 	configpat   []testConfig | 	configpat      []testConfig | ||||||
| 	failpat     []testFailure | 	failpat        []testFailure | ||||||
| 	skiploadpat []*regexp.Regexp | 	skiploadpat    []*regexp.Regexp | ||||||
| 	slowpat     []*regexp.Regexp | 	slowpat        []*regexp.Regexp | ||||||
|  | 	runonlylistpat *regexp.Regexp | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type testConfig struct { | 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}) | 	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.
 | // config defines chain config for tests matching the pattern.
 | ||||||
| func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { | func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { | ||||||
| 	tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg}) | 	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 != "" { | 	if r, _ := tm.findSkip(name); r != "" { | ||||||
| 		t.Skip(r) | 		t.Skip(r) | ||||||
| 	} | 	} | ||||||
|  | 	if tm.runonlylistpat != nil { | ||||||
|  | 		if !tm.runonlylistpat.MatchString(name) { | ||||||
|  | 			t.Skip("Skipped by runonly") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| 	// Load the file as map[string]<testType>.
 | 	// Load the file as map[string]<testType>.
 | ||||||
| @ -265,3 +275,14 @@ func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value | |||||||
| 		m.MapIndex(reflect.ValueOf(key)), | 		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) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | |||||||
| @ -45,7 +45,8 @@ func TestState(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// Uses 1GB RAM per tested fork
 | 	// Uses 1GB RAM per tested fork
 | ||||||
| 	st.skipLoad(`^stStaticCall/static_Call1MB`) | 	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:
 | 	// Broken tests:
 | ||||||
| 	// Expected failures:
 | 	// Expected failures:
 | ||||||
| 	//st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/0`, "bug in test")
 | 	//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 Istanbul, older tests were moved into LegacyTests
 | ||||||
| 	for _, dir := range []string{ | 	for _, dir := range []string{ | ||||||
| 		stateTestDir, | 		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) { | 		st.walk(t, dir, func(t *testing.T, name string, test *StateTest) { | ||||||
| 			for _, subtest := range test.Subtests() { | 			for _, subtest := range test.Subtests() { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user