diff --git a/api/api_storage.go b/api/api_storage.go index c39114929..d52032650 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -166,8 +166,6 @@ type StorageMiner interface { MarketPendingDeals(ctx context.Context) (PendingDealInfo, error) //perm:write MarketPublishPendingDeals(ctx context.Context) error //perm:admin - // RuntimeSubsystems returns the subsystems that are enabled - // in this instance. RuntimeSubsystems(ctx context.Context) (MinerSubsystems, error) //perm:read DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error //perm:admin diff --git a/api/api_subsystems.go b/api/api_subsystems.go new file mode 100644 index 000000000..1894bbdd8 --- /dev/null +++ b/api/api_subsystems.go @@ -0,0 +1,63 @@ +package api + +import ( + "bytes" + "encoding/json" +) + +type MinerSubsystems []MinerSubsystem + +func (ms MinerSubsystems) Has(entry MinerSubsystem) bool { + for _, v := range ms { + if v == entry { + return true + } + + } + return false +} + +type MinerSubsystem int + +const ( + MarketsSubsystem MinerSubsystem = iota + MiningSubsystem + SealingSubsystem + SectorStorageSubsystem +) + +func (ms MinerSubsystem) String() string { + return MinerSubsystemToString[ms] +} + +var MinerSubsystemToString = map[MinerSubsystem]string{ + MarketsSubsystem: "Markets", + MiningSubsystem: "Mining", + SealingSubsystem: "Sealing", + SectorStorageSubsystem: "SectorStorage", +} + +var MinerSubsystemToID = map[string]MinerSubsystem{ + "Markets": MarketsSubsystem, + "Mining": MiningSubsystem, + "Sealing": SealingSubsystem, + "SectorStorage": SectorStorageSubsystem, +} + +func (ms MinerSubsystem) MarshalJSON() ([]byte, error) { + buffer := bytes.NewBufferString(`"`) + buffer.WriteString(MinerSubsystemToString[ms]) + buffer.WriteString(`"`) + return buffer.Bytes(), nil +} + +func (ms *MinerSubsystem) UnmarshalJSON(b []byte) error { + var j string + err := json.Unmarshal(b, &j) + if err != nil { + return err + } + // TODO: handle zero value + *ms = MinerSubsystemToID[j] + return nil +} diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index f9addc940..1e712a0ae 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -16,10 +16,10 @@ import ( "github.com/google/uuid" "github.com/ipfs/go-cid" "github.com/ipfs/go-filestore" - "github.com/libp2p/go-libp2p-core/metrics" + metrics "github.com/libp2p/go-libp2p-core/metrics" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" + protocol "github.com/libp2p/go-libp2p-core/protocol" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/multiformats/go-multiaddr" @@ -265,12 +265,6 @@ func init() { addExample(api.CheckStatusCode(0)) addExample(map[string]interface{}{"abc": 123}) - addExample(api.MinerSubsystems{ - api.SubsystemMining, - api.SubsystemSealing, - api.SubsystemSectorStorage, - api.SubsystemMarkets, - }) } func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) { diff --git a/api/miner_subsystems.go b/api/miner_subsystems.go deleted file mode 100644 index a77de7e3c..000000000 --- a/api/miner_subsystems.go +++ /dev/null @@ -1,79 +0,0 @@ -package api - -import ( - "encoding/json" -) - -// MinerSubsystem represents a miner subsystem. Int and string values are not -// guaranteed to be stable over time is not -// guaranteed to be stable over time. -type MinerSubsystem int - -const ( - // SubsystemUnknown is a placeholder for the zero value. It should never - // be used. - SubsystemUnknown MinerSubsystem = iota - // SubsystemMarkets signifies the storage and retrieval - // deal-making subsystem. - SubsystemMarkets - // SubsystemMining signifies the mining subsystem. - SubsystemMining - // SubsystemSealing signifies the sealing subsystem. - SubsystemSealing - // SubsystemSectorStorage signifies the sector storage subsystem. - SubsystemSectorStorage -) - -var MinerSubsystemToString = map[MinerSubsystem]string{ - SubsystemUnknown: "Unknown", - SubsystemMarkets: "Markets", - SubsystemMining: "Mining", - SubsystemSealing: "Sealing", - SubsystemSectorStorage: "SectorStorage", -} - -var MinerSubsystemToID = map[string]MinerSubsystem{ - "Unknown": SubsystemUnknown, - "Markets": SubsystemMarkets, - "Mining": SubsystemMining, - "Sealing": SubsystemSealing, - "SectorStorage": SubsystemSectorStorage, -} - -func (ms MinerSubsystem) MarshalJSON() ([]byte, error) { - return json.Marshal(MinerSubsystemToString[ms]) -} - -func (ms *MinerSubsystem) UnmarshalJSON(b []byte) error { - var j string - err := json.Unmarshal(b, &j) - if err != nil { - return err - } - s, ok := MinerSubsystemToID[j] - if !ok { - *ms = SubsystemUnknown - } else { - *ms = s - } - return nil -} - -type MinerSubsystems []MinerSubsystem - -func (ms MinerSubsystems) Has(entry MinerSubsystem) bool { - for _, v := range ms { - if v == entry { - return true - } - } - return false -} - -func (ms MinerSubsystem) String() string { - s, ok := MinerSubsystemToString[ms] - if !ok { - return MinerSubsystemToString[SubsystemUnknown] - } - return s -} diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 1fd8121c9..ccdc26dc7 100644 Binary files a/build/openrpc/miner.json.gz and b/build/openrpc/miner.json.gz differ diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index f37952057..4c409788f 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -21,7 +21,6 @@ import ( "github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/lotus/api/v0api" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" "github.com/filecoin-project/lotus/api" @@ -50,13 +49,7 @@ var infoCmd = &cli.Command{ } func infoCmdAct(cctx *cli.Context) error { - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - marketsApi, closer, err := lcli.GetMarketsAPI(cctx) + nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) if err != nil { return err } @@ -70,19 +63,12 @@ func infoCmdAct(cctx *cli.Context) error { ctx := lcli.ReqContext(cctx) - subsystems, err := minerApi.RuntimeSubsystems(ctx) + subsystems, err := nodeApi.RuntimeSubsystems(ctx) if err != nil { return err } - fmt.Println("Enabled subsystems (from miner API):", subsystems) - - subsystems, err = marketsApi.RuntimeSubsystems(ctx) - if err != nil { - return err - } - - fmt.Println("Enabled subsystems (from markets API):", subsystems) + fmt.Println("Enabled subsystems:", subsystems) fmt.Print("Chain: ") @@ -116,303 +102,288 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Println() - err = handleMiningInfo(ctx, cctx, fullapi, minerApi) - if err != nil { - return err - } - - err = handleMarketsInfo(ctx, marketsApi) - if err != nil { - return err - } - - return nil -} - -func handleMiningInfo(ctx context.Context, cctx *cli.Context, fullapi v0api.FullNode, nodeApi api.StorageMiner) error { - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - mact, err := fullapi.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullapi), blockstore.NewMemory()) - mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) - if err != nil { - return err - } - - // Sector size - mi, err := fullapi.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize))) - fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize) - - pow, err := fullapi.StateMinerPower(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - fmt.Printf("Power: %s / %s (%0.4f%%)\n", - color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), - types.DeciStr(pow.TotalPower.QualityAdjPower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), - pow.TotalPower.QualityAdjPower, - ), - ) - - fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", - color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), - types.SizeStr(pow.TotalPower.RawBytePower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), - pow.TotalPower.RawBytePower, - ), - ) - secCounts, err := fullapi.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - proving := secCounts.Active + secCounts.Faulty - nfaults := secCounts.Faulty - fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize))))) - if nfaults == 0 { - fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize))))) - } else { - var faultyPercentage float64 - if secCounts.Live != 0 { - faultyPercentage = float64(100*nfaults) / float64(secCounts.Live) - } - fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n", - types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize)))), - types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))), - faultyPercentage) - } - - if !pow.HasMinPower { - fmt.Print("Below minimum power threshold, no blocks will be won") - } else { - - winRatio := new(corebig.Rat).SetFrac( - types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int, - pow.TotalPower.QualityAdjPower.Int, - ) - - if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 { - - // if the corresponding poisson distribution isn't infinitely small then - // throw it into the mix as well, accounting for multi-wins - winRationWithPoissonFloat := -math.Expm1(-winRatioFloat) - winRationWithPoisson := new(corebig.Rat).SetFloat64(winRationWithPoissonFloat) - if winRationWithPoisson != nil { - winRatio = winRationWithPoisson - winRatioFloat = winRationWithPoissonFloat - } - - weekly, _ := new(corebig.Rat).Mul( - winRatio, - new(corebig.Rat).SetInt64(7*builtin.EpochsInDay), - ).Float64() - - avgDuration, _ := new(corebig.Rat).Mul( - new(corebig.Rat).SetInt64(builtin.EpochDurationSeconds), - new(corebig.Rat).Inv(winRatio), - ).Float64() - - fmt.Print("Projected average block win rate: ") - color.Blue( - "%.02f/week (every %s)", - weekly, - (time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(), - ) - - // Geometric distribution of P(Y < k) calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples - // https://www.wolframalpha.com/input/?i=t+%3E+0%3B+p+%3E+0%3B+p+%3C+1%3B+c+%3E+0%3B+c+%3C1%3B+1-%281-p%29%5E%28t%29%3Dc%3B+solve+t - // t == how many dice-rolls (epochs) before win - // p == winRate == ( minerPower / netPower ) - // c == target probability of win ( 99.9% in this case ) - fmt.Print("Projected block win with ") - color.Green( - "99.9%% probability every %s", - (time.Second * time.Duration( - builtin.EpochDurationSeconds*math.Log(1-0.999)/ - math.Log(1-winRatioFloat), - )).Truncate(time.Second).String(), - ) - fmt.Println("(projections DO NOT account for future network and miner growth)") - } - } - - fmt.Println() - - spendable := big.Zero() - - // NOTE: there's no need to unlock anything here. Funds only - // vest on deadline boundaries, and they're unlocked by cron. - lockedFunds, err := mas.LockedFunds() - if err != nil { - return xerrors.Errorf("getting locked funds: %w", err) - } - availBalance, err := mas.AvailableBalance(mact.Balance) - if err != nil { - return xerrors.Errorf("getting available balance: %w", err) - } - spendable = big.Add(spendable, availBalance) - - fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance).Short())) - fmt.Printf(" PreCommit: %s\n", types.FIL(lockedFunds.PreCommitDeposits).Short()) - fmt.Printf(" Pledge: %s\n", types.FIL(lockedFunds.InitialPledgeRequirement).Short()) - fmt.Printf(" Vesting: %s\n", types.FIL(lockedFunds.VestingFunds).Short()) - colorTokenAmount(" Available: %s\n", availBalance) - - mb, err := fullapi.StateMarketBalance(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting market balance: %w", err) - } - spendable = big.Add(spendable, big.Sub(mb.Escrow, mb.Locked)) - - fmt.Printf("Market Balance: %s\n", types.FIL(mb.Escrow).Short()) - fmt.Printf(" Locked: %s\n", types.FIL(mb.Locked).Short()) - colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked)) - - wb, err := fullapi.WalletBalance(ctx, mi.Worker) - if err != nil { - return xerrors.Errorf("getting worker balance: %w", err) - } - spendable = big.Add(spendable, wb) - color.Cyan("Worker Balance: %s", types.FIL(wb).Short()) - if len(mi.ControlAddresses) > 0 { - cbsum := big.Zero() - for _, ca := range mi.ControlAddresses { - b, err := fullapi.WalletBalance(ctx, ca) - if err != nil { - return xerrors.Errorf("getting control address balance: %w", err) - } - cbsum = big.Add(cbsum, b) - } - spendable = big.Add(spendable, cbsum) - - fmt.Printf(" Control: %s\n", types.FIL(cbsum).Short()) - } - colorTokenAmount("Total Spendable: %s\n", spendable) - - fmt.Println() - - if !cctx.Bool("hide-sectors-info") { - fmt.Println("Sectors:") - err = sectorsInfo(ctx, nodeApi) + if subsystems.Has(api.SectorStorageSubsystem) { + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } - } - // TODO: grab actr state / info - // * Sealed sectors (count / bytes) - // * Power - - return nil -} - -func handleMarketsInfo(ctx context.Context, nodeApi api.StorageMiner) error { - deals, err := nodeApi.MarketListIncompleteDeals(ctx) - if err != nil { - return err - } - - type dealStat struct { - count, verifCount int - bytes, verifBytes uint64 - } - dsAdd := func(ds *dealStat, deal storagemarket.MinerDeal) { - ds.count++ - ds.bytes += uint64(deal.Proposal.PieceSize) - if deal.Proposal.VerifiedDeal { - ds.verifCount++ - ds.verifBytes += uint64(deal.Proposal.PieceSize) - } - } - - showDealStates := map[storagemarket.StorageDealStatus]struct{}{ - storagemarket.StorageDealActive: {}, - storagemarket.StorageDealTransferring: {}, - storagemarket.StorageDealStaged: {}, - storagemarket.StorageDealAwaitingPreCommit: {}, - storagemarket.StorageDealSealing: {}, - storagemarket.StorageDealPublish: {}, - storagemarket.StorageDealCheckForAcceptance: {}, - storagemarket.StorageDealPublishing: {}, - } - - var total dealStat - perState := map[storagemarket.StorageDealStatus]*dealStat{} - for _, deal := range deals { - if _, ok := showDealStates[deal.State]; !ok { - continue - } - if perState[deal.State] == nil { - perState[deal.State] = new(dealStat) + mact, err := fullapi.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err } - dsAdd(&total, deal) - dsAdd(perState[deal.State], deal) - } + tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullapi), blockstore.NewMemory()) + mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) + if err != nil { + return err + } - type wstr struct { - str string - status storagemarket.StorageDealStatus - } - sorted := make([]wstr, 0, len(perState)) - for status, stat := range perState { - st := strings.TrimPrefix(storagemarket.DealStates[status], "StorageDeal") - sorted = append(sorted, wstr{ - str: fmt.Sprintf(" %s:\t%d\t\t%s\t(Verified: %d\t%s)\n", st, stat.count, types.SizeStr(types.NewInt(stat.bytes)), stat.verifCount, types.SizeStr(types.NewInt(stat.verifBytes))), - status: status, - }, + // Sector size + mi, err := fullapi.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize))) + fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize) + + pow, err := fullapi.StateMinerPower(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + fmt.Printf("Power: %s / %s (%0.4f%%)\n", + color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), + types.DeciStr(pow.TotalPower.QualityAdjPower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), + pow.TotalPower.QualityAdjPower, + ), ) - } - sort.Slice(sorted, func(i, j int) bool { - if sorted[i].status == storagemarket.StorageDealActive || sorted[j].status == storagemarket.StorageDealActive { - return sorted[i].status == storagemarket.StorageDealActive + + fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", + color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), + types.SizeStr(pow.TotalPower.RawBytePower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), + pow.TotalPower.RawBytePower, + ), + ) + secCounts, err := fullapi.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) + if err != nil { + return err } - return sorted[i].status > sorted[j].status - }) - fmt.Println() - fmt.Printf("Storage Deals: %d, %s\n", total.count, types.SizeStr(types.NewInt(total.bytes))) - - tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) - for _, e := range sorted { - _, _ = tw.Write([]byte(e.str)) - } - - _ = tw.Flush() - fmt.Println() - - retrievals, err := nodeApi.MarketListRetrievalDeals(ctx) - if err != nil { - return xerrors.Errorf("getting retrieval deal list: %w", err) - } - - var retrComplete dealStat - for _, retrieval := range retrievals { - if retrieval.Status == retrievalmarket.DealStatusCompleted { - retrComplete.count++ - retrComplete.bytes += retrieval.TotalSent + proving := secCounts.Active + secCounts.Faulty + nfaults := secCounts.Faulty + fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize))))) + if nfaults == 0 { + fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize))))) + } else { + var faultyPercentage float64 + if secCounts.Live != 0 { + faultyPercentage = float64(100*nfaults) / float64(secCounts.Live) + } + fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n", + types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize)))), + types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))), + faultyPercentage) } + + if !pow.HasMinPower { + fmt.Print("Below minimum power threshold, no blocks will be won") + } else { + + winRatio := new(corebig.Rat).SetFrac( + types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int, + pow.TotalPower.QualityAdjPower.Int, + ) + + if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 { + + // if the corresponding poisson distribution isn't infinitely small then + // throw it into the mix as well, accounting for multi-wins + winRationWithPoissonFloat := -math.Expm1(-winRatioFloat) + winRationWithPoisson := new(corebig.Rat).SetFloat64(winRationWithPoissonFloat) + if winRationWithPoisson != nil { + winRatio = winRationWithPoisson + winRatioFloat = winRationWithPoissonFloat + } + + weekly, _ := new(corebig.Rat).Mul( + winRatio, + new(corebig.Rat).SetInt64(7*builtin.EpochsInDay), + ).Float64() + + avgDuration, _ := new(corebig.Rat).Mul( + new(corebig.Rat).SetInt64(builtin.EpochDurationSeconds), + new(corebig.Rat).Inv(winRatio), + ).Float64() + + fmt.Print("Projected average block win rate: ") + color.Blue( + "%.02f/week (every %s)", + weekly, + (time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(), + ) + + // Geometric distribution of P(Y < k) calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples + // https://www.wolframalpha.com/input/?i=t+%3E+0%3B+p+%3E+0%3B+p+%3C+1%3B+c+%3E+0%3B+c+%3C1%3B+1-%281-p%29%5E%28t%29%3Dc%3B+solve+t + // t == how many dice-rolls (epochs) before win + // p == winRate == ( minerPower / netPower ) + // c == target probability of win ( 99.9% in this case ) + fmt.Print("Projected block win with ") + color.Green( + "99.9%% probability every %s", + (time.Second * time.Duration( + builtin.EpochDurationSeconds*math.Log(1-0.999)/ + math.Log(1-winRatioFloat), + )).Truncate(time.Second).String(), + ) + fmt.Println("(projections DO NOT account for future network and miner growth)") + } + } + + fmt.Println() + + spendable := big.Zero() + + // NOTE: there's no need to unlock anything here. Funds only + // vest on deadline boundaries, and they're unlocked by cron. + lockedFunds, err := mas.LockedFunds() + if err != nil { + return xerrors.Errorf("getting locked funds: %w", err) + } + availBalance, err := mas.AvailableBalance(mact.Balance) + if err != nil { + return xerrors.Errorf("getting available balance: %w", err) + } + spendable = big.Add(spendable, availBalance) + + fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance).Short())) + fmt.Printf(" PreCommit: %s\n", types.FIL(lockedFunds.PreCommitDeposits).Short()) + fmt.Printf(" Pledge: %s\n", types.FIL(lockedFunds.InitialPledgeRequirement).Short()) + fmt.Printf(" Vesting: %s\n", types.FIL(lockedFunds.VestingFunds).Short()) + colorTokenAmount(" Available: %s\n", availBalance) + + mb, err := fullapi.StateMarketBalance(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting market balance: %w", err) + } + spendable = big.Add(spendable, big.Sub(mb.Escrow, mb.Locked)) + + fmt.Printf("Market Balance: %s\n", types.FIL(mb.Escrow).Short()) + fmt.Printf(" Locked: %s\n", types.FIL(mb.Locked).Short()) + colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked)) + + wb, err := fullapi.WalletBalance(ctx, mi.Worker) + if err != nil { + return xerrors.Errorf("getting worker balance: %w", err) + } + spendable = big.Add(spendable, wb) + color.Cyan("Worker Balance: %s", types.FIL(wb).Short()) + if len(mi.ControlAddresses) > 0 { + cbsum := big.Zero() + for _, ca := range mi.ControlAddresses { + b, err := fullapi.WalletBalance(ctx, ca) + if err != nil { + return xerrors.Errorf("getting control address balance: %w", err) + } + cbsum = big.Add(cbsum, b) + } + spendable = big.Add(spendable, cbsum) + + fmt.Printf(" Control: %s\n", types.FIL(cbsum).Short()) + } + colorTokenAmount("Total Spendable: %s\n", spendable) + + fmt.Println() + + if !cctx.Bool("hide-sectors-info") { + fmt.Println("Sectors:") + err = sectorsInfo(ctx, nodeApi) + if err != nil { + return err + } + } + + // TODO: grab actr state / info + // * Sealed sectors (count / bytes) + // * Power } - fmt.Printf("Retrieval Deals (complete): %d, %s\n", retrComplete.count, types.SizeStr(types.NewInt(retrComplete.bytes))) + if subsystems.Has(api.MarketsSubsystem) { + deals, err := nodeApi.MarketListIncompleteDeals(ctx) + if err != nil { + return err + } - fmt.Println() + type dealStat struct { + count, verifCount int + bytes, verifBytes uint64 + } + dsAdd := func(ds *dealStat, deal storagemarket.MinerDeal) { + ds.count++ + ds.bytes += uint64(deal.Proposal.PieceSize) + if deal.Proposal.VerifiedDeal { + ds.verifCount++ + ds.verifBytes += uint64(deal.Proposal.PieceSize) + } + } + + showDealStates := map[storagemarket.StorageDealStatus]struct{}{ + storagemarket.StorageDealActive: {}, + storagemarket.StorageDealTransferring: {}, + storagemarket.StorageDealStaged: {}, + storagemarket.StorageDealAwaitingPreCommit: {}, + storagemarket.StorageDealSealing: {}, + storagemarket.StorageDealPublish: {}, + storagemarket.StorageDealCheckForAcceptance: {}, + storagemarket.StorageDealPublishing: {}, + } + + var total dealStat + perState := map[storagemarket.StorageDealStatus]*dealStat{} + for _, deal := range deals { + if _, ok := showDealStates[deal.State]; !ok { + continue + } + if perState[deal.State] == nil { + perState[deal.State] = new(dealStat) + } + + dsAdd(&total, deal) + dsAdd(perState[deal.State], deal) + } + + type wstr struct { + str string + status storagemarket.StorageDealStatus + } + sorted := make([]wstr, 0, len(perState)) + for status, stat := range perState { + st := strings.TrimPrefix(storagemarket.DealStates[status], "StorageDeal") + sorted = append(sorted, wstr{ + str: fmt.Sprintf(" %s:\t%d\t\t%s\t(Verified: %d\t%s)\n", st, stat.count, types.SizeStr(types.NewInt(stat.bytes)), stat.verifCount, types.SizeStr(types.NewInt(stat.verifBytes))), + status: status, + }, + ) + } + sort.Slice(sorted, func(i, j int) bool { + if sorted[i].status == storagemarket.StorageDealActive || sorted[j].status == storagemarket.StorageDealActive { + return sorted[i].status == storagemarket.StorageDealActive + } + return sorted[i].status > sorted[j].status + }) + + fmt.Printf("Storage Deals: %d, %s\n", total.count, types.SizeStr(types.NewInt(total.bytes))) + + tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) + for _, e := range sorted { + _, _ = tw.Write([]byte(e.str)) + } + + _ = tw.Flush() + fmt.Println() + + retrievals, err := nodeApi.MarketListRetrievalDeals(ctx) + if err != nil { + return xerrors.Errorf("getting retrieval deal list: %w", err) + } + + var retrComplete dealStat + for _, retrieval := range retrievals { + if retrieval.Status == retrievalmarket.DealStatusCompleted { + retrComplete.count++ + retrComplete.bytes += retrieval.TotalSent + } + } + + fmt.Printf("Retrieval Deals (complete): %d, %s\n", retrComplete.count, types.SizeStr(types.NewInt(retrComplete.bytes))) + + fmt.Println() + } return nil } diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 3b6d5ac51..dcd3abd4d 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -1528,23 +1528,13 @@ Response: `{}` ### RuntimeSubsystems -RuntimeSubsystems returns the subsystems that are enabled -in this instance. Perms: read Inputs: `null` -Response: -```json -[ - "Mining", - "Sealing", - "SectorStorage", - "Markets" -] -``` +Response: `null` ## Sealing diff --git a/node/builder_miner.go b/node/builder_miner.go index 3be055de7..d62cbcad9 100644 --- a/node/builder_miner.go +++ b/node/builder_miner.go @@ -72,7 +72,7 @@ func ConfigStorageMiner(c interface{}) Option { return Options( ConfigCommon(&cfg.Common, enableLibp2pNode), - Override(new(api.MinerSubsystems), modules.ExtractEnabledMinerSubsystems(cfg.Subsystems)), + Override(new([]api.MinerSubsystem), modules.AddMinerSubsystems(cfg.Subsystems)), Override(new(stores.LocalStorage), From(new(repo.LockedRepo))), Override(new(*stores.Local), modules.LocalStorage), Override(new(*stores.Remote), modules.RemoteStorage), @@ -216,6 +216,7 @@ func StorageMiner(out *api.StorageMiner, subsystemsCfg config.MinerSubsystemConf func(s *Settings) error { resAPI := &impl.StorageMinerAPI{} + s.invokes[ExtractApiKey] = fx.Populate(resAPI) *out = resAPI return nil diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 0fbd12111..86c84fee7 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -23,8 +23,8 @@ import ( "github.com/filecoin-project/go-address" datatransfer "github.com/filecoin-project/go-data-transfer" "github.com/filecoin-project/go-fil-markets/piecestore" - "github.com/filecoin-project/go-fil-markets/retrievalmarket" - "github.com/filecoin-project/go-fil-markets/storagemarket" + retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket" + storagemarket "github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/go-state-types/abi" sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" @@ -48,11 +48,11 @@ import ( type StorageMinerAPI struct { fx.In + Subsystems api.MinerSubsystems + api.Common api.Net - EnabledSubsystems api.MinerSubsystems - Full api.FullNode LocalStore *stores.Local RemoteStore *stores.Remote @@ -706,7 +706,7 @@ func (sm *StorageMinerAPI) ComputeProof(ctx context.Context, ssi []builtin.Secto } func (sm *StorageMinerAPI) RuntimeSubsystems(context.Context) (res api.MinerSubsystems, err error) { - return sm.EnabledSubsystems, nil + return sm.Subsystems, nil } var _ api.StorageMiner = &StorageMinerAPI{} diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 5497eab58..06ef78ca0 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -1008,18 +1008,19 @@ func mutateCfg(r repo.LockedRepo, mutator func(*config.StorageMiner)) error { return multierr.Combine(typeErr, setConfigErr) } -func ExtractEnabledMinerSubsystems(cfg config.MinerSubsystemConfig) (res api.MinerSubsystems) { +func AddMinerSubsystems(cfg config.MinerSubsystemConfig) (res api.MinerSubsystems) { if cfg.EnableMining { - res = append(res, api.SubsystemMining) + res = append(res, api.MiningSubsystem) } if cfg.EnableSealing { - res = append(res, api.SubsystemSealing) + res = append(res, api.SealingSubsystem) } if cfg.EnableSectorStorage { - res = append(res, api.SubsystemSectorStorage) + res = append(res, api.SectorStorageSubsystem) } if cfg.EnableMarkets { - res = append(res, api.SubsystemMarkets) + res = append(res, api.MarketsSubsystem) } - return res + + return }