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

[EVM] adding commitment over state updates #6451

Merged
merged 14 commits into from
Sep 18, 2024
29 changes: 18 additions & 11 deletions fvm/evm/emulator/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/holiman/uint256"
"github.com/onflow/atree"
"github.com/onflow/crypto/hash"
gethCommon "github.com/onflow/go-ethereum/common"
gethCore "github.com/onflow/go-ethereum/core"
gethTracing "github.com/onflow/go-ethereum/core/tracing"
Expand Down Expand Up @@ -178,7 +179,8 @@ func (bl *BlockView) RunTransaction(
}

// all commit errors (StateDB errors) has to be returned
if err := proc.commit(true); err != nil {
res.StateChangeCommitment, err = proc.commit(true)
if err != nil {
return nil, err
}

Expand Down Expand Up @@ -224,7 +226,8 @@ func (bl *BlockView) BatchRunTransactions(txs []*gethTypes.Transaction) ([]*type
}

// all commit errors (StateDB errors) has to be returned
if err := proc.commit(false); err != nil {
res.StateChangeCommitment, err = proc.commit(false)
if err != nil {
return nil, err
}

Expand Down Expand Up @@ -332,18 +335,18 @@ type procedure struct {
}

// commit commits the changes to the state (with optional finalization)
func (proc *procedure) commit(finalize bool) error {
err := proc.state.Commit(finalize)
func (proc *procedure) commit(finalize bool) (hash.Hash, error) {
stateUpdateCommitment, err := proc.state.Commit(finalize)
if err != nil {
// if known types (state errors) don't do anything and return
if types.IsAFatalError(err) || types.IsAStateError(err) || types.IsABackendError(err) {
return err
return stateUpdateCommitment, err
}

// else is a new fatal error
return types.NewFatalError(err)
return stateUpdateCommitment, types.NewFatalError(err)
}
return nil
return stateUpdateCommitment, nil
}

func (proc *procedure) mintTo(
Expand Down Expand Up @@ -386,7 +389,8 @@ func (proc *procedure) mintTo(
}

// commit and finalize the state and return any stateDB error
return res, proc.commit(true)
res.StateChangeCommitment, err = proc.commit(true)
return res, err
}

func (proc *procedure) withdrawFrom(
Expand Down Expand Up @@ -432,7 +436,8 @@ func (proc *procedure) withdrawFrom(
proc.state.SubBalance(bridge, value, gethTracing.BalanceIncreaseWithdrawal)

// commit and finalize the state and return any stateDB error
return res, proc.commit(true)
res.StateChangeCommitment, err = proc.commit(true)
return res, err
}

// deployAt deploys a contract at the given target address
Expand Down Expand Up @@ -574,7 +579,8 @@ func (proc *procedure) deployAt(
res.CumulativeGasUsed = proc.config.BlockTotalGasUsedSoFar + res.GasConsumed

proc.state.SetCode(addr, ret)
return res, proc.commit(true)
res.StateChangeCommitment, err = proc.commit(true)
return res, err
}

func (proc *procedure) runDirect(
Expand All @@ -590,7 +596,8 @@ func (proc *procedure) runDirect(
return nil, err
}
// commit and finalize the state and return any stateDB error
return res, proc.commit(true)
res.StateChangeCommitment, err = proc.commit(true)
return res, err
}

// run runs a geth core.message and returns the
Expand Down
64 changes: 46 additions & 18 deletions fvm/evm/emulator/state/stateDB.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/holiman/uint256"
"github.com/onflow/atree"
"github.com/onflow/crypto/hash"
gethCommon "github.com/onflow/go-ethereum/common"
gethStateless "github.com/onflow/go-ethereum/core/stateless"
gethTracing "github.com/onflow/go-ethereum/core/tracing"
Expand Down Expand Up @@ -347,10 +348,10 @@ func (db *StateDB) Preimages() map[gethCommon.Hash][]byte {
}

// Commit commits state changes back to the underlying
func (db *StateDB) Commit(finalize bool) error {
// return error if any has been acumulated
func (db *StateDB) Commit(finalize bool) (hash.Hash, error) {
// return error if any has been accumulated
if db.cachedError != nil {
return wrapError(db.cachedError)
return nil, wrapError(db.cachedError)
}

var err error
Expand Down Expand Up @@ -378,43 +379,61 @@ func (db *StateDB) Commit(finalize bool) error {
return bytes.Compare(sortedAddresses[i][:], sortedAddresses[j][:]) < 0
})

updateCommitter := NewUpdateCommitter()
// update accounts
for _, addr := range sortedAddresses {
deleted := false
// first we need to delete accounts
if db.HasSelfDestructed(addr) {
err = db.baseView.DeleteAccount(addr)
if err != nil {
return wrapError(err)
return nil, wrapError(err)
}
err = updateCommitter.DeleteAccount(addr)
if err != nil {
return nil, wrapError(err)
}
deleted = true
}
if deleted {
continue
}

bal := db.GetBalance(addr)
nonce := db.GetNonce(addr)
code := db.GetCode(addr)
codeHash := db.GetCodeHash(addr)
// create new accounts
if db.IsCreated(addr) {
err = db.baseView.CreateAccount(
addr,
db.GetBalance(addr),
db.GetNonce(addr),
db.GetCode(addr),
db.GetCodeHash(addr),
bal,
nonce,
code,
codeHash,
)
if err != nil {
return wrapError(err)
return nil, wrapError(err)
}
err = updateCommitter.CreateAccount(addr, bal, nonce, codeHash)
if err != nil {
return nil, wrapError(err)
}
continue
}
err = db.baseView.UpdateAccount(
addr,
db.GetBalance(addr),
db.GetNonce(addr),
db.GetCode(addr),
db.GetCodeHash(addr),
bal,
nonce,
code,
codeHash,
)
if err != nil {
return wrapError(err)
return nil, wrapError(err)
}
err = updateCommitter.UpdateAccount(addr, bal, nonce, codeHash)
if err != nil {
return nil, wrapError(err)
}
}

Expand All @@ -437,20 +456,29 @@ func (db *StateDB) Commit(finalize bool) error {
if db.HasSelfDestructed(sk.Address) {
continue
}
val := db.GetState(sk.Address, sk.Key)
err = db.baseView.UpdateSlot(
sk,
db.GetState(sk.Address, sk.Key),
val,
)
if err != nil {
return wrapError(err)
return nil, wrapError(err)
}
err = updateCommitter.UpdateSlot(sk.Address, sk.Key, val)
janezpodhostnik marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, wrapError(err)
}
}

// don't purge views yet, people might call the logs etc
updateCommit := updateCommitter.Commitment()
if finalize {
return db.Finalize()
err := db.Finalize()
if err != nil {
return nil, err
}
}
return nil
return updateCommit, nil
}

// Finalize flushes all the changes
Expand Down
34 changes: 22 additions & 12 deletions fvm/evm/emulator/state/stateDB_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ func TestStateDB(t *testing.T) {
ret = db.GetCommittedState(addr1, key1)
require.Equal(t, gethCommon.Hash{}, ret)

err = db.Commit(true)
commit, err := db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)

ret = db.GetCommittedState(addr1, key1)
require.Equal(t, value1, ret)
Expand Down Expand Up @@ -272,10 +273,10 @@ func TestStateDB(t *testing.T) {
require.NoError(t, err)

db.CreateAccount(testutils.RandomCommonAddress(t))

err = db.Commit(true)
commit, err := db.Commit(true)
// ret := db.Error()
require.Error(t, err)
require.Empty(t, commit)
// check wrapping
require.True(t, types.IsAStateError(err))
})
Expand All @@ -297,9 +298,10 @@ func TestStateDB(t *testing.T) {

db.CreateAccount(testutils.RandomCommonAddress(t))

err = db.Commit(true)
commit, err := db.Commit(true)
// ret := db.Error()
require.Error(t, err)
require.Empty(t, commit)
// check wrapping
require.True(t, types.IsAFatalError(err))
})
Expand All @@ -321,17 +323,19 @@ func TestStateDB(t *testing.T) {
// accounts without slots
db.CreateAccount(addr1)
require.NoError(t, db.Error())
err = db.Commit(true)
commit, err := db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)

root = db.GetStorageRoot(addr1)
require.NoError(t, db.Error())
require.Equal(t, gethTypes.EmptyRootHash, root)

db.AddBalance(addr1, uint256.NewInt(100), tracing.BalanceChangeTouchAccount)
require.NoError(t, db.Error())
err = db.Commit(true)
commit, err = db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)

root = db.GetStorageRoot(addr1)
require.NoError(t, db.Error())
Expand All @@ -344,8 +348,9 @@ func TestStateDB(t *testing.T) {
require.NoError(t, db.Error())
db.SetState(addr1, key, value)
require.NoError(t, db.Error())
err = db.Commit(true)
commit, err = db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)

root = db.GetStorageRoot(addr1)
require.NoError(t, db.Error())
Expand All @@ -367,8 +372,9 @@ func TestStateDB(t *testing.T) {
db.SetCode(addr1, code1)
db.AddBalance(addr1, balance1, tracing.BalanceChangeTransfer)
require.NoError(t, db.Error())
err = db.Commit(true)
commit, err := db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)
// renew db
db, err = state.NewStateDB(ledger, rootAddr)
require.NoError(t, err)
Expand All @@ -388,8 +394,9 @@ func TestStateDB(t *testing.T) {
db.AddBalance(addr2, balance2, tracing.BalanceChangeTransfer)
require.NoError(t, db.Error())
// commit and renew db
err = db.Commit(true)
commit, err = db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)
db, err = state.NewStateDB(ledger, rootAddr)
require.NoError(t, err)
// call self destruct should not work
Expand All @@ -400,8 +407,9 @@ func TestStateDB(t *testing.T) {
require.Empty(t, db.GetCode(addr2))
require.NoError(t, db.Error())
// commit and renew db
err = db.Commit(true)
commit, err = db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)
db, err = state.NewStateDB(ledger, rootAddr)
require.NoError(t, err)
// set code and call contract creation
Expand All @@ -411,8 +419,9 @@ func TestStateDB(t *testing.T) {
// now calling selfdestruct should do the job
db.Selfdestruct6780(addr2)
require.NoError(t, db.Error())
err = db.Commit(true)
commit, err = db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)
db, err = state.NewStateDB(ledger, rootAddr)
require.NoError(t, err)
// now query
Expand All @@ -438,8 +447,9 @@ func TestStateDB(t *testing.T) {
db.Selfdestruct6780(addr3)
require.NoError(t, db.Error())
// commit changes
err = db.Commit(true)
commit, err = db.Commit(true)
require.NoError(t, err)
require.NotEmpty(t, commit)
// renew db
db, err = state.NewStateDB(ledger, rootAddr)
require.NoError(t, err)
Expand Down
2 changes: 1 addition & 1 deletion fvm/evm/emulator/state/state_growth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *storageTest) run(runner func(state types.StateDB)) error {

runner(state)

err = state.Commit(true)
_, err = state.Commit(true)
if err != nil {
return err
}
Expand Down
Loading
Loading