lotus/chain/stmgr/stmgr.go

361 lines
11 KiB
Go
Raw Normal View History

package stmgr
import (
"context"
"fmt"
2019-09-06 20:03:28 +00:00
"sync"
2020-09-14 11:45:20 +00:00
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2"
"golang.org/x/xerrors"
2020-09-14 11:45:20 +00:00
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/network"
2020-10-08 01:09:33 +00:00
// Used for genesis.
msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
"github.com/filecoin-project/specs-actors/v3/actors/migration/nv10"
2020-10-08 01:09:33 +00:00
2020-02-08 02:18:32 +00:00
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
)
2021-04-05 11:23:46 +00:00
const LookbackNoLimit = api.LookbackNoLimit
2021-01-16 04:12:31 +00:00
const ReceiptAmtBitwidth = 3
2020-10-02 14:14:30 +00:00
var log = logging.Logger("statemgr")
type StateManagerAPI interface {
Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error)
GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error)
LoadActorTsk(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error)
2020-09-30 14:36:16 +00:00
LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error)
ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error)
}
type versionSpec struct {
networkVersion network.Version
atOrBelow abi.ChainEpoch
}
type migration struct {
upgrade MigrationFunc
preMigrations []PreMigration
cache *nv10.MemMigrationCache
}
type StateManager struct {
cs *store.ChainStore
2019-09-06 20:03:28 +00:00
2021-01-26 23:11:31 +00:00
cancel context.CancelFunc
shutdown chan struct{}
// Determines the network version at any given epoch.
networkVersions []versionSpec
latestVersion network.Version
// Maps chain epochs to migrations.
stateMigrations map[abi.ChainEpoch]*migration
2020-10-07 23:14:11 +00:00
// 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{}
2020-11-13 08:25:29 +00:00
stCache map[string][]cid.Cid
2021-05-31 22:12:42 +00:00
tCache treeCache
2020-11-13 08:25:29 +00:00
compWait map[string]chan struct{}
stlk sync.Mutex
genesisMsigLk sync.Mutex
newVM func(context.Context, *vm.VMOpts) (*vm.VM, error)
2021-07-27 13:30:23 +00:00
syscalls vm.SyscallBuilder
2020-11-13 08:25:29 +00:00
preIgnitionVesting []msig0.State
postIgnitionVesting []msig0.State
postCalicoVesting []msig0.State
genesisPledge abi.TokenAmount
genesisMarketFunds abi.TokenAmount
tsExecMonitor ExecMonitor
}
2021-05-31 22:12:42 +00:00
// Caches a single state tree
type treeCache struct {
root cid.Cid
tree *state.StateTree
}
2021-07-27 13:30:23 +00:00
func NewStateManager(cs *store.ChainStore, sys vm.SyscallBuilder) *StateManager {
sm, err := NewStateManagerWithUpgradeSchedule(cs, sys, DefaultUpgradeSchedule())
if err != nil {
panic(fmt.Sprintf("default upgrade schedule is invalid: %s", err))
}
return sm
}
2021-07-27 13:30:23 +00:00
func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, sys vm.SyscallBuilder, us UpgradeSchedule) (*StateManager, error) {
// If we have upgrades, make sure they're in-order and make sense.
if err := us.Validate(); err != nil {
return nil, err
}
stateMigrations := make(map[abi.ChainEpoch]*migration, len(us))
2020-10-07 23:14:11 +00:00
expensiveUpgrades := make(map[abi.ChainEpoch]struct{}, len(us))
var networkVersions []versionSpec
lastVersion := network.Version0
if len(us) > 0 {
// If we have any upgrades, process them and create a version
// schedule.
for _, upgrade := range us {
if upgrade.Migration != nil || upgrade.PreMigrations != nil {
migration := &migration{
upgrade: upgrade.Migration,
preMigrations: upgrade.PreMigrations,
cache: nv10.NewMemMigrationCache(),
}
stateMigrations[upgrade.Height] = migration
}
2020-10-07 23:14:11 +00:00
if upgrade.Expensive {
expensiveUpgrades[upgrade.Height] = struct{}{}
}
networkVersions = append(networkVersions, versionSpec{
networkVersion: lastVersion,
atOrBelow: upgrade.Height,
})
lastVersion = upgrade.Network
}
} else {
// Otherwise, go directly to the latest version.
lastVersion = build.NewestNetworkVersion
}
2019-09-06 20:03:28 +00:00
return &StateManager{
2020-10-07 23:14:11 +00:00
networkVersions: networkVersions,
latestVersion: lastVersion,
stateMigrations: stateMigrations,
expensiveUpgrades: expensiveUpgrades,
newVM: vm.NewVM,
2021-07-27 13:30:23 +00:00
syscalls: sys,
2020-10-07 23:14:11 +00:00
cs: cs,
stCache: make(map[string][]cid.Cid),
2021-05-31 22:12:42 +00:00
tCache: treeCache{
root: cid.Undef,
tree: nil,
},
compWait: make(map[string]chan struct{}),
}, nil
2019-09-06 20:03:28 +00:00
}
2021-07-27 13:30:23 +00:00
func NewStateManagerWithUpgradeScheduleAndMonitor(cs *store.ChainStore, sys vm.SyscallBuilder, us UpgradeSchedule, em ExecMonitor) (*StateManager, error) {
sm, err := NewStateManagerWithUpgradeSchedule(cs, sys, us)
if err != nil {
return nil, err
}
sm.tsExecMonitor = em
return sm, nil
}
2019-09-06 20:03:28 +00:00
func cidsToKey(cids []cid.Cid) string {
var out string
for _, c := range cids {
out += c.KeyString()
}
return out
}
// Start starts the state manager's optional background processes. At the moment, this schedules
// pre-migration functions to run ahead of network upgrades.
//
// This method is not safe to invoke from multiple threads or concurrently with Stop.
func (sm *StateManager) Start(context.Context) error {
2021-01-26 23:11:31 +00:00
var ctx context.Context
ctx, sm.cancel = context.WithCancel(context.Background())
sm.shutdown = make(chan struct{})
go sm.preMigrationWorker(ctx)
return nil
}
// Stop starts the state manager's background processes.
//
// This method is not safe to invoke concurrently with Start.
2021-01-26 23:11:31 +00:00
func (sm *StateManager) Stop(ctx context.Context) error {
if sm.cancel != nil {
sm.cancel()
2021-01-26 23:11:31 +00:00
select {
case <-sm.shutdown:
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
func (sm *StateManager) ChainStore() *store.ChainStore {
return sm.cs
}
// ResolveToKeyAddress is similar to `vm.ResolveToKeyAddr` but does not allow `Actor` type of addresses.
// Uses the `TipSet` `ts` to generate the VM state.
func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
switch addr.Protocol() {
case address.BLS, address.SECP256K1:
return addr, nil
case address.Actor:
return address.Undef, xerrors.New("cannot resolve actor address to key address")
default:
}
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
cst := cbor.NewCborStore(sm.cs.StateBlockstore())
// First try to resolve the actor in the parent state, so we don't have to compute anything.
tree, err := state.LoadStateTree(cst, ts.ParentState())
if err != nil {
return address.Undef, xerrors.Errorf("failed to load parent state tree: %w", err)
}
resolved, err := vm.ResolveToKeyAddr(tree, cst, addr)
if err == nil {
return resolved, nil
}
// If that fails, compute the tip-set and try again.
2019-10-10 11:13:26 +00:00
st, _, err := sm.TipSetState(ctx, ts)
if err != nil {
return address.Undef, xerrors.Errorf("resolve address failed to get tipset state: %w", err)
}
tree, err = state.LoadStateTree(cst, st)
if err != nil {
return address.Undef, xerrors.Errorf("failed to load state tree")
}
return vm.ResolveToKeyAddr(tree, cst, addr)
}
2021-05-31 22:12:42 +00:00
// ResolveToKeyAddressAtFinality is similar to stmgr.ResolveToKeyAddress but fails if the ID address being resolved isn't reorg-stable yet.
// It should not be used for consensus-critical subsystems.
2021-05-31 22:12:42 +00:00
func (sm *StateManager) ResolveToKeyAddressAtFinality(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
switch addr.Protocol() {
case address.BLS, address.SECP256K1:
return addr, nil
case address.Actor:
return address.Undef, xerrors.New("cannot resolve actor address to key address")
default:
}
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
2021-05-31 22:12:42 +00:00
var err error
if ts.Height() > policy.ChainFinality {
ts, err = sm.ChainStore().GetTipsetByHeight(ctx, ts.Height()-policy.ChainFinality, ts, true)
if err != nil {
return address.Undef, xerrors.Errorf("failed to load lookback tipset: %w", err)
}
}
cst := cbor.NewCborStore(sm.cs.StateBlockstore())
2021-05-31 22:12:42 +00:00
tree := sm.tCache.tree
2021-05-31 22:12:42 +00:00
if tree == nil || sm.tCache.root != ts.ParentState() {
tree, err = state.LoadStateTree(cst, ts.ParentState())
if err != nil {
return address.Undef, xerrors.Errorf("failed to load parent state tree: %w", err)
}
sm.tCache = treeCache{
root: ts.ParentState(),
tree: tree,
}
}
resolved, err := vm.ResolveToKeyAddr(tree, cst, addr)
if err == nil {
return resolved, nil
}
return address.Undef, xerrors.New("ID address not found in lookback state")
}
func (sm *StateManager) GetBlsPublicKey(ctx context.Context, addr address.Address, ts *types.TipSet) (pubk []byte, err error) {
kaddr, err := sm.ResolveToKeyAddress(ctx, addr, ts)
if err != nil {
return pubk, xerrors.Errorf("failed to resolve address to key address: %w", err)
}
if kaddr.Protocol() != address.BLS {
return pubk, xerrors.Errorf("address must be BLS address to load bls public key")
}
return kaddr.Payload(), nil
}
2020-02-23 15:50:36 +00:00
func (sm *StateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
cst := cbor.NewCborStore(sm.cs.StateBlockstore())
2020-09-14 22:43:12 +00:00
state, err := state.LoadStateTree(cst, sm.parentState(ts))
2020-02-23 15:50:36 +00:00
if err != nil {
return address.Undef, xerrors.Errorf("load state tree: %w", err)
}
return state.LookupID(addr)
}
2020-01-21 01:53:55 +00:00
func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) error {
tschain := []*types.TipSet{ts}
for ts.Height() != 0 {
next, err := sm.cs.LoadTipSet(ts.Parents())
if err != nil {
return err
}
tschain = append(tschain, next)
ts = next
}
lastState := tschain[len(tschain)-1].ParentState()
for i := len(tschain) - 1; i >= 0; i-- {
cur := tschain[i]
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
if cur.ParentState() != lastState {
return xerrors.Errorf("tipset chain had state mismatch at height %d", cur.Height())
}
st, _, err := sm.TipSetState(ctx, cur)
if err != nil {
return err
}
lastState = st
}
return nil
}
2020-09-14 12:17:45 +00:00
func (sm *StateManager) SetVMConstructor(nvm func(context.Context, *vm.VMOpts) (*vm.VM, error)) {
sm.newVM = nvm
}
2020-07-25 22:29:33 +00:00
2020-09-14 11:45:20 +00:00
func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoch) network.Version {
2020-10-01 22:33:32 +00:00
// The epochs here are the _last_ epoch for every version, or -1 if the
// version is disabled.
for _, spec := range sm.networkVersions {
if height <= spec.atOrBelow {
return spec.networkVersion
}
2020-09-28 22:58:07 +00:00
}
return sm.latestVersion
}
2021-07-27 13:49:01 +00:00
func (sm *StateManager) VMSys() vm.SyscallBuilder {
return sm.syscalls
}