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
 | ||||
| 	// 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") | ||||
| ) | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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)
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -37,7 +37,7 @@ var ( | ||||
| 	baseDir      = filepath.Join(".", "testdata") | ||||
| 	blockTestDir = filepath.Join(baseDir, "BlockchainTests") | ||||
| 	stateTestDir = filepath.Join(baseDir, "GeneralStateTests") | ||||
| 	legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") | ||||
| 	//legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests")
 | ||||
| 	transactionTestDir = filepath.Join(baseDir, "TransactionTests") | ||||
| 	vmTestDir          = filepath.Join(baseDir, "VMTests") | ||||
| 	rlpTestDir         = filepath.Join(baseDir, "RLPTests") | ||||
| @ -93,6 +93,7 @@ type testMatcher struct { | ||||
| 	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]<testType>.
 | ||||
| @ -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) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user