only forbid Call* at expensive forks
This commit is contained in:
parent
a4e954197c
commit
e8253d22c6
@ -18,7 +18,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
)
|
||||
|
||||
var ErrWouldFork = errors.New("refusing explicit call due to state fork at epoch")
|
||||
var ErrExpensiveFork = 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) {
|
||||
ctx, span := trace.StartSpan(ctx, "statemanager.Call")
|
||||
@ -29,7 +29,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
|
||||
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) {
|
||||
for ts.Height() > 0 && sm.hasExpensiveFork(ctx, ts.Height()-1) {
|
||||
var err error
|
||||
ts, err = sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
if err != nil {
|
||||
@ -41,14 +41,15 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
|
||||
bstate := ts.ParentState()
|
||||
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.
|
||||
// If we have to run an expensive 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
|
||||
if bheight-1 > 0 && sm.hasExpensiveFork(ctx, bheight-1) {
|
||||
return nil, ErrExpensiveFork
|
||||
}
|
||||
|
||||
// Run the (not expensive) migration.
|
||||
bstate, err := sm.handleStateForks(ctx, bstate, bheight-1, nil, ts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to handle fork: %w", err)
|
||||
@ -133,7 +134,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
||||
// 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)) {
|
||||
for ts.Height() > 0 && (sm.hasExpensiveFork(ctx, ts.Height()) || sm.hasExpensiveFork(ctx, ts.Height()-1)) {
|
||||
var err error
|
||||
ts, err = sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
if err != nil {
|
||||
@ -142,9 +143,9 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// When we're not at the genesis block, make sure we don't have an expensive migration.
|
||||
if ts.Height() > 0 && (sm.hasExpensiveFork(ctx, ts.Height()) || sm.hasExpensiveFork(ctx, ts.Height()-1)) {
|
||||
return nil, ErrExpensiveFork
|
||||
}
|
||||
|
||||
state, _, err := sm.TipSetState(ctx, ts)
|
||||
|
@ -45,6 +45,7 @@ type UpgradeFunc func(ctx context.Context, sm *StateManager, cb ExecCallback, ol
|
||||
type Upgrade struct {
|
||||
Height abi.ChainEpoch
|
||||
Network network.Version
|
||||
Expensive bool
|
||||
Migration UpgradeFunc
|
||||
}
|
||||
|
||||
@ -68,6 +69,7 @@ func DefaultUpgradeSchedule() UpgradeSchedule {
|
||||
}, {
|
||||
Height: build.UpgradeActorsV2Height,
|
||||
Network: network.Version4,
|
||||
Expensive: true,
|
||||
Migration: UpgradeActorsV2,
|
||||
}, {
|
||||
Height: build.UpgradeLiftoffHeight,
|
||||
@ -124,8 +126,8 @@ func (sm *StateManager) handleStateForks(ctx context.Context, root cid.Cid, heig
|
||||
return retCid, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) hasStateFork(ctx context.Context, height abi.ChainEpoch) bool {
|
||||
_, ok := sm.stateMigrations[height]
|
||||
func (sm *StateManager) hasExpensiveFork(ctx context.Context, height abi.ChainEpoch) bool {
|
||||
_, ok := sm.expensiveUpgrades[height]
|
||||
return ok
|
||||
}
|
||||
|
||||
|
@ -248,6 +248,7 @@ func TestForkRefuseCall(t *testing.T) {
|
||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||
cg.ChainStore(), UpgradeSchedule{{
|
||||
Network: 1,
|
||||
Expensive: true,
|
||||
Height: testForkHeight,
|
||||
Migration: func(ctx context.Context, sm *StateManager, cb ExecCallback,
|
||||
root cid.Cid, height abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) {
|
||||
@ -297,7 +298,7 @@ func TestForkRefuseCall(t *testing.T) {
|
||||
switch ts.TipSet.TipSet().Height() {
|
||||
case testForkHeight, testForkHeight + 1:
|
||||
// If I had a fork, or I _will_ have a fork, it should fail.
|
||||
require.Equal(t, ErrWouldFork, err)
|
||||
require.Equal(t, ErrExpensiveFork, err)
|
||||
default:
|
||||
require.NoError(t, err)
|
||||
require.True(t, ret.MsgRct.ExitCode.IsSuccess())
|
||||
@ -307,7 +308,7 @@ func TestForkRefuseCall(t *testing.T) {
|
||||
ret, err = sm.Call(ctx, m, ts.TipSet.TipSet())
|
||||
switch ts.TipSet.TipSet().Height() {
|
||||
case testForkHeight + 1:
|
||||
require.Equal(t, ErrWouldFork, err)
|
||||
require.Equal(t, ErrExpensiveFork, err)
|
||||
default:
|
||||
require.NoError(t, err)
|
||||
require.True(t, ret.MsgRct.ExitCode.IsSuccess())
|
||||
|
@ -53,6 +53,10 @@ type StateManager struct {
|
||||
|
||||
// Maps chain epochs to upgrade functions.
|
||||
stateMigrations map[abi.ChainEpoch]UpgradeFunc
|
||||
// A set of potentially expensive/time consuming upgrades. Explicit
|
||||
// calls for, e.g., gas estimation fail against this epoch with
|
||||
// ErrExpensiveFork.
|
||||
expensiveUpgrades map[abi.ChainEpoch]struct{}
|
||||
|
||||
stCache map[string][]cid.Cid
|
||||
compWait map[string]chan struct{}
|
||||
@ -78,6 +82,7 @@ func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule
|
||||
}
|
||||
|
||||
stateMigrations := make(map[abi.ChainEpoch]UpgradeFunc, len(us))
|
||||
expensiveUpgrades := make(map[abi.ChainEpoch]struct{}, len(us))
|
||||
var networkVersions []versionSpec
|
||||
lastVersion := network.Version0
|
||||
if len(us) > 0 {
|
||||
@ -87,6 +92,9 @@ func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule
|
||||
if upgrade.Migration != nil {
|
||||
stateMigrations[upgrade.Height] = upgrade.Migration
|
||||
}
|
||||
if upgrade.Expensive {
|
||||
expensiveUpgrades[upgrade.Height] = struct{}{}
|
||||
}
|
||||
networkVersions = append(networkVersions, versionSpec{
|
||||
networkVersion: lastVersion,
|
||||
atOrBelow: upgrade.Height,
|
||||
@ -102,6 +110,7 @@ func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule
|
||||
networkVersions: networkVersions,
|
||||
latestVersion: lastVersion,
|
||||
stateMigrations: stateMigrations,
|
||||
expensiveUpgrades: expensiveUpgrades,
|
||||
newVM: vm.NewVM,
|
||||
cs: cs,
|
||||
stCache: make(map[string][]cid.Cid),
|
||||
|
@ -164,7 +164,7 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message,
|
||||
var res *api.InvocResult
|
||||
for {
|
||||
res, err = a.Stmgr.CallWithGas(ctx, &msg, priorMsgs, ts)
|
||||
if err != stmgr.ErrWouldFork {
|
||||
if err != stmgr.ErrExpensiveFork {
|
||||
break
|
||||
}
|
||||
ts, err = a.Chain.GetTipSetFromKey(ts.Parents())
|
||||
|
@ -314,7 +314,7 @@ func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, tsk types.
|
||||
}
|
||||
for {
|
||||
res, err = a.StateManager.Call(ctx, msg, ts)
|
||||
if err != stmgr.ErrWouldFork {
|
||||
if err != stmgr.ErrExpensiveFork {
|
||||
break
|
||||
}
|
||||
ts, err = a.Chain.GetTipSetFromKey(ts.Parents())
|
||||
|
Loading…
Reference in New Issue
Block a user