Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
06e4318
Bump onflow/go-ethereum dependency to v1.14.9
m-Peter Mar 17, 2025
bd143ea
Replace SkipAccountChecks flag with newly-added SkipNonceChecks & Ski…
m-Peter Mar 17, 2025
f4984b3
Bump onflow/go-ethereum dependency to v1.14.10
m-Peter Mar 18, 2025
0051e2a
Bump onflow/go-ethereum dependency to v1.14.11
m-Peter Mar 19, 2025
c9e8a7c
Bump onflow/go-ethereum dependency to v1.14.12
m-Peter Mar 22, 2025
e6846a9
Update StateDB methods to conform to StateDB Geth interface
m-Peter Mar 22, 2025
7118700
Bump onflow/go-ethereum dependency to v1.14.13
m-Peter Mar 24, 2025
3e4f48e
Bump onflow/go-ethereum dependency to v1.15.0
m-Peter Mar 27, 2025
46ae92d
Update breaking changes to StateDB interface
m-Peter Mar 27, 2025
a43e5ec
Bump onflow/go-ethereum dependency to v1.15.1
m-Peter Mar 30, 2025
b2135ec
Bump onflow/go-ethereum dependency to v1.15.2
m-Peter Mar 31, 2025
0a1b938
Update creation of LogsBloom for EVM tx receipts
m-Peter Mar 31, 2025
fbc8c10
Bump onflow/go-ethereum dependency to v1.15.3
m-Peter Mar 31, 2025
024f2e1
Update breaking changes in COA contract deployments
m-Peter Mar 31, 2025
34a46fc
Enable EVM Pectra hard-fork
m-Peter Apr 2, 2025
4954acb
Bump onflow/go-ethereum dependency to v1.15.4
m-Peter Apr 3, 2025
12c14c9
Bump onflow/go-ethereum dependency to v1.15.5
m-Peter Apr 4, 2025
3da12b9
Bump onflow/go-ethereum dependency to v1.15.6
m-Peter Apr 4, 2025
5490f42
Bump onflow/go-ethereum dependency to v1.15.7
m-Peter Apr 4, 2025
dbf6009
Merge branch 'feature/pectra-upgrade' into mpeter/update-go-ethereum-…
m-Peter Apr 9, 2025
53c44ce
Update function description for SetState on DeltaView type
m-Peter Apr 9, 2025
05b8ee2
Update SelfDestruct6780 to fetch current balance before the self-dest…
m-Peter Apr 9, 2025
a69d18c
Remove panic from newly-added Finalise on StateDB and call it on each…
m-Peter Apr 9, 2025
de0a491
Remove comments about the deprecated SkipAccountChecks EVM message field
m-Peter Apr 9, 2025
0fbe41d
Set the TxContext on EVM by calling SetTxContext and passing the conf…
m-Peter Apr 9, 2025
40cf351
Update StateDB.SetCode method to return the previous code of an address
m-Peter Apr 9, 2025
5cc3dad
Remove duplicated import of Geth tracing package
m-Peter Apr 9, 2025
f171919
Remove duplicated import of Geth tracing package on StateDB
m-Peter Apr 9, 2025
60b7938
Fix SelfDestruct6780 to return proper values for contract self-destruct
m-Peter Apr 9, 2025
09f0329
Fix comment for AddBalance of StateDB
m-Peter Apr 9, 2025
7e2a29e
Bump onflow/go-ethereum dependency to tag v1.15.7
m-Peter Apr 10, 2025
d5fdbe3
Allow toggling Prague hard-fork via BlockContext IsPrague field
m-Peter Apr 10, 2025
2ba6b6a
Bump onflow/go-ethereum dependency to v1.15.8
m-Peter Apr 17, 2025
3c90a8c
Update comment and return value for StateDB.AccessEvents method
m-Peter Apr 17, 2025
6afd9c0
Bump onflow/go-ethereum dependency to tag v1.15.8
m-Peter Apr 18, 2025
1435ec5
Add comment describing the new Finalise method from StateDB interface
m-Peter Apr 21, 2025
ebd7db9
Bump onflow/go-ethereum dependency to v1.15.9
m-Peter Apr 22, 2025
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
2 changes: 1 addition & 1 deletion fvm/evm/emulator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func MakeChainConfig(chainID *big.Int) *gethParams.ChainConfig {
// Fork scheduling based on timestamps
ShanghaiTime: &zero, // already on Shanghai
CancunTime: &zero, // already on Cancun
PragueTime: nil, // not on Prague
PragueTime: &zero, // already on Prague
VerkleTime: nil, // not on Verkle
}
}
Expand Down
55 changes: 36 additions & 19 deletions fvm/evm/emulator/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,13 @@ func (em *Emulator) NewReadOnlyBlockView(ctx types.BlockContext) (types.ReadOnly

// NewBlockView constructs a new block view (mutable)
func (em *Emulator) NewBlockView(ctx types.BlockContext) (types.BlockView, error) {
cfg := newConfig(ctx)
if !ctx.IsPrague {
cfg.ChainConfig.PragueTime = nil
}

return &BlockView{
config: newConfig(ctx),
config: cfg,
rootAddr: em.rootAddr,
ledger: em.ledger,
}, nil
Expand Down Expand Up @@ -304,8 +309,9 @@ func (bl *BlockView) DryRunTransaction(

// use the from as the signer
msg.From = from
// we need to skip nonce check for dry run
msg.SkipAccountChecks = true
// we need to skip nonce/eoa check for dry run
msg.SkipNonceChecks = true
msg.SkipFromEOACheck = true

// run and return without committing the state changes
return proc.run(msg, tx.Hash(), tx.Type())
Expand All @@ -317,16 +323,18 @@ func (bl *BlockView) newProcedure() (*procedure, error) {
return nil, err
}
cfg := bl.config
evm := gethVM.NewEVM(
*cfg.BlockContext,
execState,
cfg.ChainConfig,
cfg.EVMConfig,
)
evm.SetTxContext(*cfg.TxContext)

return &procedure{
config: cfg,
evm: gethVM.NewEVM(
*cfg.BlockContext,
*cfg.TxContext,
execState,
cfg.ChainConfig,
cfg.EVMConfig,
),
state: execState,
evm: evm,
state: execState,
}, nil
}

Expand All @@ -338,6 +346,9 @@ type procedure struct {

// commit commits the changes to the state (with optional finalization)
func (proc *procedure) commit(finalize bool) (hash.Hash, error) {
// Calling `StateDB.Finalise(true)` is currently a no-op, but
// we add it here to be more in line with how its envisioned.
proc.state.Finalise(true)
stateUpdateCommitment, err := proc.state.Commit(finalize)
if err != nil {
// if known types (state errors) don't do anything and return
Expand Down Expand Up @@ -511,11 +522,15 @@ func (proc *procedure) deployAt(
proc.state.CreateAccount(callerCommon)
}
// increment the nonce for the caller
proc.state.SetNonce(callerCommon, proc.state.GetNonce(callerCommon)+1)
proc.state.SetNonce(
callerCommon,
proc.state.GetNonce(callerCommon)+1,
gethTracing.NonceChangeContractCreator,
)

// setup account
proc.state.CreateAccount(addr)
proc.state.SetNonce(addr, 1) // (EIP-158)
proc.state.SetNonce(addr, 1, gethTracing.NonceChangeNewContract) // (EIP-158)
if call.Value.Sign() > 0 {
proc.evm.Context.Transfer( // transfer value
proc.state,
Expand All @@ -530,12 +545,14 @@ func (proc *procedure) deployAt(
var err error
inter := gethVM.NewEVMInterpreter(proc.evm)
contract := gethVM.NewContract(
gethVM.AccountRef(callerCommon),
gethVM.AccountRef(addr),
callerCommon,
addr,
castedValue,
call.GasLimit)
call.GasLimit,
nil,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For the deployment of COAs, we pass nil for the jumpDests argument, as it is not exposed through the EVM object. See also:

)

contract.SetCallCode(&addr, gethCrypto.Keccak256Hash(call.Data), call.Data)
contract.SetCallCode(gethCrypto.Keccak256Hash(call.Data), call.Data)
// update access list (Berlin)
proc.state.AddAddressToAccessList(addr)

Expand Down Expand Up @@ -639,11 +656,11 @@ func (proc *procedure) run(
gasPool := (*gethCore.GasPool)(&proc.config.BlockContext.GasLimit)

// transit the state
execResult, err := gethCore.NewStateTransition(
execResult, err := gethCore.ApplyMessage(
proc.evm,
msg,
gasPool,
).TransitionDb()
)
if err != nil {
// if the error is a fatal error or a non-fatal state error or a backend err return it
// this condition should never happen given all StateDB errors are withheld for the commit time.
Expand Down
15 changes: 10 additions & 5 deletions fvm/evm/emulator/state/delta.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,18 +387,23 @@ func (d *DeltaView) GetState(sk types.SlotAddress) (gethCommon.Hash, error) {
return d.parent.GetState(sk)
}

// SetState adds sets a value for the given slot of the main storage
func (d *DeltaView) SetState(sk types.SlotAddress, value gethCommon.Hash) error {
// SetState sets or adds a value for the given slot of the main storage.
// It returns the previous value in any case.
func (d *DeltaView) SetState(
sk types.SlotAddress,
value gethCommon.Hash,
) (gethCommon.Hash, error) {
lastValue, err := d.GetState(sk)
if err != nil {
return err
return gethCommon.Hash{}, err
}
// if the value hasn't changed, skip
if value == lastValue {
return nil
return lastValue, nil
}
d.slots[sk] = value
return nil

return lastValue, nil
}

// GetStorageRoot returns some sort of storage root for the given address
Expand Down
10 changes: 8 additions & 2 deletions fvm/evm/emulator/state/delta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,9 @@ func TestDeltaView(t *testing.T) {

// set slot1 with some new value
newValue := gethCommon.BytesToHash([]byte{9, 8})
err = view.SetState(slot1, newValue)
prevValue, err := view.SetState(slot1, newValue)
require.NoError(t, err)
require.Equal(t, value, prevValue)

value, err = view.GetState(slot1)
require.NoError(t, err)
Expand Down Expand Up @@ -694,8 +695,13 @@ func TestDeltaView(t *testing.T) {
key := testutils.RandomCommonHash(t)
value := testutils.RandomCommonHash(t)
sk := types.SlotAddress{Address: addr1, Key: key}
err = view.SetState(sk, value)

stateValue, err := view.GetState(sk)
require.NoError(t, err)

prevValue, err := view.SetState(sk, value)
require.NoError(t, err)
require.Equal(t, stateValue, prevValue)

vret, err := view.GetState(sk)
require.NoError(t, err)
Expand Down
80 changes: 61 additions & 19 deletions fvm/evm/emulator/state/stateDB.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/onflow/atree"
"github.com/onflow/crypto/hash"
gethCommon "github.com/onflow/go-ethereum/common"
gethState "github.com/onflow/go-ethereum/core/state"
gethStateless "github.com/onflow/go-ethereum/core/stateless"
gethTracing "github.com/onflow/go-ethereum/core/tracing"
gethTypes "github.com/onflow/go-ethereum/core/types"
Expand Down Expand Up @@ -102,21 +103,29 @@ func (db *StateDB) IsNewContract(addr gethCommon.Address) bool {
return db.latestView().IsNewContract(addr)
}

// SelfDestruct flags the address for deletion.
// SelfDestruct flags the address for deletion and returns the previous balance.
//
// while this address exists for the rest of transaction,
// the balance of this account is return zero after the SelfDestruct call.
func (db *StateDB) SelfDestruct(addr gethCommon.Address) {
// While this address exists for the rest of the transaction,
// the balance of this account is cleared after the SelfDestruct call.
func (db *StateDB) SelfDestruct(addr gethCommon.Address) uint256.Int {
db.handleError(fmt.Errorf("legacy self destruct is not supported"))
return uint256.Int{}
}

// Selfdestruct6780 would only follow the self destruct steps if account is a new contract
// SelfDestruct6780 would only follow the self destruct steps if account is a new contract
// either just created, or address had balance before but got a contract deployed to it (in this tx).
func (db *StateDB) Selfdestruct6780(addr gethCommon.Address) {
// Returns the previous balance and a boolean value denoting whether the address was self destructed.
func (db *StateDB) SelfDestruct6780(addr gethCommon.Address) (uint256.Int, bool) {
balance, err := db.latestView().GetBalance(addr)
db.handleError(err)

if db.IsNewContract(addr) {
err := db.latestView().SelfDestruct(addr)
db.handleError(err)
return *balance, true
}

return *balance, false
}

// HasSelfDestructed returns true if address is flagged with self destruct.
Expand All @@ -126,33 +135,45 @@ func (db *StateDB) HasSelfDestructed(addr gethCommon.Address) bool {
}

// SubBalance substitutes the amount from the balance of the given address
// and returns the previous balance.
func (db *StateDB) SubBalance(
addr gethCommon.Address,
amount *uint256.Int,
reason gethTracing.BalanceChangeReason,
) {
) uint256.Int {
// negative amounts are not accepted.
if amount.Sign() < 0 {
db.handleError(types.ErrInvalidBalance)
return
return uint256.Int{}
}
err := db.latestView().SubBalance(addr, amount)
prevBalance, err := db.latestView().GetBalance(addr)
db.handleError(err)

err = db.latestView().SubBalance(addr, amount)
db.handleError(err)

return *prevBalance
}

// AddBalance adds the amount from the balance of the given address
// AddBalance adds the amount to the balance of the given address
// and returns the previous balance.
func (db *StateDB) AddBalance(
addr gethCommon.Address,
amount *uint256.Int,
reason gethTracing.BalanceChangeReason,
) {
) uint256.Int {
// negative amounts are not accepted.
if amount.Sign() < 0 {
db.handleError(types.ErrInvalidBalance)
return
return uint256.Int{}
}
err := db.latestView().AddBalance(addr, amount)
prevBalance, err := db.latestView().GetBalance(addr)
db.handleError(err)

err = db.latestView().AddBalance(addr, amount)
db.handleError(err)

return *prevBalance
}

// GetBalance returns the balance of the given address
Expand All @@ -170,7 +191,11 @@ func (db *StateDB) GetNonce(addr gethCommon.Address) uint64 {
}

// SetNonce sets the nonce value for the given address
func (db *StateDB) SetNonce(addr gethCommon.Address, nonce uint64) {
func (db *StateDB) SetNonce(
addr gethCommon.Address,
nonce uint64,
reason gethTracing.NonceChangeReason,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For some reason, the Geth folks introduced this gethTracing.NonceChangeReason function parameter, but it's not used anywhere within the method's body.
See: https://github.com/ethereum/go-ethereum/blob/v1.15.8/core/state/statedb.go#L435-L440

) {
err := db.latestView().SetNonce(addr, nonce)
db.handleError(err)
}
Expand All @@ -196,10 +221,14 @@ func (db *StateDB) GetCodeSize(addr gethCommon.Address) int {
return codeSize
}

// SetCode sets the code for the given address
func (db *StateDB) SetCode(addr gethCommon.Address, code []byte) {
// SetCode sets the code for the given address, and returns the
// previous code located at the given address, if any.
func (db *StateDB) SetCode(addr gethCommon.Address, code []byte) (prev []byte) {
prev = db.GetCode(addr)
err := db.latestView().SetCode(addr, code)
db.handleError(err)

return prev
}

// AddRefund adds the amount to the total (gas) refund
Expand Down Expand Up @@ -254,10 +283,17 @@ func (db *StateDB) GetStorageRoot(addr gethCommon.Address) gethCommon.Hash {
return root
}

// SetState sets a value for the given storage slot
func (db *StateDB) SetState(addr gethCommon.Address, key gethCommon.Hash, value gethCommon.Hash) {
err := db.latestView().SetState(types.SlotAddress{Address: addr, Key: key}, value)
// SetState sets a value for the given storage slot.
// It returns the previous value in any case.
func (db *StateDB) SetState(
addr gethCommon.Address,
key gethCommon.Hash,
value gethCommon.Hash,
) gethCommon.Hash {
prevState, err := db.latestView().SetState(types.SlotAddress{Address: addr, Key: key}, value)
db.handleError(err)

return prevState
}

// GetTransientState returns the value for the given key of the transient storage
Expand Down Expand Up @@ -481,6 +517,8 @@ func (db *StateDB) Commit(finalize bool) (hash.Hash, error) {
return updateCommit, nil
}

func (db *StateDB) Finalise(deleteEmptyObjects bool) {}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a no-op in our case, because our custom implementation of StateDB, already handles the finalization and deletion of empty objects as part of Commit .


// Finalize flushes all the changes
// to the permanent storage
func (db *StateDB) Finalize() error {
Expand Down Expand Up @@ -542,6 +580,10 @@ func (s *StateDB) Witness() *gethStateless.Witness {
return nil
}

func (s *StateDB) AccessEvents() *gethState.AccessEvents {
return &gethState.AccessEvents{}
}

func (db *StateDB) latestView() *DeltaView {
return db.views[len(db.views)-1]
}
Expand Down
Loading
Loading