Add mpool manage
command
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
parent
87df73a455
commit
7535c5bb53
@ -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
|
||||||
|
@ -34,6 +34,7 @@ var MpoolCmd = &cli.Command{
|
|||||||
MpoolFindCmd,
|
MpoolFindCmd,
|
||||||
MpoolConfig,
|
MpoolConfig,
|
||||||
MpoolGasPerfCmd,
|
MpoolGasPerfCmd,
|
||||||
|
mpoolManage,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
356
cli/mpool_manage.go
Normal file
356
cli/mpool_manage.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
22
cli/send.go
22
cli/send.go
@ -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
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
110
cli/send_test.go
110
cli/send_test.go
@ -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())
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
3
go.mod
@ -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
4
go.sum
@ -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=
|
||||||
|
Loading…
Reference in New Issue
Block a user