Skip to content

Commit

Permalink
Adjust various CLI display ratios to arbitrary precision
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
ribasushi committed May 25, 2021
1 parent 216fa5c commit 20fe10a
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 40 deletions.
5 changes: 5 additions & 0 deletions chain/types/bigint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
}
Expand Down
26 changes: 19 additions & 7 deletions cli/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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))
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/lotus-shed/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
70 changes: 54 additions & 16 deletions cmd/lotus-storage-miner/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"context"
"fmt"
"math"
corebig "math/big"
"sort"
"time"

Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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)))),
Expand All @@ -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)")
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/lotus-storage-miner/proving.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
59 changes: 46 additions & 13 deletions testplans/lotus-soup/rfwp/chain_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"encoding/json"
"fmt"
"io"
"math"
corebig "math/big"
"os"
"sort"
"text/tabwriter"
Expand All @@ -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"
)
Expand Down Expand Up @@ -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))

Expand All @@ -608,16 +617,40 @@ 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)
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),
)

fmt.Fprintln(w, "Expected block win rate: ")
fmt.Fprintf(w, "%.4f/day (every %s)\n", winPerDay, winRate.Truncate(time.Second))
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(
corebig.NewRat(int64(time.Second)*builtin.EpochDurationSeconds, 1),
new(corebig.Rat).Inv(winRatio),
).Float64()

fmt.Fprintf(w, "Projected average block win rate: %.02f/week (every %s)\n",
weekly,
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.Duration(int64(
float64(time.Second*builtin.EpochDurationSeconds)*math.Log(1-0.999)/math.Log(1-winRatioFloat),
)).Truncate(time.Second).String(),
)
}
}

Expand Down

0 comments on commit 20fe10a

Please sign in to comment.