Skip to content

Commit

Permalink
all: impl eip-7702
Browse files Browse the repository at this point in the history
  • Loading branch information
lightclient committed Jun 27, 2024
1 parent 27654d3 commit e28e0bc
Show file tree
Hide file tree
Showing 14 changed files with 517 additions and 14 deletions.
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 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
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 (
"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.PragueTime = u64(0)
signer := types.LatestSigner(gspec.Config)

_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
b.SetCoinbase(aa)
// One transaction to Coinbase

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

auth2, _ := types.SignAuth(&types.Authorization{
ChainID: new(big.Int).Set(gspec.Config.ChainID),
Address: bb,
Nonce: []uint64{0},
}, key2)

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).Hooks()}, 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 actual.Cmp(fortyTwo) != 0 {
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
}
if code := state.GetCode(addr1); code != nil {
t.Fatalf("addr1 code not cleared: got %s", common.Bytes2Hex(code))
}
if code := state.GetCode(addr2); code != nil {
t.Fatalf("addr2 code not cleared: got %s", common.Bytes2Hex(code))
}
}
30 changes: 29 additions & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ func (m *mutation) isDelete() bool {
return m.typ == deletion
}

type delegation struct {
code []byte
codeHash common.Hash
}

// StateDB structs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
Expand Down Expand Up @@ -140,6 +145,9 @@ type StateDB struct {
// Transient storage
transientStorage transientStorage

// Transient delegation of accounts to code
transientDelegation map[common.Address]delegation

// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal *journal
Expand Down Expand Up @@ -355,6 +363,9 @@ func (s *StateDB) TxIndex() int {
}

func (s *StateDB) GetCode(addr common.Address) []byte {
if d, ok := s.transientDelegation[addr]; ok {
return d.code
}
stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.Code()
Expand All @@ -363,6 +374,9 @@ func (s *StateDB) GetCode(addr common.Address) []byte {
}

func (s *StateDB) GetCodeSize(addr common.Address) int {
if d, ok := s.transientDelegation[addr]; ok {
return len(d.code)
}
stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.CodeSize()
Expand All @@ -371,6 +385,9 @@ func (s *StateDB) GetCodeSize(addr common.Address) int {
}

func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
if d, ok := s.transientDelegation[addr]; ok {
return d.codeHash
}
stateObject := s.getStateObject(addr)
if stateObject != nil {
return common.BytesToHash(stateObject.CodeHash())
Expand Down Expand Up @@ -1324,7 +1341,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
// - Reset access list (Berlin)
// - Add coinbase to access list (EIP-3651)
// - Reset transient storage (EIP-1153)
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList, authList []types.SetCodeDelegation) {
if rules.IsEIP2929 && rules.IsEIP4762 {
panic("eip2929 and eip4762 are both activated")
}
Expand Down Expand Up @@ -1353,6 +1370,17 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
}
// Reset transient storage at the beginning of transaction execution
s.transientStorage = newTransientStorage()

// Set temporary code delegations.
s.transientDelegation = nil
if authList != nil {
td := make(map[common.Address]delegation)
for _, auth := range authList {
td[auth.From] = delegation{s.GetCode(auth.Target), s.GetCodeHash(auth.Target)}
s.accessList.AddAddress(auth.From)
}
s.transientDelegation = td
}
}

// AddAddressToAccessList adds the given address to the access list
Expand Down
4 changes: 2 additions & 2 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,12 +426,12 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr

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

func TestProcessVerkle(t *testing.T) {
Expand Down
37 changes: 34 additions & 3 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (result *ExecutionResult) Revert() []byte {
}

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
func IntrinsicGas(data []byte, accessList types.AccessList, authList types.AuthorizationList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if isContractCreation && isHomestead {
Expand Down Expand Up @@ -114,6 +114,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
}
if authList != nil {
gas += uint64(len(authList)) * params.TxAuthTupleGas
}
return gas, nil
}

Expand Down Expand Up @@ -141,6 +144,7 @@ type Message struct {
AccessList types.AccessList
BlobGasFeeCap *big.Int
BlobHashes []common.Hash
AuthList types.AuthorizationList

// When SkipAccountChecks is true, the message nonce is not checked against the
// account nonce in state. It also disables checking that the sender is an EOA.
Expand All @@ -160,6 +164,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
Value: tx.Value(),
Data: tx.Data(),
AccessList: tx.AccessList(),
AuthList: tx.AuthList(),
SkipAccountChecks: false,
BlobHashes: tx.BlobHashes(),
BlobGasFeeCap: tx.BlobGasFeeCap(),
Expand Down Expand Up @@ -357,6 +362,7 @@ func (st *StateTransition) preCheck() error {
}
}
}

return st.buyGas()
}

Expand Down Expand Up @@ -394,7 +400,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
)

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

// Check authorizations list validity.
var delegations []types.SetCodeDelegation
if msg.AuthList != nil {
for _, auth := range msg.AuthList {
authority, err := auth.Authority()
if err != nil {
continue
}
var nonce *uint64
if len(auth.Nonce) > 1 {
return nil, fmt.Errorf("authorization must be either empty list or contain exactly one element")
}
if len(auth.Nonce) == 1 {
tmp := auth.Nonce[0]
nonce = &tmp
}
if nonce != nil {
if have := st.state.GetNonce(authority); have != *nonce {
continue
}
}
delegations = append(delegations, types.SetCodeDelegation{From: authority, Nonce: nonce, Target: auth.Address})
}
}

// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList, delegations)

var (
ret []byte
Expand Down
2 changes: 1 addition & 1 deletion core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
}
// Ensure the transaction has more gas than the bare minimum needed to cover
// the transaction metadata
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time))
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))
if err != nil {
return err
}
Expand Down
12 changes: 12 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
AccessListTxType = 0x01
DynamicFeeTxType = 0x02
BlobTxType = 0x03
SetCodeTxType = 0x04
)

// Transaction is an Ethereum transaction.
Expand Down Expand Up @@ -206,6 +207,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
inner = new(DynamicFeeTx)
case BlobTxType:
inner = new(BlobTx)
case SetCodeTxType:
inner = new(SetCodeTx)
default:
return nil, ErrTxTypeNotSupported
}
Expand Down Expand Up @@ -466,6 +469,15 @@ func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction {
return cpy
}

// AuthList returns the authorizations list of the transaction.
func (tx *Transaction) AuthList() AuthorizationList {
setcodetx, ok := tx.inner.(*SetCodeTx)
if !ok {
return nil
}
return setcodetx.AuthList
}

// SetTime sets the decoding time of a transaction. This is used by tests to set
// arbitrary times and by persistent transaction pools when loading old txs from
// disk.
Expand Down
Loading

0 comments on commit e28e0bc

Please sign in to comment.