Message sending UI
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
parent
d782250aba
commit
86e90dc6f1
@ -331,6 +331,7 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu
|
||||
Code: api.CheckStatusMessageBaseFeeLowerBound,
|
||||
Hint: map[string]interface{}{
|
||||
"baseFeeLowerBound": baseFeeLowerBound,
|
||||
"baseFee": baseFee,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -343,9 +344,6 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu
|
||||
}
|
||||
|
||||
result[i] = append(result[i], check)
|
||||
if !check.OK {
|
||||
goto checkState
|
||||
}
|
||||
|
||||
// 8. Base Fee upper bound
|
||||
check = api.MessageCheckStatus{
|
||||
@ -354,6 +352,7 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu
|
||||
Code: api.CheckStatusMessageBaseFeeUpperBound,
|
||||
Hint: map[string]interface{}{
|
||||
"baseFeeUpperBound": baseFeeUpperBound,
|
||||
"baseFee": baseFee,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func GetFullNodeServices(ctx *cli.Context) (ServicesAPI, error) {
|
||||
return tn.(ServicesAPI), nil
|
||||
}
|
||||
|
||||
api, c, err := GetFullNodeAPI(ctx)
|
||||
api, c, err := GetFullNodeAPIV1(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
24
cli/send.go
24
cli/send.go
@ -2,7 +2,6 @@ package cli
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
@ -137,23 +136,30 @@ var sendCmd = &cli.Command{
|
||||
params.Params = decparams
|
||||
}
|
||||
|
||||
params.Force = cctx.Bool("force")
|
||||
|
||||
if cctx.IsSet("nonce") {
|
||||
n := cctx.Uint64("nonce")
|
||||
params.Nonce = &n
|
||||
}
|
||||
|
||||
msgCid, err := srv.Send(ctx, params)
|
||||
|
||||
proto, err := srv.MessageForSend(ctx, params)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrSendBalanceTooLow) {
|
||||
return fmt.Errorf("--force must be specified for this action to have an effect; you have been warned: %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) {
|
||||
proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("from UI: %w", err)
|
||||
}
|
||||
return xerrors.Errorf("executing send: %w", err)
|
||||
|
||||
msg, _, err = srv.PublishMessage(ctx, proto, true)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cctx.App.Writer, "%s\n", msgCid)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("publishing message: %w", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cctx.App.Writer, "%s\n", msg.Cid())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -1,18 +1,6 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
ucli "github.com/urfave/cli/v2"
|
||||
)
|
||||
/*
|
||||
|
||||
var arbtCid = (&types.Message{
|
||||
From: mustAddr(address.NewIDAddress(2)),
|
||||
@ -126,3 +114,4 @@ func TestSendCLI(t *testing.T) {
|
||||
})
|
||||
|
||||
}
|
||||
*/
|
||||
|
235
cli/sending_ui.go
Normal file
235
cli/sending_ui.go
Normal file
@ -0,0 +1,235 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/Kubuxu/imtui"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
var interactiveSolves = map[api.CheckStatusCode]bool{
|
||||
api.CheckStatusMessageBaseFee: true,
|
||||
api.CheckStatusMessageBaseFeeLowerBound: true,
|
||||
api.CheckStatusMessageBaseFeeUpperBound: true,
|
||||
}
|
||||
|
||||
func baseFeeFromHints(hint map[string]interface{}) big.Int {
|
||||
bHint, ok := hint["baseFee"]
|
||||
if !ok {
|
||||
return big.Zero()
|
||||
}
|
||||
bHintS, ok := bHint.(string)
|
||||
if !ok {
|
||||
return big.Zero()
|
||||
}
|
||||
|
||||
var err error
|
||||
baseFee, err := big.FromString(bHintS)
|
||||
if err != nil {
|
||||
return big.Zero()
|
||||
}
|
||||
return baseFee
|
||||
}
|
||||
|
||||
func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer,
|
||||
proto *types.Message, checkGroups [][]api.MessageCheckStatus,
|
||||
interactive bool) (*types.Message, error) {
|
||||
|
||||
fmt.Fprintf(printer, "Following checks have failed:\n")
|
||||
printChecks(printer, checkGroups, proto.Cid())
|
||||
if !interactive {
|
||||
return nil, ErrCheckFailed
|
||||
}
|
||||
|
||||
if interactive {
|
||||
if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Cid()); feeCapBad {
|
||||
fmt.Fprintf(printer, "Fee of the message can be adjusted\n")
|
||||
if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) {
|
||||
var err error
|
||||
proto, err = runFeeCapAdjustmentUI(proto, baseFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
checks, err := s.RunChecksForPrototype(ctx, proto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Fprintf(printer, "Following checks still failed:\n")
|
||||
printChecks(printer, checks, proto.Cid())
|
||||
}
|
||||
|
||||
if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) {
|
||||
return nil, ErrAbortedByUser
|
||||
}
|
||||
}
|
||||
return proto, nil
|
||||
}
|
||||
|
||||
var ErrAbortedByUser = errors.New("aborted by user")
|
||||
|
||||
func printChecks(printer io.Writer, checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) {
|
||||
for _, checks := range checkGroups {
|
||||
for _, c := range checks {
|
||||
if c.OK {
|
||||
continue
|
||||
}
|
||||
aboutProto := c.Cid.Equals(protoCid)
|
||||
msgName := "current"
|
||||
if !aboutProto {
|
||||
msgName = c.Cid.String()
|
||||
}
|
||||
fmt.Fprintf(printer, "%s message failed a check: %s\n", msgName, c.Err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func askUser(printer io.Writer, q string, def bool) bool {
|
||||
var resp string
|
||||
fmt.Fprint(printer, q)
|
||||
fmt.Scanln(&resp)
|
||||
resp = strings.ToLower(resp)
|
||||
if len(resp) == 0 {
|
||||
return def
|
||||
}
|
||||
return resp[0] == 'y'
|
||||
}
|
||||
|
||||
func isFeeCapProblem(checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) (bool, big.Int) {
|
||||
baseFee := big.Zero()
|
||||
yes := false
|
||||
for _, checks := range checkGroups {
|
||||
for _, c := range checks {
|
||||
if c.OK {
|
||||
continue
|
||||
}
|
||||
aboutProto := c.Cid.Equals(protoCid)
|
||||
if aboutProto && interactiveSolves[c.Code] {
|
||||
yes = true
|
||||
if baseFee.IsZero() {
|
||||
baseFee = baseFeeFromHints(c.Hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return yes, baseFee
|
||||
}
|
||||
|
||||
func runFeeCapAdjustmentUI(proto *types.Message, baseFee abi.TokenAmount) (*types.Message, error) {
|
||||
t, err := imtui.NewTui()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxFee := big.Mul(proto.GasFeeCap, big.NewInt(proto.GasLimit))
|
||||
send := false
|
||||
t.SetScene(ui(baseFee, proto.GasLimit, &maxFee, &send))
|
||||
|
||||
err = t.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !send {
|
||||
return nil, fmt.Errorf("aborted by user")
|
||||
}
|
||||
|
||||
proto.GasFeeCap = big.Div(maxFee, big.NewInt(proto.GasLimit))
|
||||
|
||||
return proto, nil
|
||||
}
|
||||
|
||||
func ui(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *bool) func(*imtui.Tui) error {
|
||||
orignalMaxFee := *maxFee
|
||||
required := big.Mul(baseFee, big.NewInt(gasLimit))
|
||||
safe := big.Mul(required, big.NewInt(10))
|
||||
|
||||
price := fmt.Sprintf("%s", types.FIL(*maxFee).Unitless())
|
||||
|
||||
return func(t *imtui.Tui) error {
|
||||
if t.CurrentKey != nil {
|
||||
if t.CurrentKey.Key() == tcell.KeyRune {
|
||||
pF, err := types.ParseFIL(price)
|
||||
switch t.CurrentKey.Rune() {
|
||||
case 's', 'S':
|
||||
price = types.FIL(safe).Unitless()
|
||||
case '+':
|
||||
if err == nil {
|
||||
p := big.Mul(big.Int(pF), types.NewInt(11))
|
||||
p = big.Div(p, types.NewInt(10))
|
||||
price = fmt.Sprintf("%s", types.FIL(p).Unitless())
|
||||
}
|
||||
case '-':
|
||||
if err == nil {
|
||||
p := big.Mul(big.Int(pF), types.NewInt(10))
|
||||
p = big.Div(p, types.NewInt(11))
|
||||
price = fmt.Sprintf("%s", types.FIL(p).Unitless())
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if t.CurrentKey.Key() == tcell.KeyEnter {
|
||||
*send = true
|
||||
return imtui.ErrNormalExit
|
||||
}
|
||||
}
|
||||
|
||||
defS := tcell.StyleDefault
|
||||
|
||||
row := 0
|
||||
t.Label(0, row, "Fee of the message is too low.", defS)
|
||||
row++
|
||||
|
||||
t.Label(0, row, fmt.Sprintf("Your configured maximum fee is: %s FIL",
|
||||
types.FIL(orignalMaxFee).Unitless()), defS)
|
||||
row++
|
||||
t.Label(0, row, fmt.Sprintf("Required maximum fee for the message: %s FIL",
|
||||
types.FIL(required).Unitless()), defS)
|
||||
row++
|
||||
w := t.Label(0, row, fmt.Sprintf("Safe maximum fee for the message: %s FIL",
|
||||
types.FIL(safe).Unitless()), defS)
|
||||
t.Label(w, row, " Press S to use it", defS)
|
||||
row++
|
||||
|
||||
w = t.Label(0, row, "Current Maximum Fee: ", defS)
|
||||
|
||||
w += t.EditFieldFiltered(w, row, 14, &price, imtui.FilterDecimal, defS.Foreground(tcell.ColorWhite).Background(tcell.ColorBlack))
|
||||
|
||||
w += t.Label(w, row, " FIL", defS)
|
||||
|
||||
pF, err := types.ParseFIL(price)
|
||||
*maxFee = abi.TokenAmount(pF)
|
||||
if err != nil {
|
||||
w += t.Label(w, row, " invalid price", defS.Foreground(tcell.ColorMaroon).Bold(true))
|
||||
} else if maxFee.GreaterThanEqual(safe) {
|
||||
w += t.Label(w, row, " SAFE", defS.Foreground(tcell.ColorDarkGreen).Bold(true))
|
||||
} else if maxFee.GreaterThanEqual(required) {
|
||||
w += t.Label(w, row, " low", defS.Foreground(tcell.ColorYellow).Bold(true))
|
||||
over := big.Div(big.Mul(*maxFee, big.NewInt(100)), required)
|
||||
w += t.Label(w, row,
|
||||
fmt.Sprintf(" %.1fx over the minimum", float64(over.Int64())/100.0), defS)
|
||||
} else {
|
||||
w += t.Label(w, row, " too low", defS.Foreground(tcell.ColorRed).Bold(true))
|
||||
}
|
||||
row += 2
|
||||
|
||||
t.Label(0, row, fmt.Sprintf("Current Base Fee is: %s", types.FIL(baseFee)), defS)
|
||||
row++
|
||||
t.Label(0, row, fmt.Sprintf("Resulting FeeCap is: %s",
|
||||
types.FIL(big.Div(*maxFee, big.NewInt(gasLimit)))), defS)
|
||||
row++
|
||||
t.Label(0, row, "You can use '+' and '-' to adjust the fee.", defS)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
146
cli/services.go
146
cli/services.go
@ -4,14 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
@ -22,12 +22,23 @@ import (
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination=servicesmock_test.go -package=cli -self_package github.com/filecoin-project/lotus/cli . ServicesAPI
|
||||
|
||||
type ServicesAPI interface {
|
||||
// Sends executes a send given SendParams
|
||||
Send(ctx context.Context, params SendParams) (cid.Cid, error)
|
||||
GetBaseFee(ctx context.Context) (abi.TokenAmount, error)
|
||||
|
||||
// MessageForSend creates a prototype of a message based on SendParams
|
||||
MessageForSend(ctx context.Context, params SendParams) (*types.Message, error)
|
||||
|
||||
// DecodeTypedParamsFromJSON takes in information needed to identify a method and converts JSON
|
||||
// parameters to bytes of their CBOR encoding
|
||||
DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error)
|
||||
|
||||
RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error)
|
||||
|
||||
// PublishMessage takes in a message prototype and publishes it
|
||||
// before publishing the message, it runs checks on the node, message and mpool to verify that
|
||||
// message is valid and won't be stuck.
|
||||
// if `force` is true, it skips the checks
|
||||
PublishMessage(ctx context.Context, prototype *types.Message, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error)
|
||||
|
||||
// Close ends the session of services and disconnects from RPC, using Services after Close is called
|
||||
// most likely will result in an error
|
||||
// Should not be called concurrently
|
||||
@ -35,7 +46,7 @@ type ServicesAPI interface {
|
||||
}
|
||||
|
||||
type ServicesImpl struct {
|
||||
api v0api.FullNode
|
||||
api api.FullNode
|
||||
closer jsonrpc.ClientCloser
|
||||
}
|
||||
|
||||
@ -48,6 +59,16 @@ func (s *ServicesImpl) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServicesImpl) GetBaseFee(ctx context.Context) (abi.TokenAmount, error) {
|
||||
// not used but useful
|
||||
|
||||
ts, err := s.api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return big.Zero(), xerrors.Errorf("getting head: %w", err)
|
||||
}
|
||||
return ts.MinTicketBlock().ParentBaseFee, nil
|
||||
}
|
||||
|
||||
func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) {
|
||||
act, err := s.api.StateGetActor(ctx, to, types.EmptyTSK)
|
||||
if err != nil {
|
||||
@ -72,6 +93,76 @@ func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type CheckInfo struct {
|
||||
MessageTie cid.Cid
|
||||
CurrentMessageTie bool
|
||||
|
||||
Check api.MessageCheckStatus
|
||||
}
|
||||
|
||||
var ErrCheckFailed = fmt.Errorf("check has failed")
|
||||
|
||||
func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) {
|
||||
var outChecks [][]api.MessageCheckStatus
|
||||
checks, err := s.api.MpoolCheckMessages(ctx, []*types.Message{prototype})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("message check: %w", err)
|
||||
}
|
||||
outChecks = append(outChecks, checks...)
|
||||
|
||||
checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.From)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("pending mpool check: %w", err)
|
||||
}
|
||||
outChecks = append(outChecks, checks...)
|
||||
|
||||
return outChecks, nil
|
||||
}
|
||||
|
||||
// PublishMessage modifies prototype to include gas estimation
|
||||
// Errors with ErrCheckFailed if any of the checks fail
|
||||
// First group of checks is related to the message prototype
|
||||
func (s *ServicesImpl) PublishMessage(ctx context.Context,
|
||||
prototype *types.Message, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) {
|
||||
|
||||
gasedMsg, err := s.api.GasEstimateMessageGas(ctx, prototype, nil, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("estimating gas: %w", err)
|
||||
}
|
||||
*prototype = *gasedMsg
|
||||
|
||||
if !force {
|
||||
checks, err := s.RunChecksForPrototype(ctx, prototype)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("running checks: %w", err)
|
||||
}
|
||||
if len(checks) != 0 {
|
||||
return nil, checks, ErrCheckFailed
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: message prototype needs to have "IsNonceSet"
|
||||
if prototype.Nonce != 0 {
|
||||
sm, err := s.api.WalletSignMessage(ctx, prototype.From, prototype)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, err = s.api.MpoolPush(ctx, sm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return sm, nil, nil
|
||||
}
|
||||
|
||||
sm, err := s.api.MpoolPushMessage(ctx, prototype, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return sm, nil, nil
|
||||
}
|
||||
|
||||
type SendParams struct {
|
||||
To address.Address
|
||||
From address.Address
|
||||
@ -84,21 +175,13 @@ type SendParams struct {
|
||||
Nonce *uint64
|
||||
Method abi.MethodNum
|
||||
Params []byte
|
||||
|
||||
Force bool
|
||||
}
|
||||
|
||||
// This is specialised Send for Send command
|
||||
// There might be room for generic Send that other commands can use to send their messages
|
||||
// We will see
|
||||
|
||||
var ErrSendBalanceTooLow = errors.New("balance too low")
|
||||
|
||||
func (s *ServicesImpl) Send(ctx context.Context, params SendParams) (cid.Cid, error) {
|
||||
func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) {
|
||||
if params.From == address.Undef {
|
||||
defaddr, err := s.api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
return nil, err
|
||||
}
|
||||
params.From = defaddr
|
||||
}
|
||||
@ -127,40 +210,9 @@ func (s *ServicesImpl) Send(ctx context.Context, params SendParams) (cid.Cid, er
|
||||
} else {
|
||||
msg.GasLimit = 0
|
||||
}
|
||||
|
||||
if !params.Force {
|
||||
// Funds insufficient check
|
||||
fromBalance, err := s.api.WalletBalance(ctx, msg.From)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
totalCost := types.BigAdd(types.BigMul(msg.GasFeeCap, types.NewInt(uint64(msg.GasLimit))), msg.Value)
|
||||
|
||||
if fromBalance.LessThan(totalCost) {
|
||||
return cid.Undef, xerrors.Errorf("From balance %s less than total cost %s: %w", types.FIL(fromBalance), types.FIL(totalCost), ErrSendBalanceTooLow)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if params.Nonce != nil {
|
||||
msg.Nonce = *params.Nonce
|
||||
sm, err := s.api.WalletSignMessage(ctx, params.From, msg)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
_, err = s.api.MpoolPush(ctx, sm)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return sm.Cid(), nil
|
||||
}
|
||||
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return sm.Cid(), nil
|
||||
return msg, nil
|
||||
}
|
||||
|
@ -151,47 +151,12 @@ func TestSendService(t *testing.T) {
|
||||
|
||||
t.Run("happy", func(t *testing.T) {
|
||||
params := params
|
||||
srvcs, mockApi := setupMockSrvcs(t)
|
||||
srvcs, _ := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
msgCid, sign := makeMessageSigner()
|
||||
gomock.InOrder(
|
||||
mockApi.EXPECT().WalletBalance(ctxM, params.From).Return(types.NewInt(balance), nil),
|
||||
mockApi.EXPECT().MpoolPushMessage(ctxM, MessageMatcher(params), nil).DoAndReturn(sign),
|
||||
)
|
||||
|
||||
c, err := srvcs.Send(ctx, params)
|
||||
proto, err := srvcs.MessageForSend(ctx, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *msgCid, c)
|
||||
})
|
||||
|
||||
t.Run("balance-too-low", func(t *testing.T) {
|
||||
params := params
|
||||
srvcs, mockApi := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
gomock.InOrder(
|
||||
mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance-200), nil),
|
||||
// no MpoolPushMessage
|
||||
)
|
||||
|
||||
c, err := srvcs.Send(ctx, params)
|
||||
assert.Equal(t, c, cid.Undef)
|
||||
assert.ErrorIs(t, err, ErrSendBalanceTooLow)
|
||||
})
|
||||
|
||||
t.Run("force", func(t *testing.T) {
|
||||
params := params
|
||||
params.Force = true
|
||||
srvcs, mockApi := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
msgCid, sign := makeMessageSigner()
|
||||
gomock.InOrder(
|
||||
mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance-200), nil).AnyTimes(),
|
||||
mockApi.EXPECT().MpoolPushMessage(ctxM, MessageMatcher(params), nil).DoAndReturn(sign),
|
||||
)
|
||||
|
||||
c, err := srvcs.Send(ctx, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *msgCid, c)
|
||||
assert.True(t, MessageMatcher(params).Matches(proto))
|
||||
})
|
||||
|
||||
t.Run("default-from", func(t *testing.T) {
|
||||
@ -202,16 +167,14 @@ func TestSendService(t *testing.T) {
|
||||
|
||||
srvcs, mockApi := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
msgCid, sign := makeMessageSigner()
|
||||
|
||||
gomock.InOrder(
|
||||
mockApi.EXPECT().WalletDefaultAddress(ctxM).Return(a1, nil),
|
||||
mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance), nil),
|
||||
mockApi.EXPECT().MpoolPushMessage(ctxM, mm, nil).DoAndReturn(sign),
|
||||
)
|
||||
|
||||
c, err := srvcs.Send(ctx, params)
|
||||
proto, err := srvcs.MessageForSend(ctx, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *msgCid, c)
|
||||
assert.True(t, mm.Matches(proto))
|
||||
})
|
||||
|
||||
t.Run("set-nonce", func(t *testing.T) {
|
||||
@ -220,26 +183,12 @@ func TestSendService(t *testing.T) {
|
||||
params.Nonce = &n
|
||||
mm := MessageMatcher(params)
|
||||
|
||||
srvcs, mockApi := setupMockSrvcs(t)
|
||||
srvcs, _ := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
_, _ = mm, mockApi
|
||||
|
||||
var sm *types.SignedMessage
|
||||
gomock.InOrder(
|
||||
mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance), nil),
|
||||
mockApi.EXPECT().WalletSignMessage(ctxM, a1, mm).DoAndReturn(
|
||||
func(_ context.Context, _ address.Address, msg *types.Message) (*types.SignedMessage, error) {
|
||||
sm = fakeSign(msg)
|
||||
|
||||
// now we expect MpoolPush with that SignedMessage
|
||||
mockApi.EXPECT().MpoolPush(ctxM, sm).Return(sm.Cid(), nil)
|
||||
return sm, nil
|
||||
}),
|
||||
)
|
||||
|
||||
c, err := srvcs.Send(ctx, params)
|
||||
proto, err := srvcs.MessageForSend(ctx, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sm.Cid(), c)
|
||||
assert.True(t, mm.Matches(proto))
|
||||
})
|
||||
|
||||
t.Run("gas-params", func(t *testing.T) {
|
||||
@ -251,16 +200,14 @@ func TestSendService(t *testing.T) {
|
||||
gp := big.NewInt(10)
|
||||
params.GasPremium = &gp
|
||||
|
||||
srvcs, mockApi := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
msgCid, sign := makeMessageSigner()
|
||||
gomock.InOrder(
|
||||
mockApi.EXPECT().WalletBalance(ctxM, params.From).Return(types.NewInt(balance), nil),
|
||||
mockApi.EXPECT().MpoolPushMessage(ctxM, MessageMatcher(params), nil).DoAndReturn(sign),
|
||||
)
|
||||
mm := MessageMatcher(params)
|
||||
|
||||
c, err := srvcs.Send(ctx, params)
|
||||
srvcs, _ := setupMockSrvcs(t)
|
||||
defer srvcs.Close() //nolint:errcheck
|
||||
|
||||
proto, err := srvcs.MessageForSend(ctx, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *msgCid, c)
|
||||
assert.True(t, mm.Matches(proto))
|
||||
|
||||
})
|
||||
}
|
||||
|
@ -8,8 +8,10 @@ import (
|
||||
context "context"
|
||||
go_address "github.com/filecoin-project/go-address"
|
||||
abi "github.com/filecoin-project/go-state-types/abi"
|
||||
big "github.com/filecoin-project/go-state-types/big"
|
||||
api "github.com/filecoin-project/lotus/api"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
go_cid "github.com/ipfs/go-cid"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
@ -65,17 +67,63 @@ func (mr *MockServicesAPIMockRecorder) DecodeTypedParamsFromJSON(arg0, arg1, arg
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeTypedParamsFromJSON", reflect.TypeOf((*MockServicesAPI)(nil).DecodeTypedParamsFromJSON), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// Send mocks base method
|
||||
func (m *MockServicesAPI) Send(arg0 context.Context, arg1 SendParams) (go_cid.Cid, error) {
|
||||
// GetBaseFee mocks base method
|
||||
func (m *MockServicesAPI) GetBaseFee(arg0 context.Context) (big.Int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Send", arg0, arg1)
|
||||
ret0, _ := ret[0].(go_cid.Cid)
|
||||
ret := m.ctrl.Call(m, "GetBaseFee", arg0)
|
||||
ret0, _ := ret[0].(big.Int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Send indicates an expected call of Send
|
||||
func (mr *MockServicesAPIMockRecorder) Send(arg0, arg1 interface{}) *gomock.Call {
|
||||
// GetBaseFee indicates an expected call of GetBaseFee
|
||||
func (mr *MockServicesAPIMockRecorder) GetBaseFee(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockServicesAPI)(nil).Send), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseFee", reflect.TypeOf((*MockServicesAPI)(nil).GetBaseFee), arg0)
|
||||
}
|
||||
|
||||
// MessageForSend mocks base method
|
||||
func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*types.Message, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MessageForSend", arg0, arg1)
|
||||
ret0, _ := ret[0].(*types.Message)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// MessageForSend indicates an expected call of MessageForSend
|
||||
func (mr *MockServicesAPIMockRecorder) MessageForSend(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageForSend", reflect.TypeOf((*MockServicesAPI)(nil).MessageForSend), arg0, arg1)
|
||||
}
|
||||
|
||||
// PublishMessage mocks base method
|
||||
func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *types.Message, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PublishMessage", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*types.SignedMessage)
|
||||
ret1, _ := ret[1].([][]api.MessageCheckStatus)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// PublishMessage indicates an expected call of PublishMessage
|
||||
func (mr *MockServicesAPIMockRecorder) PublishMessage(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishMessage", reflect.TypeOf((*MockServicesAPI)(nil).PublishMessage), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// RunChecksForPrototype mocks base method
|
||||
func (m *MockServicesAPI) RunChecksForPrototype(arg0 context.Context, arg1 *types.Message) ([][]api.MessageCheckStatus, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RunChecksForPrototype", arg0, arg1)
|
||||
ret0, _ := ret[0].([][]api.MessageCheckStatus)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// RunChecksForPrototype indicates an expected call of RunChecksForPrototype
|
||||
func (mr *MockServicesAPIMockRecorder) RunChecksForPrototype(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunChecksForPrototype", reflect.TypeOf((*MockServicesAPI)(nil).RunChecksForPrototype), arg0, arg1)
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -8,6 +8,7 @@ require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/GeertJohan/go.rice v1.0.0
|
||||
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
|
||||
github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921
|
||||
@ -50,6 +51,7 @@ require (
|
||||
github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506
|
||||
github.com/filecoin-project/test-vectors/schema v0.0.5
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
||||
github.com/gdamore/tcell/v2 v2.2.0
|
||||
github.com/go-kit/kit v0.10.0
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
github.com/golang/mock v1.4.4
|
||||
@ -86,7 +88,7 @@ require (
|
||||
github.com/ipfs/go-ipfs-util v0.0.2
|
||||
github.com/ipfs/go-ipld-cbor v0.0.5
|
||||
github.com/ipfs/go-ipld-format v0.2.0
|
||||
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4
|
||||
github.com/ipfs/go-log/v2 v2.1.2
|
||||
github.com/ipfs/go-merkledag v0.3.2
|
||||
github.com/ipfs/go-metrics-interface v0.0.1
|
||||
github.com/ipfs/go-metrics-prometheus v0.0.2
|
||||
|
18
go.sum
18
go.sum
@ -45,6 +45,8 @@ github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETF
|
||||
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/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/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
@ -330,6 +332,10 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 h1:EzDjxMg43q1tA2c0MV3tNbaontnHLplHyFF6M5KiVP0=
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1/go.mod h1:0eHX/BVySxPc6SE2mZRoppGq7qcEagxdmQnA3dzork8=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U4=
|
||||
github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
|
||||
@ -672,8 +678,9 @@ github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW
|
||||
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
|
||||
github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
|
||||
github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
||||
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 h1:3bijxqzQ1O9yg7gd7Aqk80oaEvsJ+uXw0zSvi2qR3Jw=
|
||||
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
||||
github.com/ipfs/go-log/v2 v2.1.2 h1:a0dRiL098zY23vay1h3dimx6y94XchEUyt5h0l4VvQU=
|
||||
github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
|
||||
github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA=
|
||||
github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto=
|
||||
github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
|
||||
@ -1110,6 +1117,8 @@ github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdf
|
||||
github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE=
|
||||
github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys=
|
||||
github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
@ -1143,8 +1152,9 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
@ -1369,6 +1379,8 @@ github.com/raulk/go-watchdog v1.0.1/go.mod h1:lzSbAl5sh4rtI8tYHU01BWIDzgzqaQLj6R
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@ -1814,6 +1826,8 @@ golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
Loading…
Reference in New Issue
Block a user