diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index cfb2cdf31c..4d586b254b 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -79,8 +79,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, bms []FilecoinBlockMessages, epoch abi.ChainEpoch, r vm.Rand, - em stmgr.ExecMonitor, - vmTracing bool, + opts *stmgr.ExecutorOpts, baseFee abi.TokenAmount, ts *types.TipSet) (cid.Cid, cid.Cid, error) { done := metrics.Timer(ctx, metrics.VMApplyBlocksTotal) @@ -91,6 +90,11 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, partDone() }() + bs := sm.ChainStore().StateBlockstore() + if opts.DiscardState { + bs = blockstore.NewBuffered(bs) + } + ctx = blockstore.WithHotView(ctx) makeVm := func(base cid.Cid, e abi.ChainEpoch, timestamp uint64) (vm.Interface, error) { vmopt := &vm.VMOpts{ @@ -98,7 +102,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, Epoch: e, Timestamp: timestamp, Rand: r, - Bstore: sm.ChainStore().StateBlockstore(), + Bstore: bs, Actors: NewActorRegistry(), Syscalls: sm.Syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, @@ -106,7 +110,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, BaseFee: baseFee, LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts), TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts), - Tracing: vmTracing, + Tracing: opts.VmTracing, ReturnEvents: sm.ChainStore().IsStoringEvents(), } @@ -130,7 +134,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, return xerrors.Errorf("running cron: %w", err) } - if em != nil { + if em := opts.ExecMonitor; em != nil { if err := em.MessageApplied(ctx, ts, cronMsg.Cid(), cronMsg, ret, true); err != nil { return xerrors.Errorf("callback failed on cron message: %w", err) } @@ -175,7 +179,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, // handle state forks // XXX: The state tree - pstate, err = sm.HandleStateForks(ctx, pstate, i, em, ts) + pstate, err = sm.HandleStateForks(ctx, pstate, i, opts.ExecMonitor, ts) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("error handling state forks: %w", err) } @@ -219,7 +223,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, events = append(events, r.Events) } - if em != nil { + if em := opts.ExecMonitor; em != nil { if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil { return cid.Undef, cid.Undef, err } @@ -233,7 +237,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, GasReward: gasReward, WinCount: b.WinCount, } - rErr := t.reward(ctx, vmi, em, epoch, ts, params) + rErr := t.reward(ctx, vmi, opts.ExecMonitor, epoch, ts, params) if rErr != nil { return cid.Undef, cid.Undef, xerrors.Errorf("error applying reward: %w", rErr) } @@ -291,8 +295,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, func (t *TipSetExecutor) ExecuteTipSet(ctx context.Context, sm *stmgr.StateManager, ts *types.TipSet, - em stmgr.ExecMonitor, - vmTracing bool) (stateroot cid.Cid, rectsroot cid.Cid, err error) { + opts *stmgr.ExecutorOpts) (stateroot cid.Cid, rectsroot cid.Cid, err error) { ctx, span := trace.StartSpan(ctx, "computeTipSetState") defer span.End() @@ -340,7 +343,7 @@ func (t *TipSetExecutor) ExecuteTipSet(ctx context.Context, } baseFee := blks[0].ParentBaseFee - return t.ApplyBlocks(ctx, sm, parentEpoch, pstate, fbmsgs, blks[0].Height, r, em, vmTracing, baseFee, ts) + return t.ApplyBlocks(ctx, sm, parentEpoch, pstate, fbmsgs, blks[0].Height, r, opts, baseFee, ts) } func (t *TipSetExecutor) StoreEventsAMT(ctx context.Context, cs *store.ChainStore, events []types.Event) (cid.Cid, error) { diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 901fc2d125..86c6a21dd3 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -263,7 +263,11 @@ func (sm *StateManager) Replay(ctx context.Context, ts *types.TipSet, mcid cid.C // message to find finder.mcid = mcid - _, _, err := sm.tsExec.ExecuteTipSet(ctx, sm, ts, &finder, true) + _, _, err := sm.tsExec.ExecuteTipSet(ctx, sm, ts, &ExecutorOpts{ + ExecMonitor: &finder, + VmTracing: true, + DiscardState: true, // safe to skip the write since this is throwaway state. + }) if err != nil && !xerrors.Is(err, errHaltExecution) { return nil, nil, xerrors.Errorf("unexpected error during execution: %w", err) } diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index 57ff205b91..35b8fe4a50 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -13,6 +13,15 @@ import ( ) func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { + return sm.TipSetStateWithOpts(ctx, ts, &TipSetStateOpts{}) +} + +type TipSetStateOpts struct { + // DiscardState: see ExecutorOpts.DiscardState. + DiscardState bool +} + +func (sm *StateManager) TipSetStateWithOpts(ctx context.Context, ts *types.TipSet, opts *TipSetStateOpts) (st cid.Cid, rec cid.Cid, err error) { ctx, span := trace.StartSpan(ctx, "tipSetState") defer span.End() if span.IsRecordingEvents() { @@ -59,7 +68,10 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c return st, rec, nil } - st, rec, err = sm.tsExec.ExecuteTipSet(ctx, sm, ts, sm.tsExecMonitor, false) + st, rec, err = sm.tsExec.ExecuteTipSet(ctx, sm, ts, &ExecutorOpts{ + ExecMonitor: sm.tsExecMonitor, + DiscardState: opts.DiscardState, + }) if err != nil { return cid.Undef, cid.Undef, err } @@ -113,7 +125,11 @@ func tryLookupTipsetState(ctx context.Context, cs *store.ChainStore, ts *types.T } func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types.TipSet, em ExecMonitor) (cid.Cid, error) { - st, _, err := sm.tsExec.ExecuteTipSet(ctx, sm, ts, em, true) + st, _, err := sm.tsExec.ExecuteTipSet(ctx, sm, ts, &ExecutorOpts{ + ExecMonitor: em, + VmTracing: true, + DiscardState: true, // safe to discard the output state since we're creating no new state anyway. + }) return st, err } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index b9f8d81bf2..c2b5bfa5ce 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -97,9 +97,19 @@ func (m *migrationResultCache) Store(ctx context.Context, root cid.Cid, resultCi return nil } +// ExecutorOpts defines options for the execution. +type ExecutorOpts struct { + ExecMonitor ExecMonitor + VmTracing bool + // DiscardState, when enabled, skips writing the output state tree into the + // blockstore. This is useful when requesting execution traces for tipsets + // already in the chain, or for speculative executions. + DiscardState bool +} + type Executor interface { NewActorRegistry() *vm.ActorRegistry - ExecuteTipSet(ctx context.Context, sm *StateManager, ts *types.TipSet, em ExecMonitor, vmTracing bool) (stateroot cid.Cid, rectsroot cid.Cid, err error) + ExecuteTipSet(ctx context.Context, sm *StateManager, ts *types.TipSet, opts *ExecutorOpts) (stateroot cid.Cid, rectsroot cid.Cid, err error) } type StateManager struct { diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index c93267d50f..d8a50f1d42 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -6,6 +6,7 @@ import ( "fmt" "reflect" + "github.com/filecoin-project/lotus/blockstore" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -86,13 +87,14 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, // future. It's not guaranteed to be accurate... but that's fine. } + bs := blockstore.NewBuffered(sm.cs.StateBlockstore()) r := rand.NewStateRand(sm.cs, ts.Cids(), sm.beacon, sm.GetNetworkVersion) vmopt := &vm.VMOpts{ StateBase: base, Epoch: height, Timestamp: ts.MinTimestamp(), Rand: r, - Bstore: sm.cs.StateBlockstore(), + Bstore: bs, Actors: sm.tsExec.NewActorRegistry(), Syscalls: sm.Syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 0528c21766..e4afdc681b 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1843,7 +1843,7 @@ func messagesAndReceipts(ctx context.Context, ts *types.TipSet, cs *store.ChainS return nil, nil, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err) } - _, rcptRoot, err := sa.StateManager.TipSetState(ctx, ts) + _, rcptRoot, err := sa.StateManager.TipSetStateWithOpts(ctx, ts, &stmgr.TipSetStateOpts{DiscardState: true}) if err != nil { return nil, nil, xerrors.Errorf("failed to compute state: %w", err) }