Make state tipset usage consistent in the API

_Always_ (almost) use the tipset's parent state, instead of computing.

Exceptions:

* MinerGetBaseInfo. Fixing this would break things so we need to be
careful (although we could bump the API version, fix it, then fix the call
sites).
* StateReplay. This is replaying a message on top of the given tipset.
* GasEstimateGasLimit. This executes the message on-top-of the tipset's
computed state (unlike call which executes it on the tipset's parent state).
  * Having this method and Call apply the message at different heights is really
  weird.
This commit is contained in:
Steven Allen 2020-10-22 12:09:30 -07:00
parent e1be89b442
commit b8e3808c4f
4 changed files with 24 additions and 94 deletions

View File

@ -331,10 +331,14 @@ type FullNode interface {
// MethodGroup: State
// The State methods are used to query, inspect, and interact with chain state.
// Most methods take a TipSetKey as a parameter. The state looked up is the state at that tipset.
// Most methods take a TipSetKey as a parameter. The state looked up is the parent state of the tipset.
// A nil TipSetKey can be provided as a param, this will cause the heaviest tipset in the chain to be used.
// StateCall runs the given message and returns its result without any persisted changes.
//
// StateCall applies the message to the tipset's parent state. The
// message is not applied on-top-of the messages in the passed-in
// tipset.
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error)
// StateReplay replays a given message, assuming it was included in a block in the specified tipset.
// If no tipset key is provided, the appropriate tipset is looked up.

View File

@ -144,20 +144,6 @@ func MinerSectorInfo(ctx context.Context, sm *StateManager, maddr address.Addres
return mas.GetSector(sid)
}
func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address, snos *bitfield.BitField) ([]*miner.SectorOnChainInfo, error) {
act, err := sm.LoadActor(ctx, maddr, ts)
if err != nil {
return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err)
}
mas, err := miner.Load(sm.cs.Store(ctx), act)
if err != nil {
return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
}
return mas.LoadSectors(snos)
}
func GetSectorsForWinningPoSt(ctx context.Context, nv network.Version, pv ffiwrapper.Verifier, sm *StateManager, st cid.Cid, maddr address.Address, rand abi.PoStRandomness) ([]builtin.SectorInfo, error) {
act, err := sm.LoadActorRaw(ctx, maddr, st)
if err != nil {

View File

@ -21,7 +21,6 @@ import (
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/journal"
@ -506,33 +505,3 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type
WinningPoStProof: wpostProof,
})
}
type actCacheEntry struct {
act *types.Actor
err error
}
type cachedActorLookup struct {
tsk types.TipSetKey
cache map[address.Address]actCacheEntry
fallback gasguess.ActorLookup
}
func (c *cachedActorLookup) StateGetActor(ctx context.Context, a address.Address, tsk types.TipSetKey) (*types.Actor, error) {
if c.tsk == tsk {
e, has := c.cache[a]
if has {
return e.act, e.err
}
}
e, err := c.fallback(ctx, a, tsk)
if c.tsk == tsk {
c.cache[a] = actCacheEntry{
act: e, err: err,
}
}
return e, err
}
type ActorLookup func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error)

View File

@ -6,7 +6,6 @@ import (
"strconv"
cid "github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"go.uber.org/fx"
"golang.org/x/xerrors"
@ -35,7 +34,6 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/lib/bufbstore"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
@ -92,19 +90,20 @@ func (a *StateAPI) StateNetworkName(ctx context.Context) (dtypes.NetworkName, er
}
func (a *StateAPI) StateMinerSectors(ctx context.Context, addr address.Address, sectorNos *bitfield.BitField, tsk types.TipSetKey) ([]*miner.SectorOnChainInfo, error) {
ts, err := a.Chain.GetTipSetFromKey(tsk)
act, err := a.StateManager.LoadActorTsk(ctx, addr, tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
return nil, xerrors.Errorf("failed to load miner actor: %w", err)
}
return stmgr.GetMinerSectorSet(ctx, a.StateManager, ts, addr, sectorNos)
mas, err := miner.Load(a.StateManager.ChainStore().Store(ctx), act)
if err != nil {
return nil, xerrors.Errorf("failed to load miner actor state: %w", err)
}
return mas.LoadSectors(sectorNos)
}
func (a *StateAPI) StateMinerActiveSectors(ctx context.Context, maddr address.Address, tsk types.TipSetKey) ([]*miner.SectorOnChainInfo, error) { // TODO: only used in cli
ts, err := a.Chain.GetTipSetFromKey(tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
act, err := a.StateManager.LoadActorTsk(ctx, maddr, tsk)
if err != nil {
return nil, xerrors.Errorf("failed to load miner actor: %w", err)
@ -120,7 +119,7 @@ func (a *StateAPI) StateMinerActiveSectors(ctx context.Context, maddr address.Ad
return nil, xerrors.Errorf("merge partition active sets: %w", err)
}
return stmgr.GetMinerSectorSet(ctx, a.StateManager, ts, maddr, &activeSectors)
return mas.LoadSectors(&activeSectors)
}
func (m *StateModule) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error) {
@ -423,38 +422,12 @@ func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.
}, nil
}
func stateForTs(ctx context.Context, ts *types.TipSet, cstore *store.ChainStore, smgr *stmgr.StateManager) (*state.StateTree, error) {
if ts == nil {
ts = cstore.GetHeaviestTipSet()
}
st, _, err := smgr.TipSetState(ctx, ts)
if err != nil {
return nil, err
}
buf := bufbstore.NewBufferedBstore(cstore.Blockstore())
cst := cbor.NewCborStore(buf)
return state.LoadStateTree(cst, st)
}
func (a *StateAPI) stateForTs(ctx context.Context, ts *types.TipSet) (*state.StateTree, error) {
return stateForTs(ctx, ts, a.Chain, a.StateManager)
}
func (m *StateModule) stateForTs(ctx context.Context, ts *types.TipSet) (*state.StateTree, error) {
return stateForTs(ctx, ts, m.Chain, m.StateManager)
}
func (m *StateModule) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
ts, err := m.Chain.GetTipSetFromKey(tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
state, err := m.stateForTs(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("computing tipset state failed: %w", err)
}
return state.GetActor(actor)
return m.StateManager.LoadActor(ctx, actor, ts)
}
func (m *StateModule) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
@ -480,17 +453,12 @@ func (a *StateAPI) StateReadState(ctx context.Context, actor address.Address, ts
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
state, err := a.stateForTs(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("getting state for tipset: %w", err)
}
act, err := state.GetActor(actor)
act, err := a.StateManager.LoadActor(ctx, actor, ts)
if err != nil {
return nil, xerrors.Errorf("getting actor: %w", err)
}
blk, err := state.Store.(*cbor.BasicIpldStore).Blocks.Get(act.Head)
blk, err := a.Chain.Blockstore().Get(act.Head)
if err != nil {
return nil, xerrors.Errorf("getting actor head: %w", err)
}
@ -526,6 +494,7 @@ func (a *StateAPI) StateDecodeParams(ctx context.Context, toAddr address.Address
// This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
func (a *StateAPI) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
// XXX: Gets the state by computing the tipset state, instead of looking at the parent.
return stmgr.MinerGetBaseInfo(ctx, a.StateManager, a.Beacon, tsk, epoch, maddr, a.ProofVerifier)
}
@ -1329,11 +1298,11 @@ func (a *StateAPI) StateCirculatingSupply(ctx context.Context, tsk types.TipSetK
return types.EmptyInt, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
sTree, err := a.stateForTs(ctx, ts)
sTree, err := a.StateManager.ParentState(ts)
if err != nil {
return types.EmptyInt, err
}
return a.StateManager.GetCirculatingSupply(ctx, ts.Height(), sTree)
return a.StateManager.GetCirculatingSupply(ctx, ts.Height()-1, sTree)
}
func (a *StateAPI) StateVMCirculatingSupplyInternal(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) {
@ -1350,7 +1319,7 @@ func stateVMCirculatingSupplyInternal(
return api.CirculatingSupply{}, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
sTree, err := stateForTs(ctx, ts, cstore, smgr)
sTree, err := smgr.ParentState(ts)
if err != nil {
return api.CirculatingSupply{}, err
}
@ -1364,5 +1333,7 @@ func (m *StateModule) StateNetworkVersion(ctx context.Context, tsk types.TipSetK
return network.VersionMax, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
// TODO: Height-1 to be consistent with the rest of the APIs?
// But that's likely going to break a bunch of stuff.
return m.StateManager.GetNtwkVersion(ctx, ts.Height()), nil
}