Adjust various CLI display ratios to arbitrary precision

Originally the deviations from using float64 were insignificant, but at
exabyte scale they start to show up. Cleanup all displays, and clarify
the expectation text, adding an extra 99.9% probability calculator to
`lotus-miner info`
This commit is contained in:
Peter Rabbitson 2021-05-21 13:03:45 +02:00
parent 216fa5c965
commit c2e5a837e6
6 changed files with 132 additions and 40 deletions

View File

@ -47,6 +47,11 @@ func BigDiv(a, b BigInt) BigInt {
return BigInt{Int: big.NewInt(0).Div(a.Int, b.Int)}
}
func BigDivFloat(num, den BigInt) float64 {
res, _ := new(big.Rat).SetFrac(num.Int, den.Int).Float64()
return res
}
func BigMod(a, b BigInt) BigInt {
return BigInt{Int: big.NewInt(0).Mod(a.Int, b.Int)}
}

View File

@ -183,18 +183,23 @@ var StateMinerInfo = &cli.Command{
return err
}
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)
types.BigDivFloat(
types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)),
pow.TotalPower.RawBytePower,
),
)
fmt.Printf("Actual Power: %s / %s (%0.4f%%)\n",
color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)),
types.DeciStr(pow.TotalPower.QualityAdjPower),
float64(qpercI.Int64())/10000)
types.BigDivFloat(
types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)),
pow.TotalPower.QualityAdjPower,
),
)
fmt.Println()
@ -302,8 +307,15 @@ var StatePowerCmd = &cli.Command{
tp := power.TotalPower
if cctx.Args().Present() {
mp := power.MinerPower
percI := types.BigDiv(types.BigMul(mp.QualityAdjPower, types.NewInt(1000000)), tp.QualityAdjPower)
fmt.Printf("%s(%s) / %s(%s) ~= %0.4f%%\n", mp.QualityAdjPower.String(), types.SizeStr(mp.QualityAdjPower), tp.QualityAdjPower.String(), types.SizeStr(tp.QualityAdjPower), float64(percI.Int64())/10000)
fmt.Printf(
"%s(%s) / %s(%s) ~= %0.4f%%\n",
mp.QualityAdjPower.String(), types.SizeStr(mp.QualityAdjPower),
tp.QualityAdjPower.String(), types.SizeStr(tp.QualityAdjPower),
types.BigDivFloat(
types.BigMul(mp.QualityAdjPower, big.NewInt(100)),
tp.QualityAdjPower,
),
)
} else {
fmt.Printf("%s(%s)\n", tp.QualityAdjPower.String(), types.SizeStr(tp.QualityAdjPower))
}

View File

@ -172,12 +172,13 @@ var syncScrapePowerCmd = &cli.Command{
return err
}
qpercI := types.BigDiv(types.BigMul(totalWonPower.QualityAdjPower, types.NewInt(1000000)), totalPower.TotalPower.QualityAdjPower)
fmt.Println("Number of winning miners: ", len(miners))
fmt.Println("QAdjPower of winning miners: ", totalWonPower.QualityAdjPower)
fmt.Println("QAdjPower of all miners: ", totalPower.TotalPower.QualityAdjPower)
fmt.Println("Percentage of winning QAdjPower: ", float64(qpercI.Int64())/10000)
fmt.Println("Percentage of winning QAdjPower: ", types.BigDivFloat(
types.BigMul(totalWonPower.QualityAdjPower, big.NewInt(100)),
totalPower.TotalPower.QualityAdjPower,
))
return nil
},

View File

@ -3,6 +3,8 @@ package main
import (
"context"
"fmt"
"math"
corebig "math/big"
"sort"
"time"
@ -21,6 +23,7 @@ import (
"github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
@ -120,19 +123,23 @@ func infoCmdAct(cctx *cli.Context) error {
return err
}
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("Power: %s / %s (%0.4f%%)\n",
color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)),
types.DeciStr(pow.TotalPower.QualityAdjPower),
float64(qpercI.Int64())/10000)
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),
float64(rpercI.Int64())/10000)
types.BigDivFloat(
types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)),
pow.TotalPower.RawBytePower,
),
)
secCounts, err := api.StateMinerSectorCount(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
@ -146,7 +153,7 @@ func infoCmdAct(cctx *cli.Context) error {
} else {
var faultyPercentage float64
if secCounts.Live != 0 {
faultyPercentage = float64(10000*nfaults/secCounts.Live) / 100.
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)))),
@ -157,16 +164,47 @@ func infoCmdAct(cctx *cli.Context) error {
if !pow.HasMinPower {
fmt.Print("Below minimum power threshold, no blocks will be won")
} else {
expWinChance := float64(types.BigMul(qpercI, types.NewInt(build.BlocksPerEpoch)).Int64()) / 1000000
if expWinChance > 0 {
if expWinChance > 1 {
expWinChance = 1
}
winRate := time.Duration(float64(time.Second*time.Duration(build.BlockDelaySecs)) / expWinChance)
winPerDay := float64(time.Hour*24) / float64(winRate)
fmt.Print("Expected block win rate: ")
color.Blue("%.4f/day (every %s)", winPerDay, winRate.Truncate(time.Second))
winRatio := new(corebig.Rat).Mul(
new(corebig.Rat).SetFrac(
types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int,
pow.TotalPower.QualityAdjPower.Int,
),
// decrease the rate ever-so-slightly to very roughly account for the multi-win poisson distribution
// FIXME - this is not a scientifically derived number... like at all
corebig.NewRat(99997, 100000),
)
if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 {
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 calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples
// https://www.wolframalpha.com/input/?i=c%3D99%3B+p%3D188809111007232%3B+n%3D5740343177447735296%3B+%281-%284.99*p%2Fn%29%29%5E%28t*2880%29%3D%281-%28c%2F100%29%29
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)")
}
}

View File

@ -171,7 +171,7 @@ var provingInfoCmd = &cli.Command{
var faultPerc float64
if proving > 0 {
faultPerc = float64(faults*10000/proving) / 100
faultPerc = float64(faults * 100 / proving)
}
fmt.Printf("Current Epoch: %d\n", cd.CurrentEpoch)

View File

@ -7,6 +7,8 @@ import (
"encoding/json"
"fmt"
"io"
"math"
corebig "math/big"
"os"
"sort"
"text/tabwriter"
@ -27,6 +29,7 @@ import (
"github.com/filecoin-project/go-state-types/abi"
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
tstats "github.com/filecoin-project/lotus/tools/stats"
)
@ -581,18 +584,24 @@ func (i *MinerInfo) MarshalPlainText() ([]byte, error) {
fmt.Fprintf(w, "Sector Size: %s\n", i.SectorSize)
pow := i.MinerPower
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.Fprintf(w, "Byte Power: %s / %s (%0.4f%%)\n",
types.SizeStr(pow.MinerPower.RawBytePower),
types.SizeStr(pow.TotalPower.RawBytePower),
float64(rpercI.Int64())/10000)
types.BigDivFloat(
types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)),
pow.TotalPower.RawBytePower,
),
)
fmt.Fprintf(w, "Actual Power: %s / %s (%0.4f%%)\n",
types.DeciStr(pow.MinerPower.QualityAdjPower),
types.DeciStr(pow.TotalPower.QualityAdjPower),
float64(qpercI.Int64())/10000)
types.BigDivFloat(
types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)),
pow.TotalPower.QualityAdjPower,
),
)
fmt.Fprintf(w, "\tCommitted: %s\n", types.SizeStr(i.CommittedBytes))
@ -608,16 +617,43 @@ func (i *MinerInfo) MarshalPlainText() ([]byte, error) {
if !i.MinerPower.HasMinPower {
fmt.Fprintf(w, "Below minimum power threshold, no blocks will be won\n")
} else {
expWinChance := float64(types.BigMul(qpercI, types.NewInt(build.BlocksPerEpoch)).Int64()) / 1000000
if expWinChance > 0 {
if expWinChance > 1 {
expWinChance = 1
}
winRate := time.Duration(float64(time.Second*time.Duration(build.BlockDelaySecs)) / expWinChance)
winPerDay := float64(time.Hour*24) / float64(winRate)
fmt.Fprintln(w, "Expected block win rate: ")
fmt.Fprintf(w, "%.4f/day (every %s)\n", winPerDay, winRate.Truncate(time.Second))
winRatio := new(corebig.Rat).Mul(
new(corebig.Rat).SetFrac(
types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int,
pow.TotalPower.QualityAdjPower.Int,
),
// decrease the rate ever-so-slightly to very roughly account for the multi-win poisson distribution
// FIXME - this is not a scientifically derived number... like at all
corebig.NewRat(99997, 100000),
)
if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 {
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.Fprintf(w, "Projected average block win rate: %.02f/week (every %s)\n",
weekly,
(time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(),
)
// Geometric distribution calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples
// https://www.wolframalpha.com/input/?i=c%3D99%3B+p%3D188809111007232%3B+n%3D5740343177447735296%3B+%281-%284.99*p%2Fn%29%29%5E%28t*2880%29%3D%281-%28c%2F100%29%29
fmt.Fprintf(w, "Projected block win with 99.9%% probability every %s\n",
(time.Second * time.Duration(
builtin.EpochDurationSeconds*math.Log(1-0.999)/
math.Log(1-winRatioFloat),
)).Truncate(time.Second).String(),
)
fmt.Fprintln(w, "(projections DO NOT account for future network and miner growth)")
}
}