Skip to content

Commit f22300e

Browse files
lightclientmarioevz
andcommitted
all: impl eip-7702
Co-authored-by: lightclient <[email protected]> Co-authored-by: Mario Vega <[email protected]>
1 parent 3aae627 commit f22300e

30 files changed

+1121
-71
lines changed

cmd/evm/internal/t8ntool/transaction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func Transaction(ctx *cli.Context) error {
133133
r.Address = sender
134134
}
135135
// Check intrinsic gas
136-
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
136+
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.AuthList(), tx.To() == nil,
137137
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0)); err != nil {
138138
r.Error = err
139139
results = append(results, r)

cmd/evm/staterunner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func runStateTest(fname string, cfg vm.Config, dump bool) error {
9191
}
9292
var testsByName map[string]tests.StateTest
9393
if err := json.Unmarshal(src, &testsByName); err != nil {
94-
return err
94+
return fmt.Errorf("unable to read test file: %w", err)
9595
}
9696

9797
// Iterate over all the tests, run them and aggregate the results

core/bench_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
8383
return func(i int, gen *BlockGen) {
8484
toaddr := common.Address{}
8585
data := make([]byte, nbytes)
86-
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
86+
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
8787
signer := gen.Signer()
8888
gasPrice := big.NewInt(0)
8989
if gen.header.BaseFee != nil {

core/error.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,7 @@ var (
112112

113113
// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
114114
ErrBlobTxCreate = errors.New("blob transaction of type create")
115+
116+
// ErrEmptyAuthList is returned if a set code transaction has an empty auth list.
117+
ErrEmptyAuthList = errors.New("set code transaction with empty auth list")
115118
)

core/setcode_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package core
2+
3+
import (
4+
"bytes"
5+
"math/big"
6+
"os"
7+
"testing"
8+
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/consensus/beacon"
11+
"github.com/ethereum/go-ethereum/core/rawdb"
12+
"github.com/ethereum/go-ethereum/core/types"
13+
"github.com/ethereum/go-ethereum/core/vm"
14+
"github.com/ethereum/go-ethereum/crypto"
15+
"github.com/ethereum/go-ethereum/eth/tracers/logger"
16+
"github.com/ethereum/go-ethereum/params"
17+
"github.com/holiman/uint256"
18+
)
19+
20+
func TestEIP7702(t *testing.T) {
21+
var (
22+
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
23+
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
24+
engine = beacon.NewFaker()
25+
26+
// A sender who makes transactions, has some funds
27+
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
28+
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
29+
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
30+
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
31+
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
32+
config = *params.AllEthashProtocolChanges
33+
gspec = &Genesis{
34+
Config: &config,
35+
Alloc: types.GenesisAlloc{
36+
addr1: {Balance: funds},
37+
addr2: {Balance: funds},
38+
// The address 0xAAAA sstores 1 into slot 2.
39+
aa: {
40+
Code: []byte{
41+
byte(vm.PC), // [0]
42+
byte(vm.DUP1), // [0,0]
43+
byte(vm.DUP1), // [0,0,0]
44+
byte(vm.DUP1), // [0,0,0,0]
45+
byte(vm.PUSH1), 0x01, // [0,0,0,0,1] (value)
46+
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],
47+
byte(vm.GAS),
48+
byte(vm.CALL),
49+
byte(vm.STOP),
50+
},
51+
Nonce: 0,
52+
Balance: big.NewInt(0),
53+
},
54+
// The address 0xBBBB sstores 42 into slot 42.
55+
bb: {
56+
Code: []byte{
57+
byte(vm.PUSH1), 0x42,
58+
byte(vm.DUP1),
59+
byte(vm.SSTORE),
60+
byte(vm.STOP),
61+
},
62+
Nonce: 0,
63+
Balance: big.NewInt(0),
64+
},
65+
},
66+
}
67+
)
68+
69+
gspec.Config.BerlinBlock = common.Big0
70+
gspec.Config.LondonBlock = common.Big0
71+
gspec.Config.TerminalTotalDifficulty = common.Big0
72+
gspec.Config.TerminalTotalDifficultyPassed = true
73+
gspec.Config.ShanghaiTime = u64(0)
74+
gspec.Config.CancunTime = u64(0)
75+
gspec.Config.PragueTime = u64(0)
76+
signer := types.LatestSigner(gspec.Config)
77+
78+
auth1, _ := types.SignAuth(&types.Authorization{
79+
ChainID: new(big.Int).Set(gspec.Config.ChainID),
80+
Address: aa,
81+
Nonce: 1,
82+
}, key1)
83+
84+
auth2, _ := types.SignAuth(&types.Authorization{
85+
ChainID: new(big.Int),
86+
Address: bb,
87+
Nonce: 0,
88+
}, key2)
89+
90+
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
91+
b.SetCoinbase(aa)
92+
txdata := &types.SetCodeTx{
93+
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
94+
Nonce: 0,
95+
To: addr1,
96+
Gas: 500000,
97+
GasFeeCap: uint256.MustFromBig(newGwei(5)),
98+
GasTipCap: uint256.NewInt(2),
99+
AuthList: []*types.Authorization{auth1, auth2},
100+
}
101+
tx := types.NewTx(txdata)
102+
tx, err := types.SignTx(tx, signer, key1)
103+
if err != nil {
104+
t.Fatalf("%s", err)
105+
}
106+
b.AddTx(tx)
107+
})
108+
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil)
109+
if err != nil {
110+
t.Fatalf("failed to create tester chain: %v", err)
111+
}
112+
defer chain.Stop()
113+
if n, err := chain.InsertChain(blocks); err != nil {
114+
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
115+
}
116+
117+
var (
118+
state, _ = chain.State()
119+
fortyTwo = common.BytesToHash([]byte{0x42})
120+
actual = state.GetState(addr2, fortyTwo)
121+
)
122+
if code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address); !bytes.Equal(code, want) {
123+
t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
124+
}
125+
if code, want := state.GetCode(addr2), types.AddressToDelegation(auth2.Address); !bytes.Equal(code, want) {
126+
t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
127+
}
128+
if actual.Cmp(fortyTwo) != 0 {
129+
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
130+
}
131+
}

core/state/statedb.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,22 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
373373
return common.Hash{}
374374
}
375375

376+
func (s *StateDB) ResolveCode(addr common.Address) []byte {
377+
stateObject := s.resolveStateObject(addr)
378+
if stateObject != nil {
379+
return stateObject.Code()
380+
}
381+
return nil
382+
}
383+
384+
func (s *StateDB) ResolveCodeHash(addr common.Address) common.Hash {
385+
stateObject := s.resolveStateObject(addr)
386+
if stateObject != nil {
387+
return common.BytesToHash(stateObject.CodeHash())
388+
}
389+
return common.Hash{}
390+
}
391+
376392
// GetState retrieves the value associated with the specific key.
377393
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
378394
stateObject := s.getStateObject(addr)
@@ -599,6 +615,18 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
599615
return obj
600616
}
601617

618+
func (s *StateDB) resolveStateObject(addr common.Address) *stateObject {
619+
obj := s.getStateObject(addr)
620+
if obj == nil {
621+
return nil
622+
}
623+
addr, ok := types.ParseDelegation(obj.Code())
624+
if !ok {
625+
return obj
626+
}
627+
return s.getStateObject(addr)
628+
}
629+
602630
func (s *StateDB) setStateObject(object *stateObject) {
603631
s.stateObjects[object.Address()] = object
604632
}
@@ -1335,6 +1363,10 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
13351363
al.AddAddress(sender)
13361364
if dst != nil {
13371365
al.AddAddress(*dst)
1366+
// TODO: is this right?
1367+
if addr, ok := types.ParseDelegation(s.GetCode(*dst)); ok {
1368+
al.AddAddress(addr)
1369+
}
13381370
// If it's a create-tx, the destination will be added inside evm.create
13391371
}
13401372
for _, addr := range precompiles {

core/state_processor_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,12 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
430430

431431
var (
432432
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
433-
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true)
433+
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true)
434434
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
435435
// will not contain that copied data.
436436
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
437437
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
438-
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, true, true, true, true)
438+
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true)
439439
)
440440

441441
func TestProcessVerkle(t *testing.T) {

core/state_transition.go

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package core
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"math"
2223
"math/big"
@@ -27,6 +28,7 @@ import (
2728
"github.com/ethereum/go-ethereum/core/types"
2829
"github.com/ethereum/go-ethereum/core/vm"
2930
"github.com/ethereum/go-ethereum/crypto/kzg4844"
31+
"github.com/ethereum/go-ethereum/crypto/secp256k1"
3032
"github.com/ethereum/go-ethereum/params"
3133
"github.com/holiman/uint256"
3234
)
@@ -68,7 +70,7 @@ func (result *ExecutionResult) Revert() []byte {
6870
}
6971

7072
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
71-
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
73+
func IntrinsicGas(data []byte, accessList types.AccessList, authList types.AuthorizationList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
7274
// Set the starting gas for the raw transaction
7375
var gas uint64
7476
if isContractCreation && isHomestead {
@@ -114,6 +116,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
114116
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
115117
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
116118
}
119+
if authList != nil {
120+
gas += uint64(len(authList)) * params.CallNewAccountGas
121+
}
117122
return gas, nil
118123
}
119124

@@ -141,6 +146,7 @@ type Message struct {
141146
AccessList types.AccessList
142147
BlobGasFeeCap *big.Int
143148
BlobHashes []common.Hash
149+
AuthList types.AuthorizationList
144150

145151
// When SkipNonceChecks is true, the message nonce is not checked against the
146152
// account nonce in state.
@@ -163,6 +169,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
163169
Value: tx.Value(),
164170
Data: tx.Data(),
165171
AccessList: tx.AccessList(),
172+
AuthList: tx.AuthList(),
166173
SkipNonceChecks: false,
167174
SkipFromEOACheck: false,
168175
BlobHashes: tx.BlobHashes(),
@@ -300,10 +307,10 @@ func (st *StateTransition) preCheck() error {
300307
}
301308
if !msg.SkipFromEOACheck {
302309
// Make sure the sender is an EOA
303-
codeHash := st.state.GetCodeHash(msg.From)
304-
if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash {
310+
code := st.state.GetCode(msg.From)
311+
if 0 < len(code) && !bytes.HasPrefix(code, []byte{0xef, 0x01, 0x00}) {
305312
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
306-
msg.From.Hex(), codeHash)
313+
msg.From.Hex(), st.state.GetCodeHash(msg.From))
307314
}
308315
}
309316
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
@@ -363,6 +370,27 @@ func (st *StateTransition) preCheck() error {
363370
}
364371
}
365372
}
373+
374+
// Check that auth list isn't empty.
375+
if msg.AuthList != nil && len(msg.AuthList) == 0 {
376+
return fmt.Errorf("%w: address %v", ErrEmptyAuthList, msg.From.Hex())
377+
}
378+
379+
// TODO: remove after this spec change is merged:
380+
// https://github.com/ethereum/EIPs/pull/8845
381+
if msg.AuthList != nil {
382+
var (
383+
secp256k1N = secp256k1.S256().Params().N
384+
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
385+
)
386+
for _, auth := range msg.AuthList {
387+
if auth.V.Cmp(common.Big1) > 0 || auth.S.Cmp(secp256k1halfN) > 0 {
388+
w := fmt.Errorf("set code transaction with invalid auth signature")
389+
return fmt.Errorf("%w: address %v", w, msg.From.Hex())
390+
}
391+
}
392+
}
393+
366394
return st.buyGas()
367395
}
368396

@@ -400,7 +428,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
400428
)
401429

402430
// Check clauses 4-5, subtract intrinsic gas if everything is correct
403-
gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
431+
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.AuthList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
404432
if err != nil {
405433
return nil, err
406434
}
@@ -439,15 +467,49 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
439467
// - reset transient storage(eip 1153)
440468
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
441469

470+
if !contractCreation {
471+
// Increment the nonce for the next transaction
472+
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
473+
}
474+
475+
// Check authorizations list validity.
476+
if msg.AuthList != nil {
477+
for _, auth := range msg.AuthList {
478+
// Verify chain ID is 0 or equal to current chain ID.
479+
if auth.ChainID.Sign() != 0 && st.evm.ChainConfig().ChainID.Cmp(auth.ChainID) != 0 {
480+
continue
481+
}
482+
authority, err := auth.Authority()
483+
if err != nil {
484+
continue
485+
}
486+
// Check the authority account 1) doesn't have code or has exisiting
487+
// delegation 2) matches the auth's nonce
488+
st.state.AddAddressToAccessList(authority)
489+
code := st.state.GetCode(authority)
490+
if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok {
491+
continue
492+
}
493+
if have := st.state.GetNonce(authority); have != auth.Nonce {
494+
continue
495+
}
496+
// If the account already exists in state, refund the new account cost
497+
// charged in the initrinsic calculation.
498+
if exists := st.state.Exist(authority); exists {
499+
st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas)
500+
}
501+
st.state.SetNonce(authority, auth.Nonce+1)
502+
st.state.SetCode(authority, types.AddressToDelegation(auth.Address))
503+
}
504+
}
505+
442506
var (
443507
ret []byte
444508
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
445509
)
446510
if contractCreation {
447511
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
448512
} else {
449-
// Increment the nonce for the next transaction
450-
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
451513
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
452514
}
453515

0 commit comments

Comments
 (0)