Skip to content

Commit

Permalink
adds pre-evm-execution logic for set_code_tx
Browse files Browse the repository at this point in the history
  • Loading branch information
sudeepdino008 committed Jun 25, 2024
1 parent 518d5b8 commit 0173b95
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 242 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 @@ -825,18 +825,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
63 changes: 59 additions & 4 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package core

import (
"bytes"
"fmt"

"github.com/holiman/uint256"
Expand All @@ -28,6 +29,7 @@ import (
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 @@ -93,12 +95,13 @@ 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) {
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 @@ -108,7 +111,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 +339,61 @@ 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()
if len(auths) > 0 {
var b [33]byte
data := bytes.NewBuffer(nil)
for _, auth := range auths {

// 2. chainId check
if auth.ChainID != 0 && auth.ChainID != st.evm.ChainRules().ChainID.Uint64() {
continue
//TODO: should these errors be recorded somewhere? perhaps tracer or logs
//return nil, fmt.Errorf("invalid chainID %d", auth.ChainID)
}

// 1. authority recover
authorityPtr, err := auth.RecoverSigner(data, b[:])
if err != nil {
continue
//return nil, fmt.Errorf("authority recover failed")
}
authority := *authorityPtr

// 3. authority code should be empty
if codeHash := st.state.GetCodeHash(authority); codeHash != emptyCodeHash && codeHash != (libcommon.Hash{}) {
continue
//return nil, fmt.Errorf("authority code is not empty")
}

// 4. nonce check
if auth.Nonce != nil && st.state.GetNonce(authority) != auth.Nonce.Uint64() {
continue
//return nil, fmt.Errorf("invalid nonce")
}

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

// 6. add authority account to accesses_addresses
if !accessTuples.IsPresent(authority) {
accessTuples = append(accessTuples, types2.AccessTuple{Address: authority, StorageKeys: nil})
}

data.Reset()
}

}

// 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 +418,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)

var (
ret []byte
Expand Down
233 changes: 233 additions & 0 deletions core/types/authorization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package types

import (
"bytes"
"errors"
"fmt"
"io"

"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/length"
rlp2 "github.com/ledgerwatch/erigon-lib/rlp"
"github.com/ledgerwatch/erigon/common/u256"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
)

type Authorization struct {
ChainID uint64 `json:"chainId"`
Address libcommon.Address `json:"address"`
Nonce *uint256.Int `json:"nonce,omitempty"`
V uint256.Int `json:"v"`
R uint256.Int `json:"r"`
S uint256.Int `json:"s"`
}

func (ath *Authorization) copy() *Authorization {
cpy := &Authorization{
ChainID: ath.ChainID,
Address: ath.Address,
V: *ath.V.Clone(),
R: *ath.R.Clone(),
S: *ath.S.Clone(),
}

if ath.Nonce != nil {
cpy.Nonce = ath.Nonce.Clone()
}

return cpy
}

func (ath *Authorization) RecoverSigner(data *bytes.Buffer, b []byte) (*libcommon.Address, error) {
authLen := rlp2.U64Len(ath.ChainID)
authLen += (1 + length.Addr)
authLen += 1 // nonce
if ath.Nonce != nil {
authLen += rlp.Uint256LenExcludingHead(ath.Nonce)
}

if err := rlp.EncodeStringSizePrefix(authLen, data, b[:]); err != nil {

Check failure on line 52 in core/types/authorization.go

View workflow job for this annotation

GitHub Actions / tests (ubuntu-22.04)

unslice: could simplify b[:] to b (gocritic)
return nil, err
}

// chainId, address, nonce
if err := rlp.EncodeInt(ath.ChainID, data, b[:]); err != nil {

Check failure on line 57 in core/types/authorization.go

View workflow job for this annotation

GitHub Actions / tests (ubuntu-22.04)

unslice: could simplify b[:] to b (gocritic)
return nil, err
}

if err := rlp.EncodeAddress(&ath.Address, data, b[:]); err != nil {

Check failure on line 61 in core/types/authorization.go

View workflow job for this annotation

GitHub Actions / tests (ubuntu-22.04)

unslice: could simplify b[:] to b (gocritic)
return nil, err
}

if err := ath.Nonce.EncodeRLP(data); err != nil {
return nil, err
}

hashData := []byte{params.SetCodeMagicPrefix} //data.Bytes()
hashData = append(hashData, data.Bytes()...)
hash := crypto.Keccak256Hash(hashData)

var sig [65]byte
var arr [32]byte

ath.R.WriteToArray32(&arr)
copy(sig[:32], arr[:])

ath.S.WriteToArray32(&arr)
copy(sig[32:], arr[:])

if ath.V.Eq(u256.Num0) || ath.V.Eq(u256.Num1) {
sig[64] = byte(ath.V.Uint64())
} else {
return nil, fmt.Errorf("invalid V value: %d", ath.V.Uint64())
}

pubkey, err := crypto.Ecrecover(hash.Bytes(), sig[:])
if err != nil {
return nil, err
}

var authority libcommon.Address
copy(authority[:], crypto.Keccak256(pubkey[1:])[12:])
return &authority, nil
}

func authorizationsSize(authorizations []Authorization) int {
totalSize := 0
// ChainID uint64
// Address common.Address
// Nonce *uint256.Int
// V, R, S uint256.Int // signature values
for _, auth := range authorizations {
size := rlp2.U64Len(auth.ChainID) // chainId
size += 1 + length.Addr // address

size++
if auth.Nonce != nil {
size += rlp.Uint256LenExcludingHead(auth.Nonce)
}

size++
size += rlp.Uint256LenExcludingHead(&auth.V)

size++
size += rlp.Uint256LenExcludingHead(&auth.R)

size++
size += rlp.Uint256LenExcludingHead(&auth.S)

totalSize += size + rlp2.ListPrefixLen(size)
}
return totalSize
}

func decodeAuthorizations(auths *[]Authorization, s *rlp.Stream) error {
_, err := s.List()
if err != nil {
return fmt.Errorf("open authorizations: %w", err)
}
var b []byte
i := 0
for _, err = s.List(); err == nil; _, err = s.List() {
auth := Authorization{}
if auth.ChainID, err = s.Uint(); err != nil {
return err
}

if b, err = s.Bytes(); err != nil {
return err
}

if len(b) != 20 {
return fmt.Errorf("wrong size for Address: %d", len(b))
}
auth.Address = libcommon.BytesToAddress(b)

if b, err = s.Uint256Bytes(); err != nil {
return err
}
if len(b) > 0 {
auth.Nonce = new(uint256.Int).SetBytes(b)
}

if b, err = s.Uint256Bytes(); err != nil {
return err
}
auth.V.SetBytes(b)

if b, err = s.Uint256Bytes(); err != nil {
return err
}
auth.R.SetBytes(b)

if b, err = s.Uint256Bytes(); err != nil {
return err
}
auth.S.SetBytes(b)

*auths = append(*auths, auth)
// end of authorization
if err = s.ListEnd(); err != nil {
return fmt.Errorf("close Authorization: %w", err)
}
i++
}
if !errors.Is(err, rlp.EOL) {
return fmt.Errorf("open authorizations: %d %w", i, err)
}
if err = s.ListEnd(); err != nil {
return fmt.Errorf("close authorizations: %w", err)
}
return nil
}

func encodeAuthorizations(authorizations []Authorization, w io.Writer, b []byte) error {
// Authorization:
// ChainID uint64
// Address common.Address
// Nonce *uint256.Int
// V, R, S uint256.Int
for _, auth := range authorizations {
// 0. encode length of individual Authorization
authLen := rlp2.U64Len(auth.ChainID)
authLen += (1 + length.Addr)
authLen += 1 // nonce prefix
if auth.Nonce != nil {
authLen += rlp.Uint256LenExcludingHead(auth.Nonce)
}

authLen += (1 + rlp.Uint256LenExcludingHead(&auth.V)) + (1 + rlp.Uint256LenExcludingHead(&auth.R)) + (1 + rlp.Uint256LenExcludingHead(&auth.S))

if err := EncodeStructSizePrefix(authLen, w, b); err != nil {
return err
}

// 1. encode ChainId
if err := rlp.EncodeInt(auth.ChainID, w, b); err != nil {
return err
}
// 2. encode Address
if err := rlp.EncodeAddress(&auth.Address, w, b); err != nil {
return err
}
// 3. encode Nonce
if err := auth.Nonce.EncodeRLP(w); err != nil {
return err
}
// 4. encode V, R, S
if err := auth.V.EncodeRLP(w); err != nil {
return err
}
if err := auth.R.EncodeRLP(w); err != nil {
return err
}
if err := auth.S.EncodeRLP(w); err != nil {
return err
}
}

return nil
}
Loading

0 comments on commit 0173b95

Please sign in to comment.