Skip to content

Commit

Permalink
Implement EIP-7702: set code tx (#12178)
Browse files Browse the repository at this point in the history
Cherry pick #10812

Co-authored-by: sudeep <[email protected]>
  • Loading branch information
yperbasis and sudeepdino008 authored Oct 2, 2024
1 parent bc4bc69 commit 27783b8
Show file tree
Hide file tree
Showing 39 changed files with 9,714 additions and 259 deletions.
25 changes: 13 additions & 12 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,18 +824,19 @@ type callMsg struct {
ethereum.CallMsg
}

func (m callMsg) From() libcommon.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *libcommon.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *uint256.Int { return m.CallMsg.GasPrice }
func (m callMsg) FeeCap() *uint256.Int { return m.CallMsg.FeeCap }
func (m callMsg) Tip() *uint256.Int { return m.CallMsg.Tip }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *uint256.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() types2.AccessList { return m.CallMsg.AccessList }
func (m callMsg) IsFree() bool { return false }
func (m callMsg) From() libcommon.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *libcommon.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *uint256.Int { return m.CallMsg.GasPrice }
func (m callMsg) FeeCap() *uint256.Int { return m.CallMsg.FeeCap }
func (m callMsg) Tip() *uint256.Int { return m.CallMsg.Tip }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *uint256.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() types2.AccessList { return m.CallMsg.AccessList }
func (m callMsg) Authorizations() []types.Authorization { return m.CallMsg.Authorizations }
func (m callMsg) IsFree() bool { return false }

func (m callMsg) BlobGas() uint64 { return misc.GetBlobGasUsed(len(m.CallMsg.BlobHashes)) }
func (m callMsg) MaxFeePerBlobGas() *uint256.Int { return m.CallMsg.MaxFeePerBlobGas }
Expand Down
78 changes: 41 additions & 37 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,87 +390,91 @@ func getTransaction(txJson jsonrpc.RPCTransaction) (types.Transaction, error) {
var chainId *uint256.Int

if txJson.Value != nil {
value, overflow = uint256.FromBig((*big.Int)(txJson.Value))
value, overflow = uint256.FromBig(txJson.Value.ToInt())
if overflow {
return nil, fmt.Errorf("value field caused an overflow (uint256)")
}
}

if txJson.GasPrice != nil {
gasPrice, overflow = uint256.FromBig((*big.Int)(txJson.GasPrice))
gasPrice, overflow = uint256.FromBig(txJson.GasPrice.ToInt())
if overflow {
return nil, fmt.Errorf("gasPrice field caused an overflow (uint256)")
}
}

if txJson.ChainID != nil {
chainId, overflow = uint256.FromBig((*big.Int)(txJson.ChainID))
chainId, overflow = uint256.FromBig(txJson.ChainID.ToInt())
if overflow {
return nil, fmt.Errorf("chainId field caused an overflow (uint256)")
}
}

switch txJson.Type {
case types.LegacyTxType, types.AccessListTxType:
var toAddr = libcommon.Address{}
if txJson.To != nil {
toAddr = *txJson.To
}
legacyTx := types.NewTransaction(uint64(txJson.Nonce), toAddr, value, uint64(txJson.Gas), gasPrice, txJson.Input)
legacyTx.V.SetFromBig(txJson.V.ToInt())
legacyTx.S.SetFromBig(txJson.S.ToInt())
legacyTx.R.SetFromBig(txJson.R.ToInt())
commonTx := types.CommonTx{
Nonce: uint64(txJson.Nonce),
To: txJson.To,
Value: value,
Gas: uint64(txJson.Gas),
Data: txJson.Input,
}

if txJson.Type == types.AccessListTxType {
accessListTx := types.AccessListTx{
LegacyTx: *legacyTx,
ChainID: chainId,
AccessList: *txJson.Accesses,
}
commonTx.V.SetFromBig(txJson.V.ToInt())
commonTx.R.SetFromBig(txJson.R.ToInt())
commonTx.S.SetFromBig(txJson.S.ToInt())
if txJson.Type == types.LegacyTxType || txJson.Type == types.AccessListTxType {
legacyTx := types.LegacyTx{
CommonTx: commonTx,
GasPrice: gasPrice,
}

return &accessListTx, nil
} else {
return legacyTx, nil
if txJson.Type == types.LegacyTxType {
return &legacyTx, nil
}

case types.DynamicFeeTxType:
return &types.AccessListTx{
LegacyTx: legacyTx,
ChainID: chainId,
AccessList: *txJson.Accesses,
}, nil
} else if txJson.Type == types.DynamicFeeTxType || txJson.Type == types.SetCodeTxType {
var tip *uint256.Int
var feeCap *uint256.Int
if txJson.Tip != nil {
tip, overflow = uint256.FromBig((*big.Int)(txJson.Tip))
tip, overflow = uint256.FromBig(txJson.Tip.ToInt())
if overflow {
return nil, fmt.Errorf("maxPriorityFeePerGas field caused an overflow (uint256)")
}
}

if txJson.FeeCap != nil {
feeCap, overflow = uint256.FromBig((*big.Int)(txJson.FeeCap))
feeCap, overflow = uint256.FromBig(txJson.FeeCap.ToInt())
if overflow {
return nil, fmt.Errorf("maxFeePerGas field caused an overflow (uint256)")
}
}

dynamicFeeTx := types.DynamicFeeTransaction{
CommonTx: types.CommonTx{
Nonce: uint64(txJson.Nonce),
To: txJson.To,
Value: value,
Gas: uint64(txJson.Gas),
Data: txJson.Input,
},
CommonTx: commonTx,
ChainID: chainId,
Tip: tip,
FeeCap: feeCap,
AccessList: *txJson.Accesses,
}

dynamicFeeTx.V.SetFromBig(txJson.V.ToInt())
dynamicFeeTx.S.SetFromBig(txJson.S.ToInt())
dynamicFeeTx.R.SetFromBig(txJson.R.ToInt())
if txJson.Type == types.DynamicFeeTxType {
return &dynamicFeeTx, nil
}

return &dynamicFeeTx, nil
auths := make([]types.Authorization, 0)
for _, auth := range *txJson.Authorizations {
auths = append(auths, auth.ToAuthorization())
}

default:
return &types.SetCodeTransaction{
DynamicFeeTransaction: dynamicFeeTx,
Authorizations: auths,
}, nil
} else {
return nil, nil
}
}
Expand Down
8 changes: 6 additions & 2 deletions core/state/intra_block_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,8 +764,7 @@ func (sdb *IntraBlockState) clearJournalAndRefund() {
// Cancun fork:
// - Reset transient storage (EIP-1153)
func (sdb *IntraBlockState) Prepare(rules *chain.Rules, sender, coinbase libcommon.Address, dst *libcommon.Address,
precompiles []libcommon.Address, list types2.AccessList,
) {
precompiles []libcommon.Address, list types2.AccessList, authorities []libcommon.Address) {
if rules.IsBerlin {
// Clear out any leftover from previous executions
al := newAccessList()
Expand All @@ -789,6 +788,11 @@ func (sdb *IntraBlockState) Prepare(rules *chain.Rules, sender, coinbase libcomm
al.AddAddress(coinbase)
}
}
if rules.IsPrague {
for _, addr := range authorities {
sdb.accessList.AddAddress(addr)
}
}
// Reset transient storage at the beginning of transaction execution
sdb.transientStorage = newTransientStorage()
}
Expand Down
75 changes: 71 additions & 4 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
package core

import (
"bytes"
"fmt"

"github.com/holiman/uint256"
"github.com/ledgerwatch/log/v3"

libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/txpool/txpoolcfg"
types2 "github.com/ledgerwatch/erigon-lib/types"

cmath "github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/common/u256"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
Expand Down Expand Up @@ -92,12 +96,14 @@ type Message interface {
Data() []byte
AccessList() types2.AccessList
BlobHashes() []libcommon.Hash
Authorizations() []types.Authorization

IsFree() bool
}

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types2.AccessList, isContractCreation bool, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
// TODO: convert the input to a struct
func IntrinsicGas(data []byte, accessList types2.AccessList, isContractCreation bool, isHomestead, isEIP2028, isEIP3860 bool, authorizationsLen uint64) (uint64, error) {
// Zero and non-zero bytes are priced differently
dataLen := uint64(len(data))
dataNonZeroLen := uint64(0)
Expand All @@ -107,7 +113,7 @@ func IntrinsicGas(data []byte, accessList types2.AccessList, isContractCreation
}
}

gas, status := txpoolcfg.CalcIntrinsicGas(dataLen, dataNonZeroLen, accessList, isContractCreation, isHomestead, isEIP2028, isEIP3860)
gas, status := txpoolcfg.CalcIntrinsicGas(dataLen, dataNonZeroLen, authorizationsLen, accessList, isContractCreation, isHomestead, isEIP2028, isEIP3860)
if status != txpoolcfg.Success {
return 0, ErrGasUintOverflow
}
Expand Down Expand Up @@ -336,9 +342,70 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*evmtype
rules := st.evm.ChainRules()
vmConfig := st.evm.Config()
isEIP3860 := vmConfig.HasEip3860(rules)
accessTuples := make(types2.AccessList, 0)
if msg.AccessList() != nil {
accessTuples = append(accessTuples, msg.AccessList()...)
}

// set code tx
auths := msg.Authorizations()
verifiedAuthorities := make([]libcommon.Address, 0)
if len(auths) > 0 {
var b [33]byte
data := bytes.NewBuffer(nil)
for i, auth := range auths {
data.Reset()

// 1. authority recover
authorityPtr, err := auth.RecoverSigner(data, b[:])
if err != nil {
log.Debug("authority recover failed, skipping", "err", err, "auth index", i)
continue
}
authority := *authorityPtr

// 2. chainId check
if auth.ChainID != nil && auth.ChainID.Uint64() != 0 && auth.ChainID.Uint64() != st.evm.ChainRules().ChainID.Uint64() {
log.Debug("invalid chainID, skipping", "chainId", auth.ChainID, "auth index", i)
continue
}

// 3. authority code should be empty
if codeHash := st.state.GetCodeHash(authority); codeHash != emptyCodeHash && codeHash != (libcommon.Hash{}) {
log.Debug("authority code is not empty, skipping", "auth index", i)
continue
}

// 4. nonce check
if len(auth.Nonce) > 0 && st.state.GetNonce(authority) != auth.Nonce[0] {
log.Debug("invalid nonce, skipping", "auth index", i)
continue
}

// 5. set code of authority to code associated with address
st.state.SetCode(authority, st.state.GetCode(auth.Address))

// 6. add authority account to accesses_addresses
verifiedAuthorities = append(verifiedAuthorities, authority)
// authority is added to accessed_address in prepare step
}
}

if len(verifiedAuthorities) > 0 {
oldPostApplyMessage := st.evm.Context.PostApplyMessage
st.evm.Context.PostApplyMessage = func(ibs evmtypes.IntraBlockState, sender libcommon.Address, coinbase libcommon.Address, result *evmtypes.ExecutionResult) {
for _, authority := range verifiedAuthorities {
ibs.SetCode(authority, nil)
}

if oldPostApplyMessage != nil {
oldPostApplyMessage(ibs, sender, coinbase, result)
}
}
}

// Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, isEIP3860)
gas, err := IntrinsicGas(st.data, accessTuples, contractCreation, rules.IsHomestead, rules.IsIstanbul, isEIP3860, uint64(len(auths)))
if err != nil {
return nil, err
}
Expand All @@ -363,7 +430,7 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*evmtype
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
st.state.Prepare(rules, msg.From(), coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
st.state.Prepare(rules, msg.From(), coinbase, msg.To(), vm.ActivePrecompiles(rules), accessTuples, verifiedAuthorities)

var (
ret []byte
Expand Down
10 changes: 3 additions & 7 deletions core/types/access_list_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,7 @@ func encodeAccessList(al types2.AccessList, w io.Writer, b []byte) error {
if err := EncodeStructSizePrefix(tupleLen, w, b); err != nil {
return err
}
b[0] = 128 + 20
if _, err := w.Write(b[:1]); err != nil {
return err
}
if _, err := w.Write(tuple.Address.Bytes()); err != nil {
if err := rlp.EncodeOptionalAddress(&tuple.Address, w, b); err != nil {
return err
}
if err := EncodeStructSizePrefix(storageLen, w, b); err != nil {
Expand Down Expand Up @@ -450,13 +446,13 @@ func (tx *AccessListTx) WithSignature(signer Signer, sig []byte) (Transaction, e
cpy.ChainID = signer.ChainID()
return cpy, nil
}
func (tx *AccessListTx) FakeSign(address libcommon.Address) (Transaction, error) {
func (tx *AccessListTx) FakeSign(address libcommon.Address) Transaction {
cpy := tx.copy()
cpy.R.Set(u256.Num1)
cpy.S.Set(u256.Num1)
cpy.V.Set(u256.Num4)
cpy.from.Store(address)
return cpy, nil
return cpy
}

// Hash computes the hash (but not for signatures!)
Expand Down
Loading

0 comments on commit 27783b8

Please sign in to comment.