avoid estimating gas and explicitly calling blocks on fork tipsets
These tipsets can be slow.
This commit is contained in:
parent
fe912223bd
commit
dab1107f5b
@ -2,6 +2,7 @@ package stmgr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
@ -17,17 +18,37 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/chain/vm"
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrWouldFork = errors.New("refusing explicit call due to state fork at epoch")
|
||||||
|
|
||||||
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
|
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
|
||||||
ctx, span := trace.StartSpan(ctx, "statemanager.Call")
|
ctx, span := trace.StartSpan(ctx, "statemanager.Call")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
// If no tipset is provided, try to find one without a fork.
|
||||||
if ts == nil {
|
if ts == nil {
|
||||||
ts = sm.cs.GetHeaviestTipSet()
|
ts = sm.cs.GetHeaviestTipSet()
|
||||||
|
|
||||||
|
// Search back till we find a height with no fork, or we reach the beginning.
|
||||||
|
for ts.Height() > 0 && sm.hasStateFork(ctx, ts.Height()-1) {
|
||||||
|
var err error
|
||||||
|
ts, err = sm.cs.GetTipSetFromKey(ts.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to find a non-forking epoch: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bstate := ts.ParentState()
|
bstate := ts.ParentState()
|
||||||
bheight := ts.Height()
|
bheight := ts.Height()
|
||||||
|
|
||||||
|
// If we have to run a migration, and we're not at genesis, return an
|
||||||
|
// error because the migration will take too long.
|
||||||
|
//
|
||||||
|
// We allow this at height 0 for at-genesis migrations (for testing).
|
||||||
|
if bheight-1 > 0 && sm.hasStateFork(ctx, bheight-1) {
|
||||||
|
return nil, ErrWouldFork
|
||||||
|
}
|
||||||
|
|
||||||
bstate, err := sm.handleStateForks(ctx, bstate, bheight-1, nil, ts)
|
bstate, err := sm.handleStateForks(ctx, bstate, bheight-1, nil, ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to handle fork: %w", err)
|
return nil, fmt.Errorf("failed to handle fork: %w", err)
|
||||||
@ -106,6 +127,24 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
|||||||
|
|
||||||
if ts == nil {
|
if ts == nil {
|
||||||
ts = sm.cs.GetHeaviestTipSet()
|
ts = sm.cs.GetHeaviestTipSet()
|
||||||
|
|
||||||
|
// Search back till we find a height with no fork, or we reach the beginning.
|
||||||
|
// We need the _previous_ height to have no fork, because we'll
|
||||||
|
// run the fork logic in `sm.TipSetState`. We need the _current_
|
||||||
|
// height to have no fork, because we'll run it inside this
|
||||||
|
// function before executing the given message.
|
||||||
|
for ts.Height() > 0 && (sm.hasStateFork(ctx, ts.Height()) || sm.hasStateFork(ctx, ts.Height()-1)) {
|
||||||
|
var err error
|
||||||
|
ts, err = sm.cs.GetTipSetFromKey(ts.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to find a non-forking epoch: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When we're not at the genesis block, make sure we're at a migration height.
|
||||||
|
if ts.Height() > 0 && (sm.hasStateFork(ctx, ts.Height()) || sm.hasStateFork(ctx, ts.Height()-1)) {
|
||||||
|
return nil, ErrWouldFork
|
||||||
}
|
}
|
||||||
|
|
||||||
state, _, err := sm.TipSetState(ctx, ts)
|
state, _, err := sm.TipSetState(ctx, ts)
|
||||||
|
@ -124,6 +124,11 @@ func (sm *StateManager) handleStateForks(ctx context.Context, root cid.Cid, heig
|
|||||||
return retCid, nil
|
return retCid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sm *StateManager) hasStateFork(ctx context.Context, height abi.ChainEpoch) bool {
|
||||||
|
_, ok := sm.stateMigrations[height]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func doTransfer(cb ExecCallback, tree types.StateTree, from, to address.Address, amt abi.TokenAmount) error {
|
func doTransfer(cb ExecCallback, tree types.StateTree, from, to address.Address, amt abi.TokenAmount) error {
|
||||||
fromAct, err := tree.GetActor(from)
|
fromAct, err := tree.GetActor(from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -160,7 +160,18 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message,
|
|||||||
priorMsgs = append(priorMsgs, m)
|
priorMsgs = append(priorMsgs, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := a.Stmgr.CallWithGas(ctx, &msg, priorMsgs, ts)
|
// Try calling until we find a height with no migration.
|
||||||
|
var res *api.InvocResult
|
||||||
|
for {
|
||||||
|
res, err = a.Stmgr.CallWithGas(ctx, &msg, priorMsgs, ts)
|
||||||
|
if err != stmgr.ErrWouldFork {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ts, err = a.Chain.GetTipSetFromKey(ts.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return -1, xerrors.Errorf("getting parent tipset: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, xerrors.Errorf("CallWithGas failed: %w", err)
|
return -1, xerrors.Errorf("CallWithGas failed: %w", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user