diff --git a/api/api_full.go b/api/api_full.go index a84d393ad50..7aea4c4433b 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -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. diff --git a/build/version.go b/build/version.go index fe9fc07c586..a53da927447 100644 --- a/build/version.go +++ b/build/version.go @@ -89,7 +89,7 @@ func VersionForType(nodeType NodeType) (Version, error) { // semver versions of the rpc api exposed var ( - FullAPIVersion = newVer(1, 0, 0) + FullAPIVersion = newVer(1, 1, 0) MinerAPIVersion = newVer(1, 0, 1) WorkerAPIVersion = newVer(1, 0, 0) ) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 1d59b263007..86bb3a6e093 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -145,20 +145,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 { diff --git a/chain/sync_test.go b/chain/sync_test.go index 559a73bf525..9570eda328f 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -573,11 +573,14 @@ func TestDuplicateNonce(t *testing.T) { base := tu.g.CurTipset + // Get the banker from computed tipset state, not the parent. + st, _, err := tu.g.StateManager().TipSetState(context.TODO(), base.TipSet()) + require.NoError(t, err) + ba, err := tu.g.StateManager().LoadActorRaw(context.TODO(), tu.g.Banker(), st) + require.NoError(t, err) + // Produce a message from the banker to the rcvr makeMsg := func(rcvr address.Address) *types.SignedMessage { - - ba, err := tu.nds[0].StateGetActor(context.TODO(), tu.g.Banker(), base.TipSet().Key()) - require.NoError(t, err) msg := types.Message{ To: rcvr, From: tu.g.Banker(), diff --git a/documentation/en/api-methods-miner.md b/documentation/en/api-methods-miner.md index 66512a02c73..19c85385028 100644 --- a/documentation/en/api-methods-miner.md +++ b/documentation/en/api-methods-miner.md @@ -168,7 +168,7 @@ Response: ```json { "Version": "string value", - "APIVersion": 65536, + "APIVersion": 65792, "BlockDelay": 42 } ``` diff --git a/documentation/en/api-methods-worker.md b/documentation/en/api-methods-worker.md index 2224a73f9ae..f86fa52e07d 100644 --- a/documentation/en/api-methods-worker.md +++ b/documentation/en/api-methods-worker.md @@ -146,7 +146,7 @@ Perms: admin Inputs: `null` -Response: `65536` +Response: `65792` ## Add diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 839c3271b4c..a35481135ae 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -254,7 +254,7 @@ Response: ```json { "Version": "string value", - "APIVersion": 65536, + "APIVersion": 65792, "BlockDelay": 42 } ``` @@ -3309,7 +3309,7 @@ Response: ## 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. @@ -3362,6 +3362,10 @@ Response: `null` ### StateCall 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. + Perms: read diff --git a/miner/miner.go b/miner/miner.go index 4075c8cc6f0..efdbe489652 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -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) diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 4377571aec3..04105b22575 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -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" ) @@ -94,19 +92,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) -} -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) + mas, err := miner.Load(a.StateManager.ChainStore().Store(ctx), act) if err != nil { - return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + 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 act, err := a.StateManager.LoadActorTsk(ctx, maddr, tsk) if err != nil { return nil, xerrors.Errorf("failed to load miner actor: %w", err) @@ -122,7 +121,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) { @@ -426,38 +425,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) { @@ -483,17 +456,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) } @@ -529,6 +497,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) } @@ -1372,11 +1341,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) { @@ -1393,7 +1362,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 } @@ -1407,5 +1376,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 } diff --git a/node/modules/mpoolnonceapi.go b/node/modules/mpoolnonceapi.go index 294f4d95478..efcb14037db 100644 --- a/node/modules/mpoolnonceapi.go +++ b/node/modules/mpoolnonceapi.go @@ -4,6 +4,7 @@ import ( "context" "go.uber.org/fx" + "golang.org/x/xerrors" "github.com/filecoin-project/lotus/node/impl/full" @@ -21,13 +22,41 @@ type MpoolNonceAPI struct { StateAPI full.StateAPI } -// GetNonce gets the nonce from actor state +// GetNonce gets the nonce from current chain head. func (a *MpoolNonceAPI) GetNonce(addr address.Address) (uint64, error) { - act, err := a.StateAPI.StateGetActor(context.Background(), addr, types.EmptyTSK) + ts := a.StateAPI.Chain.GetHeaviestTipSet() + + // make sure we have a key address so we can compare with messages + keyAddr, err := a.StateAPI.StateManager.ResolveToKeyAddress(context.TODO(), addr, ts) + if err != nil { + return 0, err + } + + // Load the last nonce from the state, if it exists. + highestNonce := uint64(0) + if baseActor, err := a.StateAPI.StateManager.LoadActorRaw(context.TODO(), addr, ts.ParentState()); err != nil { + if !xerrors.Is(err, types.ErrActorNotFound) { + return 0, err + } + } else { + highestNonce = baseActor.Nonce + } + + // Otherwise, find the highest nonce in the tipset. + msgs, err := a.StateAPI.Chain.MessagesForTipset(ts) if err != nil { return 0, err } - return act.Nonce, nil + for _, msg := range msgs { + vmmsg := msg.VMMessage() + if vmmsg.From != keyAddr { + continue + } + if vmmsg.Nonce >= highestNonce { + highestNonce = vmmsg.Nonce + 1 + } + } + return highestNonce, nil } var _ messagesigner.MpoolNonceAPI = (*MpoolNonceAPI)(nil) diff --git a/node/node_test.go b/node/node_test.go index 69d6147bcbf..fb1f1e810b8 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -92,6 +92,10 @@ func TestAPIDealFlowReal(t *testing.T) { } func TestDealMining(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode") + } + logging.SetLogLevel("miner", "ERROR") logging.SetLogLevel("chainstore", "ERROR") logging.SetLogLevel("chain", "ERROR")