11package main
22
33import (
4+ "bufio"
45 "context"
6+ "encoding/json"
57 "fmt"
8+ "math/rand"
9+ "os"
10+ "sort"
11+ "strconv"
12+ "time"
613
714 "github.com/ipfs/go-datastore"
815 dsq "github.com/ipfs/go-datastore/query"
916 "github.com/urfave/cli/v2"
1017 "golang.org/x/xerrors"
1118
19+ "github.com/filecoin-project/go-f3/gpbft"
20+
21+ lcli "github.com/filecoin-project/lotus/cli"
22+ cliutil "github.com/filecoin-project/lotus/cli/util"
1223 "github.com/filecoin-project/lotus/node/repo"
1324)
1425
@@ -17,6 +28,170 @@ var f3Cmd = &cli.Command{
1728 Description : "f3 related commands" ,
1829 Subcommands : []* cli.Command {
1930 f3ClearStateCmd ,
31+ f3GenExplicitPower ,
32+ },
33+ }
34+
35+ func loadF3IDList (path string ) ([]gpbft.ActorID , error ) {
36+ file , err := os .Open (path )
37+ if err != nil {
38+ return nil , fmt .Errorf ("failed to open file: %w" , err )
39+ }
40+ defer file .Close () //nolint:errcheck
41+
42+ var ids []gpbft.ActorID
43+ scanner := bufio .NewScanner (file )
44+ for scanner .Scan () {
45+ line := scanner .Text ()
46+ if line == "" || line [0 ] == '#' {
47+ continue
48+ }
49+ id , err := strconv .ParseUint (line , 10 , 64 )
50+ if err != nil {
51+ return nil , fmt .Errorf ("failed to parse ID: %w" , err )
52+ }
53+
54+ ids = append (ids , gpbft .ActorID (id ))
55+ }
56+
57+ if err := scanner .Err (); err != nil {
58+ return nil , fmt .Errorf ("error reading file: %w" , err )
59+ }
60+
61+ return ids , nil
62+ }
63+
64+ var f3GenExplicitPower = & cli.Command {
65+ Name : "gen-explicit-power" ,
66+ Description : "generates an explicit power table" ,
67+
68+ Flags : []cli.Flag {
69+ & cli.PathFlag {
70+ Name : "good-list" ,
71+ Usage : "new line delimited file with known good IDs to be included" ,
72+ },
73+ & cli.PathFlag {
74+ Name : "bad-list" ,
75+ Usage : "new line delimited file with known bad IDs to be excluded" ,
76+ },
77+ & cli.IntFlag {
78+ Name : "n" ,
79+ Usage : "generate N entries, exclusive with ratio" ,
80+ },
81+ & cli.Float64Flag {
82+ Name : "ratio" ,
83+ Usage : "generate given ratio of full power table, exclusive with N" ,
84+ },
85+ & cli.Int64Flag {
86+ Name : "seed" ,
87+ Usage : "seed for randomization, -1 will use current nano time" ,
88+ Value : - 1 ,
89+ },
90+ & cli.Uint64Flag {
91+ Name : "iteration" ,
92+ Usage : "the iteration of randomization, random entries will be exclusive across iterations" ,
93+ Value : 0 ,
94+ },
95+ & cli.StringFlag {
96+ Name : "tipset" ,
97+ Usage : "specify tipset to call method on (pass comma separated array of cids) or @epoch" ,
98+ },
99+ },
100+
101+ Action : func (cctx * cli.Context ) error {
102+ ctx := cliutil .ReqContext (cctx )
103+ api , closer , err := cliutil .GetFullNodeAPIV1 (cctx )
104+ if err != nil {
105+ return fmt .Errorf ("getting api: %w" , err )
106+ }
107+ defer closer ()
108+
109+ ts , err := lcli .LoadTipSet (ctx , cctx , api )
110+ if err != nil {
111+ return fmt .Errorf ("getting chain head: %w" , err )
112+ }
113+ if cctx .IsSet ("N" ) && cctx .IsSet ("ratio" ) {
114+ return fmt .Errorf ("N and ratio options are exclusive" )
115+ }
116+
117+ allPowerEntries , err := api .F3GetECPowerTable (ctx , ts .Key ())
118+ if err != nil {
119+ return fmt .Errorf ("getting power entries: %w" , err )
120+ }
121+
122+ powerMap := map [gpbft.ActorID ]gpbft.PowerEntry {}
123+ for _ , pe := range allPowerEntries {
124+ powerMap [pe .ID ] = pe
125+ }
126+ var goodList []gpbft.ActorID
127+ if goodPath := cctx .Path ("good-list" ); goodPath != "" {
128+ goodList , err = loadF3IDList (goodPath )
129+ if err != nil {
130+ return fmt .Errorf ("loading good list: %w" , err )
131+ }
132+ }
133+
134+ var badList []gpbft.ActorID
135+ if badPath := cctx .Path ("bad-list" ); badPath != "" {
136+ badList , err = loadF3IDList (badPath )
137+ if err != nil {
138+ return fmt .Errorf ("loading bad list: %w" , err )
139+ }
140+ }
141+ total := len (powerMap )
142+ for _ , id := range badList {
143+ delete (powerMap , id )
144+ }
145+
146+ var result gpbft.PowerEntries
147+ add := func (id gpbft.ActorID ) {
148+ result = append (result , powerMap [id ])
149+ delete (powerMap , id )
150+ }
151+
152+ for _ , id := range goodList {
153+ if _ , ok := powerMap [id ]; ok {
154+ add (id )
155+ }
156+ }
157+
158+ seed := cctx .Int64 ("seed" )
159+ if seed == - 1 {
160+ seed = time .Now ().UnixNano ()
161+ }
162+ rng := rand .New (rand .NewSource (seed ))
163+
164+ endSize := cctx .Int ("N" )
165+ if cctx .IsSet ("ratio" ) {
166+ endSize = int (float64 (total ) * cctx .Float64 ("ratio" ))
167+ }
168+ if toAdd := endSize - len (result ); toAdd > 0 {
169+ var powerList gpbft.PowerEntries
170+ for _ , pe := range powerMap {
171+ powerList = append (powerList , pe )
172+ }
173+ rng .Shuffle (len (powerList ), powerList .Swap )
174+
175+ iteration := cctx .Int ("iteration" )
176+ startIdx := min (toAdd * iteration , len (powerList ))
177+ endIdx := min (toAdd * (iteration + 1 ), len (powerList ))
178+ result = append (result , powerList [startIdx :endIdx ]... )
179+ }
180+
181+ if len (result ) > endSize {
182+ result = result [:endSize ]
183+ }
184+ sort .Sort (result )
185+ res , err := json .MarshalIndent (result , " " , " " )
186+ if err != nil {
187+ return fmt .Errorf ("marshalling to json: %w" , err )
188+ }
189+ _ , err = cctx .App .Writer .Write (res )
190+ if err != nil {
191+ return fmt .Errorf ("writing result: %w" , err )
192+ }
193+
194+ return nil
20195 },
21196}
22197
0 commit comments