Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/evm/internal/t8ntool/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func Transaction(ctx *cli.Context) error {
r.Address = sender
}
// Check intrinsic gas
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.AuthList(), tx.To() == nil,
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0)); err != nil {
r.Error = err
results = append(results, r)
Expand Down
2 changes: 1 addition & 1 deletion cmd/evm/staterunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
}
var testsByName map[string]tests.StateTest
if err := json.Unmarshal(src, &testsByName); err != nil {
return err
return fmt.Errorf("unable to read test file: %w", err)
}

// Iterate over all the tests, run them and aggregate the results
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
signer := gen.Signer()
gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil {
Expand Down
3 changes: 3 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,7 @@ var (

// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
ErrBlobTxCreate = errors.New("blob transaction of type create")

// ErrEmptyAuthList is returned if a set code transaction has an empty auth list.
ErrEmptyAuthList = errors.New("set code transaction with empty auth list")
)
131 changes: 131 additions & 0 deletions core/setcode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package core

import (
"bytes"
"math/big"
"os"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/core/rawdb"
"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/eth/tracers/logger"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)

func TestEIP7702(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
engine = beacon.NewFaker()

// A sender who makes transactions, has some funds
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
config = *params.AllEthashProtocolChanges
gspec = &Genesis{
Config: &config,
Alloc: types.GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
// The address 0xAAAA sstores 1 into slot 2.
aa: {
Code: []byte{
byte(vm.PC), // [0]
byte(vm.DUP1), // [0,0]
byte(vm.DUP1), // [0,0,0]
byte(vm.DUP1), // [0,0,0,0]
byte(vm.PUSH1), 0x01, // [0,0,0,0,1] (value)
byte(vm.PUSH20), addr2[0], addr2[1], addr2[2], addr2[3], addr2[4], addr2[5], addr2[6], addr2[7], addr2[8], addr2[9], addr2[10], addr2[11], addr2[12], addr2[13], addr2[14], addr2[15], addr2[16], addr2[17], addr2[18], addr2[19],
byte(vm.GAS),
byte(vm.CALL),
byte(vm.STOP),
},
Nonce: 0,
Balance: big.NewInt(0),
},
// The address 0xBBBB sstores 42 into slot 42.
bb: {
Code: []byte{
byte(vm.PUSH1), 0x42,
byte(vm.DUP1),
byte(vm.SSTORE),
byte(vm.STOP),
},
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
)

gspec.Config.BerlinBlock = common.Big0
gspec.Config.LondonBlock = common.Big0
gspec.Config.TerminalTotalDifficulty = common.Big0
gspec.Config.TerminalTotalDifficultyPassed = true
gspec.Config.ShanghaiTime = u64(0)
gspec.Config.CancunTime = u64(0)
gspec.Config.PragueTime = u64(0)
signer := types.LatestSigner(gspec.Config)

auth1, _ := types.SignAuth(&types.Authorization{
ChainID: new(big.Int).Set(gspec.Config.ChainID),
Address: aa,
Nonce: 1,
}, key1)

auth2, _ := types.SignAuth(&types.Authorization{
ChainID: new(big.Int),
Address: bb,
Nonce: 0,
}, key2)

_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
b.SetCoinbase(aa)
txdata := &types.SetCodeTx{
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
Nonce: 0,
To: addr1,
Gas: 500000,
GasFeeCap: uint256.MustFromBig(newGwei(5)),
GasTipCap: uint256.NewInt(2),
AuthList: []*types.Authorization{auth1, auth2},
}
tx := types.NewTx(txdata)
tx, err := types.SignTx(tx, signer, key1)
if err != nil {
t.Fatalf("%s", err)
}
b.AddTx(tx)
})
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
defer chain.Stop()
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}

var (
state, _ = chain.State()
fortyTwo = common.BytesToHash([]byte{0x42})
actual = state.GetState(addr2, fortyTwo)
)
if code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address); !bytes.Equal(code, want) {
t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
}
if code, want := state.GetCode(addr2), types.AddressToDelegation(auth2.Address); !bytes.Equal(code, want) {
t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
}
if actual.Cmp(fortyTwo) != 0 {
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
}
}
11 changes: 6 additions & 5 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,7 @@ func (s *stateObject) empty() bool {

// newObject creates a state object.
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
var (
origin = acct
created = acct == nil // true if the account was not existent
)
origin := acct
if acct == nil {
acct = types.NewEmptyStateAccount()
}
Expand All @@ -123,7 +120,6 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
originStorage: make(Storage),
pendingStorage: make(Storage),
dirtyStorage: make(Storage),
created: created,
}
}

Expand Down Expand Up @@ -300,6 +296,10 @@ func (s *stateObject) finalise(prefetch bool) {
if len(s.dirtyStorage) > 0 {
s.dirtyStorage = make(Storage)
}
// Revoke the flag at the end of the transaction. It finalizes the status
// of the newly-created object as it's no longer eligible for self-destruct
// by EIP-6780. For non-newly-created objects, it's a no-op.
s.created = false
}

// updateTrie is responsible for persisting cached storage changes into the
Expand Down Expand Up @@ -528,6 +528,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
obj.selfDestructed = s.selfDestructed
obj.dirtyCode = s.dirtyCode
obj.deleted = s.deleted
obj.created = s.created
return obj
}

Expand Down
47 changes: 45 additions & 2 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,23 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
return common.Hash{}
}

// GetState retrieves a value from the given account's storage trie.
func (s *StateDB) ResolveCode(addr common.Address) []byte {
stateObject := s.resolveStateObject(addr)
if stateObject != nil {
return stateObject.Code()
}
return nil
}

func (s *StateDB) ResolveCodeHash(addr common.Address) common.Hash {
stateObject := s.resolveStateObject(addr)
if stateObject != nil {
return common.BytesToHash(stateObject.CodeHash())
}
return common.Hash{}
}

// GetState retrieves the value associated with the specific key.
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := s.getStateObject(addr)
if stateObject != nil {
Expand Down Expand Up @@ -773,6 +789,18 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
return obj
}

func (s *StateDB) resolveStateObject(addr common.Address) *stateObject {
obj := s.getStateObject(addr)
if obj == nil {
return nil
}
addr, ok := types.ParseDelegation(obj.Code())
if !ok {
return obj
}
return s.getStateObject(addr)
}

func (s *StateDB) setStateObject(object *stateObject) {
s.stateObjects[object.Address()] = object
}
Expand Down Expand Up @@ -845,6 +873,18 @@ func (s *StateDB) CreateAccount(addr common.Address) {
}
}

// CreateContract is used whenever a contract is created. This may be preceded
// by CreateAccount, but that is not required if it already existed in the
// state due to funds sent beforehand.
// This operation sets the 'created'-flag, which is required in order to
// correctly handle EIP-6780 'delete-in-same-transaction' logic.
func (s *StateDB) CreateContract(addr common.Address) {
obj := s.getStateObject(addr)
if !obj.created {
obj.created = true
}
}

// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
func (s *StateDB) Copy() *StateDB {
Expand Down Expand Up @@ -1045,7 +1085,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
} else {
obj.finalise(true) // Prefetch slots in the background
}
obj.created = false
s.stateObjectsPending[addr] = struct{}{}
s.stateObjectsDirty[addr] = struct{}{}

Expand Down Expand Up @@ -1812,6 +1851,10 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
al.AddAddress(sender)
if dst != nil {
al.AddAddress(*dst)
// TODO: is this right?
if addr, ok := types.ParseDelegation(s.GetCode(*dst)); ok {
al.AddAddress(addr)
}
// If it's a create-tx, the destination will be added inside evm.create
}
for _, addr := range precompiles {
Expand Down
20 changes: 20 additions & 0 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,26 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
s.CreateAccount(addr)
},
},
{
name: "CreateContract",
fn: func(a testAction, s *StateDB) {
if !s.Exist(addr) {
s.CreateAccount(addr)
}
contractHash := s.GetCodeHash(addr)
emptyCode := contractHash == (common.Hash{}) || contractHash == types.EmptyCodeHash
storageRoot := s.GetStorageRoot(addr)
emptyStorage := storageRoot == (common.Hash{}) || storageRoot == types.EmptyRootHash
if s.GetNonce(addr) == 0 && emptyCode && emptyStorage {
s.CreateContract(addr)
// We also set some code here, to prevent the
// CreateContract action from being performed twice in a row,
// which would cause a difference in state when unrolling
// the journal.
s.SetCode(addr, []byte{1})
}
},
},
{
name: "SelfDestruct",
fn: func(a testAction, s *StateDB) {
Expand Down
Loading