diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index e683cfd96d7..8faad718e37 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -2,6 +2,7 @@ package power import ( "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -60,3 +61,10 @@ type Claim struct { // Sum of quality adjusted power for a miner's sectors. QualityAdjPower abi.StoragePower } + +func AddClaims(a Claim, b Claim) Claim { + return Claim{ + RawBytePower: big.Add(a.RawBytePower, b.RawBytePower), + QualityAdjPower: big.Add(a.QualityAdjPower, b.QualityAdjPower), + } +} diff --git a/cmd/lotus-shed/sync.go b/cmd/lotus-shed/sync.go index bfe7cc8b79e..65d2b6d6f50 100644 --- a/cmd/lotus-shed/sync.go +++ b/cmd/lotus-shed/sync.go @@ -2,6 +2,15 @@ package main import ( "fmt" + "strconv" + + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/actors/builtin/power" + + "github.com/filecoin-project/go-address" + + "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -16,6 +25,7 @@ var syncCmd = &cli.Command{ Flags: []cli.Flag{}, Subcommands: []*cli.Command{ syncValidateCmd, + syncScrapePowerCmd, }, } @@ -62,3 +72,113 @@ var syncValidateCmd = &cli.Command{ return nil }, } + +var syncScrapePowerCmd = &cli.Command{ + Name: "scrape-power", + Usage: "given a height and a tipset, reports what percentage of mining power had a winning ticket between the tipset and height", + ArgsUsage: "[height tipsetkey]", + Action: func(cctx *cli.Context) error { + if cctx.Args().Len() < 1 { + fmt.Println("usage: [blockCid1 blockCid2...]") + fmt.Println("Any CIDs passed after the height will be used as the tipset key") + fmt.Println("If no block CIDs are provided, chain head will be used") + return nil + } + + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + if cctx.Args().Len() < 1 { + fmt.Println("usage: ...") + fmt.Println("At least one block cid must be provided") + return nil + } + + h, err := strconv.ParseInt(cctx.Args().Get(0), 10, 0) + if err != nil { + return err + } + + height := abi.ChainEpoch(h) + + var ts *types.TipSet + var startTsk types.TipSetKey + if cctx.NArg() > 1 { + var tscids []cid.Cid + args := cctx.Args().Slice() + + for _, s := range args[1:] { + c, err := cid.Decode(s) + if err != nil { + return fmt.Errorf("block cid was invalid: %s", err) + } + tscids = append(tscids, c) + } + + startTsk = types.NewTipSetKey(tscids...) + ts, err = api.ChainGetTipSet(ctx, startTsk) + if err != nil { + return err + } + } else { + ts, err = api.ChainHead(ctx) + if err != nil { + return err + } + + startTsk = ts.Key() + } + + if ts.Height() < height { + return fmt.Errorf("start tipset's height < stop height: %d < %d", ts.Height(), height) + } + + miners := make(map[address.Address]struct{}) + for ts.Height() >= height { + for _, blk := range ts.Blocks() { + _, found := miners[blk.Miner] + if !found { + // do the thing + miners[blk.Miner] = struct{}{} + } + } + + ts, err = api.ChainGetTipSet(ctx, ts.Parents()) + if err != nil { + return err + } + } + + totalWonPower := power.Claim{ + RawBytePower: big.Zero(), + QualityAdjPower: big.Zero(), + } + for miner := range miners { + mp, err := api.StateMinerPower(ctx, miner, startTsk) + if err != nil { + return err + } + + totalWonPower = power.AddClaims(totalWonPower, mp.MinerPower) + } + + totalPower, err := api.StateMinerPower(ctx, address.Undef, startTsk) + if err != nil { + 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) + + return nil + }, +}