Skip to content

Commit 955384a

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

22 files changed

+862
-35
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)

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/setcode_test.go

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

core/state/statedb.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ func (m *mutation) isDelete() bool {
7373
return m.typ == deletion
7474
}
7575

76+
type delegation struct {
77+
code []byte
78+
codeHash common.Hash
79+
}
80+
7681
// StateDB structs within the ethereum protocol are used to store anything
7782
// within the merkle trie. StateDBs take care of caching and storing
7883
// nested states. It's the general query interface to retrieve:
@@ -141,6 +146,9 @@ type StateDB struct {
141146
// Transient storage
142147
transientStorage transientStorage
143148

149+
// Transient delegation of accounts to code
150+
transientDelegation map[common.Address]delegation
151+
144152
// Journal of state modifications. This is the backbone of
145153
// Snapshot and RevertToSnapshot.
146154
journal *journal
@@ -319,6 +327,9 @@ func (s *StateDB) SubRefund(gas uint64) {
319327
// Exist reports whether the given account address exists in the state.
320328
// Notably this also returns true for self-destructed accounts.
321329
func (s *StateDB) Exist(addr common.Address) bool {
330+
if _, ok := s.transientDelegation[addr]; ok {
331+
return true
332+
}
322333
return s.getStateObject(addr) != nil
323334
}
324335

@@ -364,6 +375,9 @@ func (s *StateDB) TxIndex() int {
364375
}
365376

366377
func (s *StateDB) GetCode(addr common.Address) []byte {
378+
if d, ok := s.transientDelegation[addr]; ok {
379+
return d.code
380+
}
367381
stateObject := s.getStateObject(addr)
368382
if stateObject != nil {
369383
return stateObject.Code()
@@ -372,6 +386,9 @@ func (s *StateDB) GetCode(addr common.Address) []byte {
372386
}
373387

374388
func (s *StateDB) GetCodeSize(addr common.Address) int {
389+
if d, ok := s.transientDelegation[addr]; ok {
390+
return len(d.code)
391+
}
375392
stateObject := s.getStateObject(addr)
376393
if stateObject != nil {
377394
return stateObject.CodeSize()
@@ -380,6 +397,9 @@ func (s *StateDB) GetCodeSize(addr common.Address) int {
380397
}
381398

382399
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
400+
if d, ok := s.transientDelegation[addr]; ok {
401+
return d.codeHash
402+
}
383403
stateObject := s.getStateObject(addr)
384404
if stateObject != nil {
385405
return common.BytesToHash(stateObject.CodeHash())
@@ -1391,7 +1411,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
13911411
// - Reset access list (Berlin)
13921412
// - Add coinbase to access list (EIP-3651)
13931413
// - Reset transient storage (EIP-1153)
1394-
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
1414+
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList, authList []types.SetCodeDelegation) {
13951415
if rules.IsEIP2929 && rules.IsEIP4762 {
13961416
panic("eip2929 and eip4762 are both activated")
13971417
}
@@ -1420,6 +1440,17 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
14201440
}
14211441
// Reset transient storage at the beginning of transaction execution
14221442
s.transientStorage = newTransientStorage()
1443+
1444+
// Set temporary code delegations.
1445+
s.transientDelegation = nil
1446+
if authList != nil {
1447+
td := make(map[common.Address]delegation)
1448+
for _, auth := range authList {
1449+
td[auth.From] = delegation{s.GetCode(auth.Target), s.GetCodeHash(auth.Target)}
1450+
s.accessList.AddAddress(auth.From)
1451+
}
1452+
s.transientDelegation = td
1453+
}
14231454
}
14241455

14251456
// AddAddressToAccessList adds the given address to the access list

core/state_processor_test.go

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

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

437437
func TestProcessVerkle(t *testing.T) {

core/state_transition.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func (result *ExecutionResult) Revert() []byte {
6868
}
6969

7070
// 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) {
71+
func IntrinsicGas(data []byte, accessList types.AccessList, authList types.AuthorizationList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
7272
// Set the starting gas for the raw transaction
7373
var gas uint64
7474
if isContractCreation && isHomestead {
@@ -114,6 +114,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
114114
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
115115
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
116116
}
117+
if authList != nil {
118+
gas += uint64(len(authList)) * params.TxAuthTupleGas
119+
}
117120
return gas, nil
118121
}
119122

@@ -141,6 +144,7 @@ type Message struct {
141144
AccessList types.AccessList
142145
BlobGasFeeCap *big.Int
143146
BlobHashes []common.Hash
147+
AuthList types.AuthorizationList
144148

145149
// When SkipAccountChecks is true, the message nonce is not checked against the
146150
// account nonce in state. It also disables checking that the sender is an EOA.
@@ -160,6 +164,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
160164
Value: tx.Value(),
161165
Data: tx.Data(),
162166
AccessList: tx.AccessList(),
167+
AuthList: tx.AuthList(),
163168
SkipAccountChecks: false,
164169
BlobHashes: tx.BlobHashes(),
165170
BlobGasFeeCap: tx.BlobGasFeeCap(),
@@ -357,6 +362,7 @@ func (st *StateTransition) preCheck() error {
357362
}
358363
}
359364
}
365+
360366
return st.buyGas()
361367
}
362368

@@ -394,7 +400,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
394400
)
395401

396402
// Check clauses 4-5, subtract intrinsic gas if everything is correct
397-
gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
403+
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.AuthList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
398404
if err != nil {
399405
return nil, err
400406
}
@@ -428,10 +434,39 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
428434
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(msg.Data), params.MaxInitCodeSize)
429435
}
430436

437+
// Check authorizations list validity.
438+
var delegations []types.SetCodeDelegation
439+
if msg.AuthList != nil {
440+
seen := make(map[common.Address]bool)
441+
for _, auth := range msg.AuthList {
442+
authority, err := auth.Authority()
443+
if err != nil {
444+
continue
445+
}
446+
var nonce *uint64
447+
if len(auth.Nonce) > 1 {
448+
return nil, fmt.Errorf("authorization must be either empty list or contain exactly one element")
449+
}
450+
if len(auth.Nonce) == 1 {
451+
tmp := auth.Nonce[0]
452+
nonce = &tmp
453+
}
454+
if nonce != nil {
455+
if have := st.state.GetNonce(authority); have != *nonce {
456+
continue
457+
}
458+
}
459+
if _, ok := seen[authority]; !ok {
460+
seen[authority] = true
461+
delegations = append(delegations, types.SetCodeDelegation{From: authority, Nonce: nonce, Target: auth.Address})
462+
}
463+
}
464+
}
465+
431466
// Execute the preparatory steps for state transition which includes:
432467
// - prepare accessList(post-berlin)
433468
// - reset transient storage(eip 1153)
434-
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
469+
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList, delegations)
435470

436471
var (
437472
ret []byte

core/txpool/legacypool/legacypool.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ func New(config Config, chain BlockChain) *LegacyPool {
278278
// pool, specifically, whether it is a Legacy, AccessList or Dynamic transaction.
279279
func (pool *LegacyPool) Filter(tx *types.Transaction) bool {
280280
switch tx.Type() {
281-
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType:
281+
case types.SetCodeTxType, types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType:
282282
return true
283283
default:
284284
return false
@@ -610,7 +610,8 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
610610
Accept: 0 |
611611
1<<types.LegacyTxType |
612612
1<<types.AccessListTxType |
613-
1<<types.DynamicFeeTxType,
613+
1<<types.DynamicFeeTxType |
614+
1<<types.SetCodeTxType,
614615
MaxSize: txMaxSize,
615616
MinTip: pool.gasTip.Load().ToBig(),
616617
}

core/txpool/validation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
103103
}
104104
// Ensure the transaction has more gas than the bare minimum needed to cover
105105
// the transaction metadata
106-
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time))
106+
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.AuthList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time))
107107
if err != nil {
108108
return err
109109
}

0 commit comments

Comments
 (0)