diff --git a/chain/types/fil.go b/chain/types/fil.go index 3dabb5e77cb..6742dd18000 100644 --- a/chain/types/fil.go +++ b/chain/types/fil.go @@ -23,6 +23,29 @@ func (f FIL) Unitless() string { return strings.TrimRight(strings.TrimRight(r.FloatString(18), "0"), ".") } +var unitPrefixes = []string{"a", "f", "p", "n", "μ", "m"} + +func (f FIL) Short() string { + n := BigInt(f) + + dn := uint64(1) + var prefix string + for _, p := range unitPrefixes { + if n.LessThan(NewInt(dn * 1000)) { + prefix = p + break + } + dn *= 1000 + } + + r := new(big.Rat).SetFrac(f.Int, big.NewInt(int64(dn))) + if r.Sign() == 0 { + return "0" + } + + return strings.TrimRight(strings.TrimRight(r.FloatString(3), "0"), ".") + " " + prefix + "FIL" +} + func (f FIL) Format(s fmt.State, ch rune) { switch ch { case 's', 'v': diff --git a/chain/types/fil_test.go b/chain/types/fil_test.go new file mode 100644 index 00000000000..6cbc44c5f9b --- /dev/null +++ b/chain/types/fil_test.go @@ -0,0 +1,68 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFilShort(t *testing.T) { + for _, s := range []struct { + fil string + expect string + }{ + + {fil: "1", expect: "1 FIL"}, + {fil: "1.1", expect: "1.1 FIL"}, + {fil: "12", expect: "12 FIL"}, + {fil: "123", expect: "123 FIL"}, + {fil: "123456", expect: "123456 FIL"}, + {fil: "123.23", expect: "123.23 FIL"}, + {fil: "123456.234", expect: "123456.234 FIL"}, + {fil: "123456.2341234", expect: "123456.234 FIL"}, + {fil: "123456.234123445", expect: "123456.234 FIL"}, + + {fil: "0.1", expect: "100 mFIL"}, + {fil: "0.01", expect: "10 mFIL"}, + {fil: "0.001", expect: "1 mFIL"}, + + {fil: "0.0001", expect: "100 μFIL"}, + {fil: "0.00001", expect: "10 μFIL"}, + {fil: "0.000001", expect: "1 μFIL"}, + + {fil: "0.0000001", expect: "100 nFIL"}, + {fil: "0.00000001", expect: "10 nFIL"}, + {fil: "0.000000001", expect: "1 nFIL"}, + + {fil: "0.0000000001", expect: "100 pFIL"}, + {fil: "0.00000000001", expect: "10 pFIL"}, + {fil: "0.000000000001", expect: "1 pFIL"}, + + {fil: "0.0000000000001", expect: "100 fFIL"}, + {fil: "0.00000000000001", expect: "10 fFIL"}, + {fil: "0.000000000000001", expect: "1 fFIL"}, + + {fil: "0.0000000000000001", expect: "100 aFIL"}, + {fil: "0.00000000000000001", expect: "10 aFIL"}, + {fil: "0.000000000000000001", expect: "1 aFIL"}, + + {fil: "0.0000012", expect: "1.2 μFIL"}, + {fil: "0.00000123", expect: "1.23 μFIL"}, + {fil: "0.000001234", expect: "1.234 μFIL"}, + {fil: "0.0000012344", expect: "1.234 μFIL"}, + {fil: "0.00000123444", expect: "1.234 μFIL"}, + + {fil: "0.0002212", expect: "221.2 μFIL"}, + {fil: "0.00022123", expect: "221.23 μFIL"}, + {fil: "0.000221234", expect: "221.234 μFIL"}, + {fil: "0.0002212344", expect: "221.234 μFIL"}, + {fil: "0.00022123444", expect: "221.234 μFIL"}, + } { + s := s + t.Run(s.fil, func(t *testing.T) { + f, err := ParseFIL(s.fil) + require.NoError(t, err) + require.Equal(t, s.expect, f.Short()) + }) + } +} diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index 8612fa27a08..7bedd2b94a7 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -14,6 +14,7 @@ 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" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" "github.com/filecoin-project/lotus/api" @@ -59,7 +60,7 @@ func infoCmdAct(cctx *cli.Context) error { ctx := lcli.ReqContext(cctx) - fmt.Print("Full node: ") + fmt.Print("Chain: ") head, err := api.ChainHead(ctx) if err != nil { @@ -75,6 +76,20 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Printf("[%s]", color.RedString("sync behind! (%s behind)", time.Now().Sub(time.Unix(int64(head.MinTimestamp()), 0)).Truncate(time.Second))) } + basefee := head.MinTicketBlock().ParentBaseFee + gasCol := []color.Attribute{color.FgBlue} + switch { + case basefee.GreaterThan(big.NewInt(7000_000_000)): // 7 nFIL + gasCol = []color.Attribute{color.BgRed, color.FgBlack} + case basefee.GreaterThan(big.NewInt(3000_000_000)): // 3 nFIL + gasCol = []color.Attribute{color.FgRed} + case basefee.GreaterThan(big.NewInt(750_000_000)): // 750 uFIL + gasCol = []color.Attribute{color.FgYellow} + case basefee.GreaterThan(big.NewInt(100_000_000)): // 100 uFIL + gasCol = []color.Attribute{color.FgGreen} + } + fmt.Printf(" [basefee %s]", color.New(gasCol...).Sprint(types.FIL(basefee).Short())) + fmt.Println() maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor")) @@ -93,15 +108,14 @@ func infoCmdAct(cctx *cli.Context) error { return err } - fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) - // Sector size mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) if err != nil { return err } - fmt.Printf("Sector Size: %s\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize)))) + ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize))) + fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize) pow, err := api.StateMinerPower(ctx, maddr, types.EmptyTSK) if err != nil { @@ -111,16 +125,16 @@ func infoCmdAct(cctx *cli.Context) error { rpercI := types.BigDiv(types.BigMul(pow.MinerPower.RawBytePower, types.NewInt(1000000)), pow.TotalPower.RawBytePower) qpercI := types.BigDiv(types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(1000000)), pow.TotalPower.QualityAdjPower) - fmt.Printf("Byte Power: %s / %s (%0.4f%%)\n", - color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), - types.SizeStr(pow.TotalPower.RawBytePower), - float64(rpercI.Int64())/10000) - - fmt.Printf("Actual Power: %s / %s (%0.4f%%)\n", + fmt.Printf("Power: %s / %s (%0.4f%%)\n", color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), types.DeciStr(pow.TotalPower.QualityAdjPower), float64(qpercI.Int64())/10000) + fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", + color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), + types.SizeStr(pow.TotalPower.RawBytePower), + float64(rpercI.Int64())/10000) + secCounts, err := api.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) if err != nil { return err @@ -168,6 +182,10 @@ func infoCmdAct(cctx *cli.Context) error { var nactiveDeals, nVerifDeals, ndeals uint64 var activeDealBytes, activeVerifDealBytes, dealBytes abi.PaddedPieceSize for _, deal := range deals { + if deal.State == storagemarket.StorageDealError { + continue + } + ndeals++ dealBytes += deal.Proposal.PieceSize @@ -186,6 +204,8 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Printf("\tActive: %d, %s (Verified: %d, %s)\n", nactiveDeals, types.SizeStr(types.NewInt(uint64(activeDealBytes))), nVerifDeals, types.SizeStr(types.NewInt(uint64(activeVerifDealBytes)))) 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() @@ -196,32 +216,46 @@ func infoCmdAct(cctx *cli.Context) error { if err != nil { return xerrors.Errorf("getting available balance: %w", err) } - fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance))) - fmt.Printf("\tPreCommit: %s\n", types.FIL(lockedFunds.PreCommitDeposits)) - fmt.Printf("\tPledge: %s\n", types.FIL(lockedFunds.InitialPledgeRequirement)) - fmt.Printf("\tVesting: %s\n", types.FIL(lockedFunds.VestingFunds)) - color.Green("\tAvailable: %s", types.FIL(availBalance)) - wb, err := api.WalletBalance(ctx, mi.Worker) - if err != nil { - return xerrors.Errorf("getting worker balance: %w", err) - } - color.Cyan("Worker Balance: %s", types.FIL(wb)) + 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()) + color.Green(" Available: %s", types.FIL(availBalance).Short()) mb, err := api.StateMarketBalance(ctx, maddr, types.EmptyTSK) if err != nil { return xerrors.Errorf("getting market balance: %w", err) } - fmt.Printf("Market (Escrow): %s\n", types.FIL(mb.Escrow)) - fmt.Printf("Market (Locked): %s\n", types.FIL(mb.Locked)) + spendable = big.Add(spendable, big.Sub(mb.Escrow, mb.Locked)) - fmt.Println() + fmt.Printf("Market Balance: %s\n", types.FIL(mb.Escrow).Short()) + fmt.Printf(" Locked: %s\n", types.FIL(mb.Locked).Short()) + color.Green(" Available: %s\n", types.FIL(big.Sub(mb.Escrow, mb.Locked)).Short()) - sealdur, err := nodeApi.SectorGetExpectedSealDuration(ctx) + wb, err := api.WalletBalance(ctx, mi.Worker) if err != nil { - return err + 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 := api.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("Expected Seal Duration: %s\n\n", sealdur) + fmt.Printf(" Control: %s\n", types.FIL(cbsum).Short()) + } + fmt.Printf("Total Spendable: %s\n", color.YellowString(types.FIL(spendable).Short())) + + fmt.Println() if !cctx.Bool("hide-sectors-info") { fmt.Println("Sectors:")