Line data Source code
1 : #include "fd_repair.h"
2 : #include "../../ballet/sha256/fd_sha256.h"
3 : #include "../../ballet/bmtree/fd_bmtree.h"
4 :
5 : void *
6 0 : fd_repair_new( void * shmem, fd_pubkey_t * identity_key ) {
7 :
8 0 : if( FD_UNLIKELY( !shmem ) ) {
9 0 : FD_LOG_WARNING(( "NULL mem" ));
10 0 : return NULL;
11 0 : }
12 :
13 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_repair_align() ) ) ) {
14 0 : FD_LOG_WARNING(( "misaligned mem" ));
15 0 : return NULL;
16 0 : }
17 :
18 0 : ulong footprint = fd_repair_footprint();
19 0 : fd_memset( shmem, 0, footprint );
20 :
21 0 : fd_repair_t * repair = (fd_repair_t *)shmem;
22 0 : repair->identity_key = *identity_key;
23 :
24 0 : return shmem;
25 0 : }
26 :
27 : fd_repair_t *
28 0 : fd_repair_join( void * shrepair ) {
29 0 : fd_repair_t * repair = (fd_repair_t *)shrepair;
30 :
31 0 : if( FD_UNLIKELY( !repair ) ) {
32 0 : FD_LOG_WARNING(( "NULL repair" ));
33 0 : return NULL;
34 0 : }
35 :
36 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)repair, fd_repair_align() ) ) ) {
37 0 : FD_LOG_WARNING(( "misaligned repair" ));
38 0 : return NULL;
39 0 : }
40 :
41 0 : fd_wksp_t * wksp = fd_wksp_containing( repair );
42 0 : if( FD_UNLIKELY( !wksp ) ) {
43 0 : FD_LOG_WARNING(( "repair must be part of a workspace" ));
44 0 : return NULL;
45 0 : }
46 :
47 0 : return repair;
48 0 : }
49 :
50 : void *
51 0 : fd_repair_leave( fd_repair_t const * repair ) {
52 :
53 0 : if( FD_UNLIKELY( !repair ) ) {
54 0 : FD_LOG_WARNING(( "NULL repair" ));
55 0 : return NULL;
56 0 : }
57 :
58 0 : return (void *)repair;
59 0 : }
60 :
61 : void *
62 0 : fd_repair_delete( void * repair ) {
63 :
64 0 : if( FD_UNLIKELY( !repair ) ) {
65 0 : FD_LOG_WARNING(( "NULL repair" ));
66 0 : return NULL;
67 0 : }
68 :
69 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)repair, fd_repair_align() ) ) ) {
70 0 : FD_LOG_WARNING(( "misaligned repair" ));
71 0 : return NULL;
72 0 : }
73 :
74 0 : return repair;
75 0 : }
76 :
77 : fd_repair_msg_t *
78 0 : fd_repair_pong( fd_repair_t * repair, fd_hash_t * ping_token ) {
79 0 : uchar pre_image[FD_REPAIR_PONG_PREIMAGE_SZ];
80 0 : memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
81 0 : memcpy( pre_image+16UL, ping_token->uc, 32UL );
82 :
83 : /* Generate response hash token */
84 0 : fd_sha256_hash( pre_image, FD_REPAIR_PONG_PREIMAGE_SZ, &repair->msg.pong.hash );
85 :
86 0 : repair->msg.kind = FD_REPAIR_KIND_PONG;
87 0 : repair->msg.pong.from = repair->identity_key;
88 0 : return &repair->msg;
89 0 : }
90 :
91 : fd_repair_msg_t *
92 : fd_repair_shred( fd_repair_t * repair,
93 : fd_pubkey_t const * to,
94 : ulong ts,
95 : uint nonce,
96 : ulong slot,
97 0 : ulong shred_idx ) {
98 0 : memset(&repair->msg, 0, sizeof(fd_repair_msg_t));
99 0 : repair->msg.kind = FD_REPAIR_KIND_SHRED;
100 0 : repair->msg.shred.from = repair->identity_key;
101 0 : repair->msg.shred.to = *to;
102 0 : repair->msg.shred.ts = ts;
103 0 : repair->msg.shred.nonce = nonce;
104 0 : repair->msg.shred.slot = slot;
105 0 : repair->msg.shred.shred_idx = shred_idx;
106 0 : return &repair->msg;
107 0 : }
108 :
109 : fd_repair_msg_t *
110 : fd_repair_highest_shred( fd_repair_t * repair,
111 : fd_pubkey_t const * to,
112 : ulong ts,
113 : uint nonce,
114 : ulong slot,
115 0 : ulong shred_idx ) {
116 0 : memset(&repair->msg, 0, sizeof(fd_repair_msg_t));
117 0 : repair->msg.kind = FD_REPAIR_KIND_HIGHEST_SHRED;
118 0 : repair->msg.highest_shred.from = repair->identity_key;
119 0 : repair->msg.highest_shred.to = *to;
120 0 : repair->msg.highest_shred.ts = ts;
121 0 : repair->msg.highest_shred.nonce = nonce;
122 0 : repair->msg.highest_shred.slot = slot;
123 0 : repair->msg.highest_shred.shred_idx = shred_idx;
124 0 : return &repair->msg;
125 0 : }
126 :
127 : fd_repair_msg_t *
128 : fd_repair_orphan( fd_repair_t * repair,
129 : fd_pubkey_t const * to,
130 : ulong ts,
131 : uint nonce,
132 0 : ulong slot ) {
133 0 : memset(&repair->msg, 0, sizeof(fd_repair_msg_t));
134 0 : repair->msg.kind = FD_REPAIR_KIND_ORPHAN;
135 0 : repair->msg.orphan.from = repair->identity_key;
136 0 : repair->msg.orphan.to = *to;
137 0 : repair->msg.orphan.ts = ts;
138 0 : repair->msg.orphan.nonce = nonce;
139 0 : repair->msg.orphan.slot = slot;
140 0 : return &repair->msg;
141 0 : }
142 :
143 : fd_repair_msg_t *
144 : ag_repair_parent_and_fec_set_count( fd_repair_t * repair,
145 : fd_pubkey_t const * to,
146 : ulong ts,
147 : uint nonce,
148 : ulong slot,
149 0 : fd_hash_t const * block_id ) {
150 0 : memset(&repair->msg, 0, sizeof(fd_repair_msg_t));
151 0 : repair->msg.kind = AG_REPAIR_KIND_PARENT_FEC_COUNT;
152 0 : repair->msg.parent_fec_set_count.from = repair->identity_key;
153 0 : repair->msg.parent_fec_set_count.to = *to;
154 0 : repair->msg.parent_fec_set_count.ts = ts;
155 0 : repair->msg.parent_fec_set_count.nonce = nonce;
156 0 : repair->msg.parent_fec_set_count.slot = slot;
157 0 : repair->msg.parent_fec_set_count.block_id = *block_id;
158 0 : return &repair->msg;
159 0 : }
160 :
161 : fd_repair_msg_t *
162 : ag_repair_fec_set_root( fd_repair_t * repair,
163 : fd_pubkey_t const * to,
164 : ulong ts,
165 : uint nonce,
166 : ulong slot,
167 : fd_hash_t const * block_id,
168 0 : uint fec_set_idx ) {
169 0 : memset(&repair->msg, 0, sizeof(fd_repair_msg_t));
170 0 : repair->msg.kind = AG_REPAIR_KIND_FEC_ROOT;
171 0 : repair->msg.fec_set_root.from = repair->identity_key;
172 0 : repair->msg.fec_set_root.to = *to;
173 0 : repair->msg.fec_set_root.ts = ts;
174 0 : repair->msg.fec_set_root.nonce = nonce;
175 0 : repair->msg.fec_set_root.slot = slot;
176 0 : repair->msg.fec_set_root.block_id = *block_id;
177 0 : repair->msg.fec_set_root.fec_set_idx = fec_set_idx;
178 0 : return &repair->msg;
179 0 : }
180 :
181 : fd_repair_msg_t *
182 : ag_repair_shred_block_id( fd_repair_t * repair,
183 : fd_pubkey_t const * to,
184 : ulong ts,
185 : uint nonce,
186 : ulong slot,
187 : fd_hash_t const * block_id,
188 0 : uint shred_idx ) {
189 0 : memset(&repair->msg, 0, sizeof(fd_repair_msg_t));
190 0 : repair->msg.kind = AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID;
191 0 : repair->msg.shred_block_id.from = repair->identity_key;
192 0 : repair->msg.shred_block_id.to = *to;
193 0 : repair->msg.shred_block_id.ts = ts;
194 0 : repair->msg.shred_block_id.nonce = nonce;
195 0 : repair->msg.shred_block_id.slot = slot;
196 0 : repair->msg.shred_block_id.shred_idx = shred_idx;
197 0 : repair->msg.shred_block_id.block_id = *block_id;
198 0 : return &repair->msg;
199 0 : }
200 :
201 : int
202 : ag_repair_response_de( ag_repair_response_t * response,
203 : uchar const * buf,
204 : ulong buf_sz,
205 0 : ulong fec_set_max ) {
206 0 : uchar const * cur = buf;
207 0 : ulong rem = buf_sz;
208 :
209 0 : if( FD_UNLIKELY( rem<sizeof(uint) ) ) return -1;
210 0 : response->kind = fd_uint_load_4_fast( cur );
211 0 : cur += sizeof(uint); rem -= sizeof(uint);
212 :
213 0 : ulong proof_sz; /* total bytes proof */
214 0 : uchar * proof;
215 :
216 0 : switch( response->kind ) {
217 0 : case AG_REPAIR_RESPONSE_PARENT_FEC_SET_COUNT: {
218 0 : ag_parent_fec_count_res_t * res = &response->parent_fec_set_res;
219 :
220 0 : if( FD_UNLIKELY( rem < sizeof(uint) ) ) return -1;
221 0 : res->fec_set_count = fd_uint_load_4_fast( cur );
222 0 : cur += sizeof(uint); rem -= sizeof(uint);
223 0 : if( FD_UNLIKELY( !res->fec_set_count || res->fec_set_count>fec_set_max ) ) return -1;
224 :
225 0 : if( FD_UNLIKELY( rem < sizeof(ulong) ) ) return -1;
226 0 : res->parent_slot = fd_ulong_load_8_fast( cur );
227 0 : cur += sizeof(ulong); rem -= sizeof(ulong);
228 :
229 0 : if( FD_UNLIKELY( rem < sizeof(fd_hash_t) ) ) return -1;
230 0 : memcpy( res->parent_block_id.uc, cur, sizeof(fd_hash_t) );
231 0 : cur += sizeof(fd_hash_t); rem -= sizeof(fd_hash_t);
232 :
233 0 : if( FD_UNLIKELY( rem < sizeof(ulong) ) ) return -1;
234 0 : proof_sz = fd_ulong_load_8_fast( cur );
235 0 : cur += sizeof(ulong); rem -= sizeof(ulong);
236 :
237 0 : res->proof_len = proof_sz / FD_SHRED_MERKLE_NODE_SZ;
238 0 : proof = res->parent_proof[0];
239 0 : break;
240 0 : }
241 :
242 0 : case AG_REPAIR_RESPONSE_FEC_SET_ROOT: {
243 0 : ag_fec_root_res_t * res = &response->fec_set_root;
244 :
245 0 : if( FD_UNLIKELY( rem < FD_SHRED_MERKLE_NODE_SZ ) ) return -1;
246 0 : memcpy( res->root, cur, FD_SHRED_MERKLE_NODE_SZ );
247 0 : cur += FD_SHRED_MERKLE_NODE_SZ; rem -= FD_SHRED_MERKLE_NODE_SZ;
248 :
249 0 : if( FD_UNLIKELY( rem < sizeof(ulong) ) ) return -1;
250 0 : proof_sz = fd_ulong_load_8_fast( cur );
251 0 : cur += sizeof(ulong); rem -= sizeof(ulong);
252 :
253 0 : res->proof_len = proof_sz / FD_SHRED_MERKLE_NODE_SZ;
254 0 : proof = res->fec_proof[0];
255 0 : break;
256 0 : }
257 0 : default: return -1;
258 0 : }
259 :
260 0 : if( FD_UNLIKELY( proof_sz % FD_SHRED_MERKLE_NODE_SZ ) ) return -1;
261 0 : if( FD_UNLIKELY( proof_sz > AG_MAX_FEC_PROOF_NODE_CNT*FD_SHRED_MERKLE_NODE_SZ ) ) return -1;
262 0 : if( FD_UNLIKELY( rem != proof_sz+sizeof(uint) ) ) return -1; /* proof + trailing nonce, nothing more */
263 :
264 0 : memcpy( proof, cur, proof_sz );
265 0 : cur += proof_sz;
266 :
267 0 : response->nonce = fd_uint_load_4_fast( cur );
268 0 : return 0;
269 0 : }
270 :
271 : /* verify_merkle_proof reconstructs the double-merkle root from leaf at
272 : leaf_idx + proof and checks it equals block_id. 0 on match, -1 else. */
273 :
274 : static int
275 : verify_merkle_proof( fd_bmtree_node_t * leaf,
276 : ulong leaf_idx,
277 : uchar const * proof,
278 : ulong proof_len,
279 0 : fd_hash_t const * block_id ) {
280 0 : fd_bmtree_node_t root[1];
281 0 : if( FD_UNLIKELY( !fd_bmtree_from_proof( leaf, leaf_idx, root, proof, proof_len, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ ) ) ) return -1;
282 0 : if( FD_UNLIKELY( 0!=memcmp( root->hash, block_id->uc, sizeof(fd_hash_t) ) ) ) return -1;
283 0 : return 0;
284 0 : }
285 :
286 : int
287 : ag_repair_parent_fec_count_verify( ag_parent_fec_count_res_t const * res,
288 0 : fd_hash_t const * block_id ) {
289 : /* The parent-info leaf is the last of the tree's fec_set_count+1
290 : leaves, so the proof must be exactly as deep as that tree. */
291 0 : if( FD_UNLIKELY( res->proof_len != fd_bmtree_depth( res->fec_set_count+1UL )-1UL ) ) return -1;
292 :
293 0 : fd_bmtree_node_t leaf[1];
294 0 : fd_sha256_t sha[1];
295 0 : fd_sha256_init ( sha );
296 0 : fd_sha256_append( sha, &res->parent_slot, sizeof(ulong) );
297 0 : fd_sha256_append( sha, res->parent_block_id.uc, sizeof(fd_hash_t) );
298 0 : fd_sha256_append( sha, &res->fec_set_count, sizeof(uint) );
299 0 : fd_sha256_fini ( sha, leaf->hash );
300 :
301 0 : return verify_merkle_proof( leaf, res->fec_set_count, res->parent_proof[0], res->proof_len, block_id );
302 0 : }
303 :
304 : int
305 : ag_repair_fec_set_root_verify( ag_fec_root_res_t const * res,
306 : fd_hash_t const * block_id,
307 : uint fec_set_idx,
308 0 : uint fec_set_count ) {
309 : /* https://github.com/anza-xyz/agave/blob/5f11d68ab206fee323e8d29acd96d517fae3c9ec/core/src/repair/serve_repair.rs#L381 */
310 0 : if( FD_UNLIKELY( !fec_set_count ) ) return -1;
311 0 : if( FD_UNLIKELY( fec_set_idx / FD_FEC_SHRED_CNT >= fec_set_count ) ) return -1;
312 0 : if( FD_UNLIKELY( res->proof_len != fd_bmtree_depth( fec_set_count+1UL )-1UL ) ) return -1;
313 :
314 0 : fd_bmtree_node_t leaf[1] = {0};
315 0 : memcpy( leaf->hash, res->root, FD_SHRED_MERKLE_NODE_SZ );
316 :
317 0 : return verify_merkle_proof( leaf, fec_set_idx / FD_FEC_SHRED_CNT, res->fec_proof[0], res->proof_len, block_id );
318 0 : }
319 :
320 : ulong
321 : ag_repair_parent_fec_count_ser( uchar * buf,
322 : ulong buf_sz,
323 : uint fec_set_count,
324 : ulong parent_slot,
325 : fd_hash_t const * parent_block_id,
326 : uchar const * proof,
327 : ulong proof_len,
328 0 : uint nonce ) {
329 0 : ulong proof_sz = fd_ulong_sat_mul( proof_len, FD_SHRED_MERKLE_NODE_SZ );
330 0 : if( FD_UNLIKELY( proof_sz==ULONG_MAX )) return 0UL;
331 :
332 0 : ulong sz = fd_ulong_sat_add( sizeof(uint) + sizeof(uint) + sizeof(ulong) + sizeof(fd_hash_t) + sizeof(ulong) + sizeof(uint),
333 0 : proof_sz );
334 0 : if( FD_UNLIKELY( sz>buf_sz )) return 0UL;
335 :
336 0 : uchar * cur = buf;
337 0 : FD_STORE( uint, cur, AG_REPAIR_RESPONSE_PARENT_FEC_SET_COUNT ); cur += sizeof(uint);
338 0 : FD_STORE( uint, cur, fec_set_count ); cur += sizeof(uint);
339 0 : FD_STORE( ulong, cur, parent_slot ); cur += sizeof(ulong);
340 0 : memcpy( cur, parent_block_id->uc, sizeof(fd_hash_t) ); cur += sizeof(fd_hash_t);
341 0 : FD_STORE( ulong, cur, proof_sz ); cur += sizeof(ulong);
342 0 : memcpy( cur, proof, proof_sz ); cur += proof_sz;
343 0 : FD_STORE( uint, cur, nonce ); cur += sizeof(uint);
344 0 : return sz;
345 0 : }
346 :
347 : ulong
348 : ag_repair_fec_set_root_ser( uchar * buf,
349 : ulong buf_sz,
350 : uchar const * root,
351 : uchar const * proof,
352 : ulong proof_len,
353 0 : uint nonce ) {
354 0 : ulong proof_sz = fd_ulong_sat_mul( proof_len, FD_SHRED_MERKLE_NODE_SZ );
355 0 : if( FD_UNLIKELY( proof_sz == ULONG_MAX )) return 0UL;
356 :
357 0 : ulong sz = fd_ulong_sat_add( sizeof(uint) + FD_SHRED_MERKLE_NODE_SZ + sizeof(ulong) + sizeof(uint),
358 0 : proof_sz );
359 0 : if( FD_UNLIKELY( sz>buf_sz ) ) return 0UL;
360 :
361 0 : uchar * cur = buf;
362 0 : FD_STORE( uint, cur, AG_REPAIR_RESPONSE_FEC_SET_ROOT ); cur += sizeof(uint);
363 0 : memcpy( cur, root, FD_SHRED_MERKLE_NODE_SZ ); cur += FD_SHRED_MERKLE_NODE_SZ;
364 0 : FD_STORE( ulong, cur, proof_sz ); cur += sizeof(ulong);
365 0 : memcpy( cur, proof, proof_sz ); cur += proof_sz;
366 0 : FD_STORE( uint, cur, nonce ); cur += sizeof(uint);
367 0 : return sz;
368 0 : }
369 :
370 : int
371 : fd_repair_ping_de( fd_repair_ping_t * ping,
372 : uchar const * buf,
373 0 : ulong buf_sz ) {
374 0 : if( FD_UNLIKELY( buf_sz!=sizeof(fd_repair_ping_t) )) return -1;
375 :
376 0 : ping->kind = fd_uint_load_4_fast( buf );
377 0 : buf += sizeof(uint);
378 :
379 0 : if( FD_UNLIKELY( ping->kind != FD_REPAIR_KIND_PING && ping->kind != AG_REPAIR_KIND_PING )) return -1;
380 :
381 : /* pong section */
382 :
383 0 : memcpy( &ping->ping.from, buf, sizeof(fd_pubkey_t) );
384 0 : buf += sizeof(fd_pubkey_t);
385 :
386 0 : memcpy( &ping->ping.hash, buf, sizeof(fd_hash_t) );
387 0 : buf += sizeof(fd_hash_t);
388 :
389 0 : memcpy( ping->ping.sig, buf, sizeof(fd_ed25519_sig_t) );
390 0 : buf += sizeof(fd_ed25519_sig_t);
391 0 : return 0;
392 0 : }
393 :
394 : int
395 : fd_repair_ping_ser( fd_repair_ping_t const * ping,
396 : uchar buf[static sizeof(fd_repair_ping_t)],
397 0 : ulong buf_sz ) {
398 0 : ulong off = 0;
399 0 : if( FD_UNLIKELY( buf_sz!=sizeof(fd_repair_ping_t) )) return -1;
400 :
401 0 : FD_STORE( uint, buf+off, ping->kind );
402 0 : off += sizeof(uint);
403 :
404 0 : FD_STORE( fd_pubkey_t, buf+off, ping->ping.from );
405 0 : off += sizeof(fd_pubkey_t);
406 :
407 0 : FD_STORE( fd_hash_t, buf+off, ping->ping.hash );
408 0 : off += sizeof(fd_hash_t);
409 :
410 0 : memcpy( buf+off, ping->ping.sig, sizeof(fd_ed25519_sig_t) );
411 0 : off += sizeof(fd_ed25519_sig_t);
412 :
413 0 : return 0;
414 0 : }
|