Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement EIP-7702: set code tx #12178

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading