diff --git a/api/api_full.go b/api/api_full.go index 3c28b5982..dcda26c3b 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -126,7 +126,8 @@ type FullNode interface { StateMinerSectorSize(context.Context, address.Address, types.TipSetKey) (abi.SectorSize, error) StateMinerFaults(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error) StatePledgeCollateral(context.Context, types.TipSetKey) (types.BigInt, error) - StateWaitMsg(context.Context, cid.Cid) (*MsgWait, error) + StateWaitMsg(context.Context, cid.Cid) (*MsgLookup, error) + StateSearchMsg(context.Context, cid.Cid) (*MsgLookup, error) StateListMiners(context.Context, types.TipSetKey) ([]address.Address, error) StateListActors(context.Context, types.TipSetKey) ([]address.Address, error) StateMarketBalance(context.Context, address.Address, types.TipSetKey) (MarketBalance, error) @@ -188,7 +189,7 @@ type DealInfo struct { Duration uint64 } -type MsgWait struct { +type MsgLookup struct { Receipt types.MessageReceipt TipSet *types.TipSet } diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 9eb73c637..840e94489 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -118,7 +118,8 @@ type FullNodeStruct struct { StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"` StateReadState func(context.Context, *types.Actor, types.TipSetKey) (*api.ActorState, error) `perm:"read"` StatePledgeCollateral func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateWaitMsg func(context.Context, cid.Cid) (*api.MsgWait, error) `perm:"read"` + StateWaitMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"` + StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"` StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"` StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"` StateMarketBalance func(context.Context, address.Address, types.TipSetKey) (api.MarketBalance, error) `perm:"read"` @@ -490,9 +491,14 @@ func (c *FullNodeStruct) StatePledgeCollateral(ctx context.Context, tsk types.Ti return c.Internal.StatePledgeCollateral(ctx, tsk) } -func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid) (*api.MsgWait, error) { +func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) { return c.Internal.StateWaitMsg(ctx, msgc) } + +func (c *FullNodeStruct) StateSearchMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) { + return c.Internal.StateSearchMsg(ctx, msgc) +} + func (c *FullNodeStruct) StateListMiners(ctx context.Context, tsk types.TipSetKey) ([]address.Address, error) { return c.Internal.StateListMiners(ctx, tsk) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 6894b8fd3..6dc1be8e0 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -583,6 +583,37 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid) (*type } } +func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*types.TipSet, *types.MessageReceipt, error) { + msg, err := sm.cs.GetCMessage(mcid) + if err != nil { + return nil, nil, fmt.Errorf("failed to load message: %w", err) + } + + head := sm.cs.GetHeaviestTipSet() + + r, err := sm.tipsetExecutedMessage(head, mcid, msg.VMMessage()) + if err != nil { + return nil, nil, err + } + + if r != nil { + return head, r, nil + } + + fts, r, err := sm.searchBackForMsg(ctx, head, msg) + + if err != nil { + log.Warnf("failed to look back through chain for message %s", mcid) + return nil, nil, err + } + + if fts == nil { + return nil, nil, nil + } + + return fts, r, nil +} + func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m store.ChainMsg) (*types.TipSet, *types.MessageReceipt, error) { cur := from diff --git a/cli/state.go b/cli/state.go index 47a6b87d6..344d3687d 100644 --- a/cli/state.go +++ b/cli/state.go @@ -58,6 +58,7 @@ var stateCmd = &cli.Command{ stateCallCmd, stateGetDealSetCmd, stateWaitMsgCmd, + stateSearchMsgCmd, stateMinerInfo, }, } @@ -855,6 +856,45 @@ var stateWaitMsgCmd = &cli.Command{ }, } +var stateSearchMsgCmd = &cli.Command{ + Name: "search-msg", + Usage: "Search to see whether a message has appeared on chain", + ArgsUsage: "[messageCid]", + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must specify message cid to search for") + } + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := ReqContext(cctx) + + msg, err := cid.Decode(cctx.Args().First()) + if err != nil { + return err + } + + mw, err := api.StateSearchMsg(ctx, msg) + if err != nil { + return err + } + + if mw != nil { + fmt.Printf("message was executed in tipset: %s", mw.TipSet.Cids()) + fmt.Printf("\nExit Code: %d", mw.Receipt.ExitCode) + fmt.Printf("\nGas Used: %d", mw.Receipt.GasUsed) + fmt.Printf("\nReturn: %x", mw.Receipt.Return) + } else { + fmt.Print("message was not found on chain") + } + return nil + }, +} + var stateCallCmd = &cli.Command{ Name: "call", Usage: "Invoke a method on an actor locally", diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 49131a02c..3426d3e6a 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -281,7 +281,7 @@ func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, p return &out, nil } -func (a *StateAPI) StateWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) { +func (a *StateAPI) StateWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgLookup, error) { // TODO: consider using event system for this, expose confidence ts, recpt, err := a.StateManager.WaitForMessage(ctx, msg) @@ -289,12 +289,28 @@ func (a *StateAPI) StateWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, return nil, err } - return &api.MsgWait{ + return &api.MsgLookup{ Receipt: *recpt, TipSet: ts, }, nil } +func (a *StateAPI) StateSearchMsg(ctx context.Context, msg cid.Cid) (*api.MsgLookup, error) { + ts, recpt, err := a.StateManager.SearchForMessage(ctx, msg) + if err != nil { + return nil, err + } + + if ts != nil { + return &api.MsgLookup{ + Receipt: *recpt, + TipSet: ts, + }, nil + } else { + return nil, nil + } +} + func (a *StateAPI) StateGetReceipt(ctx context.Context, msg cid.Cid, tsk types.TipSetKey) (*types.MessageReceipt, error) { ts, err := a.Chain.GetTipSetFromKey(tsk) if err != nil { diff --git a/storage/miner.go b/storage/miner.go index 1b535d786..0111933d7 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -51,7 +51,7 @@ type storageMinerApi interface { StateMinerSectors(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) StateMinerSectorSize(context.Context, address.Address, types.TipSetKey) (abi.SectorSize, error) - StateWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) // TODO: removeme eventually + StateWaitMsg(context.Context, cid.Cid) (*api.MsgLookup, error) // TODO: removeme eventually StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error) diff --git a/storage/sealing/sealing.go b/storage/sealing/sealing.go index b74d34db1..402ff8a11 100644 --- a/storage/sealing/sealing.go +++ b/storage/sealing/sealing.go @@ -38,7 +38,7 @@ type sealingApi interface { // TODO: trim down StateMinerSectors(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) StateMinerSectorSize(context.Context, address.Address, types.TipSetKey) (abi.SectorSize, error) - StateWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) // TODO: removeme eventually + StateWaitMsg(context.Context, cid.Cid) (*api.MsgLookup, error) // TODO: removeme eventually StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error)