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 <ychiaoli18@users.noreply.github.com>
This commit is contained in:
parent
64de59ab44
commit
e78d1306ee
@ -100,6 +100,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context,
|
|||||||
NetworkVersion: sm.GetNetworkVersion(ctx, e),
|
NetworkVersion: sm.GetNetworkVersion(ctx, e),
|
||||||
BaseFee: baseFee,
|
BaseFee: baseFee,
|
||||||
LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts),
|
LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts),
|
||||||
|
TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts),
|
||||||
Tracing: vmTracing,
|
Tracing: vmTracing,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
|
|||||||
NetworkVersion: nvGetter(ctx, vmHeight),
|
NetworkVersion: nvGetter(ctx, vmHeight),
|
||||||
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
||||||
LookbackState: LookbackStateGetterForTipset(sm, ts),
|
LookbackState: LookbackStateGetterForTipset(sm, ts),
|
||||||
|
TipSetGetter: TipSetGetterForTipset(sm.cs, ts),
|
||||||
Tracing: true,
|
Tracing: true,
|
||||||
}
|
}
|
||||||
vmi, err := sm.newVM(ctx, vmopt)
|
vmi, err := sm.newVM(ctx, vmopt)
|
||||||
|
@ -95,6 +95,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch,
|
|||||||
NetworkVersion: sm.GetNetworkVersion(ctx, height),
|
NetworkVersion: sm.GetNetworkVersion(ctx, height),
|
||||||
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
||||||
LookbackState: LookbackStateGetterForTipset(sm, ts),
|
LookbackState: LookbackStateGetterForTipset(sm, ts),
|
||||||
|
TipSetGetter: TipSetGetterForTipset(sm.cs, ts),
|
||||||
Tracing: true,
|
Tracing: true,
|
||||||
}
|
}
|
||||||
vmi, err := sm.newVM(ctx, vmopt)
|
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) {
|
func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, cid.Cid, error) {
|
||||||
var lbr abi.ChainEpoch
|
var lbr abi.ChainEpoch
|
||||||
lb := policy.GetWinningPoStSectorSetLookback(sm.GetNetworkVersion(ctx, round))
|
lb := policy.GetWinningPoStSectorSetLookback(sm.GetNetworkVersion(ctx, round))
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
block "github.com/ipfs/go-block-format"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
typegen "github.com/whyrusleeping/cbor-gen"
|
typegen "github.com/whyrusleeping/cbor-gen"
|
||||||
|
|
||||||
@ -98,6 +99,28 @@ func (k *TipSetKey) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
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 {
|
func (k TipSetKey) MarshalCBOR(writer io.Writer) error {
|
||||||
if err := typegen.WriteMajorTypeHeader(writer, typegen.MajByteString, uint64(len(k.Bytes()))); err != nil {
|
if err := typegen.WriteMajorTypeHeader(writer, typegen.MajByteString, uint64(len(k.Bytes()))); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -45,6 +45,7 @@ type FvmExtern struct {
|
|||||||
blockstore.Blockstore
|
blockstore.Blockstore
|
||||||
epoch abi.ChainEpoch
|
epoch abi.ChainEpoch
|
||||||
lbState LookbackStateGetter
|
lbState LookbackStateGetter
|
||||||
|
tsGet TipSetGetter
|
||||||
base cid.Cid
|
base cid.Cid
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +100,14 @@ func (t *FvmExecutionTrace) ToExecutionTrace() types.ExecutionTrace {
|
|||||||
return ret
|
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
|
// 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
|
// 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) {
|
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,
|
Rand: opts.Rand,
|
||||||
Blockstore: opts.Bstore,
|
Blockstore: opts.Bstore,
|
||||||
lbState: opts.LookbackState,
|
lbState: opts.LookbackState,
|
||||||
|
tsGet: opts.TipSetGetter,
|
||||||
base: opts.StateBase,
|
base: opts.StateBase,
|
||||||
epoch: opts.Epoch,
|
epoch: opts.Epoch,
|
||||||
},
|
},
|
||||||
|
@ -192,6 +192,7 @@ type (
|
|||||||
CircSupplyCalculator func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error)
|
CircSupplyCalculator func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error)
|
||||||
NtwkVersionGetter func(context.Context, abi.ChainEpoch) network.Version
|
NtwkVersionGetter func(context.Context, abi.ChainEpoch) network.Version
|
||||||
LookbackStateGetter func(context.Context, abi.ChainEpoch) (*state.StateTree, error)
|
LookbackStateGetter func(context.Context, abi.ChainEpoch) (*state.StateTree, error)
|
||||||
|
TipSetGetter func(context.Context, abi.ChainEpoch) (types.TipSetKey, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Interface = (*LegacyVM)(nil)
|
var _ Interface = (*LegacyVM)(nil)
|
||||||
@ -223,6 +224,7 @@ type VMOpts struct {
|
|||||||
NetworkVersion network.Version
|
NetworkVersion network.Version
|
||||||
BaseFee abi.TokenAmount
|
BaseFee abi.TokenAmount
|
||||||
LookbackState LookbackStateGetter
|
LookbackState LookbackStateGetter
|
||||||
|
TipSetGetter TipSetGetter
|
||||||
Tracing bool
|
Tracing bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +203,9 @@ type ExecuteMessageParams struct {
|
|||||||
|
|
||||||
// Lookback is the LookbackStateGetter; returns the state tree at a given epoch.
|
// Lookback is the LookbackStateGetter; returns the state tree at a given epoch.
|
||||||
Lookback vm.LookbackStateGetter
|
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.
|
// 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()
|
params.Rand = NewFixedRand()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This lookback state returns the supplied precondition state tree, unconditionally.
|
if params.TipSetGetter == nil {
|
||||||
// This is obviously not correct, but the lookback state tree is only used to validate the
|
// TODO: If/when we start writing conformance tests against the EVM, we'll need to
|
||||||
// worker key when verifying a consensus fault. If the worker key hasn't changed in the
|
// actually implement this and (unfortunately) capture any tipsets looked up by
|
||||||
// current finality window, this workaround is enough.
|
// messages.
|
||||||
// The correct solutions are documented in https://github.com/filecoin-project/ref-fvm/issues/381,
|
params.TipSetGetter = func(context.Context, abi.ChainEpoch) (types.TipSetKey, error) {
|
||||||
// but they're much harder to implement, and the tradeoffs aren't clear.
|
return types.EmptyTSK, nil
|
||||||
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.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{
|
vmOpts := &vm.VMOpts{
|
||||||
@ -239,7 +253,8 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP
|
|||||||
Rand: params.Rand,
|
Rand: params.Rand,
|
||||||
BaseFee: params.BaseFee,
|
BaseFee: params.BaseFee,
|
||||||
NetworkVersion: params.NetworkVersion,
|
NetworkVersion: params.NetworkVersion,
|
||||||
LookbackState: lookback,
|
LookbackState: params.Lookback,
|
||||||
|
TipSetGetter: params.TipSetGetter,
|
||||||
}
|
}
|
||||||
|
|
||||||
var vmi vm.Interface
|
var vmi vm.Interface
|
||||||
|
Loading…
Reference in New Issue
Block a user