diff --git a/chain/consensus/filcns/compute_state.go b/chain/consensus/filcns/compute_state.go index 6b5405543ea..6ce9679475a 100644 --- a/chain/consensus/filcns/compute_state.go +++ b/chain/consensus/filcns/compute_state.go @@ -100,6 +100,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, NetworkVersion: sm.GetNetworkVersion(ctx, e), BaseFee: baseFee, LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts), + TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts), Tracing: vmTracing, } diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 92066ca8101..062679977e1 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -89,6 +89,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. NetworkVersion: sm.GetNetworkVersion(ctx, pheight+1), BaseFee: types.NewInt(0), LookbackState: LookbackStateGetterForTipset(sm, ts), + TipSetGetter: TipSetGetterForTipset(sm.cs, ts), Tracing: true, } @@ -226,6 +227,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri NetworkVersion: sm.GetNetworkVersion(ctx, ts.Height()+1), BaseFee: ts.Blocks()[0].ParentBaseFee, LookbackState: LookbackStateGetterForTipset(sm, ts), + TipSetGetter: TipSetGetterForTipset(sm.cs, ts), Tracing: true, } vmi, err := sm.newVM(ctx, vmopt) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index a466029ce2a..af4a0cd9e2a 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -95,6 +95,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, NetworkVersion: sm.GetNetworkVersion(ctx, height), BaseFee: ts.Blocks()[0].ParentBaseFee, LookbackState: LookbackStateGetterForTipset(sm, ts), + TipSetGetter: TipSetGetterForTipset(sm.cs, ts), Tracing: true, } vmi, err := sm.newVM(ctx, vmopt) @@ -131,6 +132,16 @@ func LookbackStateGetterForTipset(sm *StateManager, ts *types.TipSet) vm.Lookbac } } +func TipSetGetterForTipset(cs *store.ChainStore, ts *types.TipSet) vm.TipSetGetter { + return func(ctx context.Context, round abi.ChainEpoch) (types.TipSetKey, error) { + ts, err := cs.GetTipsetByHeight(ctx, round, ts, true) + if err != nil { + return types.EmptyTSK, err + } + return ts.Key(), nil + } +} + func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, cid.Cid, error) { var lbr abi.ChainEpoch lb := policy.GetWinningPoStSectorSetLookback(sm.GetNetworkVersion(ctx, round)) diff --git a/chain/vm/fvm.go b/chain/vm/fvm.go index 44047076175..b229c826cae 100644 --- a/chain/vm/fvm.go +++ b/chain/vm/fvm.go @@ -45,6 +45,7 @@ type FvmExtern struct { blockstore.Blockstore epoch abi.ChainEpoch lbState LookbackStateGetter + tsGet TipSetGetter base cid.Cid } @@ -99,6 +100,14 @@ func (t *FvmExecutionTrace) ToExecutionTrace() types.ExecutionTrace { return ret } +func (x *FvmExtern) TipsetCid(ctx context.Context, epoch abi.ChainEpoch) (cid.Cid, error) { + tsk, err := x.tsGet(ctx, epoch) + if err != nil { + return cid.Undef, err + } + return tsk.Cid() +} + // VerifyConsensusFault is similar to the one in syscalls.go used by the Lotus VM, except it never errors // Errors are logged and "no fault" is returned, which is functionally what go-actors does anyway func (x *FvmExtern) VerifyConsensusFault(ctx context.Context, a, b, extra []byte) (*ffi_cgo.ConsensusFault, int64) { @@ -294,6 +303,7 @@ func defaultFVMOpts(ctx context.Context, opts *VMOpts) (*ffi.FVMOpts, error) { Rand: opts.Rand, Blockstore: opts.Bstore, lbState: opts.LookbackState, + tsGet: opts.TipSetGetter, base: opts.StateBase, epoch: opts.Epoch, }, diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 669f7e30632..ecd87caf050 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -192,6 +192,7 @@ type ( CircSupplyCalculator func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error) NtwkVersionGetter func(context.Context, abi.ChainEpoch) network.Version LookbackStateGetter func(context.Context, abi.ChainEpoch) (*state.StateTree, error) + TipSetGetter func(context.Context, abi.ChainEpoch) (types.TipSetKey, error) ) var _ Interface = (*LegacyVM)(nil) @@ -223,6 +224,7 @@ type VMOpts struct { NetworkVersion network.Version BaseFee abi.TokenAmount LookbackState LookbackStateGetter + TipSetGetter TipSetGetter Tracing bool } diff --git a/conformance/driver.go b/conformance/driver.go index db8f2ccba18..fc03a48d8c5 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -203,6 +203,9 @@ type ExecuteMessageParams struct { // Lookback is the LookbackStateGetter; returns the state tree at a given epoch. Lookback vm.LookbackStateGetter + + // TipSetGetter returns the tipset key at any given epoch. + TipSetGetter vm.TipSetGetter } // ExecuteMessage executes a conformance test vector message in a temporary VM. @@ -217,15 +220,26 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP params.Rand = NewFixedRand() } - // TODO: This lookback state returns the supplied precondition state tree, unconditionally. - // This is obviously not correct, but the lookback state tree is only used to validate the - // worker key when verifying a consensus fault. If the worker key hasn't changed in the - // current finality window, this workaround is enough. - // The correct solutions are documented in https://github.com/filecoin-project/ref-fvm/issues/381, - // but they're much harder to implement, and the tradeoffs aren't clear. - var lookback vm.LookbackStateGetter = func(ctx context.Context, epoch abi.ChainEpoch) (*state.StateTree, error) { - cst := cbor.NewCborStore(bs) - return state.LoadStateTree(cst, params.Preroot) + if params.TipSetGetter == nil { + // TODO: If/when we start writing conformance tests against the EVM, we'll need to + // actually implement this and (unfortunately) capture any tipsets looked up by + // messages. + params.TipSetGetter = func(context.Context, abi.ChainEpoch) (types.TipSetKey, error) { + return types.EmptyTSK, nil + } + } + + if params.Lookback == nil { + // TODO: This lookback state returns the supplied precondition state tree, unconditionally. + // This is obviously not correct, but the lookback state tree is only used to validate the + // worker key when verifying a consensus fault. If the worker key hasn't changed in the + // current finality window, this workaround is enough. + // The correct solutions are documented in https://github.com/filecoin-project/ref-fvm/issues/381, + // but they're much harder to implement, and the tradeoffs aren't clear. + params.Lookback = func(ctx context.Context, epoch abi.ChainEpoch) (*state.StateTree, error) { + cst := cbor.NewCborStore(bs) + return state.LoadStateTree(cst, params.Preroot) + } } vmOpts := &vm.VMOpts{ @@ -239,7 +253,8 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP Rand: params.Rand, BaseFee: params.BaseFee, NetworkVersion: params.NetworkVersion, - LookbackState: lookback, + LookbackState: params.Lookback, + TipSetGetter: params.TipSetGetter, } var vmi vm.Interface