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 #10812

Merged
merged 7 commits into from
Jul 12, 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 @@ -828,18 +828,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 @@ -395,87 +395,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
10 changes: 7 additions & 3 deletions core/state/intra_block_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,10 +803,9 @@ 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 sdb.trace {
fmt.Printf("ibs.Prepare %x, %x, %x, %x, %v, %v\n", sender, coinbase, dst, precompiles, list, rules)
fmt.Printf("ibs.Prepare %x, %x, %x, %x, %v, %v, %v\n", sender, coinbase, dst, precompiles, list, rules, authorities)
}
if rules.IsBerlin {
// Clear out any leftover from previous executions
Expand All @@ -833,6 +832,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 @@ -20,17 +20,21 @@
package core

import (
"bytes"
"fmt"

"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/log/v3"

"github.com/ledgerwatch/erigon-lib/common"
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/core/tracing"
"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 @@ -96,12 +100,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) {
sudeepdino008 marked this conversation as resolved.
Show resolved Hide resolved
// Zero and non-zero bytes are priced differently
dataLen := uint64(len(data))
dataNonZeroLen := uint64(0)
Expand All @@ -111,7 +117,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 @@ -339,9 +345,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()...)
Copy link
Collaborator

@AskAlexSharov AskAlexSharov Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks similar to slices.Clone()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, will remove it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops. Auto-merge was on...will put this in a new pr.

}

// set code tx
auths := msg.Authorizations()
verifiedAuthorities := make([]common.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
sudeepdino008 marked this conversation as resolved.
Show resolved Hide resolved

// 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 common.Address, coinbase common.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 @@ -366,7 +433,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
12 changes: 4 additions & 8 deletions core/types/access_list_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,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 @@ -453,13 +449,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 Expand Up @@ -507,7 +503,7 @@ func (tx *AccessListTx) GetChainID() *uint256.Int {
return tx.ChainID
}

func (tx *AccessListTx) cashedSender() (sender libcommon.Address, ok bool) {
func (tx *AccessListTx) cachedSender() (sender libcommon.Address, ok bool) {
s := tx.from.Load()
if s == nil {
return sender, false
Expand Down
Loading
Loading