fix: stmgr: make the tipset and height agree when estimating gas (#10216)

* fix: stmgr: make the tipset and height agree when estimating gas

Specifically re-execute all messages in the current tipset, tacking the new
message onto the end. That way, the epoch is the epoch of the current tipset.

We could try to "make" a fake block and use that, but that's unlikely to
work well.

* fix: stmgr: only apply tipset messages for CallWithGas

* fix: itest: window post dispute
This commit is contained in:
Steven Allen 2023-02-10 12:49:26 -08:00 committed by GitHub
parent 1996694a2a
commit 01e616f29e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 23 deletions

View File

@ -44,12 +44,12 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
msg.Value = types.NewInt(0) msg.Value = types.NewInt(0)
} }
return sm.callInternal(ctx, msg, nil, ts, cid.Undef, sm.GetNetworkVersion, false) return sm.callInternal(ctx, msg, nil, ts, cid.Undef, sm.GetNetworkVersion, false, false)
} }
// CallWithGas calculates the state for a given tipset, and then applies the given message on top of that state. // CallWithGas calculates the state for a given tipset, and then applies the given message on top of that state.
func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet) (*api.InvocResult, error) { func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet) (*api.InvocResult, error) {
return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true) return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true, true)
} }
// CallAtStateAndVersion allows you to specify a message to execute on the given stateCid and network version. // CallAtStateAndVersion allows you to specify a message to execute on the given stateCid and network version.
@ -61,13 +61,13 @@ func (sm *StateManager) CallAtStateAndVersion(ctx context.Context, msg *types.Me
return v return v
} }
return sm.callInternal(ctx, msg, nil, nil, stateCid, nvGetter, true) return sm.callInternal(ctx, msg, nil, nil, stateCid, nvGetter, true, false)
} }
// - If no tipset is specified, the first tipset without an expensive migration or one in its parent is used. // - If no tipset is specified, the first tipset without an expensive migration or one in its parent is used.
// - If executing a message at a given tipset or its parent would trigger an expensive migration, the call will // - If executing a message at a given tipset or its parent would trigger an expensive migration, the call will
// fail with ErrExpensiveFork. // fail with ErrExpensiveFork.
func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, stateCid cid.Cid, nvGetter rand.NetworkVersionGetter, checkGas bool) (*api.InvocResult, error) { func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, stateCid cid.Cid, nvGetter rand.NetworkVersionGetter, checkGas, applyTsMessages bool) (*api.InvocResult, error) {
ctx, span := trace.StartSpan(ctx, "statemanager.callInternal") ctx, span := trace.StartSpan(ctx, "statemanager.callInternal")
defer span.End() defer span.End()
@ -107,22 +107,18 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
} }
} }
var vmHeight abi.ChainEpoch // Unless executing on a specific state cid, apply all the messages from the current tipset
if checkGas { // first. Unfortunately, we can't just execute the tipset, because that will run cron. We
// Since we're simulating a future message, pretend we're applying it in the "next" tipset // don't want to apply miner messages after cron runs in a given epoch.
vmHeight = ts.Height() + 1 if stateCid == cid.Undef {
if stateCid == cid.Undef { stateCid = ts.ParentState()
stateCid, _, err = sm.TipSetState(ctx, ts) }
if err != nil { if applyTsMessages {
return nil, xerrors.Errorf("computing tipset state: %w", err) tsMsgs, err := sm.cs.MessagesForTipset(ctx, ts)
} if err != nil {
} return nil, xerrors.Errorf("failed to lookup messages for parent tipset: %w", err)
} else {
// If we're not checking gas, we don't want to have to execute the tipset like above. This saves a lot of computation time
vmHeight = pts.Height() + 1
if stateCid == cid.Undef {
stateCid = ts.ParentState()
} }
priorMsgs = append(tsMsgs, priorMsgs...)
} }
// Technically, the tipset we're passing in here should be ts+1, but that may not exist. // Technically, the tipset we're passing in here should be ts+1, but that may not exist.
@ -142,14 +138,14 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
buffStore := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) buffStore := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync())
vmopt := &vm.VMOpts{ vmopt := &vm.VMOpts{
StateBase: stateCid, StateBase: stateCid,
Epoch: vmHeight, Epoch: ts.Height(),
Timestamp: ts.MinTimestamp(), Timestamp: ts.MinTimestamp(),
Rand: rand.NewStateRand(sm.cs, ts.Cids(), sm.beacon, nvGetter), Rand: rand.NewStateRand(sm.cs, ts.Cids(), sm.beacon, nvGetter),
Bstore: buffStore, Bstore: buffStore,
Actors: sm.tsExec.NewActorRegistry(), Actors: sm.tsExec.NewActorRegistry(),
Syscalls: sm.Syscalls, Syscalls: sm.Syscalls,
CircSupplyCalc: sm.GetVMCirculatingSupply, CircSupplyCalc: sm.GetVMCirculatingSupply,
NetworkVersion: nvGetter(ctx, vmHeight), NetworkVersion: nvGetter(ctx, ts.Height()),
BaseFee: ts.Blocks()[0].ParentBaseFee, BaseFee: ts.Blocks()[0].ParentBaseFee,
LookbackState: LookbackStateGetterForTipset(sm, ts), LookbackState: LookbackStateGetterForTipset(sm, ts),
TipSetGetter: TipSetGetterForTipset(sm.cs, ts), TipSetGetter: TipSetGetterForTipset(sm.cs, ts),

View File

@ -113,7 +113,7 @@ func TestWindowPostDispute(t *testing.T) {
//stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001 //stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001
di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK)
require.NoError(t, err) require.NoError(t, err)
if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.PeriodStart > 1 { if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.Open > 1 {
break break
} }
build.Clock.Sleep(blocktime) build.Clock.Sleep(blocktime)
@ -217,7 +217,8 @@ func TestWindowPostDispute(t *testing.T) {
//stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001 //stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001
di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK)
require.NoError(t, err) require.NoError(t, err)
if di.Index == evilSectorLoc.Deadline {
if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.Open > 1 {
break break
} }
build.Clock.Sleep(blocktime) build.Clock.Sleep(blocktime)