Line data Source code
1 : #include "fd_shredder.h"
2 : #include "../../ballet/shred/fd_shred.h"
3 :
4 : void *
5 : fd_shredder_new( void * mem,
6 : fd_shredder_sign_fn * signer,
7 54 : void * signer_ctx ) {
8 54 : fd_shredder_t * shredder = (fd_shredder_t *)mem;
9 :
10 54 : if( FD_UNLIKELY( !mem ) ) {
11 0 : FD_LOG_WARNING(( "NULL shredder memory" ));
12 0 : return NULL;
13 0 : }
14 :
15 54 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_shredder_align() ) ) ) {
16 0 : FD_LOG_WARNING(( "misaligned shredder memory" ));
17 0 : return NULL;
18 0 : }
19 :
20 54 : shredder->shred_version = 0;
21 54 : shredder->entry_batch = NULL;
22 54 : shredder->sz = 0UL;
23 54 : shredder->offset = 0UL;
24 :
25 54 : fd_memset( &(shredder->meta), 0, sizeof(fd_entry_batch_meta_t) );
26 54 : shredder->slot = ULONG_MAX;
27 54 : shredder->data_idx_offset = 0UL;
28 54 : shredder->parity_idx_offset = 0UL;
29 :
30 54 : shredder->signer = signer;
31 54 : shredder->signer_ctx = signer_ctx;
32 :
33 54 : FD_COMPILER_MFENCE();
34 54 : FD_VOLATILE( shredder->magic ) = FD_SHREDDER_MAGIC;
35 54 : FD_COMPILER_MFENCE();
36 :
37 54 : return (void *)shredder;
38 54 : }
39 :
40 : fd_shredder_t *
41 54 : fd_shredder_join( void * mem ) {
42 54 : if( FD_UNLIKELY( !mem ) ) {
43 0 : FD_LOG_WARNING(( "NULL shredder memory" ));
44 0 : return NULL;
45 0 : }
46 :
47 54 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_shredder_align() ) ) ) {
48 0 : FD_LOG_WARNING(( "misaligned shredder memory" ));
49 0 : return NULL;
50 0 : }
51 :
52 54 : fd_shredder_t * shredder = (fd_shredder_t *)mem;
53 :
54 54 : if( FD_UNLIKELY( shredder->magic!=FD_SHREDDER_MAGIC ) ) {
55 0 : FD_LOG_WARNING(( "bad magic" ));
56 0 : return NULL;
57 0 : }
58 :
59 54 : return shredder;
60 54 : }
61 :
62 : void *
63 0 : fd_shredder_leave( fd_shredder_t * shredder ) {
64 0 : return (void *)shredder;
65 0 : }
66 :
67 : void *
68 0 : fd_shredder_delete( void * mem ) {
69 0 : fd_shredder_t * shredder = (fd_shredder_t *)mem;
70 :
71 0 : if( FD_UNLIKELY( shredder->magic!=FD_SHREDDER_MAGIC ) ) {
72 0 : FD_LOG_WARNING(( "bad magic" ));
73 0 : return NULL;
74 0 : }
75 :
76 0 : FD_COMPILER_MFENCE();
77 0 : FD_VOLATILE( shredder->magic ) = 0UL;
78 0 : FD_COMPILER_MFENCE();
79 :
80 0 : return (void *)shredder;
81 0 : }
82 :
83 :
84 : fd_shredder_t *
85 : fd_shredder_skip_batch( fd_shredder_t * shredder,
86 : ulong entry_batch_sz,
87 : ulong slot,
88 180 : int block_complete ) {
89 :
90 180 : if( FD_UNLIKELY( entry_batch_sz==0UL ) ) return NULL;
91 :
92 180 : if( FD_UNLIKELY( slot != shredder->slot ) ) {
93 45 : shredder->data_idx_offset = 0UL;
94 45 : shredder->parity_idx_offset = 0UL;
95 45 : }
96 :
97 180 : ulong data_shred_cnt = fd_shredder_count_data_shreds( entry_batch_sz, block_complete );
98 180 : ulong parity_shred_cnt = fd_shredder_count_parity_shreds( entry_batch_sz, block_complete );
99 :
100 180 : shredder->data_idx_offset += data_shred_cnt;
101 180 : shredder->parity_idx_offset += parity_shred_cnt;
102 180 : shredder->slot = slot;
103 :
104 180 : return shredder;
105 180 : }
106 :
107 :
108 : fd_shredder_t *
109 : fd_shredder_init_batch( fd_shredder_t * shredder,
110 : void const * entry_batch,
111 : ulong entry_batch_sz,
112 : ulong slot,
113 600663 : fd_entry_batch_meta_t const * metadata ) {
114 :
115 600663 : if( FD_UNLIKELY( entry_batch_sz==0UL ) ) return NULL; /* FIXME: should this warn? Silently expand it to 1 byte? */
116 :
117 600663 : shredder->entry_batch = entry_batch;
118 600663 : shredder->sz = entry_batch_sz;
119 600663 : shredder->offset = 0UL;
120 :
121 600663 : if( FD_UNLIKELY( slot != shredder->slot ) ) {
122 600072 : shredder->data_idx_offset = 0UL;
123 600072 : shredder->parity_idx_offset = 0UL;
124 600072 : }
125 :
126 600663 : shredder->slot = slot;
127 600663 : shredder->meta = *metadata;
128 :
129 600663 : return shredder;
130 600663 : }
131 :
132 :
133 : fd_fec_set_t *
134 : fd_shredder_next_fec_set( fd_shredder_t * shredder,
135 : fd_fec_set_t * result,
136 2014905 : uchar * chained_merkle_root ) {
137 2014905 : uchar const * entry_batch = shredder->entry_batch;
138 2014905 : ulong offset = shredder->offset;
139 2014905 : ulong entry_sz = shredder->sz;
140 :
141 2014905 : fd_ed25519_sig_t __attribute__((aligned(32UL))) root_signature;
142 :
143 2014905 : if( FD_UNLIKELY( (offset==entry_sz) ) ) return NULL;
144 :
145 1414911 : ulong entry_bytes_remaining = entry_sz - offset;
146 :
147 : /* Set the shred type */
148 1414911 : int block_complete = !!shredder->meta.block_complete;
149 1414911 : int is_chained = 1;
150 1414911 : int is_resigned = block_complete & (entry_bytes_remaining<=FD_SHREDDER_RESIGNED_FEC_SET_PAYLOAD_SZ);
151 :
152 1414911 : uchar data_type = fd_uchar_if( is_resigned, FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED, FD_SHRED_TYPE_MERKLE_DATA_CHAINED );
153 1414911 : uchar code_type = fd_uchar_if( is_resigned, FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED, FD_SHRED_TYPE_MERKLE_CODE_CHAINED );
154 :
155 : /* how many total payload bytes in this FEC set? */
156 1414911 : ulong chunk_size;
157 1414911 : if( FD_LIKELY( !block_complete ) ) { /* the easy case */
158 751089 : chunk_size = fd_ulong_min( entry_bytes_remaining, FD_SHREDDER_CHAINED_FEC_SET_PAYLOAD_SZ );
159 751089 : } else if( FD_UNLIKELY( is_resigned ) ) {
160 300021 : chunk_size = fd_ulong_min( entry_bytes_remaining, FD_SHREDDER_RESIGNED_FEC_SET_PAYLOAD_SZ );
161 363801 : } else {
162 : /* Save room for the resigned batch. The difference is > 0 because
163 : if it weren't, we would have taken the is_resigned case. */
164 363801 : chunk_size = fd_ulong_min( entry_bytes_remaining-FD_SHREDDER_RESIGNED_FEC_SET_PAYLOAD_SZ, FD_SHREDDER_CHAINED_FEC_SET_PAYLOAD_SZ );
165 363801 : }
166 1414911 : ulong last_in_batch = (chunk_size+offset==entry_sz);
167 1414911 : ulong adj_entry_sz = offset+chunk_size;
168 :
169 1414911 : ulong data_shred_cnt = FD_FEC_SHRED_CNT;
170 1414911 : ulong parity_shred_cnt = FD_FEC_SHRED_CNT;
171 : /* Our notion of tree depth counts the root, while the shred version
172 : doesn't. */
173 1414911 : ulong tree_depth = fd_bmtree_depth( data_shred_cnt+parity_shred_cnt )-1UL;
174 1414911 : ulong data_shred_payload_sz = 1115UL - 20UL*tree_depth - 32UL*(ulong)is_chained - 64UL*(ulong)is_resigned;
175 1414911 : ulong parity_shred_payload_sz = data_shred_payload_sz + FD_SHRED_DATA_HEADER_SZ - FD_SHRED_SIGNATURE_SZ;
176 1414911 : ulong data_merkle_sz = parity_shred_payload_sz + 32UL*(ulong)is_chained;
177 1414911 : ulong parity_merkle_sz = data_merkle_sz + FD_SHRED_CODE_HEADER_SZ - FD_SHRED_SIGNATURE_SZ;
178 :
179 1414911 : fd_reedsol_t * reedsol = fd_reedsol_encode_init( shredder->reedsol, parity_shred_payload_sz );
180 :
181 : /* Write headers and copy the data shred payload */
182 1414911 : ulong flags_for_last = ((last_in_batch & (ulong)block_complete)<<7) | (last_in_batch<<6);
183 46692063 : for( ulong i=0UL; i<data_shred_cnt; i++ ) {
184 45277152 : fd_shred_t * shred = result->data_shreds[ i ].s;
185 :
186 : /* Size in bytes of the payload section of this data shred,
187 : excluding any zero-padding */
188 45277152 : ulong shred_payload_sz = fd_ulong_min( adj_entry_sz-offset, data_shred_payload_sz );
189 :
190 45277152 : shred->variant = fd_shred_variant( data_type, (uchar)tree_depth );
191 45277152 : shred->slot = shredder->slot;
192 45277152 : shred->idx = (uint )(shredder->data_idx_offset + i);
193 45277152 : shred->version = (ushort)(shredder->shred_version);
194 45277152 : shred->fec_set_idx = (uint )(shredder->data_idx_offset);
195 45277152 : shred->data.parent_off = (ushort)(shredder->meta.parent_offset);
196 45277152 : shred->data.flags = (uchar )(fd_ulong_if( i==data_shred_cnt-1UL, flags_for_last, 0UL ) | (shredder->meta.reference_tick & 0x3FUL));
197 45277152 : shred->data.size = (ushort)(FD_SHRED_DATA_HEADER_SZ + shred_payload_sz);
198 :
199 45277152 : uchar * payload = fd_memcpy( result->data_shreds[ i ].b + FD_SHRED_DATA_HEADER_SZ , entry_batch+offset, shred_payload_sz );
200 45277152 : offset += shred_payload_sz;
201 :
202 : /* Write zero-padding, possibly a no-op */
203 45277152 : fd_memset( payload+shred_payload_sz, 0, data_shred_payload_sz-shred_payload_sz );
204 :
205 : /* Set the last bytes of the signature field to the Merkle tree
206 : prefix so we can use the faster batch sha256 API to compute the
207 : Merkle tree */
208 45277152 : fd_memcpy( shred->signature + 64UL - 26UL, "\x00SOLANA_MERKLE_SHREDS_LEAF", 26UL );
209 :
210 : /* Prepare to generate parity data: data shred starts right after
211 : signature and goes until start of Merkle proof. */
212 45277152 : fd_reedsol_encode_add_data_shred( reedsol, ((uchar*)shred) + sizeof(fd_ed25519_sig_t) );
213 :
214 : /* Set chained merkle root */
215 45277152 : uchar * merkle = ((uchar*)shred) + fd_shred_chain_off( shred->variant );
216 45277152 : memcpy( merkle, chained_merkle_root, FD_SHRED_MERKLE_ROOT_SZ );
217 45277152 : }
218 :
219 46692063 : for( ulong j=0UL; j<parity_shred_cnt; j++ ) {
220 45277152 : fd_shred_t * shred = result->parity_shreds[ j ].s;
221 45277152 : shred->variant = fd_shred_variant( code_type, (uchar)tree_depth );
222 45277152 : shred->slot = shredder->slot;
223 45277152 : shred->idx = (uint )(shredder->parity_idx_offset + j);
224 45277152 : shred->version = (ushort)(shredder->shred_version);
225 45277152 : shred->fec_set_idx = (uint )(shredder->data_idx_offset);
226 45277152 : shred->code.data_cnt = (ushort)(data_shred_cnt);
227 45277152 : shred->code.code_cnt = (ushort)(parity_shred_cnt);
228 45277152 : shred->code.idx = (ushort)(j);
229 :
230 45277152 : fd_memcpy( shred->signature + 64UL - 26UL, "\x00SOLANA_MERKLE_SHREDS_LEAF", 26UL );
231 :
232 : /* Prepare to generate parity data: parity info starts right after
233 : signature and goes until start of Merkle proof. */
234 45277152 : fd_reedsol_encode_add_parity_shred( reedsol, result->parity_shreds[ j ].b + FD_SHRED_CODE_HEADER_SZ );
235 :
236 : /* Set chained merkle root */
237 45277152 : uchar * merkle = ((uchar*)shred) + fd_shred_chain_off( shred->variant );
238 45277152 : memcpy( merkle, chained_merkle_root, FD_SHRED_MERKLE_ROOT_SZ );
239 45277152 : }
240 :
241 : /* Generate parity data */
242 1414911 : fd_reedsol_encode_fini( reedsol );
243 :
244 : /* Generate Merkle leaves */
245 1414911 : fd_sha256_batch_t * sha256 = fd_sha256_batch_init( shredder->sha256 );
246 1414911 : fd_bmtree_node_t * leaves = shredder->bmtree_leaves;
247 :
248 46692063 : for( ulong i=0UL; i<data_shred_cnt; i++ )
249 45277152 : fd_sha256_batch_add( sha256, result->data_shreds[i].b+sizeof(fd_ed25519_sig_t)-26UL, data_merkle_sz+26UL, leaves[i].hash );
250 46692063 : for( ulong j=0UL; j<parity_shred_cnt; j++ )
251 45277152 : fd_sha256_batch_add( sha256, result->parity_shreds[j].b+sizeof(fd_ed25519_sig_t)-26UL, parity_merkle_sz+26UL, leaves[j+data_shred_cnt].hash );
252 1414911 : fd_sha256_batch_fini( sha256 );
253 :
254 : /* Generate Merkle Proofs */
255 1414911 : fd_bmtree_commit_t * bmtree = fd_bmtree_commit_init( shredder->_bmtree_footprint, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ, tree_depth+1UL );
256 1414911 : fd_bmtree_commit_append( bmtree, leaves, data_shred_cnt+parity_shred_cnt );
257 1414911 : uchar * root = fd_bmtree_commit_fini( bmtree );
258 :
259 : /* Sign Merkle Root */
260 1414911 : shredder->signer( shredder->signer_ctx, root_signature, root );
261 :
262 : /* Write signature and Merkle proof */
263 46692063 : for( ulong i=0UL; i<data_shred_cnt; i++ ) {
264 45277152 : fd_shred_t * shred = result->data_shreds[ i ].s;
265 45277152 : fd_memcpy( shred->signature, root_signature, FD_ED25519_SIG_SZ );
266 :
267 45277152 : uchar * merkle = result->data_shreds[ i ].b + fd_shred_merkle_off( shred );
268 45277152 : fd_bmtree_get_proof( bmtree, merkle, i );
269 :
270 : /* Agave doesn't seem to set the rentransmitter signature when the shred is first created,
271 : i.e. the leader sends shreds with rentransmitter signature set to 0.
272 : https://github.com/anza-xyz/agave/blob/v2.2.10/ledger/src/shred/merkle.rs#L1417-L1418 */
273 45277152 : if( FD_UNLIKELY( is_resigned ) ) {
274 9600672 : memset( ((uchar*)shred) + fd_shred_retransmitter_sig_off( shred ), 0, 64UL );
275 9600672 : }
276 45277152 : }
277 :
278 46692063 : for( ulong j=0UL; j<parity_shred_cnt; j++ ) {
279 45277152 : fd_shred_t * shred = result->parity_shreds[ j ].s;
280 45277152 : fd_memcpy( shred->signature, root_signature, FD_ED25519_SIG_SZ );
281 :
282 45277152 : uchar * merkle = result->parity_shreds[ j ].b + fd_shred_merkle_off( shred );
283 45277152 : fd_bmtree_get_proof( bmtree, merkle, data_shred_cnt+j );
284 :
285 45277152 : if( FD_UNLIKELY( is_resigned ) ) {
286 9600672 : memset( ((uchar*)shred) + fd_shred_retransmitter_sig_off( shred ), 0, 64UL );
287 9600672 : }
288 45277152 : }
289 :
290 1414911 : shredder->offset = offset;
291 1414911 : shredder->data_idx_offset += data_shred_cnt;
292 1414911 : shredder->parity_idx_offset += parity_shred_cnt;
293 :
294 1414911 : memcpy( chained_merkle_root, root, FD_SHRED_MERKLE_ROOT_SZ );
295 :
296 1414911 : return result;
297 2014905 : }
298 :
299 600651 : fd_shredder_t * fd_shredder_fini_batch( fd_shredder_t * shredder ) {
300 600651 : shredder->entry_batch = NULL;
301 600651 : shredder->sz = 0UL;
302 600651 : shredder->offset = 0UL;
303 :
304 600651 : return shredder;
305 600651 : }
|