Line data Source code
1 : #ifndef HEADER_fd_src_ballet_shred_fd_shred_h 2 : #define HEADER_fd_src_ballet_shred_fd_shred_h 3 : 4 : #include "../bmtree/fd_bmtree.h" 5 : 6 : /* Shreds form the on-wire representation of Solana block data 7 : optimized for transmission over unreliable links/WAN. 8 : 9 : ### Layout 10 : 11 : Each shred is 1228 bytes long. 12 : 13 : +------------------------+ 14 : | Common Shred Header | 83 bytes 15 : +------------------------+ 16 : | Data Header | 5 bytes 17 : | or Coding Header | or 6 bytes 18 : +------------------------+ 19 : | | variable 20 : | Payload | length 21 : | | 22 : +------------------------+ 23 : 24 : for Merkle shreds, followed by: 25 : 26 : +------------------------+ 27 : | (Chained merkle root) | 32 bytes 28 : +------------------------+ 29 : +------------------------+ 30 : | Merkle node #0 (root) | 20 bytes 31 : +------------------------+ 32 : | Merkle node #1 | 20 bytes 33 : .......................... 34 : 35 : for resigned shreds shreds, followed by: 36 : 37 : +------------------------+ 38 : | signature | 64 bytes 39 : .......................... 40 : 41 : ### Shredding 42 : 43 : For a given input data blob (usually an entry batch), 44 : data shreds are derived by simply splitting up the blob into subslices. 45 : 46 : Each shred is sized such that it fits into a single UDP packet, 47 : i.e. currently bound by the generally accepted IPv6 MTU of 1280 bytes. 48 : 49 : ### Forward Error Correction 50 : 51 : Coding shreds implement Reed-Solomon error correction to provide tolerance against packet loss. 52 : 53 : Each data shred is first assigned an FEC set. 54 : For the vector of data shreds in each set, a corresponding vector of coding shreds contains parity data. 55 : 56 : FEC sets and entry batches do not necessarily align. 57 : 58 : ### Merkle Inclusion Proofs 59 : 60 : Data and coding shreds come in two variants respectively: legacy and merkle. 61 : Merkle shreds extend legacy shreds by adding FEC set inclusion proofs. 62 : 63 : It allows the block producer to commit to the vector of shreds that make up an FEC set. 64 : The inclusion proof is used to verify whether a shred is part of the FEC set commitment. 65 : 66 : The length of the inclusion proof is indicated by the variant field. 67 : 68 : ### resigned shreds 69 : 70 : Resigned shreds allow for an additional signature to be added on to lock down 71 : the retransmitter for turbine propagation 72 : 73 : ### Authentication 74 : 75 : Shreds are signed by the block producer. 76 : Consequentially, only the block producer is able to create valid shreds for any given block. */ 77 : 78 : #include "../fd_ballet.h" 79 : 80 : /* FD_SHRED_MAX_SZ: The max byte size of a shred. 81 : This limit derives from the IPv6 MTU of 1280 bytes, minus 48 bytes 82 : for the UDP/IPv6 headers and another 4 bytes for good measure. Most 83 : shreds are this size, but Merkle data shreds may be smaller. */ 84 102429051 : #define FD_SHRED_MAX_SZ (1228UL) 85 : /* FD_SHRED_MIN_SZ: The minimum byte size of a shred. 86 : A code shred of the max size covers a data shred of the minimum size 87 : with no padding. */ 88 102427464 : #define FD_SHRED_MIN_SZ (1203UL) 89 : /* FD_SHRED_DATA_HEADER_SZ: size of all headers for data type shreds. */ 90 100564290 : #define FD_SHRED_DATA_HEADER_SZ (0x58UL) 91 : /* FD_SHRED_CODE_HEADER_SZ: size of all headers for coding type shreds. */ 92 51177516 : #define FD_SHRED_CODE_HEADER_SZ (0x59UL) 93 : /* This is a conservative bound. 94 : It's possible for a modified validator to create a data shred with 95 : this much payload. 96 : A validator that follows the default shredding policy should have 97 : payloads of no more than 1015 bytes. 98 : In general, shreds that are chained or resigned should have smaller 99 : payloads and a tigher bound. */ 100 0 : #define FD_SHRED_DATA_PAYLOAD_MAX (FD_SHRED_MIN_SZ-FD_SHRED_DATA_HEADER_SZ) 101 : 102 : /* FD_SHRED_TYPE_* identifies the type of a shred. 103 : It is located at the four high bits of byte 0x40 (64) of the shred header 104 : and can be extracted using the fd_shred_type() function. */ 105 : /* FD_SHRED_TYPE_LEGACY_DATA: A shred carrying raw binary data. */ 106 101460588 : #define FD_SHRED_TYPE_LEGACY_DATA ((uchar)0xA0) 107 : /* FD_SHRED_TYPE_LEGACY_CODE: A shred carrying Reed-Solomon ECC. */ 108 : #define FD_SHRED_TYPE_LEGACY_CODE ((uchar)0x50) 109 : /* FD_SHRED_TYPE_MERKLE_DATA: A shred carrying raw binary data and a merkle inclusion proof. */ 110 50478285 : #define FD_SHRED_TYPE_MERKLE_DATA ((uchar)0x80) 111 : /* FD_SHRED_TYPE_MERKLE_CODE: A shred carrying Reed-Solomon ECC and a merkle inclusion proof. */ 112 153216249 : #define FD_SHRED_TYPE_MERKLE_CODE ((uchar)0x40) 113 : /* FD_SHRED_TYPE_MERKLE_DATA_CHAINED: A shred carrying raw binary data and a chained merkle inclusion proof. */ 114 23319 : #define FD_SHRED_TYPE_MERKLE_DATA_CHAINED ((uchar)0x90) 115 : /* FD_SHRED_TYPE_MERKLE_CODE_CHAINED: A shred carrying Reed-Solomon ECC and a chained merkle inclusion proof. */ 116 22455 : #define FD_SHRED_TYPE_MERKLE_CODE_CHAINED ((uchar)0x60) 117 : 118 : /* FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED: A shred carrying raw binary data and a chained merkle inclusion proof and resigned. */ 119 101486922 : #define FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED ((uchar)0xB0) 120 : /* FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED: A shred carrying Reed-Solomon ECC and a chained merkle inclusion proof and resigned. */ 121 101485290 : #define FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED ((uchar)0x70) 122 : 123 : /* FD_SHRED_TYPEMASK_DATA: bitwise AND with type matches data shred */ 124 : #define FD_SHRED_TYPEMASK_DATA FD_SHRED_TYPE_MERKLE_DATA 125 : /* FD_SHRED_TYPEMASK_CODE: bitwise AND with type matches code shred */ 126 101457648 : #define FD_SHRED_TYPEMASK_CODE FD_SHRED_TYPE_MERKLE_CODE 127 : 128 : /* FD_SHRED_MERKLE_ROOT_SZ: the size of a merkle tree root in bytes. */ 129 22002 : #define FD_SHRED_MERKLE_ROOT_SZ (32UL) 130 : /* FD_SHRED_MERKLE_NODE_SZ: the size of a merkle inclusion proof node in bytes. */ 131 102946299 : #define FD_SHRED_MERKLE_NODE_SZ (20UL) 132 : /* FD_SHRED_MERKLE_LAYER_CNT: the count of inclusion proof layers in the binary merkle tree. */ 133 168 : #define FD_SHRED_MERKLE_LAYER_CNT (10UL) 134 : /* FD_SHRED_SIGNATURE_SZ: the size of a signature in a shred. */ 135 101467743 : #define FD_SHRED_SIGNATURE_SZ (64UL) 136 : /* A merkle inclusion proof node. */ 137 : typedef uchar fd_shred_merkle_t[FD_SHRED_MERKLE_NODE_SZ]; 138 : 139 : /* Constants relating to the data shred "flags" field. */ 140 : 141 : /* Mask of the "reference tick" field in shred.data.flags */ 142 : #define FD_SHRED_DATA_REF_TICK_MASK ((uchar)0x3f) 143 : /* Mask of the "slot complete" bit in shred.data.flags 144 : Indicates the last shred in a slot. */ 145 : #define FD_SHRED_DATA_FLAG_SLOT_COMPLETE ((uchar)0x80) 146 : /* Mask of the "data batch complete" bit in shred.data.flags */ 147 0 : #define FD_SHRED_DATA_FLAG_DATA_COMPLETE ((uchar)0x40) 148 : 149 : /* Maximum number of data shreds in a slot, also maximum number of parity shreds in a slot */ 150 0 : #define FD_SHRED_MAX_PER_SLOT (1 << 15UL) /* 32,768 shreds */ 151 : 152 : /* 36,536,320 bytes per slot */ 153 0 : #define FD_SHRED_DATA_PAYLOAD_MAX_PER_SLOT (FD_SHRED_DATA_PAYLOAD_MAX * FD_SHRED_MAX_PER_SLOT) 154 : 155 : /* Offset of the shred variant. Used for parsing. */ 156 : #define FD_SHRED_VARIANT_OFF 0x40 157 : 158 : /* Firedancer-specific internal error codes. 159 : 160 : These are not part of the Solana protocol. */ 161 : 162 0 : #define FD_SHRED_EBATCH 0x4000 /* End of batch reached (success) 163 : no more shreds and found FD_SHRED_DATA_FLAG_DATA_COMPLETE */ 164 0 : #define FD_SHRED_ESLOT 0x8000 /* End of slot reached (success) 165 : no more shreds and found FD_SHRED_DATA_FLAG_SLOT_COMPLETE */ 166 0 : #define FD_SHRED_ENOMEM 12 /* Error: Target buffer too small */ 167 0 : #define FD_SHRED_EINVAL 22 /* Error: Invalid shred data */ 168 0 : #define FD_SHRED_EPIPE 32 /* Error: Expected data in source buffer, got EOF */ 169 : 170 : /* Primary shred data structure. 171 : Relies heavily on packed fields and unaligned memory accesses. */ 172 : struct __attribute__((packed)) fd_shred { 173 : /* Ed25519 signature over the shred 174 : 175 : For legacy type shreds, signs over content of the shred structure past this signature field. 176 : For merkle type shreds, signs over the first node of the inclusion proof (merkle root). */ 177 : /* 0x00 */ fd_ed25519_sig_t signature; 178 : 179 : /* Shred variant specifier 180 : Consists of two four bit fields. (Deliberately not using bit fields here) 181 : 182 : The high four bits indicate the shred type: 183 : - 0101: legacy code 184 : - 1010: legacy data 185 : - 0100: merkle code 186 : - 0110: merkle code (chained) 187 : - 0111: merkle code (chained resigned) 188 : - 1000: merkle data 189 : - 1001: merkle data (chained) 190 : - 1011: merkle data (chained resigned) 191 : 192 : For legacy type shreds, the low four bits are set to static patterns. 193 : For merkle type shreds, the low four bits are set to the number of non-root nodes in the inclusion proof. 194 : For merkle code type shreds, the 3rd highest bit represents if the merkle tree is chained. 195 : For merkle data type shreds, the 4th highest bit represents if the merkle tree is chained. 196 : For merkle code type shreds, the 4th highest bit represents if the shred is resigned. 197 : For merkle data type shreds, the 3th highest bit represents if the shred is resigned. 198 : */ 199 : /* 0x40 */ uchar variant; 200 : 201 : /* Slot number that this shred is part of */ 202 : /* 0x41 */ ulong slot; 203 : 204 : /* Index of this shred within the slot */ 205 : /* 0x49 */ uint idx; 206 : 207 : /* Hash of the genesis version and historical hard forks of the current chain */ 208 : /* 0x4d */ ushort version; 209 : 210 : /* Index into the vector of FEC sets for this slot. For data shreds, fec_set_idx<=idx. */ 211 : /* 0x4f */ uint fec_set_idx; 212 : 213 : union { 214 : /* Common data shred header */ 215 : struct __attribute__((packed)) { 216 : /* Slot number difference between this block and the parent block. 217 : parent_off <= slot. 218 : Always greater than zero, except for slot 0, in which case the 219 : previous invariant forces this to be 0. */ 220 : /* 0x53 */ ushort parent_off; 221 : 222 : /* Bit field (MSB first) 223 : See FD_SHRED_DATA_FLAG_* 224 : 225 : [XX.. ....] Block complete? 0b00=no 0b01=no 0b11=yes (implies Entry batch complete) 226 : [.X.. ....] Entry batch complete? 0b0=no 0b1=yes 227 : [..XX XXXX] Reference tick number */ 228 : /* 0x55 */ uchar flags; 229 : 230 : /* Shred size: size of data shred headers (88 bytes) + payload length */ 231 : /* 0x56 */ ushort size; 232 : } data; 233 : 234 : /* Common coding shred header */ 235 : struct __attribute__((packed)) { 236 : /* Total number of data shreds in FEC set. Must be positive <= FD_REEDSOL_DATA_SHREDS_MAX. */ 237 : /* 0x53 */ ushort data_cnt; 238 : 239 : /* Total number of coding shreds in FEC set. Must be positive <= FD_REEDSOL_CODE_SHREDS_MAX. */ 240 : /* 0x55 */ ushort code_cnt; 241 : 242 : /* Index within the vector of coding shreds in slot. In [0, 243 : code_cnt). Also, shred.code.idx <= shred.idx. */ 244 : /* 0x57 */ ushort idx; 245 : } code; 246 : }; 247 : }; 248 : typedef struct fd_shred fd_shred_t; 249 : 250 : FD_PROTOTYPES_BEGIN 251 : 252 : /* fd_shred_parse: Parses and validates an untrusted shred stored in 253 : bytes buf[i] for i in [0, sz). sz must be at least FD_SHRED_MIN_SZ 254 : bytes. Allows trailing data. 255 : 256 : The returned pointer either equals the input pointer or is NULL if 257 : the given shred is malformed or violates any invariants described 258 : above. */ 259 : FD_FN_PURE fd_shred_t const * 260 : fd_shred_parse( uchar const * buf, 261 : ulong sz ); 262 : 263 : /* fd_shred_type: Returns the value of the shred's type field. (FD_SHRED_TYPE_*) */ 264 : FD_FN_CONST static inline uchar 265 308909802 : fd_shred_type( uchar variant ) { 266 308909802 : return variant & 0xf0; 267 308909802 : } 268 : 269 : /* fd_shred_variant: Returns the encoded variant field 270 : given the shred type and merkle proof length. */ 271 : FD_FN_CONST static inline uchar 272 : fd_shred_variant( uchar type, 273 101837721 : uchar merkle_cnt ) { 274 101837721 : if( FD_LIKELY( type==FD_SHRED_TYPE_LEGACY_DATA ) ) 275 3 : merkle_cnt = 0x05; 276 101837721 : if( FD_LIKELY( type==FD_SHRED_TYPE_LEGACY_CODE ) ) 277 3 : merkle_cnt = 0x0a; 278 101837721 : return (uchar)(type | merkle_cnt); 279 101837721 : } 280 : 281 : FD_FN_PURE static inline ulong 282 101456202 : fd_shred_sz( fd_shred_t const * shred ) { 283 101456202 : uchar type = fd_shred_type( shred->variant ); 284 101456202 : return fd_ulong_if( 285 101456202 : type & FD_SHRED_TYPEMASK_CODE, 286 101456202 : FD_SHRED_MAX_SZ, 287 101456202 : fd_ulong_if( type==FD_SHRED_TYPE_LEGACY_DATA, shred->data.size, FD_SHRED_MIN_SZ) 288 101456202 : ); /* Legacy data */ 289 101456202 : } 290 : 291 : /* fd_shred_header_sz: Returns the header size of a shred. 292 : Returns zero if the shred has an invalid variant. 293 : 294 : Accesses offsets up to FD_SHRED_HEADER_MIN_SZ. */ 295 : FD_FN_CONST static inline ulong 296 9921 : fd_shred_header_sz( uchar variant ) { 297 9921 : uchar type = fd_shred_type( variant ); 298 9921 : if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) 299 4533 : return FD_SHRED_DATA_HEADER_SZ; 300 5388 : if( FD_LIKELY( type & FD_SHRED_TYPEMASK_CODE ) ) 301 5388 : return FD_SHRED_CODE_HEADER_SZ; 302 0 : return 0; 303 5388 : } 304 : 305 : /* fd_shred_merkle_cnt: Returns number of nodes in the merkle inclusion 306 : proof. Note that this excludes the root. Returns zero if the given 307 : shred is not a merkle variant. */ 308 : FD_FN_CONST static inline uint 309 101470488 : fd_shred_merkle_cnt( uchar variant ) { 310 101470488 : uchar type = fd_shred_type( variant ); 311 101470488 : if( FD_UNLIKELY( ( type == FD_SHRED_TYPE_LEGACY_DATA ) | ( type == FD_SHRED_TYPE_LEGACY_CODE ) ) ) 312 45 : return 0; 313 101470443 : return (variant&0xfU); 314 101470488 : } 315 : 316 : /* fd_shred_merkle_sz: Returns the size in bytes of the merkle inclusion proof. 317 : Returns zero if the given shred is not a merkle variant. */ 318 : FD_FN_CONST static inline ulong 319 101461281 : fd_shred_merkle_sz( uchar variant ) { 320 101461281 : return fd_shred_merkle_cnt( variant ) * FD_SHRED_MERKLE_NODE_SZ; 321 101461281 : } 322 : 323 : 324 : /* fd_shred_is_chained: Returns true if the shred is a chained merkle data or code shred. */ 325 : FD_FN_CONST static inline uchar 326 18858 : fd_shred_is_chained( ulong type ) { 327 18858 : return (uchar)( 328 18858 : ( type == FD_SHRED_TYPE_MERKLE_DATA_CHAINED ) 329 18858 : | ( type == FD_SHRED_TYPE_MERKLE_CODE_CHAINED ) 330 18858 : | ( type == FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED ) 331 18858 : | ( type == FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED ) ); 332 18858 : } 333 : 334 : /* fd_shred_is_resigned: Returns true if the shred is resigned by the retransmitter */ 335 : FD_FN_CONST static inline uchar 336 101464515 : fd_shred_is_resigned( ulong type ) { 337 101464515 : return ( type == FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED ) 338 101464515 : | ( type == FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED ); 339 101464515 : } 340 : 341 : /* fd_shred_is_{data,code} return 1 if the provided shred type is one of 342 : the data (or code, respectively) types, and 0 if not. The value 343 : provided for type must be a valid shred type (one of the 344 : FD_SHRED_TYPE_* values). For the purposes of these functions, 345 : properties beyond data/code are ignored; e.g. a chained resigned 346 : Merkle data shred is considered a data shred. */ 347 4510254 : FD_FN_CONST static inline uchar fd_shred_is_data( ulong type ) { return (type & 0xC0UL)==0x80UL; } 348 294 : FD_FN_CONST static inline uchar fd_shred_is_code( ulong type ) { return (type & 0xC0UL)==0x40UL; } 349 : 350 : /* fd_shred_swap_type: changes data into code or vice versa without 351 : affecting leagacy, merkle, chained, or resigned status. For example, 352 : fd_shred_swap_type( chained resigned data ) == chained resigned code. 353 : fd_shred_swap_type( merkle code ) == merkle data. */ 354 : FD_FN_CONST static inline uchar 355 3060 : fd_shred_swap_type( ulong type ) { 356 : /* Swap bits 4 and 5. Swap bits 6 and 7. */ 357 3060 : return (uchar)(((type & 0x50UL)<<1) | ((type&0xA0UL)>>1)); 358 3060 : } 359 : 360 : /* fd_shred_payload_sz: Returns the payload size of a shred. 361 : Undefined behavior if the shred has not passed `fd_shred_parse`. */ 362 : FD_FN_PURE static inline ulong 363 294 : fd_shred_payload_sz( fd_shred_t const * shred ) { 364 294 : ulong type = fd_shred_type( shred->variant ); 365 294 : if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) { 366 147 : return shred->data.size - FD_SHRED_DATA_HEADER_SZ; 367 147 : } else { 368 147 : return fd_shred_sz( shred ) - FD_SHRED_CODE_HEADER_SZ 369 147 : - fd_shred_merkle_sz( shred->variant ) 370 147 : - fd_ulong_if( fd_shred_is_chained( type ), FD_SHRED_MERKLE_ROOT_SZ, 0 ) 371 147 : - fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0 ); 372 147 : } 373 294 : } 374 : 375 : /* fd_shred_merkle_off: Returns the byte offset of the merkle inclusion proof of a shred. 376 : 377 : The provided shred must have passed validation in fd_shred_parse(). */ 378 : FD_FN_PURE static inline ulong 379 101449767 : fd_shred_merkle_off( fd_shred_t const * shred ) { 380 101449767 : ulong type = fd_shred_type( shred->variant ); 381 101449767 : return fd_shred_sz( shred ) 382 101449767 : - fd_shred_merkle_sz( shred->variant ) 383 101449767 : - fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0 ); 384 101449767 : } 385 : 386 : /* fd_shred_merkle_nodes: Returns a pointer to the shred's merkle proof data. 387 : 388 : The provided shred must have passed validation in fd_shred_parse(). */ 389 : FD_FN_PURE static inline fd_shred_merkle_t const * 390 3021 : fd_shred_merkle_nodes( fd_shred_t const * shred ) { 391 3021 : uchar const * ptr = (uchar const *)shred; 392 3021 : ptr += fd_shred_merkle_off( shred ); 393 3021 : return (fd_shred_merkle_t const *)ptr; 394 3021 : } 395 : 396 : /* fd_shred_merkle_root: Assuming that `shred` is a Merkle variant, 397 : reconstructs the merkle root from a shred and populates it in 398 : root_out. Returns 1 on success, 0 on failure. The output value must 399 : be ignored if a failure is returned. U.B. if the shred is not a 400 : merkle variant. */ 401 : FD_FN_PURE int 402 : fd_shred_merkle_root( fd_shred_t const * shred, void * bmtree_mem, fd_bmtree_node_t * root_out ); 403 : 404 : /* fd_shred_data_payload: Returns a pointer to a data shred payload. 405 : 406 : The provided shred must have passed validation in fd_shred_parse(), 407 : and must satisfy `type&FD_SHRED_TYPEMASK_DATA` 408 : where `uchar type = fd_shred_type( shred->variant )`. */ 409 : FD_FN_CONST static inline uchar const * 410 9 : fd_shred_data_payload( fd_shred_t const * shred ) { 411 9 : return (uchar const *)shred + FD_SHRED_DATA_HEADER_SZ; 412 9 : } 413 : 414 : /* fd_shred_code_payload: Returns a pointer to a coding shred payload. 415 : 416 : The provided shred must have passed validation in fd_shred_parse(), 417 : and must satisfy `type&FD_SHRED_TYPEMASK_CODE` 418 : where `uchar type = fd_shred_type( shred->variant )`. */ 419 : FD_FN_CONST static inline uchar const * 420 0 : fd_shred_code_payload( fd_shred_t const * shred ) { 421 0 : return (uchar const *)shred + FD_SHRED_CODE_HEADER_SZ; 422 0 : } 423 : 424 : /* fd_shred_chain_offset: Assuming that `shred` is a chained Merkle 425 : variant, compute the offset from the start of the shred to the start 426 : of the chained Merkle root. U.B. if the shred is not a chained 427 : variant. */ 428 : FD_FN_CONST static inline ulong 429 1446 : fd_shred_chain_off( uchar variant ) { 430 1446 : ulong type = fd_shred_type( variant ); 431 1446 : return fd_ulong_if( type & FD_SHRED_TYPEMASK_CODE, FD_SHRED_MAX_SZ, FD_SHRED_MIN_SZ ) 432 1446 : - FD_SHRED_MERKLE_ROOT_SZ 433 1446 : - fd_shred_merkle_sz( variant ) 434 1446 : - fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0 ); 435 1446 : } 436 : 437 : /* fd_shred_retrasmitter_sig_off: Assuming that `shred` is a resigned 438 : variant, compute the offset from the start of the shred to the start 439 : of the retransmitter signature. U.B if the shred is not a resigned 440 : chained type. */ 441 : FD_FN_PURE static inline ulong 442 192 : fd_shred_retransmitter_sig_off( fd_shred_t const * shred ) { 443 192 : return fd_shred_sz( shred )-FD_SHRED_SIGNATURE_SZ; 444 192 : } 445 : 446 : FD_PROTOTYPES_END 447 : 448 : #endif /* HEADER_fd_src_ballet_shred_fd_shred_h */