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