Line data Source code
1 : #include "fd_block_marker_serde.h"
2 :
3 : #include "../../ballet/txn/fd_compact_u16.h"
4 :
5 0 : #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 0 : uchar * buf ) {
119 0 : header_serde_t hdr;
120 :
121 0 : hdr.version = (uchar)1;
122 0 : hdr.parent_slot = header->parent_slot;
123 0 : hdr.parent_block_id = header->parent_block_id.uc;
124 :
125 0 : ulong off = 0UL;
126 0 : buf[ off ] = hdr.version; off += sizeof(uchar);
127 0 : FD_STORE( ulong, buf+off, hdr.parent_slot ); off += sizeof(ulong);
128 0 : memcpy( buf+off, hdr.parent_block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
129 :
130 0 : return off;
131 0 : }
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 0 : uchar * buf ) {
143 0 : 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 0 : if( FD_UNLIKELY( nbits>AG_VAT_MAX || bits>nbits ) ) return 0UL;
145 :
146 0 : bitmap_serde_t bm;
147 :
148 0 : bm.version = (uchar)0; /* base2 */
149 0 : bm.bit_cnt = (ushort)nbits;
150 0 : bm.payload = NULL; /* written straight into buf below */
151 0 : bm.payload_sz = FD_BLOCK_BITMAP_SER_SZ( nbits )-FD_BLOCK_BITMAP_SER_HDR_SZ;
152 :
153 0 : ulong off = 0UL;
154 0 : buf[ off ] = bm.version; off += sizeof(uchar);
155 0 : FD_STORE( ushort, buf+off, bm.bit_cnt ); off += sizeof(ushort);
156 0 : fd_memset( buf+off, 0, bm.payload_sz );
157 0 : for( ulong i=0UL; i<bits; i++ ) {
158 0 : if( fd_bls_set_test( signer_set, i ) ) buf[ off+(i>>3) ] |= (uchar)( 1U << (i&7U) );
159 0 : }
160 0 : off += bm.payload_sz;
161 0 : return off;
162 0 : }
163 :
164 : static ulong
165 : votes_aggregate_ser( fd_block_footer_cert_t const * cert,
166 0 : uchar * buf ) {
167 0 : votes_aggregate_serde_t aggregate;
168 :
169 0 : aggregate.signature = cert->sig; /* already compressed */
170 0 : aggregate.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
171 0 : aggregate.bitmap = NULL; /* written straight into buf by bitmap_ser below */
172 :
173 0 : ulong off = 0UL;
174 0 : ulong sz;
175 0 : memcpy( buf+off, aggregate.signature, FD_BLS_SIG_COMPRESSED_SZ ); off += FD_BLS_SIG_COMPRESSED_SZ;
176 0 : FD_STORE( ushort, buf+off, aggregate.bitmap_sz ); off += sizeof(ushort);
177 0 : if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
178 0 : off += sz;
179 0 : return off;
180 0 : }
181 :
182 : static ulong
183 : final_cert_ser( fd_block_footer_t const * footer,
184 0 : uchar * buf ) {
185 0 : 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 0 : if( FD_UNLIKELY( !is_fast && footer->final_cert.slot!=footer->notar_cert.slot ) ) return 0UL;
189 :
190 0 : final_cert_serde_t cert;
191 :
192 0 : cert.slot = is_fast ? footer->fast_final_cert.slot : footer->final_cert.slot;
193 0 : cert.block_id = is_fast ? footer->fast_final_cert.block_id.uc : footer->notar_cert.block_id.uc;
194 0 : cert.final_aggregate = NULL; /* written straight into buf by votes_aggregate_ser below */
195 0 : cert.has_notar_aggregate = (uchar)!is_fast;
196 0 : cert.notar_aggregate = NULL;
197 :
198 0 : ulong off = 0UL;
199 0 : ulong sz;
200 0 : FD_STORE( ulong, buf+off, cert.slot ); off += sizeof(ulong);
201 0 : memcpy( buf+off, cert.block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
202 0 : if( FD_UNLIKELY( !(sz=votes_aggregate_ser( is_fast ? &footer->fast_final_cert : &footer->final_cert, buf+off )) ) ) return 0UL;
203 0 : off += sz;
204 0 : buf[ off ] = cert.has_notar_aggregate; off += sizeof(uchar);
205 0 : if( cert.has_notar_aggregate ) {
206 0 : if( FD_UNLIKELY( !(sz=votes_aggregate_ser( &footer->notar_cert, buf+off )) ) ) return 0UL;
207 0 : off += sz;
208 0 : }
209 0 : return off;
210 0 : }
211 :
212 : static ulong
213 : skip_reward_cert_ser( fd_block_footer_cert_t const * cert,
214 0 : uchar * buf ) {
215 0 : skip_reward_cert_serde_t reward;
216 :
217 0 : reward.slot = cert->slot;
218 0 : reward.signature = cert->sig; /* already compressed */
219 0 : reward.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
220 0 : reward.bitmap = NULL; /* written straight into buf by bitmap_ser below */
221 :
222 0 : ulong off = 0UL;
223 0 : ulong sz;
224 0 : FD_STORE( ulong, buf+off, reward.slot ); off += sizeof(ulong);
225 0 : memcpy( buf+off, reward.signature, FD_BLS_SIG_COMPRESSED_SZ ); off += FD_BLS_SIG_COMPRESSED_SZ;
226 0 : off += fd_cu16_enc( reward.bitmap_sz, buf+off );
227 0 : if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
228 0 : off += sz;
229 0 : return off;
230 0 : }
231 :
232 : static ulong
233 : notar_reward_cert_ser( fd_block_footer_cert_t const * cert,
234 0 : uchar * buf ) {
235 0 : notar_reward_cert_serde_t reward;
236 :
237 0 : reward.slot = cert->slot;
238 0 : reward.block_id = cert->block_id.uc;
239 0 : reward.signature = cert->sig; /* already compressed */
240 0 : reward.bitmap_sz = (ushort)FD_BLOCK_BITMAP_SER_SZ( cert->nbits );
241 0 : reward.bitmap = NULL; /* written straight into buf by bitmap_ser below */
242 :
243 0 : ulong off = 0UL;
244 0 : ulong sz;
245 0 : FD_STORE( ulong, buf+off, reward.slot ); off += sizeof(ulong);
246 0 : memcpy( buf+off, reward.block_id, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
247 0 : memcpy( buf+off, reward.signature, FD_BLS_SIG_COMPRESSED_SZ ); off += FD_BLS_SIG_COMPRESSED_SZ;
248 0 : off += fd_cu16_enc( reward.bitmap_sz, buf+off );
249 0 : if( FD_UNLIKELY( !(sz=bitmap_ser( cert->nbits, cert->signer_set, buf+off )) ) ) return 0UL;
250 0 : off += sz;
251 0 : return off;
252 0 : }
253 :
254 : static ulong
255 : footer_ser( fd_block_footer_t const * footer,
256 0 : uchar * buf ) {
257 0 : if( FD_UNLIKELY( footer->has_fast_final_cert && footer->has_final_cert ) ) return 0UL; /* one finalization shape or the other */
258 0 : if( FD_UNLIKELY( footer->user_agent_len>FD_BLOCK_FOOTER_USER_AGENT_MAX ) ) return 0UL;
259 :
260 0 : footer_serde_t ftr;
261 :
262 0 : ftr.version = (uchar)1;
263 0 : ftr.bank_hash = footer->bank_hash.uc;
264 0 : ftr.block_producer_time_nanos = footer->block_producer_time_nanos;
265 0 : ftr.user_agent_len = (uchar)footer->user_agent_len;
266 0 : ftr.user_agent = footer->user_agent;
267 0 : ftr.has_block_final_cert = (uchar)( footer->has_fast_final_cert || footer->has_final_cert );
268 0 : ftr.block_final_cert = NULL; /* the three cert bodies are written straight into buf below */
269 0 : ftr.has_skip_reward_cert = (uchar)!!footer->has_skip_reward_cert;
270 0 : ftr.skip_reward_cert = NULL;
271 0 : ftr.has_notar_reward_cert = (uchar)!!footer->has_notar_reward_cert;
272 0 : ftr.notar_reward_cert = NULL;
273 :
274 0 : ulong off = 0UL;
275 0 : ulong sz;
276 0 : buf[ off ] = ftr.version; off += sizeof(uchar);
277 0 : memcpy( buf+off, ftr.bank_hash, sizeof(fd_hash_t) ); off += sizeof(fd_hash_t);
278 0 : FD_STORE( ulong, buf+off, ftr.block_producer_time_nanos ); off += sizeof(ulong);
279 0 : buf[ off ] = ftr.user_agent_len; off += sizeof(uchar);
280 0 : memcpy( buf+off, ftr.user_agent, ftr.user_agent_len ); off += ftr.user_agent_len;
281 0 : buf[ off ] = ftr.has_block_final_cert; off += sizeof(uchar);
282 0 : if( ftr.has_block_final_cert ) {
283 0 : if( FD_UNLIKELY( !(sz=final_cert_ser( footer, buf+off )) ) ) return 0UL;
284 0 : off += sz;
285 0 : }
286 0 : buf[ off ] = ftr.has_skip_reward_cert; off += sizeof(uchar);
287 0 : if( ftr.has_skip_reward_cert ) {
288 0 : if( FD_UNLIKELY( !(sz=skip_reward_cert_ser( &footer->skip_reward_cert, buf+off )) ) ) return 0UL;
289 0 : off += sz;
290 0 : }
291 0 : buf[ off ] = ftr.has_notar_reward_cert; off += sizeof(uchar);
292 0 : if( ftr.has_notar_reward_cert ) {
293 0 : if( FD_UNLIKELY( !(sz=notar_reward_cert_ser( &footer->notar_reward_cert, buf+off )) ) ) return 0UL;
294 0 : off += sz;
295 0 : }
296 :
297 0 : return off;
298 0 : }
299 :
300 : ulong
301 : fd_block_marker_ser( fd_block_marker_t const * self,
302 0 : uchar buf[ static FD_BLOCK_MARKER_SER_MAX ] ) {
303 0 : uchar * payload = buf+FD_BLOCK_MARKER_PREAMBLE_SZ;
304 0 : ulong payload_sz;
305 0 : switch( self->kind ) {
306 0 : case FD_BLOCK_MARKER_KIND_FOOTER: payload_sz = footer_ser( &self->footer, payload ); break;
307 0 : case FD_BLOCK_MARKER_KIND_HEADER: payload_sz = header_ser( &self->header, payload ); break;
308 0 : default: return 0UL; /* nothing we produce is an UpdateParent or GenesisCertificate */
309 0 : }
310 0 : if( FD_UNLIKELY( !payload_sz ) ) return 0UL;
311 :
312 0 : marker_serde_t marker;
313 :
314 0 : marker.entry_cnt = 0UL;
315 0 : marker.version = (ushort)1;
316 0 : marker.tag = (uchar)self->kind;
317 0 : marker.length = (ushort)payload_sz;
318 0 : marker.payload = payload; /* already in place */
319 :
320 0 : ulong off = 0UL;
321 0 : FD_STORE( ulong, buf+off, marker.entry_cnt ); off += sizeof(ulong);
322 0 : FD_STORE( ushort, buf+off, marker.version ); off += sizeof(ushort);
323 0 : buf[ off ] = marker.tag; off += sizeof(uchar);
324 0 : FD_STORE( ushort, buf+off, marker.length ); off += sizeof(ushort);
325 0 : off += payload_sz;
326 :
327 0 : return off;
328 0 : }
329 :
330 : static int
331 : header_de( fd_block_header_t * header,
332 : uchar const * buf,
333 : ulong buf_sz,
334 0 : ulong * sz ) {
335 0 : FAIL( buf_sz<FD_BLOCK_HEADER_SER_SZ, SZ );
336 :
337 0 : header_serde_t hdr; ulong off = 0UL;
338 0 : hdr.version = buf[ off ]; off += sizeof(uchar);
339 0 : FAIL( hdr.version!=1, INVAL ); /* VersionedBlockHeader has only V1 */
340 0 : hdr.parent_slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
341 0 : hdr.parent_block_id = buf+off; off += sizeof(fd_hash_t);
342 :
343 0 : header->parent_slot = hdr.parent_slot;
344 0 : memcpy( header->parent_block_id.uc, hdr.parent_block_id, sizeof(fd_hash_t) );
345 :
346 0 : *sz = off;
347 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
348 0 : }
349 :
350 : static int
351 : update_parent_de( fd_update_parent_t * update_parent,
352 : uchar const * buf,
353 : ulong buf_sz,
354 0 : ulong * sz ) {
355 0 : FAIL( buf_sz<FD_UPDATE_PARENT_SER_SZ, SZ );
356 :
357 0 : update_parent_serde_t upd; ulong off = 0UL;
358 0 : upd.version = buf[ off ]; off += sizeof(uchar);
359 0 : FAIL( upd.version!=1, INVAL ); /* VersionedUpdateParent has only V1 */
360 0 : upd.new_parent_slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
361 0 : upd.new_parent_block_id = buf+off; off += sizeof(fd_hash_t);
362 :
363 0 : update_parent->new_parent_slot = upd.new_parent_slot;
364 0 : memcpy( update_parent->new_parent_block_id.uc, upd.new_parent_block_id, sizeof(fd_hash_t) );
365 :
366 0 : *sz = off;
367 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
368 0 : }
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 0 : ulong b_sz ) {
380 0 : FAIL( b_sz<FD_BLOCK_BITMAP_SER_HDR_SZ, SZ );
381 :
382 0 : bitmap_serde_t bm; ulong off = 0UL;
383 0 : bm.version = b[ off ]; off += sizeof(uchar);
384 0 : FAIL( bm.version!=0, INVAL ); /* base2 only */
385 0 : bm.bit_cnt = FD_LOAD( ushort, b+off ); off += sizeof(ushort);
386 0 : FAIL( (ulong)bm.bit_cnt>FD_BLS_SET_MAX, SZ );
387 0 : bm.payload = b+off;
388 0 : bm.payload_sz = b_sz-off;
389 0 : FAIL( bm.payload_sz!=FD_BLOCK_BITMAP_SER_SZ( bm.bit_cnt )-FD_BLOCK_BITMAP_SER_HDR_SZ, INVAL );
390 :
391 0 : fd_bls_set_null( signer_set );
392 0 : for( ulong i=0UL; i<(ulong)bm.bit_cnt; i++ ) {
393 0 : if( (bm.payload[ i>>3 ] >> (i&7U)) & 1U ) fd_bls_set_insert( signer_set, i );
394 0 : }
395 0 : *nbits = bm.bit_cnt;
396 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
397 0 : }
398 :
399 : static int
400 : votes_aggregate_de( fd_block_footer_cert_t * cert,
401 : uchar const * buf,
402 : ulong buf_sz,
403 0 : ulong * sz ) {
404 0 : FAIL( buf_sz<FD_BLOCK_VOTES_AGGREGATE_SER_HDR_SZ, SZ );
405 :
406 0 : votes_aggregate_serde_t aggregate; ulong off = 0UL;
407 0 : aggregate.signature = buf+off; off += FD_BLS_SIG_COMPRESSED_SZ;
408 0 : aggregate.bitmap_sz = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
409 0 : FAIL( (ulong)aggregate.bitmap_sz>buf_sz-off, SZ );
410 0 : aggregate.bitmap = buf+off; off += aggregate.bitmap_sz;
411 :
412 0 : int err = bitmap_de( &cert->nbits, cert->signer_set, aggregate.bitmap, aggregate.bitmap_sz );
413 0 : if( FD_UNLIKELY( err ) ) return err;
414 0 : memcpy( cert->sig, aggregate.signature, FD_BLS_SIG_COMPRESSED_SZ ); /* stays compressed for votor */
415 :
416 0 : *sz = off;
417 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
418 0 : }
419 :
420 : static int
421 : final_cert_de( fd_block_footer_t * footer,
422 : uchar const * buf,
423 : ulong buf_sz,
424 0 : ulong * sz ) {
425 0 : FAIL( buf_sz<FD_BLOCK_FINAL_CERT_SER_HDR_SZ, SZ );
426 :
427 0 : final_cert_serde_t cert; ulong off = 0UL;
428 0 : cert.slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
429 0 : cert.block_id = buf+off; off += sizeof(fd_hash_t);
430 0 : cert.final_aggregate = buf+off;
431 :
432 : /* zeroed so final_cert.block_id stays zero on the slow path */
433 0 : fd_block_footer_cert_t final_agg[1]; fd_memset( final_agg, 0, sizeof(final_agg) );
434 0 : ulong agg_sz;
435 0 : int err = votes_aggregate_de( final_agg, cert.final_aggregate, buf_sz-off, &agg_sz );
436 0 : if( FD_UNLIKELY( err ) ) return err;
437 0 : off += agg_sz;
438 :
439 0 : FAIL( buf_sz-off<sizeof(uchar), SZ );
440 0 : cert.has_notar_aggregate = buf[ off ]; off += sizeof(uchar);
441 0 : FAIL( cert.has_notar_aggregate>1, INVAL );
442 0 : cert.notar_aggregate = cert.has_notar_aggregate ? buf+off : NULL;
443 :
444 0 : if( !cert.notar_aggregate ) {
445 0 : footer->has_fast_final_cert = 1;
446 0 : footer->fast_final_cert = *final_agg;
447 0 : footer->fast_final_cert.slot = cert.slot;
448 0 : memcpy( footer->fast_final_cert.block_id.uc, cert.block_id, sizeof(fd_hash_t) );
449 0 : } else {
450 0 : err = votes_aggregate_de( &footer->notar_cert, cert.notar_aggregate, buf_sz-off, &agg_sz );
451 0 : if( FD_UNLIKELY( err ) ) return err;
452 0 : off += agg_sz;
453 0 : footer->has_final_cert = 1;
454 0 : footer->final_cert = *final_agg;
455 0 : footer->final_cert.slot = cert.slot;
456 0 : footer->notar_cert.slot = cert.slot;
457 0 : memcpy( footer->notar_cert.block_id.uc, cert.block_id, sizeof(fd_hash_t) );
458 0 : }
459 :
460 0 : *sz = off;
461 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
462 0 : }
463 :
464 : static int
465 : skip_reward_cert_de( fd_block_footer_cert_t * cert,
466 : uchar const * buf,
467 : ulong buf_sz,
468 0 : ulong * sz ) {
469 0 : FAIL( buf_sz<FD_BLOCK_SKIP_REWARD_CERT_SER_HDR_SZ, SZ );
470 :
471 0 : skip_reward_cert_serde_t reward; ulong off = 0UL;
472 0 : reward.slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
473 0 : reward.signature = buf+off; off += FD_BLS_SIG_COMPRESSED_SZ;
474 0 : FAIL( buf_sz==off, SZ ); /* no byte for the ShortU16 */
475 0 : ulong cu16_sz = fd_cu16_dec_sz( buf+off, buf_sz-off );
476 0 : FAIL( !cu16_sz, INVAL ); /* ShortU16 malformed (or a multi byte one cut short, which fd_cu16_dec_sz cannot tell apart) */
477 0 : reward.bitmap_sz = fd_cu16_dec_fixed( buf+off, cu16_sz ); off += cu16_sz;
478 0 : FAIL( (ulong)reward.bitmap_sz>buf_sz-off, SZ );
479 0 : reward.bitmap = buf+off; off += reward.bitmap_sz;
480 :
481 0 : int err = bitmap_de( &cert->nbits, cert->signer_set, reward.bitmap, reward.bitmap_sz );
482 0 : if( FD_UNLIKELY( err ) ) return err;
483 0 : cert->slot = reward.slot; /* block_id stays zero */
484 0 : memcpy( cert->sig, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );
485 :
486 0 : *sz = off;
487 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
488 0 : }
489 :
490 : static int
491 : notar_reward_cert_de( fd_block_footer_cert_t * cert,
492 : uchar const * buf,
493 : ulong buf_sz,
494 0 : ulong * sz ) {
495 0 : FAIL( buf_sz<FD_BLOCK_NOTAR_REWARD_CERT_SER_HDR_SZ, SZ );
496 :
497 0 : notar_reward_cert_serde_t reward; ulong off = 0UL;
498 0 : reward.slot = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
499 0 : reward.block_id = buf+off; off += sizeof(fd_hash_t);
500 0 : reward.signature = buf+off; off += FD_BLS_SIG_COMPRESSED_SZ;
501 0 : FAIL( buf_sz==off, SZ ); /* no byte for the ShortU16 */
502 0 : ulong cu16_sz = fd_cu16_dec_sz( buf+off, buf_sz-off );
503 0 : FAIL( !cu16_sz, INVAL ); /* ShortU16 malformed (or a multi byte one cut short, which fd_cu16_dec_sz cannot tell apart) */
504 0 : reward.bitmap_sz = fd_cu16_dec_fixed( buf+off, cu16_sz ); off += cu16_sz;
505 0 : FAIL( (ulong)reward.bitmap_sz>buf_sz-off, SZ );
506 0 : reward.bitmap = buf+off; off += reward.bitmap_sz;
507 :
508 0 : int err = bitmap_de( &cert->nbits, cert->signer_set, reward.bitmap, reward.bitmap_sz );
509 0 : if( FD_UNLIKELY( err ) ) return err;
510 0 : cert->slot = reward.slot;
511 0 : memcpy( cert->block_id.uc, reward.block_id, sizeof(fd_hash_t) );
512 0 : memcpy( cert->sig, reward.signature, FD_BLS_SIG_COMPRESSED_SZ );
513 :
514 0 : *sz = off;
515 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
516 0 : }
517 :
518 : static int
519 : footer_de( fd_block_footer_t * footer,
520 : uchar const * buf,
521 : ulong buf_sz,
522 0 : ulong * sz ) {
523 0 : FAIL( buf_sz<FD_BLOCK_FOOTER_SER_HDR_SZ, SZ );
524 :
525 0 : footer_serde_t ftr; ulong off = 0UL;
526 0 : ftr.version = buf[ off ]; off += sizeof(uchar);
527 0 : FAIL( ftr.version!=1, INVAL ); /* VersionedBlockFooter has only V1 */
528 0 : ftr.bank_hash = buf+off; off += sizeof(fd_hash_t);
529 0 : ftr.block_producer_time_nanos = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
530 0 : ftr.user_agent_len = buf[ off ]; off += sizeof(uchar);
531 0 : FAIL( buf_sz-off<(ulong)ftr.user_agent_len, SZ );
532 0 : ftr.user_agent = buf+off; off += ftr.user_agent_len;
533 :
534 0 : memcpy( footer->bank_hash.uc, ftr.bank_hash, sizeof(fd_hash_t) );
535 0 : footer->block_producer_time_nanos = ftr.block_producer_time_nanos;
536 0 : footer->user_agent_len = (ulong)ftr.user_agent_len;
537 0 : memcpy( footer->user_agent, ftr.user_agent, ftr.user_agent_len );
538 :
539 0 : int err;
540 0 : ulong part_sz;
541 :
542 0 : FAIL( buf_sz-off<sizeof(uchar), SZ );
543 0 : ftr.has_block_final_cert = buf[ off ]; off += sizeof(uchar);
544 0 : FAIL( ftr.has_block_final_cert>1, INVAL );
545 0 : ftr.block_final_cert = ftr.has_block_final_cert ? buf+off : NULL;
546 0 : if( ftr.block_final_cert ) {
547 0 : 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 0 : if( FD_UNLIKELY( err ) ) return err;
549 0 : off += part_sz;
550 0 : }
551 :
552 0 : FAIL( buf_sz-off<sizeof(uchar), SZ );
553 0 : ftr.has_skip_reward_cert = buf[ off ]; off += sizeof(uchar);
554 0 : FAIL( ftr.has_skip_reward_cert>1, INVAL );
555 0 : ftr.skip_reward_cert = ftr.has_skip_reward_cert ? buf+off : NULL;
556 0 : if( ftr.skip_reward_cert ) {
557 0 : err = skip_reward_cert_de( &footer->skip_reward_cert, ftr.skip_reward_cert, buf_sz-off, &part_sz );
558 0 : if( FD_UNLIKELY( err ) ) return err;
559 0 : off += part_sz;
560 0 : footer->has_skip_reward_cert = 1;
561 0 : }
562 :
563 0 : FAIL( buf_sz-off<sizeof(uchar), SZ );
564 0 : ftr.has_notar_reward_cert = buf[ off ]; off += sizeof(uchar);
565 0 : FAIL( ftr.has_notar_reward_cert>1, INVAL );
566 0 : ftr.notar_reward_cert = ftr.has_notar_reward_cert ? buf+off : NULL;
567 0 : if( ftr.notar_reward_cert ) {
568 0 : err = notar_reward_cert_de( &footer->notar_reward_cert, ftr.notar_reward_cert, buf_sz-off, &part_sz );
569 0 : if( FD_UNLIKELY( err ) ) return err;
570 0 : off += part_sz;
571 0 : footer->has_notar_reward_cert = 1;
572 0 : }
573 :
574 0 : *sz = off;
575 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
576 0 : }
577 :
578 : int
579 : fd_block_marker_de( fd_block_marker_t * self,
580 : uchar const * buf,
581 0 : ulong buf_sz ) {
582 0 : FAIL( buf_sz<FD_BLOCK_MARKER_PREAMBLE_SZ, SZ );
583 :
584 0 : marker_serde_t marker; ulong off = 0UL;
585 0 : marker.entry_cnt = FD_LOAD( ulong, buf+off ); off += sizeof(ulong);
586 0 : marker.version = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
587 0 : marker.tag = buf[ off ]; off += sizeof(uchar);
588 0 : marker.length = FD_LOAD( ushort, buf+off ); off += sizeof(ushort);
589 0 : FAIL( marker.entry_cnt!=0UL, INVAL ); /* an entry batch, not a marker */
590 0 : FAIL( marker.version!=1, INVAL ); /* VersionedBlockMarker has only V1 */
591 0 : FAIL( (ulong)marker.length>buf_sz-off, SZ );
592 0 : marker.payload = buf+off;
593 :
594 0 : fd_memset( self, 0, sizeof(fd_block_marker_t) );
595 0 : self->kind = (uint)marker.tag;
596 :
597 0 : int err;
598 0 : ulong payload_sz;
599 0 : switch( self->kind ) {
600 0 : case FD_BLOCK_MARKER_KIND_FOOTER: err = footer_de ( &self->footer, marker.payload, marker.length, &payload_sz ); break;
601 0 : case FD_BLOCK_MARKER_KIND_HEADER: err = header_de ( &self->header, marker.payload, marker.length, &payload_sz ); break;
602 0 : case FD_BLOCK_MARKER_KIND_UPDATE_PARENT: err = update_parent_de( &self->update_parent, marker.payload, marker.length, &payload_sz ); break;
603 0 : case FD_BLOCK_MARKER_KIND_GENESIS_CERT: return FD_BLOCK_MARKER_DE_ERR_UNSUPPORTED;
604 0 : default: return FD_BLOCK_MARKER_DE_ERR_INVAL;
605 0 : }
606 0 : if( FD_UNLIKELY( err ) ) return err;
607 0 : FAIL( payload_sz!=(ulong)marker.length, SZ ); /* LengthPrefixed::len is exact */
608 :
609 0 : return FD_BLOCK_MARKER_DE_SUCCESS;
610 0 : }
|