Skip to content

Commit 7ea9f4f

Browse files
committed
feat: add settle-deal to lotus-shed
1 parent 06515b6 commit 7ea9f4f

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
- default to ProveCommit aggregation
3232
- remove config options: AggregateCommits, AggregateAboveBaseFee, BatchPreCommitAboveBaseFee
3333
- feat(paych): add EnablePaymentChannelManager config option and disable payment channel manager by default ([filecoin-project/lotus#13139](https://github.com/filecoin-project/lotus/pull/13139))
34+
- feat: add `lotus-shed market settle-deal` CLI command
3435

3536
# Node v1.33.0 / 2025-05-08
3637
The Lotus v1.33.0 release introduces experimental v2 APIs with F3 awareness, featuring a new TipSet selection mechanism that significantly enhances how applications interact with the Filecoin blockchain. This release candidate also adds F3-aware Ethereum APIs via the /v2 endpoint. All of the /v2 APIs implement intelligent fallback mechanisms between F3 and Expected Consensus and are exposed through the Lotus Gateway.

cmd/lotus-shed/market.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,35 @@ import (
66
"fmt"
77
"os"
88
"path"
9+
"strconv"
10+
"strings"
911

1012
"github.com/ipfs/go-datastore"
1113
dsq "github.com/ipfs/go-datastore/query"
1214
levelds "github.com/ipfs/go-ds-leveldb"
1315
ipldcbor "github.com/ipfs/go-ipld-cbor"
1416
logging "github.com/ipfs/go-log/v2"
17+
"github.com/samber/lo"
1518
ldbopts "github.com/syndtr/goleveldb/leveldb/opt"
1619
"github.com/urfave/cli/v2"
1720
cbg "github.com/whyrusleeping/cbor-gen"
21+
"golang.org/x/sync/errgroup"
1822
"golang.org/x/xerrors"
1923

2024
"github.com/filecoin-project/go-address"
25+
"github.com/filecoin-project/go-bitfield"
2126
"github.com/filecoin-project/go-state-types/abi"
2227
"github.com/filecoin-project/go-state-types/big"
2328
"github.com/filecoin-project/go-state-types/builtin"
2429
market14 "github.com/filecoin-project/go-state-types/builtin/v14/market"
2530
"github.com/filecoin-project/go-state-types/builtin/v14/util/adt"
2631

32+
"github.com/filecoin-project/lotus/chain/actors"
2733
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
34+
marketactor "github.com/filecoin-project/lotus/chain/actors/builtin/market"
2835
"github.com/filecoin-project/lotus/chain/types"
2936
lcli "github.com/filecoin-project/lotus/cli"
37+
"github.com/filecoin-project/lotus/cli/spcli"
3038
"github.com/filecoin-project/lotus/lib/backupds"
3139
"github.com/filecoin-project/lotus/node/repo"
3240
)
@@ -41,6 +49,7 @@ var marketCmd = &cli.Command{
4149
marketImportDatastoreCmd,
4250
marketDealsTotalStorageCmd,
4351
marketCronStateCmd,
52+
marketDealSettlementCmd,
4453
},
4554
}
4655

@@ -426,3 +435,142 @@ func openLockedRepo(path string) (repo.LockedRepo, error) {
426435

427436
return lr, nil
428437
}
438+
439+
var marketDealSettlementCmd = &cli.Command{
440+
Name: "settle-deal",
441+
Usage: "Settle deals manually, if dealIds are not provided all deals will be settled. Deal IDs can be specified as individual numbers or ranges (e.g., '123 124 125-200 220')",
442+
ArgsUsage: "[...dealIds]",
443+
Flags: []cli.Flag{
444+
&cli.StringFlag{
445+
Name: "miner",
446+
Usage: "specify the miner address",
447+
},
448+
&cli.StringFlag{
449+
Name: "from",
450+
Usage: "specify where to send the message from (any address)",
451+
Required: true,
452+
},
453+
&cli.IntFlag{
454+
Name: "concurrent",
455+
Usage: "specify the number of concurrent messages to send",
456+
Value: 1,
457+
},
458+
&cli.IntFlag{
459+
Name: "max-deals",
460+
Usage: "the maximum number of deals contained in each message",
461+
Value: 500,
462+
},
463+
},
464+
465+
Action: func(cctx *cli.Context) error {
466+
api, acloser, err := lcli.GetFullNodeAPIV1(cctx)
467+
if err != nil {
468+
return err
469+
}
470+
defer acloser()
471+
472+
ctx := lcli.ReqContext(cctx)
473+
474+
fromAddr, err := address.NewFromString(cctx.String("from"))
475+
if err != nil {
476+
return err
477+
}
478+
var maddr address.Address
479+
if len(cctx.Args().Slice()) == 0 && !cctx.IsSet("miner") {
480+
return xerrors.Errorf("If the dealIds is not provided, you must set the --miner")
481+
} else {
482+
maddr, err = address.NewFromString(cctx.String("miner"))
483+
if err != nil {
484+
return err
485+
}
486+
}
487+
488+
var (
489+
dealIDs []uint64
490+
dealId uint64
491+
)
492+
493+
// if no deal ids are provided, get all the deals for the miner
494+
if dealsIdArgs := cctx.Args().Slice(); len(dealsIdArgs) != 0 {
495+
for _, d := range dealsIdArgs {
496+
// Check if it's a range (e.g., "125-200")
497+
if strings.Contains(d, "-") {
498+
parts := strings.Split(d, "-")
499+
if len(parts) != 2 {
500+
return xerrors.Errorf("Invalid range format: %s (expected format: start-end)", d)
501+
}
502+
503+
start, err := strconv.ParseUint(strings.TrimSpace(parts[0]), 10, 64)
504+
if err != nil {
505+
return xerrors.Errorf("Error parsing start of range %s: %w", d, err)
506+
}
507+
508+
end, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
509+
if err != nil {
510+
return xerrors.Errorf("Error parsing end of range %s: %w", d, err)
511+
}
512+
513+
if start > end {
514+
return xerrors.Errorf("Invalid range %s: start must be less than or equal to end", d)
515+
}
516+
517+
// Add all deal IDs in the range (inclusive)
518+
for id := start; id <= end; id++ {
519+
dealIDs = append(dealIDs, id)
520+
}
521+
} else {
522+
// Parse as a single deal ID
523+
dealId, err = strconv.ParseUint(d, 10, 64)
524+
if err != nil {
525+
return xerrors.Errorf("Error parsing deal id: %w", err)
526+
}
527+
528+
dealIDs = append(dealIDs, dealId)
529+
}
530+
}
531+
} else {
532+
if dealIDs, err = spcli.GetMinerAllDeals(ctx, api, maddr, types.EmptyTSK); err != nil {
533+
return xerrors.Errorf("Error getting all deals for miner: %w", err)
534+
}
535+
}
536+
537+
dealChucks := lo.Chunk(dealIDs, cctx.Int("max-deals"))
538+
g, ctx := errgroup.WithContext(ctx)
539+
g.SetLimit(cctx.Int("concurrent"))
540+
541+
for _, deals := range dealChucks {
542+
deals := deals
543+
g.Go(func() error {
544+
dealParams := bitfield.NewFromSet(deals)
545+
var err error
546+
params, err := actors.SerializeParams(&dealParams)
547+
if err != nil {
548+
return err
549+
}
550+
551+
smsg, err := api.MpoolPushMessage(ctx, &types.Message{
552+
To: marketactor.Address,
553+
From: fromAddr,
554+
Value: types.NewInt(0),
555+
Method: marketactor.Methods.SettleDealPaymentsExported,
556+
Params: params,
557+
}, nil)
558+
if err != nil {
559+
return err
560+
}
561+
562+
res := smsg.Cid()
563+
564+
fmt.Printf("Requested deal settlement in message: %s\n", res)
565+
return nil
566+
})
567+
568+
}
569+
err = g.Wait()
570+
if err != nil {
571+
return err
572+
}
573+
574+
return nil
575+
},
576+
}

0 commit comments

Comments
 (0)