Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make state tipset usage consistent in the API #4545

Merged
merged 10 commits into from
Feb 5, 2021
6 changes: 5 additions & 1 deletion api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion build/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand Down
14 changes: 0 additions & 14 deletions chain/stmgr/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a drive-by fix. Removed because it was only being used in one place at this point.

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 {
Expand Down
9 changes: 6 additions & 3 deletions chain/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion documentation/en/api-methods-miner.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Response:
```json
{
"Version": "string value",
"APIVersion": 65536,
"APIVersion": 65792,
"BlockDelay": 42
}
```
Expand Down
2 changes: 1 addition & 1 deletion documentation/en/api-methods-worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ Perms: admin

Inputs: `null`

Response: `65536`
Response: `65792`

## Add

Expand Down
8 changes: 6 additions & 2 deletions documentation/en/api-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Response:
```json
{
"Version": "string value",
"APIVersion": 65536,
"APIVersion": 65792,
"BlockDelay": 42
}
```
Expand Down Expand Up @@ -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.


Expand Down Expand Up @@ -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

Expand Down
31 changes: 0 additions & 31 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead code

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)
65 changes: 18 additions & 47 deletions node/impl/full/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This avoids loading the actor twice.

}

func (m *StateModule) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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
}
Expand All @@ -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
}
35 changes: 32 additions & 3 deletions node/modules/mpoolnonceapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"go.uber.org/fx"
"golang.org/x/xerrors"

"github.com/filecoin-project/lotus/node/impl/full"

Expand All @@ -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) {
Stebalien marked this conversation as resolved.
Show resolved Hide resolved
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think msg.From can technically also be an ID address, though I don't see messages like that on-chain

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You sure? It looks like message verification will fail if the from address is not a public key.

if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
return err
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uff we should fix it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, you should abosolutely be able to send a message with an ID address as the from

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we can add that to the network upgrade. Note: we'll need to carefully check the VM to make sure we resolve everything as expected before passing it off to the actors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: we currently implicitly resolve the from address when sending. Should we not be doing that?

continue
}
if vmmsg.Nonce >= highestNonce {
highestNonce = vmmsg.Nonce + 1
}
}
return highestNonce, nil
}

var _ messagesigner.MpoolNonceAPI = (*MpoolNonceAPI)(nil)
4 changes: 4 additions & 0 deletions node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down