forked from cerc-io/plugeth
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
|
||||||
|
@ -37,7 +37,7 @@ 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")
|
||||||
@ -93,6 +93,7 @@ type testMatcher struct {
|
|||||||
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