Merge master into feat/nv13
This commit is contained in:
+7
-7
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
cliutil "github.com/filecoin-project/lotus/cli/util"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
@@ -47,18 +47,18 @@ var AuthCreateAdminToken = &cli.Command{
|
||||
|
||||
perm := cctx.String("perm")
|
||||
idx := 0
|
||||
for i, p := range apistruct.AllPermissions {
|
||||
for i, p := range api.AllPermissions {
|
||||
if auth.Permission(perm) == p {
|
||||
idx = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
if idx == 0 {
|
||||
return fmt.Errorf("--perm flag has to be one of: %s", apistruct.AllPermissions)
|
||||
return fmt.Errorf("--perm flag has to be one of: %s", api.AllPermissions)
|
||||
}
|
||||
|
||||
// slice on [:idx] so for example: 'sign' gives you [read, write, sign]
|
||||
token, err := napi.AuthNew(ctx, apistruct.AllPermissions[:idx])
|
||||
token, err := napi.AuthNew(ctx, api.AllPermissions[:idx])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -95,18 +95,18 @@ var AuthApiInfoToken = &cli.Command{
|
||||
|
||||
perm := cctx.String("perm")
|
||||
idx := 0
|
||||
for i, p := range apistruct.AllPermissions {
|
||||
for i, p := range api.AllPermissions {
|
||||
if auth.Permission(perm) == p {
|
||||
idx = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
if idx == 0 {
|
||||
return fmt.Errorf("--perm flag has to be one of: %s", apistruct.AllPermissions)
|
||||
return fmt.Errorf("--perm flag has to be one of: %s", api.AllPermissions)
|
||||
}
|
||||
|
||||
// slice on [:idx] so for example: 'sign' gives you [read, write, sign]
|
||||
token, err := napi.AuthNew(ctx, apistruct.AllPermissions[:idx])
|
||||
token, err := napi.AuthNew(ctx, api.AllPermissions[:idx])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+23
-23
@@ -31,7 +31,9 @@ import (
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lapi "github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
@@ -722,12 +724,6 @@ var ChainGetCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if ts == nil {
|
||||
ts, err = api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p = "/ipfs/" + ts.ParentState().String() + p
|
||||
if cctx.Bool("verbose") {
|
||||
fmt.Println(p)
|
||||
@@ -806,7 +802,7 @@ var ChainGetCmd = &cli.Command{
|
||||
|
||||
type apiIpldStore struct {
|
||||
ctx context.Context
|
||||
api lapi.FullNode
|
||||
api v0api.FullNode
|
||||
}
|
||||
|
||||
func (ht *apiIpldStore) Context() context.Context {
|
||||
@@ -834,7 +830,7 @@ func (ht *apiIpldStore) Put(ctx context.Context, v interface{}) (cid.Cid, error)
|
||||
panic("No mutations allowed")
|
||||
}
|
||||
|
||||
func handleAmt(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
func handleAmt(ctx context.Context, api v0api.FullNode, r cid.Cid) error {
|
||||
s := &apiIpldStore{ctx, api}
|
||||
mp, err := adt.AsArray(s, r)
|
||||
if err != nil {
|
||||
@@ -847,7 +843,7 @@ func handleAmt(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
})
|
||||
}
|
||||
|
||||
func handleHamtEpoch(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
func handleHamtEpoch(ctx context.Context, api v0api.FullNode, r cid.Cid) error {
|
||||
s := &apiIpldStore{ctx, api}
|
||||
mp, err := adt.AsMap(s, r)
|
||||
if err != nil {
|
||||
@@ -865,7 +861,7 @@ func handleHamtEpoch(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
})
|
||||
}
|
||||
|
||||
func handleHamtAddress(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
func handleHamtAddress(ctx context.Context, api v0api.FullNode, r cid.Cid) error {
|
||||
s := &apiIpldStore{ctx, api}
|
||||
mp, err := adt.AsMap(s, r)
|
||||
if err != nil {
|
||||
@@ -1121,11 +1117,13 @@ var SlashConsensusFault = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
a := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
c1, err := cid.Parse(cctx.Args().Get(0))
|
||||
@@ -1133,7 +1131,7 @@ var SlashConsensusFault = &cli.Command{
|
||||
return xerrors.Errorf("parsing cid 1: %w", err)
|
||||
}
|
||||
|
||||
b1, err := api.ChainGetBlock(ctx, c1)
|
||||
b1, err := a.ChainGetBlock(ctx, c1)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting block 1: %w", err)
|
||||
}
|
||||
@@ -1143,7 +1141,7 @@ var SlashConsensusFault = &cli.Command{
|
||||
return xerrors.Errorf("parsing cid 2: %w", err)
|
||||
}
|
||||
|
||||
b2, err := api.ChainGetBlock(ctx, c2)
|
||||
b2, err := a.ChainGetBlock(ctx, c2)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting block 2: %w", err)
|
||||
}
|
||||
@@ -1154,7 +1152,7 @@ var SlashConsensusFault = &cli.Command{
|
||||
|
||||
var fromAddr address.Address
|
||||
if from := cctx.String("from"); from == "" {
|
||||
defaddr, err := api.WalletDefaultAddress(ctx)
|
||||
defaddr, err := a.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1190,7 +1188,7 @@ var SlashConsensusFault = &cli.Command{
|
||||
return xerrors.Errorf("parsing cid extra: %w", err)
|
||||
}
|
||||
|
||||
bExtra, err := api.ChainGetBlock(ctx, cExtra)
|
||||
bExtra, err := a.ChainGetBlock(ctx, cExtra)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting block extra: %w", err)
|
||||
}
|
||||
@@ -1208,15 +1206,17 @@ var SlashConsensusFault = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: b2.Miner,
|
||||
From: fromAddr,
|
||||
Value: types.NewInt(0),
|
||||
Method: builtin.MethodsMiner.ReportConsensusFault,
|
||||
Params: enc,
|
||||
proto := &api.MessagePrototype{
|
||||
Message: types.Message{
|
||||
To: b2.Miner,
|
||||
From: fromAddr,
|
||||
Value: types.NewInt(0),
|
||||
Method: builtin.MethodsMiner.ReportConsensusFault,
|
||||
Params: enc,
|
||||
},
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg, nil)
|
||||
smsg, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+27
-10
@@ -40,6 +40,7 @@ import (
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lapi "github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||
@@ -321,6 +322,10 @@ The minimum value is 518400 (6 months).`,
|
||||
Name: "manual-piece-size",
|
||||
Usage: "if manually specifying piece cid, used to specify size (dataCid must be to a car file)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "manual-stateless-deal",
|
||||
Usage: "instructs the node to send an offline deal without registering it with the deallist/fsm",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "specify address to fund the deal with",
|
||||
@@ -460,7 +465,7 @@ The minimum value is 518400 (6 months).`,
|
||||
isVerified = verifiedDealParam
|
||||
}
|
||||
|
||||
proposal, err := api.ClientStartDeal(ctx, &lapi.StartDealParams{
|
||||
sdParams := &lapi.StartDealParams{
|
||||
Data: ref,
|
||||
Wallet: a,
|
||||
Miner: miner,
|
||||
@@ -470,7 +475,18 @@ The minimum value is 518400 (6 months).`,
|
||||
FastRetrieval: cctx.Bool("fast-retrieval"),
|
||||
VerifiedDeal: isVerified,
|
||||
ProviderCollateral: provCol,
|
||||
})
|
||||
}
|
||||
|
||||
var proposal *cid.Cid
|
||||
if cctx.Bool("manual-stateless-deal") {
|
||||
if ref.TransferType != storagemarket.TTManual || price.Int64() != 0 {
|
||||
return xerrors.New("when manual-stateless-deal is enabled, you must also provide a 'price' of 0 and specify 'manual-piece-cid' and 'manual-piece-size'")
|
||||
}
|
||||
proposal, err = api.ClientStatelessDeal(ctx, sdParams)
|
||||
} else {
|
||||
proposal, err = api.ClientStartDeal(ctx, sdParams)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1295,7 +1311,8 @@ var clientListAsksCmd = &cli.Command{
|
||||
Usage: "List asks for top miners",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "by-ping",
|
||||
Name: "by-ping",
|
||||
Usage: "sort by ping",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output-format",
|
||||
@@ -1348,7 +1365,7 @@ type QueriedAsk struct {
|
||||
Ping time.Duration
|
||||
}
|
||||
|
||||
func GetAsks(ctx context.Context, api lapi.FullNode) ([]QueriedAsk, error) {
|
||||
func GetAsks(ctx context.Context, api v0api.FullNode) ([]QueriedAsk, error) {
|
||||
isTTY := true
|
||||
if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||
isTTY = false
|
||||
@@ -1450,17 +1467,17 @@ loop:
|
||||
}
|
||||
|
||||
rt := time.Now()
|
||||
|
||||
_, err = api.ClientQueryAsk(ctx, *mi.PeerId, miner)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pingDuration := time.Now().Sub(rt)
|
||||
|
||||
atomic.AddInt64(&got, 1)
|
||||
lk.Lock()
|
||||
asks = append(asks, QueriedAsk{
|
||||
Ask: ask,
|
||||
Ping: time.Now().Sub(rt),
|
||||
Ping: pingDuration,
|
||||
})
|
||||
lk.Unlock()
|
||||
}(miner)
|
||||
@@ -1655,7 +1672,7 @@ var clientListDeals = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func dealFromDealInfo(ctx context.Context, full api.FullNode, head *types.TipSet, v api.DealInfo) deal {
|
||||
func dealFromDealInfo(ctx context.Context, full v0api.FullNode, head *types.TipSet, v api.DealInfo) deal {
|
||||
if v.DealID == 0 {
|
||||
return deal{
|
||||
LocalDeal: v,
|
||||
@@ -1674,7 +1691,7 @@ func dealFromDealInfo(ctx context.Context, full api.FullNode, head *types.TipSet
|
||||
}
|
||||
}
|
||||
|
||||
func outputStorageDeals(ctx context.Context, out io.Writer, full lapi.FullNode, localDeals []lapi.DealInfo, verbose bool, color bool, showFailed bool) error {
|
||||
func outputStorageDeals(ctx context.Context, out io.Writer, full v0api.FullNode, localDeals []lapi.DealInfo, verbose bool, color bool, showFailed bool) error {
|
||||
sort.Slice(localDeals, func(i, j int) bool {
|
||||
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
|
||||
})
|
||||
@@ -2293,7 +2310,7 @@ func ellipsis(s string, length int) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func inspectDealCmd(ctx context.Context, api lapi.FullNode, proposalCid string, dealId int) error {
|
||||
func inspectDealCmd(ctx context.Context, api v0api.FullNode, proposalCid string, dealId int) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -2346,7 +2363,7 @@ func renderDeal(di *lapi.DealInfo) {
|
||||
}
|
||||
|
||||
for _, stg := range di.DealStages.Stages {
|
||||
msg := fmt.Sprintf("%s %s: %s (%s)", color.BlueString("Stage:"), color.BlueString(strings.TrimPrefix(stg.Name, "StorageDeal")), stg.Description, color.GreenString(stg.ExpectedDuration))
|
||||
msg := fmt.Sprintf("%s %s: %s (expected duration: %s)", color.BlueString("Stage:"), color.BlueString(strings.TrimPrefix(stg.Name, "StorageDeal")), stg.Description, color.GreenString(stg.ExpectedDuration))
|
||||
if stg.UpdatedTime.Time().IsZero() {
|
||||
msg = color.YellowString(msg)
|
||||
}
|
||||
|
||||
+4
-1
@@ -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
|
||||
}
|
||||
@@ -50,6 +50,7 @@ var DaemonContext = cliutil.DaemonContext
|
||||
var ReqContext = cliutil.ReqContext
|
||||
|
||||
var GetFullNodeAPI = cliutil.GetFullNodeAPI
|
||||
var GetFullNodeAPIV1 = cliutil.GetFullNodeAPIV1
|
||||
var GetGatewayAPI = cliutil.GetGatewayAPI
|
||||
|
||||
var GetStorageMinerAPI = cliutil.GetStorageMinerAPI
|
||||
@@ -70,6 +71,7 @@ var Commands = []*cli.Command{
|
||||
WithCategory("basic", walletCmd),
|
||||
WithCategory("basic", clientCmd),
|
||||
WithCategory("basic", multisigCmd),
|
||||
WithCategory("basic", verifRegCmd),
|
||||
WithCategory("basic", paychCmd),
|
||||
WithCategory("developer", AuthCmd),
|
||||
WithCategory("developer", MpoolCmd),
|
||||
@@ -80,6 +82,7 @@ var Commands = []*cli.Command{
|
||||
WithCategory("developer", FetchParamCmd),
|
||||
WithCategory("network", NetCmd),
|
||||
WithCategory("network", SyncCmd),
|
||||
WithCategory("status", StatusCmd),
|
||||
PprofCmd,
|
||||
VersionCmd,
|
||||
}
|
||||
|
||||
+13
-4
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -237,6 +238,9 @@ var disputerStartCmd = &cli.Command{
|
||||
|
||||
dpmsgs := make([]*types.Message, 0)
|
||||
|
||||
startTime := time.Now()
|
||||
proofsChecked := uint64(0)
|
||||
|
||||
// TODO: Parallelizeable
|
||||
for _, dl := range dls {
|
||||
fullDeadlines, err := api.StateMinerDeadlines(ctx, dl.miner, tsk)
|
||||
@@ -248,7 +252,10 @@ var disputerStartCmd = &cli.Command{
|
||||
return xerrors.Errorf("deadline index %d not found in deadlines", dl.index)
|
||||
}
|
||||
|
||||
ms, err := makeDisputeWindowedPosts(ctx, api, dl, fullDeadlines[dl.index].DisputableProofCount, fromAddr)
|
||||
disputableProofs := fullDeadlines[dl.index].DisputableProofCount
|
||||
proofsChecked += disputableProofs
|
||||
|
||||
ms, err := makeDisputeWindowedPosts(ctx, api, dl, disputableProofs, fromAddr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check for disputes: %w", err)
|
||||
}
|
||||
@@ -263,6 +270,8 @@ var disputerStartCmd = &cli.Command{
|
||||
deadlineMap[dClose+Confidence] = append(deadlineMap[dClose+Confidence], *dl)
|
||||
}
|
||||
|
||||
disputeLog.Infow("checked proofs", "count", proofsChecked, "duration", time.Since(startTime))
|
||||
|
||||
// TODO: Parallelizeable / can be integrated into the previous deadline-iterating for loop
|
||||
for _, dpmsg := range dpmsgs {
|
||||
disputeLog.Infow("disputing a PoSt", "miner", dpmsg.To)
|
||||
@@ -356,7 +365,7 @@ var disputerStartCmd = &cli.Command{
|
||||
|
||||
// for a given miner, index, and maxPostIndex, tries to dispute posts from 0...postsSnapshotted-1
|
||||
// returns a list of DisputeWindowedPoSt msgs that are expected to succeed if sent
|
||||
func makeDisputeWindowedPosts(ctx context.Context, api lapi.FullNode, dl minerDeadline, postsSnapshotted uint64, sender address.Address) ([]*types.Message, error) {
|
||||
func makeDisputeWindowedPosts(ctx context.Context, api v0api.FullNode, dl minerDeadline, postsSnapshotted uint64, sender address.Address) ([]*types.Message, error) {
|
||||
disputes := make([]*types.Message, 0)
|
||||
|
||||
for i := uint64(0); i < postsSnapshotted; i++ {
|
||||
@@ -388,7 +397,7 @@ func makeDisputeWindowedPosts(ctx context.Context, api lapi.FullNode, dl minerDe
|
||||
return disputes, nil
|
||||
}
|
||||
|
||||
func makeMinerDeadline(ctx context.Context, api lapi.FullNode, mAddr address.Address) (abi.ChainEpoch, *minerDeadline, error) {
|
||||
func makeMinerDeadline(ctx context.Context, api v0api.FullNode, mAddr address.Address) (abi.ChainEpoch, *minerDeadline, error) {
|
||||
dl, err := api.StateMinerProvingDeadline(ctx, mAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return -1, nil, xerrors.Errorf("getting proving index list: %w", err)
|
||||
@@ -400,7 +409,7 @@ func makeMinerDeadline(ctx context.Context, api lapi.FullNode, mAddr address.Add
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getSender(ctx context.Context, api lapi.FullNode, fromStr string) (address.Address, error) {
|
||||
func getSender(ctx context.Context, api v0api.FullNode, fromStr string) (address.Address, error) {
|
||||
if fromStr == "" {
|
||||
return api.WalletDefaultAddress(ctx)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logging.SetLogLevel("watchdog", "ERROR")
|
||||
}
|
||||
@@ -34,6 +34,7 @@ var MpoolCmd = &cli.Command{
|
||||
MpoolFindCmd,
|
||||
MpoolConfig,
|
||||
MpoolGasPerfCmd,
|
||||
mpoolManage,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
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() //nolint:errcheck
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
_, localAddr, err := srv.LocalAddresses(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting local addresses: %w", err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
+190
-57
@@ -95,11 +95,13 @@ var msigCreateCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("multisigs must have at least one signer"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
var addrs []address.Address
|
||||
@@ -146,13 +148,20 @@ var msigCreateCmd = &cli.Command{
|
||||
|
||||
gp := types.NewInt(1)
|
||||
|
||||
msgCid, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp)
|
||||
proto, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
// wait for it to get mined into a block
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -364,11 +373,13 @@ var msigProposeCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must either pass three or five arguments"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -426,14 +437,21 @@ var msigProposeCmd = &cli.Command{
|
||||
return fmt.Errorf("actor %s is not a multisig actor", msig)
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params)
|
||||
proto, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("send proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -481,11 +499,13 @@ var msigApproveCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("usage: msig approve <msig addr> <message ID> <proposer address> <desination> <value> [ <method> <params> ]"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -515,10 +535,17 @@ var msigApproveCmd = &cli.Command{
|
||||
|
||||
var msgCid cid.Cid
|
||||
if cctx.Args().Len() == 2 {
|
||||
msgCid, err = api.MsigApprove(ctx, msig, txid, from)
|
||||
proto, err := api.MsigApprove(ctx, msig, txid, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid = sm.Cid()
|
||||
} else {
|
||||
proposer, err := address.NewFromString(cctx.Args().Get(2))
|
||||
if err != nil {
|
||||
@@ -558,15 +585,22 @@ var msigApproveCmd = &cli.Command{
|
||||
params = p
|
||||
}
|
||||
|
||||
msgCid, err = api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params)
|
||||
proto, err := api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid = sm.Cid()
|
||||
}
|
||||
|
||||
fmt.Println("sent approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -598,11 +632,13 @@ var msigRemoveProposeCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address and signer address"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -630,14 +666,21 @@ var msigRemoveProposeCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold"))
|
||||
proto, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent remove proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -676,11 +719,13 @@ var msigAddProposeCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address and signer address"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -708,14 +753,21 @@ var msigAddProposeCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold"))
|
||||
proto, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -743,11 +795,13 @@ var msigAddApproveCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, transaction id, new signer address, whether to increase threshold"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -790,14 +844,21 @@ var msigAddApproveCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc)
|
||||
proto, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent add approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -825,11 +886,13 @@ var msigAddCancelCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, transaction id, new signer address, whether to increase threshold"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -867,14 +930,21 @@ var msigAddCancelCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc)
|
||||
proto, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent add cancellation in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -902,11 +972,13 @@ var msigSwapProposeCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, old signer address, new signer address"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -939,14 +1011,21 @@ var msigSwapProposeCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd)
|
||||
proto, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent swap proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -974,11 +1053,13 @@ var msigSwapApproveCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, transaction id, old signer address, new signer address"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -1021,14 +1102,21 @@ var msigSwapApproveCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd)
|
||||
proto, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent swap approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1056,11 +1144,13 @@ var msigSwapCancelCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, transaction id, old signer address, new signer address"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -1098,14 +1188,21 @@ var msigSwapCancelCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd)
|
||||
proto, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent swap cancellation in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1133,11 +1230,13 @@ var msigLockProposeCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, start epoch, unlock duration, and amount"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -1185,14 +1284,21 @@ var msigLockProposeCmd = &cli.Command{
|
||||
return actErr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
|
||||
proto, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent lock proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1220,11 +1326,13 @@ var msigLockApproveCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, tx id, start epoch, unlock duration, and amount"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -1282,14 +1390,21 @@ var msigLockApproveCmd = &cli.Command{
|
||||
return actErr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
|
||||
proto, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent lock approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1317,11 +1432,13 @@ var msigLockCancelCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, tx id, start epoch, unlock duration, and amount"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -1374,14 +1491,21 @@ var msigLockCancelCmd = &cli.Command{
|
||||
return actErr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
|
||||
proto, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent lock cancellation in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1471,11 +1595,13 @@ var msigProposeThresholdCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address and new threshold value"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close() //nolint:errcheck
|
||||
|
||||
api := srv.FullNodeAPI()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
@@ -1511,14 +1637,21 @@ var msigProposeThresholdCmd = &cli.Command{
|
||||
return actErr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params)
|
||||
proto, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to propose change of threshold: %w", err)
|
||||
}
|
||||
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgCid := sm.Cid()
|
||||
|
||||
fmt.Println("sent change threshold proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+13
-11
@@ -2,7 +2,6 @@ package cli
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -59,10 +58,14 @@ var sendCmd = &cli.Command{
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
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 {
|
||||
if cctx.IsSet("force") {
|
||||
fmt.Println("'force' flag is deprecated, use global flag 'force-send'")
|
||||
}
|
||||
|
||||
if cctx.Args().Len() != 2 {
|
||||
return ShowHelp(cctx, fmt.Errorf("'send' expects two arguments, target and amount"))
|
||||
}
|
||||
@@ -137,23 +140,22 @@ 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("executing send: %w", err)
|
||||
return xerrors.Errorf("creating message prototype: %w", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cctx.App.Writer, "%s\n", msgCid)
|
||||
sm, err := InteractiveSend(ctx, cctx, srv, proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cctx.App.Writer, "%s\n", sm.Cid())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
+18
-79
@@ -2,24 +2,17 @@ package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"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"
|
||||
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)),
|
||||
To: mustAddr(address.NewIDAddress(1)),
|
||||
Value: types.NewInt(1000),
|
||||
}).Cid()
|
||||
|
||||
func mustAddr(a address.Address, err error) address.Address {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -49,80 +42,26 @@ func TestSendCLI(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)),
|
||||
Val: oneFil,
|
||||
}).Return(arbtCid, nil),
|
||||
mockSrvcs.EXPECT().Close(),
|
||||
)
|
||||
err := app.Run([]string{"lotus", "send", "t01", "1"})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, arbtCid.String()+"\n", buf.String())
|
||||
})
|
||||
t.Run("ErrSendBalanceTooLow", func(t *testing.T) {
|
||||
app, mockSrvcs, _, done := newMockApp(t, sendCmd)
|
||||
defer done()
|
||||
|
||||
gomock.InOrder(
|
||||
mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{
|
||||
To: mustAddr(address.NewIDAddress(1)),
|
||||
Val: oneFil,
|
||||
}).Return(cid.Undef, ErrSendBalanceTooLow),
|
||||
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{
|
||||
arbtProto := &api.MessagePrototype{
|
||||
Message: types.Message{
|
||||
From: mustAddr(address.NewIDAddress(1)),
|
||||
To: mustAddr(address.NewIDAddress(1)),
|
||||
Nonce: &zero,
|
||||
Val: oneFil,
|
||||
}).Return(arbtCid, nil),
|
||||
Value: oneFil,
|
||||
},
|
||||
}
|
||||
sigMsg := fakeSign(&arbtProto.Message)
|
||||
|
||||
gomock.InOrder(
|
||||
mockSrvcs.EXPECT().MessageForSend(gomock.Any(), SendParams{
|
||||
To: mustAddr(address.NewIDAddress(1)),
|
||||
Val: oneFil,
|
||||
}).Return(arbtProto, nil),
|
||||
mockSrvcs.EXPECT().PublishMessage(gomock.Any(), arbtProto, false).
|
||||
Return(sigMsg, nil, nil),
|
||||
mockSrvcs.EXPECT().Close(),
|
||||
)
|
||||
err := app.Run([]string{"lotus", "send", "--nonce=0", "t01", "1"})
|
||||
err := app.Run([]string{"lotus", "send", "t01", "1"})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, arbtCid.String()+"\n", buf.String())
|
||||
assert.EqualValues(t, sigMsg.Cid().String()+"\n", buf.String())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
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"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
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"
|
||||
)
|
||||
|
||||
func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI,
|
||||
proto *api.MessagePrototype) (*types.SignedMessage, 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 nil, xerrors.Errorf("from UI: %w", err)
|
||||
}
|
||||
|
||||
msg, _, err = srv.PublishMessage(ctx, proto, true)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("publishing message: %w", err)
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
var interactiveSolves = map[api.CheckStatusCode]bool{
|
||||
api.CheckStatusMessageMinBaseFee: true,
|
||||
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 *api.MessagePrototype, checkGroups [][]api.MessageCheckStatus,
|
||||
) (*api.MessagePrototype, error) {
|
||||
|
||||
fmt.Fprintf(printer, "Following checks have failed:\n")
|
||||
printChecks(printer, checkGroups, proto.Message.Cid())
|
||||
|
||||
if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.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.Message.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: %s\n", msgName, c.Code, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if baseFee.IsZero() {
|
||||
// this will only be the case if failing check is: MessageMinBaseFee
|
||||
baseFee = big.NewInt(build.MinimumBaseFee)
|
||||
}
|
||||
|
||||
return yes, baseFee
|
||||
}
|
||||
|
||||
func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount) (*api.MessagePrototype, error) {
|
||||
t, err := imtui.NewTui()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxFee := big.Mul(proto.Message.GasFeeCap, big.NewInt(proto.Message.GasLimit))
|
||||
send := false
|
||||
t.PushScene(feeUI(baseFee, proto.Message.GasLimit, &maxFee, &send))
|
||||
|
||||
err = t.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !send {
|
||||
return nil, fmt.Errorf("aborted by user")
|
||||
}
|
||||
|
||||
proto.Message.GasFeeCap = big.Div(maxFee, big.NewInt(proto.Message.GasLimit))
|
||||
|
||||
return proto, nil
|
||||
}
|
||||
|
||||
func feeUI(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
|
||||
t.PopScene()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
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).Nano()), defS)
|
||||
row++
|
||||
t.Label(0, row, fmt.Sprintf("Resulting FeeCap is: %s",
|
||||
types.FIL(big.Div(*maxFee, big.NewInt(gasLimit))).Nano()), defS)
|
||||
row++
|
||||
t.Label(0, row, "You can use '+' and '-' to adjust the fee.", defS)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+154
-44
@@ -4,13 +4,13 @@ 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/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"
|
||||
@@ -22,12 +22,30 @@ 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)
|
||||
FullNodeAPI() api.FullNode
|
||||
|
||||
GetBaseFee(ctx context.Context) (abi.TokenAmount, error)
|
||||
|
||||
// MessageForSend creates a prototype of a message based on SendParams
|
||||
MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, 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 *api.MessagePrototype) ([][]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 *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
|
||||
// most likely will result in an error
|
||||
// Should not be called concurrently
|
||||
@@ -39,6 +57,10 @@ type ServicesImpl struct {
|
||||
closer jsonrpc.ClientCloser
|
||||
}
|
||||
|
||||
func (s *ServicesImpl) FullNodeAPI() api.FullNode {
|
||||
return s.api
|
||||
}
|
||||
|
||||
func (s *ServicesImpl) Close() error {
|
||||
if s.closer == nil {
|
||||
return xerrors.Errorf("Services already closed")
|
||||
@@ -48,6 +70,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 +104,79 @@ 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 *api.MessagePrototype) ([][]api.MessageCheckStatus, error) {
|
||||
var outChecks [][]api.MessageCheckStatus
|
||||
checks, err := s.api.MpoolCheckMessages(ctx, []*api.MessagePrototype{prototype})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("message check: %w", err)
|
||||
}
|
||||
outChecks = append(outChecks, checks...)
|
||||
|
||||
checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.Message.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 *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) {
|
||||
|
||||
gasedMsg, err := s.api.GasEstimateMessageGas(ctx, &prototype.Message, nil, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("estimating gas: %w", err)
|
||||
}
|
||||
prototype.Message = *gasedMsg
|
||||
|
||||
if !force {
|
||||
checks, err := s.RunChecksForPrototype(ctx, prototype)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("running checks: %w", err)
|
||||
}
|
||||
for _, chks := range checks {
|
||||
for _, c := range chks {
|
||||
if !c.OK {
|
||||
return nil, checks, ErrCheckFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if prototype.ValidNonce {
|
||||
sm, err := s.api.WalletSignMessage(ctx, prototype.Message.From, &prototype.Message)
|
||||
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.Message, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return sm, nil, nil
|
||||
}
|
||||
|
||||
type SendParams struct {
|
||||
To address.Address
|
||||
From address.Address
|
||||
@@ -84,26 +189,18 @@ 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) (*api.MessagePrototype, 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
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
msg := types.Message{
|
||||
From: params.From,
|
||||
To: params.To,
|
||||
Value: params.Val,
|
||||
@@ -127,40 +224,53 @@ 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)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
validNonce := false
|
||||
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
|
||||
validNonce = true
|
||||
}
|
||||
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
prototype := &api.MessagePrototype{
|
||||
Message: msg,
|
||||
ValidNonce: validNonce,
|
||||
}
|
||||
return prototype, nil
|
||||
}
|
||||
|
||||
return sm.Cid(), 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
|
||||
}
|
||||
|
||||
+30
-81
@@ -9,10 +9,9 @@ import (
|
||||
"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"
|
||||
mocks "github.com/filecoin-project/lotus/api/mocks"
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -61,6 +60,7 @@ func setupMockSrvcs(t *testing.T) (*ServicesImpl, *mocks.MockFullNode) {
|
||||
return srvcs, mockApi
|
||||
}
|
||||
|
||||
// linter doesn't like dead code, so these are commented out.
|
||||
func fakeSign(msg *types.Message) *types.SignedMessage {
|
||||
return &types.SignedMessage{
|
||||
Message: *msg,
|
||||
@@ -68,15 +68,15 @@ func fakeSign(msg *types.Message) *types.SignedMessage {
|
||||
}
|
||||
}
|
||||
|
||||
func makeMessageSigner() (*cid.Cid, interface{}) {
|
||||
smCid := cid.Undef
|
||||
return &smCid,
|
||||
func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) {
|
||||
sm := fakeSign(msg)
|
||||
smCid = sm.Cid()
|
||||
return sm, nil
|
||||
}
|
||||
}
|
||||
//func makeMessageSigner() (*cid.Cid, interface{}) {
|
||||
//smCid := cid.Undef
|
||||
//return &smCid,
|
||||
//func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) {
|
||||
//sm := fakeSign(msg)
|
||||
//smCid = sm.Cid()
|
||||
//return sm, nil
|
||||
//}
|
||||
//}
|
||||
|
||||
type MessageMatcher SendParams
|
||||
|
||||
@@ -84,11 +84,13 @@ var _ gomock.Matcher = MessageMatcher{}
|
||||
|
||||
// Matches returns whether x is a match.
|
||||
func (mm MessageMatcher) Matches(x interface{}) bool {
|
||||
m, ok := x.(*types.Message)
|
||||
proto, ok := x.(*api.MessagePrototype)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
m := &proto.Message
|
||||
|
||||
if mm.From != address.Undef && mm.From != m.From {
|
||||
return false
|
||||
}
|
||||
@@ -151,47 +153,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 +169,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 +185,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 +202,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))
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
+116
-8
@@ -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,123 @@ 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) {
|
||||
// FullNodeAPI mocks base method
|
||||
func (m *MockServicesAPI) FullNodeAPI() api.FullNode {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Send", arg0, arg1)
|
||||
ret0, _ := ret[0].(go_cid.Cid)
|
||||
ret := m.ctrl.Call(m, "FullNodeAPI")
|
||||
ret0, _ := ret[0].(api.FullNode)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// FullNodeAPI indicates an expected call of FullNodeAPI
|
||||
func (mr *MockServicesAPIMockRecorder) FullNodeAPI() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullNodeAPI", reflect.TypeOf((*MockServicesAPI)(nil).FullNodeAPI))
|
||||
}
|
||||
|
||||
// GetBaseFee mocks base method
|
||||
func (m *MockServicesAPI) GetBaseFee(arg0 context.Context) (big.Int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*api.MessagePrototype, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MessageForSend", arg0, arg1)
|
||||
ret0, _ := ret[0].(*api.MessagePrototype)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *api.MessagePrototype, 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 *api.MessagePrototype) ([][]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)
|
||||
}
|
||||
|
||||
+55
-29
@@ -15,6 +15,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
|
||||
@@ -73,6 +75,50 @@ var StateCmd = &cli.Command{
|
||||
StateMarketCmd,
|
||||
StateExecTraceCmd,
|
||||
StateNtwkVersionCmd,
|
||||
StateMinerProvingDeadlineCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var StateMinerProvingDeadlineCmd = &cli.Command{
|
||||
Name: "miner-proving-deadline",
|
||||
Usage: "Retrieve information about a given miner's proving deadline",
|
||||
ArgsUsage: "[minerAddress]",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify miner to get information for")
|
||||
}
|
||||
|
||||
addr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ts, err := LoadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cd, err := api.StateMinerProvingDeadline(ctx, addr, ts.Key())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting miner info: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Period Start:\t%s\n", cd.PeriodStart)
|
||||
fmt.Printf("Index:\t\t%d\n", cd.Index)
|
||||
fmt.Printf("Open:\t\t%s\n", cd.Open)
|
||||
fmt.Printf("Close:\t\t%s\n", cd.Close)
|
||||
fmt.Printf("Challenge:\t%s\n", cd.Challenge)
|
||||
fmt.Printf("FaultCutoff:\t%s\n", cd.FaultCutoff)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -178,16 +224,19 @@ func ParseTipSetString(ts string) ([]cid.Cid, error) {
|
||||
return cids, nil
|
||||
}
|
||||
|
||||
func LoadTipSet(ctx context.Context, cctx *cli.Context, api api.FullNode) (*types.TipSet, error) {
|
||||
// LoadTipSet gets the tipset from the context, or the head from the API.
|
||||
//
|
||||
// It always gets the head from the API so commands use a consistent tipset even if time pases.
|
||||
func LoadTipSet(ctx context.Context, cctx *cli.Context, api v0api.FullNode) (*types.TipSet, error) {
|
||||
tss := cctx.String("tipset")
|
||||
if tss == "" {
|
||||
return nil, nil
|
||||
return api.ChainHead(ctx)
|
||||
}
|
||||
|
||||
return ParseTipSetRef(ctx, api, tss)
|
||||
}
|
||||
|
||||
func ParseTipSetRef(ctx context.Context, api api.FullNode, tss string) (*types.TipSet, error) {
|
||||
func ParseTipSetRef(ctx context.Context, api v0api.FullNode, tss string) (*types.TipSet, error) {
|
||||
if tss[0] == '@' {
|
||||
if tss == "@head" {
|
||||
return api.ChainHead(ctx)
|
||||
@@ -574,7 +623,7 @@ var StateListMinersCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func getDealsCounts(ctx context.Context, lapi api.FullNode) (map[address.Address]int, error) {
|
||||
func getDealsCounts(ctx context.Context, lapi v0api.FullNode) (map[address.Address]int, error) {
|
||||
allDeals, err := lapi.StateMarketDeals(ctx, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -848,14 +897,6 @@ var StateListMessagesCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if ts == nil {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts = head
|
||||
}
|
||||
|
||||
windowSize := abi.ChainEpoch(100)
|
||||
|
||||
cur := ts
|
||||
@@ -955,13 +996,6 @@ var StateComputeStateCmd = &cli.Command{
|
||||
}
|
||||
|
||||
h := abi.ChainEpoch(cctx.Uint64("vm-height"))
|
||||
if ts == nil {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts = head
|
||||
}
|
||||
if h == 0 {
|
||||
h = ts.Height()
|
||||
}
|
||||
@@ -1443,7 +1477,7 @@ var StateSearchMsgCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func printReceiptReturn(ctx context.Context, api api.FullNode, m *types.Message, r types.MessageReceipt) error {
|
||||
func printReceiptReturn(ctx context.Context, api v0api.FullNode, m *types.Message, r types.MessageReceipt) error {
|
||||
if len(r.Return) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1463,8 +1497,7 @@ func printReceiptReturn(ctx context.Context, api api.FullNode, m *types.Message,
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMsg(ctx context.Context, api api.FullNode, msg cid.Cid, mw *lapi.MsgLookup, m *types.Message) error {
|
||||
|
||||
func printMsg(ctx context.Context, api v0api.FullNode, msg cid.Cid, mw *lapi.MsgLookup, m *types.Message) error {
|
||||
if mw == nil {
|
||||
fmt.Println("message was not found on chain")
|
||||
return nil
|
||||
@@ -1764,13 +1797,6 @@ var StateSectorCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if ts == nil {
|
||||
ts, err = api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
maddr, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
var StatusCmd = &cli.Command{
|
||||
Name: "status",
|
||||
Usage: "Check node status",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "chain",
|
||||
Usage: "include chain health status",
|
||||
},
|
||||
},
|
||||
|
||||
Action: func(cctx *cli.Context) error {
|
||||
apic, closer, err := GetFullNodeAPIV1(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
inclChainStatus := cctx.Bool("chain")
|
||||
|
||||
status, err := apic.NodeStatus(ctx, inclChainStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Sync Epoch: %d\n", status.SyncStatus.Epoch)
|
||||
fmt.Printf("Epochs Behind: %d\n", status.SyncStatus.Behind)
|
||||
fmt.Printf("Peers to Publish Messages: %d\n", status.PeerStatus.PeersToPublishMsgs)
|
||||
fmt.Printf("Peers to Publish Blocks: %d\n", status.PeerStatus.PeersToPublishBlocks)
|
||||
|
||||
if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) {
|
||||
var ok100, okFin string
|
||||
if status.ChainStatus.BlocksPerTipsetLast100 >= 4.75 {
|
||||
ok100 = "[OK]"
|
||||
} else {
|
||||
ok100 = "[UNHEALTHY]"
|
||||
}
|
||||
if status.ChainStatus.BlocksPerTipsetLastFinality >= 4.75 {
|
||||
okFin = "[OK]"
|
||||
} else {
|
||||
okFin = "[UNHEALTHY]"
|
||||
}
|
||||
|
||||
fmt.Printf("Blocks per TipSet in last 100 epochs: %f %s\n", status.ChainStatus.BlocksPerTipsetLast100, ok100)
|
||||
fmt.Printf("Blocks per TipSet in last finality: %f %s\n", status.ChainStatus.BlocksPerTipsetLastFinality, okFin)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
+2
-1
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
@@ -240,7 +241,7 @@ var SyncCheckpointCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func SyncWait(ctx context.Context, napi api.FullNode, watch bool) error {
|
||||
func SyncWait(ctx context.Context, napi v0api.FullNode, watch bool) error {
|
||||
tick := time.Second / 4
|
||||
|
||||
lastLines := 0
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ type MockCLIClient struct {
|
||||
|
||||
func (c *MockCLIClient) RunCmd(input ...string) string {
|
||||
out, err := c.RunCmdRaw(input...)
|
||||
require.NoError(c.t, err)
|
||||
require.NoError(c.t, err, "output:\n%s", out)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -9,4 +9,6 @@ func QuietMiningLogs() {
|
||||
_ = log.SetLogLevel("sub", "ERROR")
|
||||
_ = log.SetLogLevel("storageminer", "ERROR")
|
||||
_ = log.SetLogLevel("pubsub", "ERROR")
|
||||
_ = log.SetLogLevel("gen", "ERROR")
|
||||
_ = log.SetLogLevel("dht/RtRefreshManager", "ERROR")
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,12 +10,12 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func parseTipSet(ctx context.Context, api api.FullNode, vals []string) (*types.TipSet, error) {
|
||||
func parseTipSet(ctx context.Context, api v0api.FullNode, vals []string) (*types.TipSet, error) {
|
||||
var headers []*types.BlockHeader
|
||||
for _, c := range vals {
|
||||
blkc, err := cid.Decode(c)
|
||||
|
||||
+38
-14
@@ -18,6 +18,8 @@ import (
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/client"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/api/v1api"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
@@ -133,13 +135,13 @@ func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetRawAPI(ctx *cli.Context, t repo.RepoType) (string, http.Header, error) {
|
||||
func GetRawAPI(ctx *cli.Context, t repo.RepoType, version string) (string, http.Header, error) {
|
||||
ainfo, err := GetAPIInfo(ctx, t)
|
||||
if err != nil {
|
||||
return "", nil, xerrors.Errorf("could not get API info: %w", err)
|
||||
}
|
||||
|
||||
addr, err := ainfo.DialArgs()
|
||||
addr, err := ainfo.DialArgs(version)
|
||||
if err != nil {
|
||||
return "", nil, xerrors.Errorf("could not get DialArgs: %w", err)
|
||||
}
|
||||
@@ -165,25 +167,38 @@ func GetAPI(ctx *cli.Context) (api.Common, jsonrpc.ClientCloser, error) {
|
||||
return tn.(api.FullNode), func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, t)
|
||||
addr, headers, err := GetRawAPI(ctx, t, "v0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewCommonRPC(ctx.Context, addr, headers)
|
||||
return client.NewCommonRPCV0(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
func GetFullNodeAPI(ctx *cli.Context) (v0api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
|
||||
return tn.(api.FullNode), func() {}, nil
|
||||
return &v0api.WrapperV1Full{FullNode: tn.(v1api.FullNode)}, func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode)
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode, "v0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewFullNodeRPC(ctx.Context, addr, headers)
|
||||
return client.NewFullNodeRPCV0(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetFullNodeAPIV1(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
|
||||
return tn.(v1api.FullNode), func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode, "v1")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewFullNodeRPCV1(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
type GetStorageMinerOptions struct {
|
||||
@@ -206,7 +221,7 @@ func GetStorageMinerAPI(ctx *cli.Context, opts ...GetStorageMinerOption) (api.St
|
||||
return tn.(api.StorageMiner), func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, repo.StorageMiner)
|
||||
addr, headers, err := GetRawAPI(ctx, repo.StorageMiner, "v0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -227,25 +242,34 @@ func GetStorageMinerAPI(ctx *cli.Context, opts ...GetStorageMinerOption) (api.St
|
||||
addr = u.String()
|
||||
}
|
||||
|
||||
return client.NewStorageMinerRPC(ctx.Context, addr, headers)
|
||||
return client.NewStorageMinerRPCV0(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetWorkerAPI(ctx *cli.Context) (api.Worker, jsonrpc.ClientCloser, error) {
|
||||
addr, headers, err := GetRawAPI(ctx, repo.Worker)
|
||||
addr, headers, err := GetRawAPI(ctx, repo.Worker, "v0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewWorkerRPC(ctx.Context, addr, headers)
|
||||
return client.NewWorkerRPCV0(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetGatewayAPI(ctx *cli.Context) (api.Gateway, jsonrpc.ClientCloser, error) {
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode)
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode, "v1")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewGatewayRPC(ctx.Context, addr, headers)
|
||||
return client.NewGatewayRPCV1(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetGatewayAPIV0(ctx *cli.Context) (v0api.Gateway, jsonrpc.ClientCloser, error) {
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode, "v0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewGatewayRPCV0(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func DaemonContext(cctx *cli.Context) context.Context {
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ func ParseApiInfo(s string) APIInfo {
|
||||
}
|
||||
}
|
||||
|
||||
func (a APIInfo) DialArgs() (string, error) {
|
||||
func (a APIInfo) DialArgs(version string) (string, error) {
|
||||
ma, err := multiaddr.NewMultiaddr(a.Addr)
|
||||
if err == nil {
|
||||
_, addr, err := manet.DialArgs(ma)
|
||||
@@ -44,14 +44,14 @@ func (a APIInfo) DialArgs() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "ws://" + addr + "/rpc/v0", nil
|
||||
return "ws://" + addr + "/rpc/" + version, nil
|
||||
}
|
||||
|
||||
_, err = url.Parse(a.Addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return a.Addr + "/rpc/v0", nil
|
||||
return a.Addr + "/rpc/" + version, nil
|
||||
}
|
||||
|
||||
func (a APIInfo) Host() (string, error) {
|
||||
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/blockstore"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
var verifRegCmd = &cli.Command{
|
||||
Name: "verifreg",
|
||||
Usage: "Interact with the verified registry actor",
|
||||
Flags: []cli.Flag{},
|
||||
Subcommands: []*cli.Command{
|
||||
verifRegVerifyClientCmd,
|
||||
verifRegListVerifiersCmd,
|
||||
verifRegListClientsCmd,
|
||||
verifRegCheckClientCmd,
|
||||
verifRegCheckVerifierCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegVerifyClientCmd = &cli.Command{
|
||||
Name: "verify-client",
|
||||
Usage: "give allowance to the specified verified client address",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "specify your verifier address to send the message from",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
froms := cctx.String("from")
|
||||
if froms == "" {
|
||||
return fmt.Errorf("must specify from address with --from")
|
||||
}
|
||||
|
||||
fromk, err := address.NewFromString(froms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cctx.Args().Len() != 2 {
|
||||
return fmt.Errorf("must specify two arguments: address and allowance")
|
||||
}
|
||||
|
||||
target, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allowance, err := types.BigFromString(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
found, dcap, err := checkVerifier(ctx, api, fromk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
return xerrors.New("sender address must be a verifier")
|
||||
}
|
||||
|
||||
if dcap.Cmp(allowance.Int) < 0 {
|
||||
return xerrors.Errorf("cannot allot more allowance than verifier data cap: %s < %s", dcap, allowance)
|
||||
}
|
||||
|
||||
// TODO: This should be abstracted over actor versions
|
||||
params, err := actors.SerializeParams(&verifreg4.AddVerifiedClientParams{Address: target, Allowance: allowance})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: verifreg.Address,
|
||||
From: fromk,
|
||||
Method: verifreg.Methods.AddVerifiedClient,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid())
|
||||
|
||||
mwait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mwait.Receipt.ExitCode != 0 {
|
||||
return fmt.Errorf("failed to add verified client: %d", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegListVerifiersCmd = &cli.Command{
|
||||
Name: "list-verifiers",
|
||||
Usage: "list all verifiers",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := blockstore.NewAPIBlockstore(api)
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
|
||||
|
||||
st, err := verifreg.Load(store, act)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return st.ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error {
|
||||
_, err := fmt.Printf("%s: %s\n", addr, dcap)
|
||||
return err
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegListClientsCmd = &cli.Command{
|
||||
Name: "list-clients",
|
||||
Usage: "list all verified clients",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := blockstore.NewAPIBlockstore(api)
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
|
||||
|
||||
st, err := verifreg.Load(store, act)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return st.ForEachClient(func(addr address.Address, dcap abi.StoragePower) error {
|
||||
_, err := fmt.Printf("%s: %s\n", addr, dcap)
|
||||
return err
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegCheckClientCmd = &cli.Command{
|
||||
Name: "check-client",
|
||||
Usage: "check verified client remaining bytes",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify client address to check")
|
||||
}
|
||||
|
||||
caddr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
dcap, err := api.StateVerifiedClientStatus(ctx, caddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dcap == nil {
|
||||
return xerrors.Errorf("client %s is not a verified client", err)
|
||||
}
|
||||
|
||||
fmt.Println(*dcap)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var verifRegCheckVerifierCmd = &cli.Command{
|
||||
Name: "check-verifier",
|
||||
Usage: "check verifiers remaining bytes",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify verifier address to check")
|
||||
}
|
||||
|
||||
vaddr, err := address.NewFromString(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
found, dcap, err := checkVerifier(ctx, api, vaddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
fmt.Println(dcap)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func checkVerifier(ctx context.Context, api v0api.FullNode, vaddr address.Address) (bool, abi.StoragePower, error) {
|
||||
vid, err := api.StateLookupID(ctx, vaddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return false, big.Zero(), err
|
||||
}
|
||||
|
||||
act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return false, big.Zero(), err
|
||||
}
|
||||
|
||||
apibs := blockstore.NewAPIBlockstore(api)
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
|
||||
|
||||
st, err := verifreg.Load(store, act)
|
||||
if err != nil {
|
||||
return false, big.Zero(), err
|
||||
}
|
||||
|
||||
return st.VerifierDataCap(vid)
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ var WaitApiCmd = &cli.Command{
|
||||
Usage: "Wait for lotus api to come online",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
for i := 0; i < 30; i++ {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
api, closer, err := GetAPI(cctx)
|
||||
if err != nil {
|
||||
fmt.Printf("Not online yet... (%s)\n", err)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
Reference in New Issue
Block a user