Line data Source code
1 : #define FD_SCRATCH_USE_HANDHOLDING 1
2 : #include "../fd_flamenco.h"
3 : #include "../../ballet/base58/fd_base58.h"
4 : #include "../types/fd_types.h"
5 : #include "../leaders/fd_leaders.h"
6 : #include "../runtime/sysvar/fd_sysvar_epoch_schedule.h"
7 : #include "fd_stakes.h"
8 :
9 : #include <errno.h>
10 : #include <stdio.h>
11 : #include <stdlib.h>
12 : #include <sys/stat.h>
13 :
14 : static int
15 0 : usage( void ) {
16 0 : fprintf( stderr,
17 0 : "usage: fd_stakes_from_snapshot {nodes/leaders} {FILE}\n"
18 0 : "\n"
19 0 : "Derive epoch stake information from snapshot.\n"
20 0 : "\n"
21 0 : "Mode:\n"
22 0 : " epochs Print available epochs\n"
23 0 : " nodes Dump active stake per node identity\n"
24 0 : " CSV format: {pubkey},{stake}\n"
25 0 : " leaders Dump leader schedule\n"
26 0 : " CSV format: {slot},{pubkey}\n"
27 0 : "\n"
28 0 : "FILE is the file path to a .tar.zst snapshot or a raw\n"
29 0 : " bincode snapshot manifest\n"
30 0 : "\n"
31 0 : "Options:\n"
32 0 : "\n"
33 0 : " --page-sz {gigantic|huge|normal} Page size\n"
34 0 : " --page-cnt 2 Page count\n"
35 0 : " --scratch-mb 1024 Scratch mem MiB\n"
36 0 : " --epoch <ulong> Epoch number\n" );
37 0 : return 0;
38 0 : }
39 :
40 : #define ACTION_NODES (0)
41 : #define ACTION_LEADERS (1)
42 : #define ACTION_EPOCHS (2)
43 :
44 : /* _find_epoch looks for epoch stakes for the requested epoch.
45 : On failure, logs error and aborts application. */
46 :
47 : static fd_epoch_stakes_t const *
48 : _find_epoch( fd_solana_manifest_t const * manifest,
49 0 : ulong epoch ) {
50 :
51 :
52 0 : if( FD_UNLIKELY( epoch==ULONG_MAX ) ) {
53 0 : fprintf( stderr, "error: missing --epoch\n" );
54 0 : usage();
55 0 : exit(1);
56 0 : }
57 :
58 0 : fd_epoch_stakes_t const * stakes = NULL;
59 0 : fd_epoch_epoch_stakes_pair_t const * epochs = manifest->bank.epoch_stakes;
60 0 : for( ulong i=0; i < manifest->bank.epoch_stakes_len; i++ ) {
61 0 : if( epochs[ i ].key==epoch ) {
62 0 : stakes = &epochs[i].value;
63 0 : break;
64 0 : }
65 0 : }
66 0 : if( FD_UNLIKELY( !stakes ) )
67 0 : FD_LOG_ERR(( "Snapshot missing EpochStakes for epoch %lu", epoch ));
68 :
69 0 : return stakes;
70 0 : }
71 :
72 : static fd_stake_weight_t *
73 : _get_stake_weights( fd_solana_manifest_t const * manifest,
74 : ulong epoch,
75 0 : ulong * out_cnt ) {
76 :
77 0 : fd_epoch_stakes_t const * stakes = _find_epoch( manifest, epoch );
78 0 : fd_vote_accounts_t const * vaccs = &stakes->stakes.vote_accounts;
79 :
80 0 : ulong vote_acc_cnt = fd_vote_accounts_pair_t_map_size( vaccs->vote_accounts_pool, vaccs->vote_accounts_root );
81 0 : FD_LOG_NOTICE(( "vote_acc_cnt=%lu", vote_acc_cnt ));
82 0 : fd_stake_weight_t * weights = fd_scratch_alloc( alignof(fd_stake_weight_t), vote_acc_cnt * sizeof(fd_stake_weight_t) );
83 0 : if( FD_UNLIKELY( !weights ) ) FD_LOG_ERR(( "fd_scratch_alloc() failed" ));
84 :
85 : /* FIXME: This will crash because the spad is not set up and no accs
86 : are passed in. */
87 :
88 0 : ulong weight_cnt = fd_stake_weights_by_node( NULL, weights, NULL );
89 0 : if( FD_UNLIKELY( weight_cnt==ULONG_MAX ) ) FD_LOG_ERR(( "fd_stake_weights_by_node() failed" ));
90 :
91 0 : *out_cnt = weight_cnt;
92 0 : return weights;
93 0 : }
94 :
95 : static int
96 0 : action_epochs( fd_solana_manifest_t const * manifest ) {
97 0 : fd_epoch_epoch_stakes_pair_t const * epochs = manifest->bank.epoch_stakes;
98 0 : for( ulong i=0; i < manifest->bank.epoch_stakes_len; i++ )
99 0 : printf( "%lu\n", epochs[ i ].key );
100 0 : return 0;
101 0 : }
102 :
103 : static int
104 : action_nodes( fd_solana_manifest_t const * manifest,
105 0 : ulong epoch ) {
106 :
107 0 : ulong weight_cnt;
108 0 : fd_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
109 :
110 0 : for( ulong i=0UL; i<weight_cnt; i++ ) {
111 0 : fd_stake_weight_t const * w = weights + i;
112 0 : char keyB58[ FD_BASE58_ENCODED_32_SZ ];
113 0 : fd_base58_encode_32( w->key.key, NULL, keyB58 );
114 0 : printf( "%s,%lu\n", keyB58, w->stake );
115 0 : }
116 :
117 0 : return 0;
118 0 : }
119 :
120 : static int
121 : action_leaders( fd_solana_manifest_t const * manifest,
122 0 : ulong epoch ) {
123 :
124 0 : ulong weight_cnt;
125 0 : fd_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
126 :
127 0 : fd_epoch_schedule_t const * sched = &manifest->bank.epoch_schedule;
128 0 : ulong slot0 = fd_epoch_slot0 ( sched, epoch );
129 0 : ulong slot_cnt = fd_epoch_slot_cnt( sched, epoch );
130 0 : ulong sched_cnt = slot_cnt/FD_EPOCH_SLOTS_PER_ROTATION;
131 :
132 0 : void * leaders_mem = fd_scratch_alloc( fd_epoch_leaders_align(), fd_epoch_leaders_footprint( weight_cnt, sched_cnt ) );
133 0 : leaders_mem = fd_epoch_leaders_new( leaders_mem, epoch, slot0, slot_cnt, weight_cnt, weights, 0UL );
134 0 : fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( leaders_mem );
135 0 : FD_TEST( leaders );
136 :
137 0 : ulong slot = slot0;
138 0 : for( ulong i=0; i<sched_cnt; i++ ) {
139 0 : fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot );
140 0 : char keyB58[ FD_BASE58_ENCODED_32_SZ ];
141 0 : fd_base58_encode_32( leader->key, NULL, keyB58 );
142 0 : for( ulong j=0; j<FD_EPOCH_SLOTS_PER_ROTATION; j++ ) {
143 0 : printf( "%lu,%s\n", slot, keyB58 );
144 0 : slot++;
145 0 : }
146 0 : }
147 :
148 0 : return 0;
149 0 : }
150 :
151 : /* _is_zstd returns 1 if given file handle points to the beginning of a
152 : zstd stream, otherwise zero. */
153 :
154 : static int
155 0 : _is_zstd( FILE * file ) {
156 0 : uint magic;
157 0 : ulong n = fread( &magic, 1UL, 4UL, file );
158 0 : if( FD_UNLIKELY( feof( file ) ) ) {
159 0 : clearerr( file );
160 0 : fseek( file, -(long)n, SEEK_CUR );
161 0 : return 0;
162 0 : }
163 0 : int err = ferror( file );
164 0 : if( FD_UNLIKELY( err ) )
165 0 : FD_LOG_ERR(( "fread() failed (%d-%s)", err, strerror( err ) ));
166 0 : fseek( file, -4L, SEEK_CUR );
167 0 : return ( magic==0xFD2FB528UL );
168 0 : }
169 :
170 : /* TODO older Solana snapshots are gzip, add support */
171 :
172 : int
173 : main( int argc,
174 : char ** argv ) {
175 : fd_boot( &argc, &argv );
176 : fd_flamenco_boot( &argc, &argv );
177 :
178 : for( int i=1; i<argc; i++ )
179 : if( 0==strcmp( argv[i], "--help" ) ) return usage();
180 :
181 : char const * _page_sz = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz", NULL, "gigantic" );
182 : ulong page_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt", NULL, 2UL );
183 : ulong scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL );
184 : ulong epoch = fd_env_strip_cmdline_ulong( &argc, &argv, "--epoch", NULL, ULONG_MAX );
185 :
186 : ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
187 : if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
188 :
189 : if( FD_UNLIKELY( argc!=3 ) ) {
190 : fprintf( stderr, "error: missing arguments\n" );
191 : return usage();
192 : }
193 :
194 : char const * mode = argv[1];
195 : char const * filepath = argv[2];
196 :
197 : int action;
198 : /**/ if( 0==strcmp( mode, "epochs" ) ) action = ACTION_EPOCHS;
199 : else if( 0==strcmp( mode, "nodes" ) ) action = ACTION_NODES;
200 : else if( 0==strcmp( mode, "leaders" ) ) action = ACTION_LEADERS;
201 : else {
202 : fprintf( stderr, "error: invalid mode \"%s\"\n", mode );
203 : return usage();
204 : }
205 :
206 : /* Create workspace and scratch allocator */
207 :
208 : fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
209 : if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
210 :
211 : ulong smax = scratch_mb << 20;
212 : void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
213 : if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
214 : ulong scratch_depth = 4UL;
215 : void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( scratch_depth ), 2UL );
216 : if( FD_UNLIKELY( !fmem ) ) FD_LOG_ERR(( "Failed to alloc scratch frames" ));
217 :
218 : fd_scratch_attach( smem, fmem, smax, scratch_depth );
219 : fd_scratch_push();
220 :
221 : /* Open file */
222 :
223 : FD_LOG_INFO(( "Reading snapshot %s", filepath ));
224 : FILE * file = fopen( filepath, "rb" );
225 : if( FD_UNLIKELY( !file ) ) FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", filepath, errno, strerror( errno ) ));
226 :
227 : /* Read manifest bytes */
228 :
229 : void * manifest_bin;
230 : ulong manifest_binsz;
231 : if( _is_zstd( file ) ) {
232 : FD_LOG_NOTICE(( "Detected .tar.zst stream" ));
233 : FD_LOG_ERR(( "TODO" ));
234 : } else {
235 : FD_LOG_NOTICE(( "Assuming raw bincode file" ));
236 :
237 : /* Allocate buffer suitable for storing file */
238 :
239 : struct stat stat;
240 : int err = fstat( fileno( file ), &stat );
241 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fstat() failed (%d-%s)", errno, strerror( errno ) ));
242 : manifest_binsz = (ulong)stat.st_size;
243 :
244 : manifest_bin = fd_wksp_alloc_laddr( wksp, 1UL, manifest_binsz, 3UL );
245 : if( FD_UNLIKELY( !manifest_bin ) ) FD_LOG_ERR(( "failed to allocate metadata buffer" ));
246 :
247 : /* Read file into buffer */
248 :
249 : ulong n = fread( manifest_bin, manifest_binsz, 1UL, file );
250 : if( FD_UNLIKELY( n!=1UL ) ) FD_LOG_ERR(( "fread() failed (eof=%d)", feof( file ) ));
251 : FD_LOG_NOTICE(( "Read manifest (%.3f MiB)", (double)manifest_binsz/(1UL<<20) ));
252 : }
253 :
254 : /* Deserialize manifest */
255 :
256 : long dt = -fd_log_wallclock();
257 :
258 : int decode_err;
259 : fd_solana_manifest_t * manifest = fd_bincode_decode_scratch(
260 : solana_manifest, manifest_bin, manifest_binsz, &decode_err );
261 : if( FD_UNLIKELY( decode_err ) ) {
262 : FD_LOG_ERR(( "Failed to decode manifest" ));
263 : }
264 :
265 : fd_wksp_free_laddr( manifest_bin ); manifest_bin = NULL;
266 : dt += fd_log_wallclock();
267 : FD_LOG_NOTICE(( "Deserialized manifest (took %.3fs)", (double)dt/1e9 ));
268 :
269 : /* Action */
270 :
271 : int res;
272 : fd_scratch_push();
273 : switch( action ) {
274 : case ACTION_LEADERS:
275 : res = action_leaders( manifest, epoch );
276 : break;
277 : case ACTION_NODES:
278 : res = action_nodes( manifest, epoch );
279 : break;
280 : case ACTION_EPOCHS:
281 : res = action_epochs( manifest );
282 : break;
283 : default:
284 : __builtin_unreachable();
285 : }
286 : fd_scratch_pop();
287 :
288 : /* Cleanup */
289 :
290 : fd_scratch_pop();
291 : FD_TEST( fd_scratch_frame_used()==0UL );
292 : fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
293 : fd_wksp_free_laddr( fmem );
294 : fclose( file );
295 : fd_wksp_detach( wksp );
296 : fd_flamenco_halt();
297 : fd_halt();
298 : return res;
299 : }
|