lotus/cmd/lotus-sim/simulation/step.go

72 lines
2.3 KiB
Go

package simulation
import (
"context"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/blockbuilder"
)
// Step steps the simulation forward one step. This may move forward by more than one epoch.
func (sim *Simulation) Step(ctx context.Context) (*types.TipSet, error) {
log.Infow("step", "epoch", sim.head.Height()+1)
messages, err := sim.popNextMessages(ctx)
if err != nil {
return nil, xerrors.Errorf("failed to select messages for block: %w", err)
}
head, err := sim.makeTipSet(ctx, messages)
if err != nil {
return nil, xerrors.Errorf("failed to make tipset: %w", err)
}
if err := sim.SetHead(head); err != nil {
return nil, xerrors.Errorf("failed to update head: %w", err)
}
return head, nil
}
// popNextMessages generates/picks a set of messages to be included in the next block.
//
// - This function is destructive and should only be called once per epoch.
// - This function does not store anything in the repo.
// - This function handles all gas estimation. The returned messages should all fit in a single
// block.
func (sim *Simulation) popNextMessages(ctx context.Context) ([]*types.Message, error) {
parentTs := sim.head
// First we make sure we don't have an upgrade at this epoch. If we do, we return no
// messages so we can just create an empty block at that epoch.
//
// This isn't what the network does, but it makes things easier. Otherwise, we'd need to run
// migrations before this epoch and I'd rather not deal with that.
nextHeight := parentTs.Height() + 1
prevVer := sim.StateManager.GetNetworkVersion(ctx, nextHeight-1)
nextVer := sim.StateManager.GetNetworkVersion(ctx, nextHeight)
if nextVer != prevVer {
log.Warnw("packing no messages for version upgrade block",
"old", prevVer,
"new", nextVer,
"epoch", nextHeight,
)
return nil, nil
}
bb, err := blockbuilder.NewBlockBuilder(
ctx, log.With("simulation", sim.name),
sim.StateManager, parentTs,
)
if err != nil {
return nil, err
}
for _, stage := range sim.stages {
// We're intentionally ignoring the "full" signal so we can try to pack a few more
// messages.
if err := stage.PackMessages(ctx, bb); err != nil && !blockbuilder.IsOutOfGas(err) {
return nil, xerrors.Errorf("when packing messages with %s: %w", stage.Name(), err)
}
}
return bb.Messages(), nil
}