1
1
package main
2
2
3
3
import (
4
+ "bufio"
4
5
"context"
6
+ "encoding/json"
5
7
"fmt"
8
+ "math/rand"
9
+ "os"
10
+ "sort"
11
+ "strconv"
12
+ "time"
6
13
7
14
"github.com/ipfs/go-datastore"
8
15
dsq "github.com/ipfs/go-datastore/query"
9
16
"github.com/urfave/cli/v2"
10
17
"golang.org/x/xerrors"
11
18
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"
12
23
"github.com/filecoin-project/lotus/node/repo"
13
24
)
14
25
@@ -17,6 +28,170 @@ var f3Cmd = &cli.Command{
17
28
Description : "f3 related commands" ,
18
29
Subcommands : []* cli.Command {
19
30
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
20
195
},
21
196
}
22
197
0 commit comments