From e78d1306ee4136b0029856d0d8e9f18976acbf51 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 22 Nov 2022 09:41:03 -0800 Subject: [PATCH] feat: fvm: add support for looking up past tipset CIDs (#9687) * feat: add support for generating tipset CIDs (cherry-picked from feat/nv18-fevm) * feat: fvm: add support for looking up past tipset CIDs We do this by adding yet another "getter" to the VM that resolves an epoch into a TipSetKey. Co-authored-by: Kevin Li --- chain/consensus/filcns/compute_state.go | 1 + chain/stmgr/call.go | 1 + chain/stmgr/utils.go | 11 ++++++++ chain/types/tipset_key.go | 23 ++++++++++++++++ chain/vm/fvm.go | 10 +++++++ chain/vm/vm.go | 2 ++ conformance/driver.go | 35 ++++++++++++++++++------- 7 files changed, 73 insertions(+), 10 deletions(-) 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 cc9aa4f53cf..c110dd4bd0d 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -151,6 +151,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr NetworkVersion: nvGetter(ctx, vmHeight), 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/types/tipset_key.go b/chain/types/tipset_key.go index 59514a792d0..15e655da7d6 100644 --- a/chain/types/tipset_key.go +++ b/chain/types/tipset_key.go @@ -7,6 +7,7 @@ import ( "io" "strings" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" typegen "github.com/whyrusleeping/cbor-gen" @@ -98,6 +99,28 @@ func (k *TipSetKey) UnmarshalJSON(b []byte) error { return nil } +func (k TipSetKey) Cid() (cid.Cid, error) { + blk, err := k.ToStorageBlock() + if err != nil { + return cid.Cid{}, err + } + return blk.Cid(), nil +} + +func (k TipSetKey) ToStorageBlock() (block.Block, error) { + buf := new(bytes.Buffer) + if err := k.MarshalCBOR(buf); err != nil { + log.Errorf("failed to marshal ts key as CBOR: %s", k) + } + + cid, err := abi.CidBuilder.Sum(buf.Bytes()) + if err != nil { + return nil, err + } + + return block.NewBlockWithCid(buf.Bytes(), cid) +} + func (k TipSetKey) MarshalCBOR(writer io.Writer) error { if err := typegen.WriteMajorTypeHeader(writer, typegen.MajByteString, uint64(len(k.Bytes()))); err != nil { return err 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