Add mpool manage command

Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
Jakub Sztandera 2021-03-25 15:24:09 +01:00
parent 87df73a455
commit 7535c5bb53
No known key found for this signature in database
GPG Key ID: 9A9AF56F8B3879BA
12 changed files with 566 additions and 129 deletions

View File

@ -360,7 +360,7 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu
} }
if m.GasFeeCap.LessThan(baseFeeUpperBound) { if m.GasFeeCap.LessThan(baseFeeUpperBound) {
check.OK = false check.OK = true // on purpose, the checks is more of a warning
check.Err = "GasFeeCap less than base fee upper bound for inclusion in next 20 epochs" check.Err = "GasFeeCap less than base fee upper bound for inclusion in next 20 epochs"
} else { } else {
check.OK = true check.OK = true

View File

@ -34,6 +34,7 @@ var MpoolCmd = &cli.Command{
MpoolFindCmd, MpoolFindCmd,
MpoolConfig, MpoolConfig,
MpoolGasPerfCmd, MpoolGasPerfCmd,
mpoolManage,
}, },
} }

356
cli/mpool_manage.go Normal file
View File

@ -0,0 +1,356 @@
package cli
import (
"context"
"fmt"
"sort"
"github.com/Kubuxu/imtui"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/messagepool"
types "github.com/filecoin-project/lotus/chain/types"
"github.com/gdamore/tcell/v2"
cid "github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
)
var mpoolManage = &cli.Command{
Name: "manage",
Action: func(cctx *cli.Context) error {
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close()
ctx := ReqContext(cctx)
_, localAddr, err := srv.LocalAddresses(ctx)
msgs, err := srv.MpoolPendingFilter(ctx, func(sm *types.SignedMessage) bool {
if sm.Message.From.Empty() {
return false
}
for _, a := range localAddr {
if a == sm.Message.From {
return true
}
}
return false
}, types.EmptyTSK)
if err != nil {
return err
}
t, err := imtui.NewTui()
if err != nil {
panic(err)
}
mm := &mmUI{
ctx: ctx,
srv: srv,
addrs: localAddr,
messages: msgs,
}
sort.Slice(mm.addrs, func(i, j int) bool {
return mm.addrs[i].String() < mm.addrs[j].String()
})
t.PushScene(mm.addrSelect())
err = t.Run()
if err != nil {
panic(err)
}
return nil
},
}
type mmUI struct {
ctx context.Context
srv ServicesAPI
addrs []address.Address
messages []*types.SignedMessage
}
func (mm *mmUI) addrSelect() func(*imtui.Tui) error {
rows := [][]string{{"Address", "No. Messages"}}
mCount := map[address.Address]int{}
for _, sm := range mm.messages {
mCount[sm.Message.From]++
}
for _, a := range mm.addrs {
rows = append(rows, []string{a.String(), fmt.Sprintf("%d", mCount[a])})
}
flex := []int{4, 1}
sel := 0
scroll := 0
return func(t *imtui.Tui) error {
if t.CurrentKey != nil && t.CurrentKey.Key() == tcell.KeyEnter {
if sel > 0 {
t.ReplaceScene(mm.messageLising(mm.addrs[sel-1]))
}
}
t.FlexTable(0, 0, 0, &sel, &scroll, rows, flex, true)
return nil
}
}
func errUI(err error) func(*imtui.Tui) error {
return func(t *imtui.Tui) error {
return err
}
}
type msgInfo struct {
sm *types.SignedMessage
checks []api.MessageCheckStatus
}
func (mi *msgInfo) Row() []string {
cidStr := mi.sm.Cid().String()
failedChecks := 0
for _, c := range mi.checks {
if !c.OK {
failedChecks++
}
}
shortAddr := mi.sm.Message.To.String()
if len(shortAddr) > 16 {
shortAddr = "…" + shortAddr[len(shortAddr)-16:]
}
var fCk string
if failedChecks == 0 {
fCk = "[:green:]OK"
} else {
fCk = "[:orange:]" + fmt.Sprintf("%d", failedChecks)
}
return []string{"…" + cidStr[len(cidStr)-32:], shortAddr,
fmt.Sprintf("%d", mi.sm.Message.Nonce), types.FIL(mi.sm.Message.Value).String(),
fmt.Sprintf("%d", mi.sm.Message.Method), fCk}
}
func (mm *mmUI) messageLising(a address.Address) func(*imtui.Tui) error {
genMsgInfos := func() ([]msgInfo, error) {
msgs, err := mm.srv.MpoolPendingFilter(mm.ctx, func(sm *types.SignedMessage) bool {
if sm.Message.From.Empty() {
return false
}
if a == sm.Message.From {
return true
}
return false
}, types.EmptyTSK)
if err != nil {
return nil, xerrors.Errorf("getting pending: %w", err)
}
msgIdx := map[cid.Cid]*types.SignedMessage{}
for _, sm := range msgs {
if sm.Message.From == a {
msgIdx[sm.Message.Cid()] = sm
msgIdx[sm.Cid()] = sm
}
}
checks, err := mm.srv.MpoolCheckPendingMessages(mm.ctx, a)
if err != nil {
return nil, xerrors.Errorf("checking pending: %w", err)
}
msgInfos := make([]msgInfo, 0, len(checks))
for _, msgChecks := range checks {
failingChecks := []api.MessageCheckStatus{}
for _, c := range msgChecks {
if !c.OK {
failingChecks = append(failingChecks, c)
}
}
msgInfos = append(msgInfos, msgInfo{
sm: msgIdx[msgChecks[0].Cid],
checks: failingChecks,
})
}
return msgInfos, nil
}
sel := 0
scroll := 0
var msgInfos []msgInfo
var rows [][]string
flex := []int{3, 2, 1, 1, 1, 1}
refresh := true
return func(t *imtui.Tui) error {
if refresh {
var err error
msgInfos, err = genMsgInfos()
if err != nil {
return xerrors.Errorf("getting msgInfos: %w", err)
}
rows = [][]string{{"Message Cid", "To", "Nonce", "Value", "Method", "Checks"}}
for _, mi := range msgInfos {
rows = append(rows, mi.Row())
}
refresh = false
}
if t.CurrentKey != nil && t.CurrentKey.Key() == tcell.KeyEnter {
if sel > 0 {
t.PushScene(mm.messageDetail(msgInfos[sel-1]))
refresh = true
return nil
}
}
t.Label(0, 0, fmt.Sprintf("Address: %s", a), tcell.StyleDefault)
t.FlexTable(1, 0, 0, &sel, &scroll, rows, flex, true)
return nil
}
}
func (mm *mmUI) messageDetail(mi msgInfo) func(*imtui.Tui) error {
baseFee, err := mm.srv.GetBaseFee(mm.ctx)
if err != nil {
return errUI(err)
}
_ = baseFee
m := mi.sm.Message
maxFee := big.Mul(m.GasFeeCap, big.NewInt(m.GasLimit))
issues := [][]string{}
for _, c := range mi.checks {
issues = append(issues, []string{c.Code.String(), c.Err})
}
issuesFlex := []int{1, 3}
var sel, scroll int
executeReprice := false
executeNoop := false
return func(t *imtui.Tui) error {
if executeReprice {
m.GasFeeCap = big.Div(maxFee, big.NewInt(m.GasLimit))
m.GasPremium = messagepool.ComputeMinRBF(m.GasPremium)
m.GasFeeCap = big.Max(m.GasFeeCap, m.GasPremium)
_, _, err := mm.srv.PublishMessage(mm.ctx, &api.MessagePrototype{
Message: m,
ValidNonce: true,
}, true)
if err != nil {
return err
}
t.PopScene()
return nil
}
if executeNoop {
nop := types.Message{
To: builtin.BurntFundsActorAddr,
From: m.From,
Nonce: m.Nonce,
Value: big.Zero(),
}
nop.GasPremium = messagepool.ComputeMinRBF(m.GasPremium)
_, _, err := mm.srv.PublishMessage(mm.ctx, &api.MessagePrototype{
Message: nop,
ValidNonce: true,
}, true)
if err != nil {
return xerrors.Errorf("publishing noop message: %w", err)
}
t.PopScene()
return nil
}
if t.CurrentKey != nil {
if t.CurrentKey.Key() == tcell.KeyLeft {
t.PopScene()
return nil
}
if t.CurrentKey.Key() == tcell.KeyRune {
switch t.CurrentKey.Rune() {
case 'R', 'r':
t.PushScene(feeUI(baseFee, m.GasLimit, &maxFee, &executeReprice))
return nil
case 'N', 'n':
t.PushScene(confirmationScene(
&executeNoop,
"Are you sure you want to cancel the message by",
"replacing it with a message with no effects?"))
return nil
}
}
}
row := 0
defS := tcell.StyleDefault
display := func(f string, args ...interface{}) {
t.Label(0, row, fmt.Sprintf(f, args...), defS)
row++
}
display("Message CID: %s", m.Cid())
display("Signed Message CID: %s", mi.sm.Cid())
row++
display("From: %s", m.From)
display("To: %s", m.To)
row++
display("Nonce: %d", m.Nonce)
display("Value: %s", types.FIL(m.Value))
row++
display("GasLimit: %d", m.GasLimit)
display("GasPremium: %s", types.FIL(m.GasPremium).Short())
display("GasFeeCap %s", types.FIL(m.GasFeeCap).Short())
row++
display("Press R to reprice this message")
display("Press N to replace this message with no-operation message")
row++
t.FlexTable(row, 0, 0, &sel, &scroll, issues, issuesFlex, false)
return nil
}
}
func confirmationScene(yes *bool, ask ...string) func(*imtui.Tui) error {
return func(t *imtui.Tui) error {
row := 0
defS := tcell.StyleDefault
display := func(f string, args ...interface{}) {
t.Label(0, row, fmt.Sprintf(f, args...), defS)
row++
}
for _, a := range ask {
display(a)
}
row++
display("Enter to confirm")
display("Esc to cancel")
if t.CurrentKey != nil {
if t.CurrentKey.Key() == tcell.KeyEnter {
*yes = true
t.PopScene()
return nil
}
}
return nil
}
}

View File

@ -58,10 +58,14 @@ var sendCmd = &cli.Command{
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "force", Name: "force",
Usage: "must be specified for the action to take effect if maybe SysErrInsufficientFunds etc", Usage: "Deprecated: use global 'force-send'",
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
if cctx.IsSet("force") {
fmt.Println("'force' flag is deprecated, use global flag 'force-send'")
}
if cctx.Args().Len() != 2 { if cctx.Args().Len() != 2 {
return ShowHelp(cctx, fmt.Errorf("'send' expects two arguments, target and amount")) return ShowHelp(cctx, fmt.Errorf("'send' expects two arguments, target and amount"))
} }
@ -145,21 +149,13 @@ var sendCmd = &cli.Command{
if err != nil { if err != nil {
return xerrors.Errorf("creating message prototype: %w", err) return xerrors.Errorf("creating message prototype: %w", err)
} }
msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force"))
if xerrors.Is(err, ErrCheckFailed) { c, err := InteractiveSend(ctx, cctx, srv, proto)
proto, err := resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true)
if err != nil { if err != nil {
return xerrors.Errorf("from UI: %w", err) return err
} }
msg, _, err = srv.PublishMessage(ctx, proto, true) //nolint fmt.Fprintf(cctx.App.Writer, "%s\n", c)
}
if err != nil {
return xerrors.Errorf("publishing message: %w", err)
}
fmt.Fprintf(cctx.App.Writer, "%s\n", msg.Cid())
return nil return nil
}, },
} }

View File

@ -1,6 +1,17 @@
package cli package cli
/* import (
"bytes"
"testing"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
types "github.com/filecoin-project/lotus/chain/types"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
ucli "github.com/urfave/cli/v2"
)
var arbtCid = (&types.Message{ var arbtCid = (&types.Message{
From: mustAddr(address.NewIDAddress(2)), From: mustAddr(address.NewIDAddress(2)),
@ -37,81 +48,26 @@ func TestSendCLI(t *testing.T) {
app, mockSrvcs, buf, done := newMockApp(t, sendCmd) app, mockSrvcs, buf, done := newMockApp(t, sendCmd)
defer done() defer done()
gomock.InOrder( arbtProto := &api.MessagePrototype{
mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ Message: types.Message{
From: mustAddr(address.NewIDAddress(1)),
To: mustAddr(address.NewIDAddress(1)), To: mustAddr(address.NewIDAddress(1)),
Val: oneFil, Value: oneFil,
}).Return(arbtCid, nil), },
mockSrvcs.EXPECT().Close(), }
) sigMsg := fakeSign(&arbtProto.Message)
err := app.Run([]string{"lotus", "send", "t01", "1"})
assert.NoError(t, err) gomock.InOrder(
assert.EqualValues(t, arbtCid.String()+"\n", buf.String()) mockSrvcs.EXPECT().MessageForSend(gomock.Any(), SendParams{
}) To: mustAddr(address.NewIDAddress(1)),
t.Run("ErrSendBalanceTooLow", func(t *testing.T) { Val: oneFil,
app, mockSrvcs, _, done := newMockApp(t, sendCmd) }).Return(arbtProto, nil),
defer done() mockSrvcs.EXPECT().PublishMessage(gomock.Any(), arbtProto, false).
Return(sigMsg, nil, nil),
gomock.InOrder( mockSrvcs.EXPECT().Close(),
mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ )
To: mustAddr(address.NewIDAddress(1)), err := app.Run([]string{"lotus", "send", "t01", "1"})
Val: oneFil, assert.NoError(t, err)
}).Return(cid.Undef, ErrSendBalanceTooLow), assert.EqualValues(t, sigMsg.Cid().String()+"\n", buf.String())
mockSrvcs.EXPECT().Close(), })
)
err := app.Run([]string{"lotus", "send", "t01", "1"})
assert.ErrorIs(t, err, ErrSendBalanceTooLow)
})
t.Run("generic-err-is-forwarded", func(t *testing.T) {
app, mockSrvcs, _, done := newMockApp(t, sendCmd)
defer done()
errMark := errors.New("something")
gomock.InOrder(
mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{
To: mustAddr(address.NewIDAddress(1)),
Val: oneFil,
}).Return(cid.Undef, errMark),
mockSrvcs.EXPECT().Close(),
)
err := app.Run([]string{"lotus", "send", "t01", "1"})
assert.ErrorIs(t, err, errMark)
})
t.Run("from-specific", func(t *testing.T) {
app, mockSrvcs, buf, done := newMockApp(t, sendCmd)
defer done()
gomock.InOrder(
mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{
To: mustAddr(address.NewIDAddress(1)),
From: mustAddr(address.NewIDAddress(2)),
Val: oneFil,
}).Return(arbtCid, nil),
mockSrvcs.EXPECT().Close(),
)
err := app.Run([]string{"lotus", "send", "--from=t02", "t01", "1"})
assert.NoError(t, err)
assert.EqualValues(t, arbtCid.String()+"\n", buf.String())
})
t.Run("nonce-specific", func(t *testing.T) {
app, mockSrvcs, buf, done := newMockApp(t, sendCmd)
defer done()
zero := uint64(0)
gomock.InOrder(
mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{
To: mustAddr(address.NewIDAddress(1)),
Nonce: &zero,
Val: oneFil,
}).Return(arbtCid, nil),
mockSrvcs.EXPECT().Close(),
)
err := app.Run([]string{"lotus", "send", "--nonce=0", "t01", "1"})
assert.NoError(t, err)
assert.EqualValues(t, arbtCid.String()+"\n", buf.String())
})
} }
*/

View File

@ -14,8 +14,35 @@ import (
types "github.com/filecoin-project/lotus/chain/types" types "github.com/filecoin-project/lotus/chain/types"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
cid "github.com/ipfs/go-cid" cid "github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
) )
func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI,
proto *api.MessagePrototype) (cid.Cid, error) {
msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force") || cctx.Bool("force-send"))
printer := cctx.App.Writer
if xerrors.Is(err, ErrCheckFailed) {
if !cctx.Bool("interactive") {
fmt.Fprintf(printer, "Following checks have failed:\n")
printChecks(printer, checks, proto.Message.Cid())
} else {
proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks)
if err != nil {
return cid.Undef, xerrors.Errorf("from UI: %w", err)
}
msg, _, err = srv.PublishMessage(ctx, proto, true)
}
}
if err != nil {
return cid.Undef, xerrors.Errorf("publishing message: %w", err)
}
return msg.Cid(), nil
}
var interactiveSolves = map[api.CheckStatusCode]bool{ var interactiveSolves = map[api.CheckStatusCode]bool{
api.CheckStatusMessageBaseFee: true, api.CheckStatusMessageBaseFee: true,
api.CheckStatusMessageBaseFeeLowerBound: true, api.CheckStatusMessageBaseFeeLowerBound: true,
@ -42,15 +69,11 @@ func baseFeeFromHints(hint map[string]interface{}) big.Int {
func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer,
proto *api.MessagePrototype, checkGroups [][]api.MessageCheckStatus, proto *api.MessagePrototype, checkGroups [][]api.MessageCheckStatus,
interactive bool) (*api.MessagePrototype, error) { ) (*api.MessagePrototype, error) {
fmt.Fprintf(printer, "Following checks have failed:\n") fmt.Fprintf(printer, "Following checks have failed:\n")
printChecks(printer, checkGroups, proto.Message.Cid()) printChecks(printer, checkGroups, proto.Message.Cid())
if !interactive {
return nil, ErrCheckFailed
}
if interactive {
if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.Cid()); feeCapBad { if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.Cid()); feeCapBad {
fmt.Fprintf(printer, "Fee of the message can be adjusted\n") fmt.Fprintf(printer, "Fee of the message can be adjusted\n")
if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) { if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) {
@ -71,7 +94,6 @@ func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer,
if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) { if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) {
return nil, ErrAbortedByUser return nil, ErrAbortedByUser
} }
}
return proto, nil return proto, nil
} }
@ -88,7 +110,7 @@ func printChecks(printer io.Writer, checkGroups [][]api.MessageCheckStatus, prot
if !aboutProto { if !aboutProto {
msgName = c.Cid.String() msgName = c.Cid.String()
} }
fmt.Fprintf(printer, "%s message failed a check: %s\n", msgName, c.Err) fmt.Fprintf(printer, "%s message failed a check %s: %s\n", msgName, c.Code, c.Err)
} }
} }
} }
@ -133,7 +155,7 @@ func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount)
maxFee := big.Mul(proto.Message.GasFeeCap, big.NewInt(proto.Message.GasLimit)) maxFee := big.Mul(proto.Message.GasFeeCap, big.NewInt(proto.Message.GasLimit))
send := false send := false
t.SetScene(ui(baseFee, proto.Message.GasLimit, &maxFee, &send)) t.PushScene(feeUI(baseFee, proto.Message.GasLimit, &maxFee, &send))
err = t.Run() err = t.Run()
if err != nil { if err != nil {
@ -148,7 +170,7 @@ func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount)
return proto, nil return proto, nil
} }
func ui(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *bool) func(*imtui.Tui) error { func feeUI(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *bool) func(*imtui.Tui) error {
orignalMaxFee := *maxFee orignalMaxFee := *maxFee
required := big.Mul(baseFee, big.NewInt(gasLimit)) required := big.Mul(baseFee, big.NewInt(gasLimit))
safe := big.Mul(required, big.NewInt(10)) safe := big.Mul(required, big.NewInt(10))
@ -180,7 +202,8 @@ func ui(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *
if t.CurrentKey.Key() == tcell.KeyEnter { if t.CurrentKey.Key() == tcell.KeyEnter {
*send = true *send = true
return imtui.ErrNormalExit t.PopScene()
return nil
} }
} }

View File

@ -39,7 +39,12 @@ type ServicesAPI interface {
// before publishing the message, it runs checks on the node, message and mpool to verify that // before publishing the message, it runs checks on the node, message and mpool to verify that
// message is valid and won't be stuck. // message is valid and won't be stuck.
// if `force` is true, it skips the checks // if `force` is true, it skips the checks
PublishMessage(ctx context.Context, prototype *api.MessagePrototype, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) PublishMessage(ctx context.Context, prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error)
LocalAddresses(ctx context.Context) (address.Address, []address.Address, error)
MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool, tsk types.TipSetKey) ([]*types.SignedMessage, error)
MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error)
// Close ends the session of services and disconnects from RPC, using Services after Close is called // Close ends the session of services and disconnects from RPC, using Services after Close is called
// most likely will result in an error // most likely will result in an error
@ -240,3 +245,41 @@ func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*
} }
return prototype, nil return prototype, nil
} }
func (s *ServicesImpl) MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool,
tsk types.TipSetKey) ([]*types.SignedMessage, error) {
msgs, err := s.api.MpoolPending(ctx, types.EmptyTSK)
if err != nil {
return nil, xerrors.Errorf("getting pending messages: %w", err)
}
out := []*types.SignedMessage{}
for _, sm := range msgs {
if filter(sm) {
out = append(out, sm)
}
}
return out, nil
}
func (s *ServicesImpl) LocalAddresses(ctx context.Context) (address.Address, []address.Address, error) {
def, err := s.api.WalletDefaultAddress(ctx)
if err != nil {
return address.Undef, nil, xerrors.Errorf("getting default addr: %w", err)
}
all, err := s.api.WalletList(ctx)
if err != nil {
return address.Undef, nil, xerrors.Errorf("getting list of addrs: %w", err)
}
return def, all, nil
}
func (s *ServicesImpl) MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error) {
checks, err := s.api.MpoolCheckPendingMessages(ctx, a)
if err != nil {
return nil, xerrors.Errorf("pending mpool check: %w", err)
}
return checks, nil
}

View File

@ -7,6 +7,7 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
mocks "github.com/filecoin-project/lotus/api/v0api/v0mocks" mocks "github.com/filecoin-project/lotus/api/v0api/v0mocks"
types "github.com/filecoin-project/lotus/chain/types" types "github.com/filecoin-project/lotus/chain/types"
@ -60,12 +61,12 @@ func setupMockSrvcs(t *testing.T) (*ServicesImpl, *mocks.MockFullNode) {
} }
// linter doesn't like dead code, so these are commented out. // linter doesn't like dead code, so these are commented out.
// func fakeSign(msg *types.Message) *types.SignedMessage { func fakeSign(msg *types.Message) *types.SignedMessage {
// return &types.SignedMessage{ return &types.SignedMessage{
// Message: *msg, Message: *msg,
// Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)},
// } }
// } }
//func makeMessageSigner() (*cid.Cid, interface{}) { //func makeMessageSigner() (*cid.Cid, interface{}) {
//smCid := cid.Undef //smCid := cid.Undef

View File

@ -96,6 +96,22 @@ func (mr *MockServicesAPIMockRecorder) GetBaseFee(arg0 interface{}) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseFee", reflect.TypeOf((*MockServicesAPI)(nil).GetBaseFee), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseFee", reflect.TypeOf((*MockServicesAPI)(nil).GetBaseFee), arg0)
} }
// LocalAddresses mocks base method
func (m *MockServicesAPI) LocalAddresses(arg0 context.Context) (go_address.Address, []go_address.Address, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LocalAddresses", arg0)
ret0, _ := ret[0].(go_address.Address)
ret1, _ := ret[1].([]go_address.Address)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// LocalAddresses indicates an expected call of LocalAddresses
func (mr *MockServicesAPIMockRecorder) LocalAddresses(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddresses", reflect.TypeOf((*MockServicesAPI)(nil).LocalAddresses), arg0)
}
// MessageForSend mocks base method // MessageForSend mocks base method
func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*api.MessagePrototype, error) { func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*api.MessagePrototype, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -111,6 +127,36 @@ func (mr *MockServicesAPIMockRecorder) MessageForSend(arg0, arg1 interface{}) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageForSend", reflect.TypeOf((*MockServicesAPI)(nil).MessageForSend), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageForSend", reflect.TypeOf((*MockServicesAPI)(nil).MessageForSend), arg0, arg1)
} }
// MpoolCheckPendingMessages mocks base method
func (m *MockServicesAPI) MpoolCheckPendingMessages(arg0 context.Context, arg1 go_address.Address) ([][]api.MessageCheckStatus, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MpoolCheckPendingMessages", arg0, arg1)
ret0, _ := ret[0].([][]api.MessageCheckStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MpoolCheckPendingMessages indicates an expected call of MpoolCheckPendingMessages
func (mr *MockServicesAPIMockRecorder) MpoolCheckPendingMessages(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolCheckPendingMessages", reflect.TypeOf((*MockServicesAPI)(nil).MpoolCheckPendingMessages), arg0, arg1)
}
// MpoolPendingFilter mocks base method
func (m *MockServicesAPI) MpoolPendingFilter(arg0 context.Context, arg1 func(*types.SignedMessage) bool, arg2 types.TipSetKey) ([]*types.SignedMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MpoolPendingFilter", arg0, arg1, arg2)
ret0, _ := ret[0].([]*types.SignedMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MpoolPendingFilter indicates an expected call of MpoolPendingFilter
func (mr *MockServicesAPIMockRecorder) MpoolPendingFilter(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolPendingFilter", reflect.TypeOf((*MockServicesAPI)(nil).MpoolPendingFilter), arg0, arg1, arg2)
}
// PublishMessage mocks base method // PublishMessage mocks base method
func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *api.MessagePrototype, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *api.MessagePrototype, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -2,7 +2,9 @@ package main
import ( import (
"context" "context"
"os"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.opencensus.io/trace" "go.opencensus.io/trace"
@ -52,6 +54,8 @@ func main() {
ctx, span := trace.StartSpan(context.Background(), "/cli") ctx, span := trace.StartSpan(context.Background(), "/cli")
defer span.End() defer span.End()
interactiveDef := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
app := &cli.App{ app := &cli.App{
Name: "lotus", Name: "lotus",
Usage: "Filecoin decentralized storage network client", Usage: "Filecoin decentralized storage network client",
@ -64,10 +68,20 @@ func main() {
Hidden: true, Hidden: true,
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
}, },
&cli.BoolFlag{
Name: "interactive",
Usage: "setting to false will disable interactive functionality of commands",
Value: interactiveDef,
},
&cli.BoolFlag{
Name: "force-send",
Usage: "if true, will ignore pre-send checks",
},
}, },
Commands: append(local, lcli.Commands...), Commands: append(local, lcli.Commands...),
} }
app.Setup() app.Setup()
app.Metadata["traceContext"] = ctx app.Metadata["traceContext"] = ctx
app.Metadata["repoType"] = repo.FullNode app.Metadata["repoType"] = repo.FullNode

3
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/GeertJohan/go.rice v1.0.0 github.com/GeertJohan/go.rice v1.0.0
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7 github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921
@ -118,6 +118,7 @@ require (
github.com/libp2p/go-libp2p-yamux v0.4.1 github.com/libp2p/go-libp2p-yamux v0.4.1
github.com/libp2p/go-maddr-filter v0.1.0 github.com/libp2p/go-maddr-filter v0.1.0
github.com/mattn/go-colorable v0.1.6 // indirect github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-isatty v0.0.12
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-base32 v0.0.3 github.com/multiformats/go-base32 v0.0.3

4
go.sum
View File

@ -42,11 +42,11 @@ github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee h1:8doiS7ib3zi6/K1
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY= github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa h1:1PPxEyGdIGVkX/kqMvLJ95a1dGS1Sz7tpNEgehEYYt0=
github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa/go.mod h1:WUmMvh9wMtqj1Xhf1hf3kp9RvL+y6odtdYxpyZjb90U=
github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7 h1:oaKenk0p5Pg7k2YRflJtiai4weJN+VsABO3zSaUVU6w=
github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7/go.mod h1:WUmMvh9wMtqj1Xhf1hf3kp9RvL+y6odtdYxpyZjb90U=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=