Skip to content

Commit 9d3fd67

Browse files
core: state_transition with AuthList (#38)
* core: state_transition with AuthList * core: statedb SetCode() returning prev code * fixup! core: state_transition with AuthList * fixup! core: statedb SetCode() returning prev code
1 parent d5d44d2 commit 9d3fd67

File tree

15 files changed

+279
-49
lines changed

15 files changed

+279
-49
lines changed

accounts/abi/bind/backends/simulated.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -801,17 +801,18 @@ type callMsg struct {
801801
ethereum.CallMsg
802802
}
803803

804-
func (m callMsg) From() common.Address { return m.CallMsg.From }
805-
func (m callMsg) Nonce() uint64 { return 0 }
806-
func (m callMsg) IsFake() bool { return true }
807-
func (m callMsg) To() *common.Address { return m.CallMsg.To }
808-
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
809-
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
810-
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
811-
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
812-
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
813-
func (m callMsg) Data() []byte { return m.CallMsg.Data }
814-
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
804+
func (m callMsg) From() common.Address { return m.CallMsg.From }
805+
func (m callMsg) Nonce() uint64 { return 0 }
806+
func (m callMsg) IsFake() bool { return true }
807+
func (m callMsg) To() *common.Address { return m.CallMsg.To }
808+
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
809+
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
810+
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
811+
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
812+
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
813+
func (m callMsg) Data() []byte { return m.CallMsg.Data }
814+
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
815+
func (m callMsg) AuthList() []types.Authorization { return nil }
815816

816817
// FIXME: support sponsored transaction in callMsg
817818
func (m callMsg) Payer() common.Address { return m.CallMsg.From }

cmd/evm/internal/t8ntool/transaction.go

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

core/bench_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
8686
return func(i int, gen *BlockGen) {
8787
toaddr := common.Address{}
8888
data := make([]byte, nbytes)
89-
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
89+
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
9090
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
9191
gasPrice := big.NewInt(0)
9292
if gen.header.BaseFee != nil {

core/blockchain_test.go

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

1919
import (
20+
"bytes"
2021
"errors"
2122
"fmt"
2223
"io/ioutil"
@@ -30,6 +31,7 @@ import (
3031
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
3132
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
3233
"github.com/ethereum/go-ethereum/common/math"
34+
"github.com/ethereum/go-ethereum/core/vm/program"
3335
"github.com/ethereum/go-ethereum/crypto/kzg4844"
3436
"github.com/ethereum/go-ethereum/eth/tracers/logger"
3537
lru "github.com/hashicorp/golang-lru/v2"
@@ -3442,7 +3444,7 @@ func testEIP2718Transition(t *testing.T, scheme string) {
34423444

34433445
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
34443446
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
3445-
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
3447+
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
34463448
if block.GasUsed() != expected {
34473449
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
34483450

@@ -3545,7 +3547,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {
35453547

35463548
// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
35473549
expectedGas := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
3548-
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
3550+
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
35493551
if block.GasUsed() != expectedGas {
35503552
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
35513553
}
@@ -4739,3 +4741,95 @@ func TestDeleteThenCreate(t *testing.T) {
47394741
}
47404742
}
47414743
}
4744+
4745+
// TestEIP7702 deploys two delegation designations and calls them. It writes one
4746+
// value to storage which is verified after.
4747+
func TestEIP7702(t *testing.T) {
4748+
var (
4749+
config = params.TestChainConfig
4750+
signer = types.LatestSigner(config)
4751+
engine = ethash.NewFaker()
4752+
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
4753+
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
4754+
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
4755+
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
4756+
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
4757+
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
4758+
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
4759+
)
4760+
gspec := &Genesis{
4761+
Config: config,
4762+
Alloc: GenesisAlloc{
4763+
addr1: {Balance: funds},
4764+
addr2: {Balance: funds},
4765+
aa: { // The address 0xAAAA calls into addr2
4766+
Code: program.New().Call(nil, addr2, 1, 0, 0, 0, 0).Bytes(),
4767+
Nonce: 0,
4768+
Balance: big.NewInt(0),
4769+
},
4770+
bb: { // The address 0xBBBB sstores 42 into slot 42.
4771+
Code: program.New().Sstore(0x42, 0x42).Bytes(),
4772+
Nonce: 0,
4773+
Balance: big.NewInt(0),
4774+
},
4775+
},
4776+
}
4777+
4778+
// Sign authorization tuples.
4779+
// The way the auths are combined, it becomes
4780+
// 1. tx -> addr1 which is delegated to 0xaaaa
4781+
// 2. addr1:0xaaaa calls into addr2:0xbbbb
4782+
// 3. addr2:0xbbbb writes to storage
4783+
auth1, _ := types.SignAuth(types.Authorization{
4784+
ChainID: gspec.Config.ChainID.Uint64(),
4785+
Address: aa,
4786+
Nonce: 1,
4787+
}, key1)
4788+
auth2, _ := types.SignAuth(types.Authorization{
4789+
ChainID: 0,
4790+
Address: bb,
4791+
Nonce: 0,
4792+
}, key2)
4793+
4794+
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
4795+
b.SetCoinbase(aa)
4796+
txdata := &types.SetCodeTx{
4797+
ChainID: gspec.Config.ChainID.Uint64(),
4798+
Nonce: 0,
4799+
To: addr1,
4800+
Gas: 500000,
4801+
GasFeeCap: uint256.MustFromBig(newGwei(5)),
4802+
GasTipCap: uint256.NewInt(2),
4803+
AuthList: []types.Authorization{auth1, auth2},
4804+
}
4805+
tx := types.MustSignNewTx(key1, signer, txdata)
4806+
b.AddTx(tx)
4807+
})
4808+
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
4809+
if err != nil {
4810+
t.Fatalf("failed to create tester chain: %v", err)
4811+
}
4812+
defer chain.Stop()
4813+
if n, err := chain.InsertChain(blocks, nil); err != nil {
4814+
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
4815+
}
4816+
4817+
// Verify delegation designations were deployed.
4818+
state, _ := chain.State()
4819+
code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address)
4820+
if !bytes.Equal(code, want) {
4821+
t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
4822+
}
4823+
code, want = state.GetCode(addr2), types.AddressToDelegation(auth2.Address)
4824+
if !bytes.Equal(code, want) {
4825+
t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
4826+
}
4827+
// Verify delegation executed the correct code.
4828+
var (
4829+
fortyTwo = common.BytesToHash([]byte{0x42})
4830+
actual = state.GetState(addr2, fortyTwo)
4831+
)
4832+
if actual.Cmp(fortyTwo) != 0 {
4833+
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
4834+
}
4835+
}

core/error.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
// Copyright 2014 The go-ethereum Authors
32
// This file is part of the go-ethereum library.
43
//
@@ -131,4 +130,20 @@ var (
131130

132131
// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
133132
ErrBlobTxCreate = errors.New("blob transaction of type create")
133+
134+
// -- EIP-7702 errors --
135+
136+
// Message validation errors:
137+
ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list")
138+
ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract")
139+
)
140+
141+
// EIP-7702 state transition errors.
142+
// Note these are just informational, and do not cause tx execution abort.
143+
var (
144+
ErrAuthorizationWrongChainID = errors.New("EIP-7702 authorization chain ID mismatch")
145+
ErrAuthorizationNonceOverflow = errors.New("EIP-7702 authorization nonce > 64 bit")
146+
ErrAuthorizationInvalidSignature = errors.New("EIP-7702 authorization has invalid signature")
147+
ErrAuthorizationDestinationHasCode = errors.New("EIP-7702 authorization destination is a contract")
148+
ErrAuthorizationNonceMismatch = errors.New("EIP-7702 authorization nonce does not match current account nonce")
134149
)

core/state/state_object.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,14 +569,15 @@ func (s *stateObject) CodeSize(db Database) int {
569569
return size
570570
}
571571

572-
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
572+
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) []byte {
573573
prevcode := s.Code()
574574
s.db.journal.append(codeChange{
575575
account: &s.address,
576576
prevhash: s.CodeHash(),
577577
prevcode: prevcode,
578578
})
579579
s.setCode(codeHash, code)
580+
return prevcode
580581
}
581582

582583
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {

core/state/statedb.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,11 +435,12 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
435435
}
436436
}
437437

438-
func (s *StateDB) SetCode(addr common.Address, code []byte) {
438+
func (s *StateDB) SetCode(addr common.Address, code []byte) []byte {
439439
stateObject := s.GetOrNewStateObject(addr)
440440
if stateObject != nil {
441-
stateObject.SetCode(crypto.Keccak256Hash(code), code)
441+
return stateObject.SetCode(crypto.Keccak256Hash(code), code)
442442
}
443+
return nil
443444
}
444445

445446
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {

core/state_processor_test.go

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,23 @@ import (
4646
func TestStateProcessorErrors(t *testing.T) {
4747
var (
4848
config = &params.ChainConfig{
49-
ChainID: big.NewInt(1),
50-
HomesteadBlock: big.NewInt(0),
51-
EIP150Block: big.NewInt(0),
52-
EIP155Block: big.NewInt(0),
53-
EIP158Block: big.NewInt(0),
54-
ByzantiumBlock: big.NewInt(0),
55-
ConstantinopleBlock: big.NewInt(0),
56-
PetersburgBlock: big.NewInt(0),
57-
IstanbulBlock: big.NewInt(0),
58-
MuirGlacierBlock: big.NewInt(0),
59-
BerlinBlock: big.NewInt(0),
60-
LondonBlock: big.NewInt(0),
61-
Ethash: new(params.EthashConfig),
49+
ChainID: big.NewInt(1),
50+
HomesteadBlock: big.NewInt(0),
51+
EIP150Block: big.NewInt(0),
52+
EIP155Block: big.NewInt(0),
53+
EIP158Block: big.NewInt(0),
54+
ByzantiumBlock: big.NewInt(0),
55+
ConstantinopleBlock: big.NewInt(0),
56+
PetersburgBlock: big.NewInt(0),
57+
IstanbulBlock: big.NewInt(0),
58+
MuirGlacierBlock: big.NewInt(0),
59+
BerlinBlock: big.NewInt(0),
60+
LondonBlock: big.NewInt(0),
61+
Ethash: new(params.EthashConfig),
62+
TerminalTotalDifficulty: big.NewInt(0),
63+
ShanghaiBlock: big.NewInt(0),
64+
CancunBlock: big.NewInt(0),
65+
PragueBlock: big.NewInt(0),
6266
}
6367
signer = types.LatestSigner(config)
6468
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
@@ -90,6 +94,21 @@ func TestStateProcessorErrors(t *testing.T) {
9094
}), signer, key1)
9195
return tx
9296
}
97+
var mkSetCodeTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, authlist []types.Authorization) *types.Transaction {
98+
tx, err := types.SignTx(types.NewTx(&types.SetCodeTx{
99+
Nonce: nonce,
100+
GasTipCap: uint256.MustFromBig(gasTipCap),
101+
GasFeeCap: uint256.MustFromBig(gasFeeCap),
102+
Gas: gasLimit,
103+
To: to,
104+
Value: new(uint256.Int),
105+
AuthList: authlist,
106+
}), signer, key1)
107+
if err != nil {
108+
t.Fatal(err)
109+
}
110+
return tx
111+
}
93112
{ // Tests against a 'recent' chain definition
94113
var (
95114
db = rawdb.NewMemoryDatabase()
@@ -205,6 +224,12 @@ func TestStateProcessorErrors(t *testing.T) {
205224
},
206225
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
207226
},
227+
{ // ErrEmptyAuthList
228+
txs: []*types.Transaction{
229+
mkSetCodeTx(0, common.Address{}, params.TxGas, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), nil),
230+
},
231+
want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
232+
},
208233
} {
209234
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
210235
_, err := blockchain.InsertChain(types.Blocks{block}, nil)
@@ -293,7 +318,7 @@ func TestStateProcessorErrors(t *testing.T) {
293318
txs: []*types.Transaction{
294319
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
295320
},
296-
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
321+
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, len(code): 4",
297322
},
298323
} {
299324
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)

0 commit comments

Comments
 (0)