Skip to content
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
11 changes: 4 additions & 7 deletions saexec/saexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package saexec

import (
"fmt"
"sync/atomic"

"github.com/ava-labs/avalanchego/utils/logging"
Expand All @@ -19,7 +20,6 @@ import (
"github.com/ava-labs/libevm/event"
"github.com/ava-labs/libevm/params"
"github.com/ava-labs/libevm/triedb"
"go.uber.org/zap"

"github.com/ava-labs/strevm/blocks"
"github.com/ava-labs/strevm/hook"
Expand Down Expand Up @@ -92,7 +92,7 @@ func New(

// Close shuts down the [Executor], waits for the currently executing block
// to complete, and then releases all resources.
func (e *Executor) Close() {
func (e *Executor) Close() error {
close(e.quit)
<-e.done

Expand All @@ -102,15 +102,12 @@ func (e *Executor) Close() {
// no-op, so we ignore it.
if root := e.LastExecuted().PostExecutionStateRoot(); root != e.snaps.DiskRoot() {
if err := e.snaps.Cap(root, 0); err != nil {
e.log.Warn(
"snapshot.Tree.Cap([last post-execution state root], 0)",
zap.Stringer("root", root),
zap.Error(err),
)
return fmt.Errorf("snapshot.Tree.Cap([last post-execution state root], 0): %v", err)
}
}

e.snaps.Release()
return nil
}

// ChainConfig returns the config originally passed to [New].
Expand Down
53 changes: 52 additions & 1 deletion saexec/saexec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import (
"github.com/ava-labs/libevm/core"
"github.com/ava-labs/libevm/core/rawdb"
"github.com/ava-labs/libevm/core/state"
"github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/core/types"
"github.com/ava-labs/libevm/core/vm"
"github.com/ava-labs/libevm/crypto"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/libevm"
libevmhookstest "github.com/ava-labs/libevm/libevm/hookstest"
"github.com/ava-labs/libevm/params"
Expand Down Expand Up @@ -61,6 +63,7 @@ type SUT struct {
chain *blockstest.ChainBuilder
wallet *saetest.Wallet
logger logging.Logger
db ethdb.Database
}

// newSUT returns a new SUT. Any >= [logging.Error] on the logger will also
Expand Down Expand Up @@ -94,13 +97,16 @@ func newSUT(tb testing.TB, hooks hook.Points) (context.Context, SUT) {

e, err := New(genesis, src, config, db, tdbConfig, hooks, logger)
require.NoError(tb, err, "New()")
tb.Cleanup(e.Close)
tb.Cleanup(func() {
require.NoErrorf(tb, e.Close(), "%T.Close()", e)
})

return ctx, SUT{
Executor: e,
chain: chain,
wallet: wallet,
logger: logger,
db: db,
}
}

Expand Down Expand Up @@ -724,3 +730,48 @@ var _ = blockstest.ModifyHeader((*blockNumSaver)(nil).store)
func (e *blockNumSaver) store(h *types.Header) {
e.num = new(big.Int).Set(h.Number)
}

func TestSnapshotPersistence(t *testing.T) {
ctx, sut := newSUT(t, defaultHooks())

e, chain, wallet := sut.Executor, sut.chain, sut.wallet

const n = 10
for range n {
b := chain.NewBlock(t, types.Transactions{
wallet.SetNonceAndSign(t, 0, &types.LegacyTx{
To: &common.Address{},
Gas: params.TxGas,
GasPrice: big.NewInt(1),
}),
})
require.NoError(t, e.Enqueue(ctx, b), "Enqueue()")
}
last := chain.Last()
require.NoErrorf(t, last.WaitUntilExecuted(ctx), "%T.Last().WaitUntilExecuted()", chain)

require.NoErrorf(t, e.Close(), "%T.Close()", e)
// [newSUT] creates a cleanup that also calls [Executor.Close], which isn't
// valid usage. The simplest workaround is to just replace the quit channel
// so it can be closed again.
e.quit = make(chan struct{})

// The crux of the test is whether we can recover the EOA nonce using only a
// new set of snapshots, recovered from the databases.
conf := snapshot.Config{
CacheSize: 128,
NoBuild: true, // i.e. MUST be loaded from disk
}
snaps, err := snapshot.New(conf, sut.db, e.StateCache().TrieDB(), last.PostExecutionStateRoot())
require.NoError(t, err, "snapshot.New(..., [post-execution state root of last-executed block])")
snap := snaps.Snapshot(last.PostExecutionStateRoot())
require.NotNilf(t, snap, "%T.Snapshot([post-execution state root of last-executed block])", snaps)

t.Run("snap.Account(EOA)", func(t *testing.T) {
eoa := wallet.Addresses()[0]
got, err := snap.Account(crypto.Keccak256Hash(eoa.Bytes()))
require.NoError(t, err)
require.NotNil(t, got) // yes, this is still possible with nil error
require.Equalf(t, uint64(n), got.Nonce, "%T.Nonce", got)
})
}
Loading