Line data Source code
1 : #include "fd_block_marker_serde.h"
2 :
3 : #include "../../ballet/txn/fd_compact_u16.h"
4 :
5 20943 : #define FAIL( cond, err ) do { if( FD_UNLIKELY( cond ) ) return FD_BLOCK_MARKER_DE_ERR_##err; } while( 0 )
6 :
7 : /* BlockComponent: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L486-L488
8 : VersionedBlockMarker: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L435-L438
9 : LengthPrefixed: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L172-L175 */
10 :
11 : struct marker_serde {
12 : ulong entry_cnt; /* BlockComponent::BlockMarker (u64, always 0) */
13 : ushort version; /* VersionedBlockMarker::V1 (u16 tag) */
14 : uchar tag; /* BlockMarkerV1 (u8 tag) */
15 : ushort length; /* LengthPrefixed<VersionedBlock*>::len (u16) */
16 : uchar const * payload; /* LengthPrefixed<VersionedBlock*>::inner (length bytes) */
17 : };
18 : typedef struct marker_serde marker_serde_t;
19 :
20 : /* VersionedBlockHeader: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L364-L367
21 : BlockHeaderV1: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L251-L254 */
22 :
23 : struct header_serde {
24 : uchar version; /* VersionedBlockHeader::V1 (u8 tag, #[wincode(tag = 1)]) */
25 : ulong parent_slot; /* BlockHeaderV1::parent_slot (Slot) */
26 : uchar const * parent_block_id; /* BlockHeaderV1::parent_block_id (Hash) */
27 : };
28 : typedef struct header_serde header_serde_t;
29 :
30 : /* VersionedUpdateParent: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L371-L374
31 : UpdateParentV1: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L257-L260 */
32 :
33 : struct update_parent_serde {
34 : uchar version; /* VersionedUpdateParent::V1 (u8 tag, #[wincode(tag = 1)]) */
35 : ulong new_parent_slot; /* UpdateParentV1::new_parent_slot (Slot) */
36 : uchar const * new_parent_block_id; /* UpdateParentV1::new_parent_block_id (Hash) */
37 : };
38 : typedef struct update_parent_serde update_parent_serde_t;
39 :
40 : /* solana_signer_store base2 bitmap: one byte version tag, u16 bit count,
41 : then the bits packed LSB first, bit i naming rank i. Only base2 goes
42 : in a footer; base3 pairs are a votor wire shape (ag_bls_serde.h). */
43 :
44 : struct bitmap_serde {
45 : uchar version; /* solana_signer_store::Version (u8 tag), base2 */
46 : ushort bit_cnt; /* solana_signer_store::num_bits (u16) */
47 : uchar const * payload; /* solana_signer_store::data_bytes */
48 : ulong payload_sz; /* bit_cnt rounded up to a byte */
49 : };
50 : typedef struct bitmap_serde bitmap_serde_t;
51 :
52 : /* VotesAggregate: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L324-L328
53 : Not the wire framing in ag_cert_serde.h: the signature is compressed
54 : and the bitmap sits under a u16 byte count rather than a u64. */
55 :
56 : struct votes_aggregate_serde {
57 : uchar const * signature; /* VotesAggregate::signature (BLSSignatureCompressed) */
58 : ushort bitmap_sz; /* VotesAggregate::bitmap len (FixIntLen<u16>) */
59 : uchar const * bitmap; /* VotesAggregate::bitmap (WincodeVec<u8, FixIntLen<u16>>) */
60 : };
61 : typedef struct votes_aggregate_serde votes_aggregate_serde_t;
62 :
63 : /* BlockFinalizationCert: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L299-L304 */
64 :
65 : struct final_cert_serde {
66 : ulong slot; /* BlockFinalizationCert::slot (Slot) */
67 : uchar const * block_id; /* BlockFinalizationCert::block_id (Hash) */
68 : uchar const * final_aggregate; /* BlockFinalizationCert::final_aggregate (VotesAggregate) */
69 : uchar has_notar_aggregate; /* BlockFinalizationCert::notar_aggregate (Option<VotesAggregate> tag) */
70 : uchar const * notar_aggregate; /* BlockFinalizationCert::notar_aggregate (VotesAggregate, NULL when absent) */
71 : };
72 : typedef struct final_cert_serde final_cert_serde_t;
73 :
74 : /* SkipRewardCertificate: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/votor-messages/src/reward_certificate.rs#L34-L42
75 : The bitmap byte count is a ShortU16, not the aggregate's u16. */
76 :
77 : struct skip_reward_cert_serde {
78 : ulong slot; /* SkipRewardCertificate::slot (Slot) */
79 : uchar const * signature; /* SkipRewardCertificate::signature (BLSSignatureCompressed) */
80 : ushort bitmap_sz; /* SkipRewardCertificate::bitmap len (ShortU16) */
81 : uchar const * bitmap; /* SkipRewardCertificate::bitmap (WincodeVec<u8, ShortU16>) */
82 : };
83 : typedef struct skip_reward_cert_serde skip_reward_cert_serde_t;
84 :
85 : /* NotarRewardCertificate: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/votor-messages/src/reward_certificate.rs#L75-L85 */
86 :
87 : struct notar_reward_cert_serde {
88 : ulong slot; /* NotarRewardCertificate::slot (Slot) */
89 : uchar const * block_id; /* NotarRewardCertificate::block_id (Hash) */
90 : uchar const * signature; /* NotarRewardCertificate::signature (BLSSignatureCompressed) */
91 : ushort bitmap_sz; /* NotarRewardCertificate::bitmap len (ShortU16) */
92 : uchar const * bitmap; /* NotarRewardCertificate::bitmap (WincodeVec<u8, ShortU16>) */
93 : };
94 : typedef struct notar_reward_cert_serde notar_reward_cert_serde_t;
95 :
96 : /* VersionedBlockFooter: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L357-L360
97 : BlockFooterV1: https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/entry/src/block_component.rs#L240-L248
98 : Each Option is a one byte tag immediately followed by its own body
99 : when present, so a cert's length shifts the next tag. */
100 :
101 : struct footer_serde {
102 : uchar version; /* VersionedBlockFooter::V1 (u8 tag, #[wincode(tag = 1)]) */
103 : uchar const * bank_hash; /* BlockFooterV1::bank_hash (Hash) */
104 : ulong block_producer_time_nanos; /* BlockFooterV1::block_producer_time_nanos (u64) */
105 : uchar user_agent_len; /* BlockFooterV1::block_user_agent len (FixIntLen<u8>) */
106 : uchar const * user_agent; /* BlockFooterV1::block_user_agent (WincodeVec<u8, FixIntLen<u8>>) */
107 : uchar has_block_final_cert; /* BlockFooterV1::block_final_cert (Option<BlockFinalizationCert> tag) */
108 : uchar const * block_final_cert; /* BlockFooterV1::block_final_cert (BlockFinalizationCert) */
109 : uchar has_skip_reward_cert; /* BlockFooterV1::skip_reward_cert (Option<SkipRewardCertificate> tag) */
110 : uchar const * skip_reward_cert; /* BlockFooterV1::skip_reward_cert (SkipRewardCertificate) */
111 : uchar has_notar_reward_cert; /* BlockFooterV1::notar_reward_cert (Option<NotarRewardCertificate> tag) */
112 : uchar const * notar_reward_cert; /* BlockFooterV1::notar_reward_cert (NotarRewardCertificate) */
113 : };
114 : typedef struct footer_serde footer_serde_t;
115 :
116 : static ulong
117 : header_ser( fd_block_header_t const * header,
118 30 : uchar * buf ) {
119 30 : header_serde_t hdr;
120 :
121 30 : hdr.version = (uchar)1;
122 30 : hdr.parent_slot = header->parent_slot;
123 30 : hdr.parent_block_id = header->parent_block_id.uc;
124 :
125 30 : ulong off = 0UL;
126 30 : buf[ off ] = hdr.version; off += sizeof(uchar);
127 30 : FD_STORE( ulong, buf+off, hdr.parent_slot ); off += sizeof(ulong);
128 30 : memcpy( buf+off, hdr.parent_block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
129 :
130 30 : return off;
131 30 : }
132 :
133 : /* bitmap_ser writes the base2 bitmap naming the ranks in signer_set,
134 : exactly nbits wide: a footer cert re-emits the width it carries, so
135 : one that came off the wire goes back out byte for byte. Returns the
136 : bytes written, or 0UL if nbits passes AG_VAT_MAX (which sizes
137 : FD_BLOCK_FOOTER_SER_MAX) or a rank sits at or past nbits. */
138 :
139 : static ulong
140 : bitmap_ser( ulong nbits,
141 : fd_bls_set_t const * signer_set,
142 120 : uchar * buf ) {
143 120 : ulong bits = fd_ulong_min( FD_BLS_SET_MAX, fd_bls_set_last( signer_set )+1UL ); /* one past the highest rank, 0 when empty */
144 120 : if( FD_UNLIKELY( nbits>AG_VAT_MAX || bits>nbits ) ) return 0UL;
145 :
146 114 : bitmap_serde_t bm;
147 :
148 114 : bm.version = (uchar)0; /* base2 */
149 114 : bm.bit_cnt = (ushort)nbits;
150 114 : bm.payload = NULL; /* written straight into buf below */
151 114 : bm.payload_sz = FD_BLOCK_BITMAP_SER_SZ( nbits )-FD_BLOCK_BITMAP_SER_HDR_SZ;
152 :
153 114 : ulong off = 0UL;
154 114 : buf[ off ] = bm.version; off += sizeof(uchar);
155 114 : FD_STORE( ushort, buf+off, bm.bit_cnt ); off += sizeof(ushort);
156 114 : fd_memset( buf+off, 0, bm.payload_sz );
157 46374 : for( ulong i=0UL; i<bits; i++ ) {
158 46260 : if( fd_bls_set_test( signer_set, i ) ) buf[ off+(i>>3) ] |= (uchar)( 1U << (i&7U) );
159 46260 : }
160 114 : off += bm.payload_sz;
161 114 : return off;
162 120 : }
163 :
164 : static ulong
165 : votes_aggregate_ser( fd_block_footer_cert_t const * cert,
166 63 : uchar * buf ) {
167 63 : votes_aggregate_serde_t aggregate;
168 :
169 63 : aggregate.signature = cert->sig; /* already compressed */
170 63 : aggregate.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
171 63 : aggregate.bitmap = NULL; /* written straight into buf by bitmap_ser below */
172 :
173 63 : ulong off = 0UL;
174 63 : ulong sz;
175 63 : memcpy( buf+off, aggregate.signature, FD_BLS_SIG_COMPRESSED_SZ ); off += FD_BLS_SIG_COMPRESSED_SZ;
176 63 : FD_STORE( ushort, buf+off, aggregate.bitmap_sz ); off += sizeof(ushort);
177 63 : if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
178 60 : off += sz;
179 60 : return off;
180 63 : }
181 :
182 : static ulong
183 : final_cert_ser( fd_block_footer_t const * footer,
184 45 : uchar * buf ) {
185 45 : int is_fast = footer->has_fast_final_cert;
186 : /* one block_id goes on the wire, so the two certs of a slow
187 : finalization must name one slot */
188 45 : if( FD_UNLIKELY( !is_fast && footer->final_cert.slot!=footer->notar_cert.slot ) ) return 0UL;
189 :
190 42 : final_cert_serde_t cert;
191 :
192 42 : cert.slot = is_fast ? footer->fast_final_cert.slot : footer->final_cert.slot;
193 42 : cert.block_id = is_fast ? footer->fast_final_cert.block_id.uc : footer->notar_cert.block_id.uc;
194 42 : cert.final_aggregate = NULL; /* written straight into buf by votes_aggregate_ser below */
195 42 : cert.has_notar_aggregate = (uchar)!is_fast;
196 42 : cert.notar_aggregate = NULL;
197 :
198 42 : ulong off = 0UL;
199 42 : ulong sz;
200 42 : FD_STORE( ulong, buf+off, cert.slot ); off += sizeof(ulong);
201 42 : memcpy( buf+off, cert.block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
202 42 : if( FD_UNLIKELY( !(sz=votes_aggregate_ser( is_fast ? &footer->fast_final_cert : &footer->final_cert, buf+off )) ) ) return 0UL;
203 39 : off += sz;
204 39 : buf[ off ] = cert.has_notar_aggregate; off += sizeof(uchar);
205 39 : if( cert.has_notar_aggregate ) {
206 21 : if( FD_UNLIKELY( !(sz=votes_aggregate_ser( &footer->notar_cert, buf+off )) ) ) return 0UL;
207 21 : off += sz;
208 21 : }
209 39 : return off;
210 39 : }
211 :
212 : static ulong
213 : skip_reward_cert_ser( fd_block_footer_cert_t const * cert,
214 30 : uchar * buf ) {
215 30 : skip_reward_cert_serde_t reward;
216 :
217 30 : reward.slot = cert->slot;
218 30 : reward.signature = cert->sig; /* already compressed */
219 30 : reward.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
220 30 : reward.bitmap = NULL; /* written straight into buf by bitmap_ser below */
221 :
222 30 : ulong off = 0UL;
223 30 : ulong sz;
224 30 : FD_STORE( ulong, buf+off, reward.slot ); off += sizeof(ulong);
225 30 : memcpy( buf+off, reward.signature, FD_BLS_SIG_COMPRESSED_SZ ); off += FD_BLS_SIG_COMPRESSED_SZ;
226 30 : off += fd_cu16_enc( reward.bitmap_sz, buf+off );
227 30 : if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
228 27 : off += sz;
229 27 : return off;
230 30 : }
231 :
232 : static ulong
233 : notar_reward_cert_ser( fd_block_footer_cert_t const * cert,
234 27 : uchar * buf ) {
235 27 : notar_reward_cert_serde_t reward;
236 :
237 27 : reward.slot = cert->slot;
238 27 : reward.block_id = cert->block_id.uc;
239 27 : reward.signature = cert->sig; /* already compressed */
240 27 : reward.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
241 27 : reward.bitmap = NULL; /* written straight into buf by bitmap_ser below */
242 :
243 27 : ulong off = 0UL;
244 27 : ulong sz;
245 27 : FD_STORE( ulong, buf+off, reward.slot ); off += sizeof(ulong);
246 27 : memcpy( buf+off, reward.block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
247 27 : memcpy( buf+off, reward.signature, FD_BLS_SIG_COMPRESSED_SZ ); off += FD_BLS_SIG_COMPRESSED_SZ;
248 27 : off += fd_cu16_enc( reward.bitmap_sz, buf+off );
249 27 : if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
250 27 : off += sz;
251 27 : return off;
252 27 : }
253 :
254 : static ulong
255 : footer_ser( fd_block_footer_t const * footer,
256 93 : uchar * buf ) {
257 93 : if( FD_UNLIKELY( footer->has_fast_final_cert && footer->has_final_cert ) ) return 0UL; /* one finalization shape or the other */
258 90 : if( FD_UNLIKELY( footer->user_agent_len>FD_BLOCK_FOOTER_USER_AGENT_MAX ) ) return 0UL;
259 :
260 87 : footer_serde_t ftr;
261 :
262 87 : ftr.version = (uchar)1;
263 87 : ftr.bank_hash = footer->bank_hash.uc;
264 87 : ftr.block_producer_time_nanos = footer->block_producer_time_nanos;
265 87 : ftr.user_agent_len = (uchar)footer->user_agent_len;
266 87 : ftr.user_agent = footer->user_agent;
267 87 : ftr.has_block_final_cert = (uchar)( footer->has_fast_final_cert || footer->has_final_cert );
268 87 : ftr.block_final_cert = NULL; /* the three cert bodies are written straight into buf below */
269 87 : ftr.has_skip_reward_cert = (uchar)!!footer->has_skip_reward_cert;
270 87 : ftr.skip_reward_cert = NULL;
271 87 : ftr.has_notar_reward_cert = (uchar)!!footer->has_notar_reward_cert;
272 87 : ftr.notar_reward_cert = NULL;
273 :
274 87 : ulong off = 0UL;
275 87 : ulong sz;
276 87 : buf[ off ] = ftr.version; off += sizeof(uchar);
277 87 : memcpy( buf+off, ftr.bank_hash, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
278 87 : FD_STORE( ulong, buf+off, ftr.block_producer_time_nanos ); off += sizeof(ulong);
279 87 : buf[ off ] = ftr.user_agent_len; off += sizeof(uchar);
280 87 : memcpy( buf+off, ftr.user_agent, ftr.user_agent_len ); off += ftr.user_agent_len;
281 87 : buf[ off ] = ftr.has_block_final_cert; off += sizeof(uchar);
282 87 : if( ftr.has_block_final_cert ) {
283 45 : if( FD_UNLIKELY( !(sz=final_cert_ser( footer, buf+off )) ) ) return 0UL;
284 39 : off += sz;
285 39 : }
286 81 : buf[ off ] = ftr.has_skip_reward_cert; off += sizeof(uchar);
287 81 : if( ftr.has_skip_reward_cert ) {
288 30 : if( FD_UNLIKELY( !(sz=skip_reward_cert_ser( &footer->skip_reward_cert, buf+off )) ) ) return 0UL;
289 27 : off += sz;
290 27 : }
291 78 : buf[ off ] = ftr.has_notar_reward_cert; off += sizeof(uchar);
292 78 : if( ftr.has_notar_reward_cert ) {
293 27 : if( FD_UNLIKELY( !(sz=notar_reward_cert_ser( &footer->notar_reward_cert, buf+off )) ) ) return 0UL;
294 27 : off += sz;
295 27 : }
296 :
297 78 : return off;
298 78 : }
299 :
300 : ulong
301 : fd_block_marker_ser( fd_block_marker_t const * self,
302 129 : uchar buf[ static FD_BLOCK_MARKER_SER_MAX ] ) {
303 129 : uchar * payload = buf+FD_BLOCK_MARKER_PREAMBLE_SZ;
304 129 : ulong payload_sz;
305 129 : switch( self->kind ) {
306 93 : case FD_BLOCK_MARKER_KIND_FOOTER: payload_sz = footer_ser( &self->footer, payload ); break;
307 30 : case FD_BLOCK_MARKER_KIND_HEADER: payload_sz = header_ser( &self->header, payload ); break;
308 6 : default: return 0UL; /* nothing we produce is an UpdateParent or GenesisCertificate */
309 129 : }
310 123 : if( FD_UNLIKELY( !payload_sz ) ) return 0UL;
311 :
312 108 : marker_serde_t marker;
313 :
314 108 : marker.entry_cnt = 0UL;
315 108 : marker.version = (ushort)1;
316 108 : marker.tag = (uchar)self->kind;
317 108 : marker.length = (ushort)payload_sz;
318 108 : marker.payload = payload; /* already in place */
319 :
320 108 : ulong off = 0UL;
321 108 : FD_STORE( ulong, buf+off, marker.entry_cnt ); off += sizeof(ulong);
322 108 : FD_STORE( ushort, buf+off, marker.version ); off += sizeof(ushort);
323 108 : buf[ off ] = marker.tag; off += sizeof(uchar);
324 108 : FD_STORE( ushort, buf+off, marker.length ); off += sizeof(ushort);
325 108 : off += payload_sz;
326 :
327 108 : return off;
328 123 : }
329 :
330 : static int
331 : header_de( fd_block_header_t * header,
332 : uchar const * buf,
333 : ulong buf_sz,
334 117 : ulong * sz ) {
335 117 : FAIL( buf_sz<FD_BLOCK_HEADER_SER_SZ, SZ );
336 :
337 117 : header_serde_t hdr; ulong off = 0UL;
338 117 : hdr.version = buf[ off ]; off += sizeof(uchar);
339 117 : FAIL( hdr.version!=1, INVAL ); /* VersionedBlockHeader has only V1 */
340 114 : hdr.parent_slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
341 114 : hdr.parent_block_id = buf+off; off += sizeof(fd_hash_t);
342 :
343 114 : header->parent_slot = hdr.parent_slot;
344 114 : memcpy( header->parent_block_id.uc, hdr.parent_block_id, sizeof(fd_hash_t) );
345 :
346 114 : *sz = off;
347 114 : return FD_BLOCK_MARKER_DE_SUCCESS;
348 117 : }
349 :
350 : static int
351 : update_parent_de( fd_update_parent_t * update_parent,
352 : uchar const * buf,
353 : ulong buf_sz,
354 12 : ulong * sz ) {
355 12 : FAIL( buf_sz<FD_UPDATE_PARENT_SER_SZ, SZ );
356 :
357 12 : update_parent_serde_t upd; ulong off = 0UL;
358 12 : upd.version = buf[ off ]; off += sizeof(uchar);
359 12 : FAIL( upd.version!=1, INVAL ); /* VersionedUpdateParent has only V1 */
360 12 : upd.new_parent_slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
361 12 : upd.new_parent_block_id = buf+off; off += sizeof(fd_hash_t);
362 :
363 12 : update_parent->new_parent_slot = upd.new_parent_slot;
364 12 : memcpy( update_parent->new_parent_block_id.uc, upd.new_parent_block_id, sizeof(fd_hash_t) );
365 :
366 12 : *sz = off;
367 12 : return FD_BLOCK_MARKER_DE_SUCCESS;
368 12 : }
369 :
370 : /* bitmap_de decodes the base2 bitmap at b into nbits and signer_set,
371 : checking the version, the width against FD_BLS_SET_MAX and the
372 : payload length against the width. The width is kept verbatim, so a
373 : cert re-encodes at the width it came with. */
374 :
375 : static int
376 : bitmap_de( ushort * nbits,
377 : fd_bls_set_t * signer_set,
378 : uchar const * b,
379 153 : ulong b_sz ) {
380 153 : FAIL( b_sz<FD_BLOCK_BITMAP_SER_HDR_SZ, SZ );
381 :
382 150 : bitmap_serde_t bm; ulong off = 0UL;
383 150 : bm.version = b[ off ]; off += sizeof(uchar);
384 150 : FAIL( bm.version!=0, INVAL ); /* base2 only */
385 147 : bm.bit_cnt = FD_LOAD( ushort, b+off ); off += sizeof(ushort);
386 147 : FAIL( (ulong)bm.bit_cnt>FD_BLS_SET_MAX, SZ );
387 141 : bm.payload = b+off;
388 141 : bm.payload_sz = b_sz-off;
389 141 : FAIL( bm.payload_sz!=FD_BLOCK_BITMAP_SER_SZ( bm.bit_cnt )-FD_BLOCK_BITMAP_SER_HDR_SZ, INVAL );
390 :
391 138 : fd_bls_set_null( signer_set );
392 72582 : for( ulong i=0UL; i<(ulong)bm.bit_cnt; i++ ) {
393 72444 : if( (bm.payload[ i>>3 ] >> (i&7U)) & 1U ) fd_bls_set_insert( signer_set, i );
394 72444 : }
395 138 : *nbits = bm.bit_cnt;
396 138 : return FD_BLOCK_MARKER_DE_SUCCESS;
397 141 : }
398 :
399 : static int
400 : votes_aggregate_de( fd_block_footer_cert_t * cert,
401 : uchar const * buf,
402 : ulong buf_sz,
403 75 : ulong * sz ) {
404 75 : FAIL( buf_sz<FD_BLOCK_VOTES_AGGREGATE_SER_HDR_SZ, SZ );
405 :
406 75 : votes_aggregate_serde_t aggregate; ulong off = 0UL;
407 75 : aggregate.signature = buf+off; off += FD_BLS_SIG_COMPRESSED_SZ;
408 75 : aggregate.bitmap_sz = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
409 75 : FAIL( (ulong)aggregate.bitmap_sz>buf_sz-off, SZ );
410 72 : aggregate.bitmap = buf+off; off += aggregate.bitmap_sz;
411 :
412 72 : int err = bitmap_de( &cert->nbits, cert->signer_set, aggregate.bitmap, aggregate.bitmap_sz );
413 72 : if( FD_UNLIKELY( err ) ) return err;
414 69 : memcpy( cert->sig, aggregate.signature, FD_BLS_SIG_COMPRESSED_SZ ); /* stays compressed for votor */
415 :
416 69 : *sz = off;
417 69 : return FD_BLOCK_MARKER_DE_SUCCESS;
418 72 : }
419 :
420 : static int
421 : final_cert_de( fd_block_footer_t * footer,
422 : uchar const * buf,
423 : ulong buf_sz,
424 51 : ulong * sz ) {
425 51 : FAIL( buf_sz<FD_BLOCK_FINAL_CERT_SER_HDR_SZ, SZ );
426 :
427 51 : final_cert_serde_t cert; ulong off = 0UL;
428 51 : cert.slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
429 51 : cert.block_id = buf+off; off += sizeof(fd_hash_t);
430 51 : cert.final_aggregate = buf+off;
431 :
432 : /* zeroed so final_cert.block_id stays zero on the slow path */
433 51 : fd_block_footer_cert_t final_agg[1]; fd_memset( final_agg, 0, sizeof(final_agg) );
434 51 : ulong agg_sz;
435 51 : int err = votes_aggregate_de( final_agg, cert.final_aggregate, buf_sz-off, &agg_sz );
436 51 : if( FD_UNLIKELY( err ) ) return err;
437 48 : off += agg_sz;
438 :
439 48 : FAIL( buf_sz-off<sizeof(uchar), SZ );
440 48 : cert.has_notar_aggregate = buf[ off ]; off += sizeof(uchar);
441 48 : FAIL( cert.has_notar_aggregate>1, INVAL );
442 45 : cert.notar_aggregate = cert.has_notar_aggregate ? buf+off : NULL;
443 :
444 45 : if( !cert.notar_aggregate ) {
445 21 : footer->has_fast_final_cert = 1;
446 21 : footer->fast_final_cert = *final_agg;
447 21 : footer->fast_final_cert.slot = cert.slot;
448 21 : memcpy( footer->fast_final_cert.block_id.uc, cert.block_id, sizeof(fd_hash_t) );
449 24 : } else {
450 24 : err = votes_aggregate_de( &footer->notar_cert, cert.notar_aggregate, buf_sz-off, &agg_sz );
451 24 : if( FD_UNLIKELY( err ) ) return err;
452 21 : off += agg_sz;
453 21 : footer->has_final_cert = 1;
454 21 : footer->final_cert = *final_agg;
455 21 : footer->final_cert.slot = cert.slot;
456 21 : footer->notar_cert.slot = cert.slot;
457 21 : memcpy( footer->notar_cert.block_id.uc, cert.block_id, sizeof(fd_hash_t) );
458 21 : }
459 :
460 42 : *sz = off;
461 42 : return FD_BLOCK_MARKER_DE_SUCCESS;
462 45 : }
463 :
464 : static int
465 : skip_reward_cert_de( fd_block_footer_cert_t * cert,
466 : uchar const * buf,
467 : ulong buf_sz,
468 54 : ulong * sz ) {
469 54 : FAIL( buf_sz<FD_BLOCK_SKIP_REWARD_CERT_SER_HDR_SZ, SZ );
470 :
471 54 : skip_reward_cert_serde_t reward; ulong off = 0UL;
472 54 : reward.slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
473 54 : reward.signature = buf+off; off += FD_BLS_SIG_COMPRESSED_SZ;
474 54 : FAIL( buf_sz==off, SZ ); /* no byte for the ShortU16 */
475 51 : ulong cu16_sz = fd_cu16_dec_sz( buf+off, buf_sz-off );
476 51 : FAIL( !cu16_sz, INVAL ); /* ShortU16 malformed (or a multi byte one cut short, which fd_cu16_dec_sz cannot tell apart) */
477 48 : reward.bitmap_sz = fd_cu16_dec_fixed( buf+off, cu16_sz ); off += cu16_sz;
478 48 : FAIL( (ulong)reward.bitmap_sz>buf_sz-off, SZ );
479 48 : reward.bitmap = buf+off; off += reward.bitmap_sz;
480 :
481 48 : int err = bitmap_de( &cert->nbits, cert->signer_set, reward.bitmap, reward.bitmap_sz );
482 48 : if( FD_UNLIKELY( err ) ) return err;
483 36 : cert->slot = reward.slot; /* block_id stays zero */
484 36 : memcpy( cert->sig, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );
485 :
486 36 : *sz = off;
487 36 : return FD_BLOCK_MARKER_DE_SUCCESS;
488 48 : }
489 :
490 : static int
491 : notar_reward_cert_de( fd_block_footer_cert_t * cert,
492 : uchar const * buf,
493 : ulong buf_sz,
494 33 : ulong * sz ) {
495 33 : FAIL( buf_sz<FD_BLOCK_NOTAR_REWARD_CERT_SER_HDR_SZ, SZ );
496 :
497 33 : notar_reward_cert_serde_t reward; ulong off = 0UL;
498 33 : reward.slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
499 33 : reward.block_id = buf+off; off += sizeof(fd_hash_t);
500 33 : reward.signature = buf+off; off += FD_BLS_SIG_COMPRESSED_SZ;
501 33 : FAIL( buf_sz==off, SZ ); /* no byte for the ShortU16 */
502 33 : ulong cu16_sz = fd_cu16_dec_sz( buf+off, buf_sz-off );
503 33 : FAIL( !cu16_sz, INVAL ); /* ShortU16 malformed (or a multi byte one cut short, which fd_cu16_dec_sz cannot tell apart) */
504 33 : reward.bitmap_sz = fd_cu16_dec_fixed( buf+off, cu16_sz ); off += cu16_sz;
505 33 : FAIL( (ulong)reward.bitmap_sz>buf_sz-off, SZ );
506 33 : reward.bitmap = buf+off; off += reward.bitmap_sz;
507 :
508 33 : int err = bitmap_de( &cert->nbits, cert->signer_set, reward.bitmap, reward.bitmap_sz );
509 33 : if( FD_UNLIKELY( err ) ) return err;
510 33 : cert->slot = reward.slot;
511 33 : memcpy( cert->block_id.uc, reward.block_id, sizeof(fd_hash_t) );
512 33 : memcpy( cert->sig, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );
513 :
514 33 : *sz = off;
515 33 : return FD_BLOCK_MARKER_DE_SUCCESS;
516 33 : }
517 :
518 : static int
519 : footer_de( fd_block_footer_t * footer,
520 : uchar const * buf,
521 : ulong buf_sz,
522 126 : ulong * sz ) {
523 126 : FAIL( buf_sz<FD_BLOCK_FOOTER_SER_HDR_SZ, SZ );
524 :
525 126 : footer_serde_t ftr; ulong off = 0UL;
526 126 : ftr.version = buf[ off ]; off += sizeof(uchar);
527 126 : FAIL( ftr.version!=1, INVAL ); /* VersionedBlockFooter has only V1 */
528 126 : ftr.bank_hash = buf+off; off += sizeof(fd_hash_t);
529 126 : ftr.block_producer_time_nanos = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
530 126 : ftr.user_agent_len = buf[ off ]; off += sizeof(uchar);
531 126 : FAIL( buf_sz-off<(ulong)ftr.user_agent_len, SZ );
532 126 : ftr.user_agent = buf+off; off += ftr.user_agent_len;
533 :
534 126 : memcpy( footer->bank_hash.uc, ftr.bank_hash, sizeof(fd_hash_t) );
535 126 : footer->block_producer_time_nanos = ftr.block_producer_time_nanos;
536 126 : footer->user_agent_len = (ulong)ftr.user_agent_len;
537 126 : memcpy( footer->user_agent, ftr.user_agent, ftr.user_agent_len );
538 :
539 126 : int err;
540 126 : ulong part_sz;
541 :
542 126 : FAIL( buf_sz-off<sizeof(uchar), SZ );
543 123 : ftr.has_block_final_cert = buf[ off ]; off += sizeof(uchar);
544 123 : FAIL( ftr.has_block_final_cert>1, INVAL );
545 120 : ftr.block_final_cert = ftr.has_block_final_cert ? buf+off : NULL;
546 120 : if( ftr.block_final_cert ) {
547 51 : err = final_cert_de( footer, ftr.block_final_cert, buf_sz-off, &part_sz ); /* sets has_fast_final_cert or has_final_cert itself */
548 51 : if( FD_UNLIKELY( err ) ) return err;
549 42 : off += part_sz;
550 42 : }
551 :
552 111 : FAIL( buf_sz-off<sizeof(uchar), SZ );
553 111 : ftr.has_skip_reward_cert = buf[ off ]; off += sizeof(uchar);
554 111 : FAIL( ftr.has_skip_reward_cert>1, INVAL );
555 111 : ftr.skip_reward_cert = ftr.has_skip_reward_cert ? buf+off : NULL;
556 111 : if( ftr.skip_reward_cert ) {
557 54 : err = skip_reward_cert_de( &footer->skip_reward_cert, ftr.skip_reward_cert, buf_sz-off, &part_sz );
558 54 : if( FD_UNLIKELY( err ) ) return err;
559 36 : off += part_sz;
560 36 : footer->has_skip_reward_cert = 1;
561 36 : }
562 :
563 93 : FAIL( buf_sz-off<sizeof(uchar), SZ );
564 93 : ftr.has_notar_reward_cert = buf[ off ]; off += sizeof(uchar);
565 93 : FAIL( ftr.has_notar_reward_cert>1, INVAL );
566 93 : ftr.notar_reward_cert = ftr.has_notar_reward_cert ? buf+off : NULL;
567 93 : if( ftr.notar_reward_cert ) {
568 33 : err = notar_reward_cert_de( &footer->notar_reward_cert, ftr.notar_reward_cert, buf_sz-off, &part_sz );
569 33 : if( FD_UNLIKELY( err ) ) return err;
570 33 : off += part_sz;
571 33 : footer->has_notar_reward_cert = 1;
572 33 : }
573 :
574 93 : *sz = off;
575 93 : return FD_BLOCK_MARKER_DE_SUCCESS;
576 93 : }
577 :
578 : int
579 : fd_block_marker_de( fd_block_marker_t * self,
580 : uchar const * buf,
581 4641 : ulong buf_sz ) {
582 4641 : FAIL( buf_sz<FD_BLOCK_MARKER_PREAMBLE_SZ, SZ );
583 :
584 4524 : marker_serde_t marker; ulong off = 0UL;
585 4524 : marker.entry_cnt = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
586 4524 : marker.version = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
587 4524 : marker.tag = buf[ off ]; off += sizeof(uchar);
588 4524 : marker.length = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
589 4524 : FAIL( marker.entry_cnt!=0UL, INVAL ); /* an entry batch, not a marker */
590 4521 : FAIL( marker.version!=1, INVAL ); /* VersionedBlockMarker has only V1 */
591 4518 : FAIL( (ulong)marker.length>buf_sz-off, SZ );
592 261 : marker.payload = buf+off;
593 :
594 261 : fd_memset( self, 0, sizeof(fd_block_marker_t) );
595 261 : self->kind = (uint)marker.tag;
596 :
597 261 : int err;
598 261 : ulong payload_sz;
599 261 : switch( self->kind ) {
600 126 : case FD_BLOCK_MARKER_KIND_FOOTER: err = footer_de ( &self->footer, marker.payload, marker.length, &payload_sz ); break;
601 117 : case FD_BLOCK_MARKER_KIND_HEADER: err = header_de ( &self->header, marker.payload, marker.length, &payload_sz ); break;
602 12 : case FD_BLOCK_MARKER_KIND_UPDATE_PARENT: err = update_parent_de( &self->update_parent, marker.payload, marker.length, &payload_sz ); break;
603 3 : case FD_BLOCK_MARKER_KIND_GENESIS_CERT: return FD_BLOCK_MARKER_DE_ERR_UNSUPPORTED;
604 3 : default: return FD_BLOCK_MARKER_DE_ERR_INVAL;
605 261 : }
606 255 : if( FD_UNLIKELY( err ) ) return err;
607 219 : FAIL( payload_sz!=(ulong)marker.length, SZ ); /* LengthPrefixed::len is exact */
608 :
609 210 : return FD_BLOCK_MARKER_DE_SUCCESS;
610 219 : }
|