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 102428826 : #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 102427233 : #define FD_SHRED_MIN_SZ (1203UL) 89 : /* FD_SHRED_DATA_HEADER_SZ: size of all headers for data type shreds. */ 90 100564068 : #define FD_SHRED_DATA_HEADER_SZ (0x58UL) 91 : /* FD_SHRED_CODE_HEADER_SZ: size of all headers for coding type shreds. */ 92 51177402 : #define FD_SHRED_CODE_HEADER_SZ (0x59UL) 93 : 94 : /* FD_SHRED_TYPE_* identifies the type of a shred. 95 : It is located at the four high bits of byte 0x40 (64) of the shred header 96 : and can be extracted using the fd_shred_type() function. */ 97 : /* FD_SHRED_TYPE_LEGACY_DATA: A shred carrying raw binary data. */ 98 105967074 : #define FD_SHRED_TYPE_LEGACY_DATA ((uchar)0xA0) 99 : /* FD_SHRED_TYPE_LEGACY_CODE: A shred carrying Reed-Solomon ECC. */ 100 : #define FD_SHRED_TYPE_LEGACY_CODE ((uchar)0x50) 101 : /* FD_SHRED_TYPE_MERKLE_DATA: A shred carrying raw binary data and a merkle inclusion proof. */ 102 54984906 : #define FD_SHRED_TYPE_MERKLE_DATA ((uchar)0x80) 103 : /* FD_SHRED_TYPE_MERKLE_CODE: A shred carrying Reed-Solomon ECC and a merkle inclusion proof. */ 104 153215931 : #define FD_SHRED_TYPE_MERKLE_CODE ((uchar)0x40) 105 : /* FD_SHRED_TYPE_MERKLE_DATA_CHAINED: A shred carrying raw binary data and a chained merkle inclusion proof. */ 106 23292 : #define FD_SHRED_TYPE_MERKLE_DATA_CHAINED ((uchar)0x90) 107 : /* FD_SHRED_TYPE_MERKLE_CODE_CHAINED: A shred carrying Reed-Solomon ECC and a chained merkle inclusion proof. */ 108 22428 : #define FD_SHRED_TYPE_MERKLE_CODE_CHAINED ((uchar)0x60) 109 : 110 : /* FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED: A shred carrying raw binary data and a chained merkle inclusion proof and resigned. */ 111 101486664 : #define FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED ((uchar)0xB0) 112 : /* FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED: A shred carrying Reed-Solomon ECC and a chained merkle inclusion proof and resigned. */ 113 101485032 : #define FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED ((uchar)0x70) 114 : 115 : /* FD_SHRED_TYPEMASK_DATA: bitwise AND with type matches data shred */ 116 : #define FD_SHRED_TYPEMASK_DATA FD_SHRED_TYPE_MERKLE_DATA 117 : /* FD_SHRED_TYPEMASK_CODE: bitwise AND with type matches code shred */ 118 101457426 : #define FD_SHRED_TYPEMASK_CODE FD_SHRED_TYPE_MERKLE_CODE 119 : 120 : /* FD_SHRED_MERKLE_ROOT_SZ: the size of a merkle tree root in bytes. */ 121 21945 : #define FD_SHRED_MERKLE_ROOT_SZ (32UL) 122 : /* FD_SHRED_MERKLE_NODE_SZ: the size of a merkle inclusion proof node in bytes. */ 123 102946059 : #define FD_SHRED_MERKLE_NODE_SZ (20UL) 124 : /* FD_SHRED_MERKLE_LAYER_CNT: the count of inclusion proof layers in the binary merkle tree. */ 125 159 : #define FD_SHRED_MERKLE_LAYER_CNT (10UL) 126 : /* FD_SHRED_SIGNATURE_SZ: the size of a signature in a shred. */ 127 101467497 : #define FD_SHRED_SIGNATURE_SZ (64UL) 128 : /* A merkle inclusion proof node. */ 129 : typedef uchar fd_shred_merkle_t[FD_SHRED_MERKLE_NODE_SZ]; 130 : 131 : /* Constants relating to the data shred "flags" field. */ 132 : 133 : /* Mask of the "reference tick" field in shred.data.flags */ 134 0 : #define FD_SHRED_DATA_REF_TICK_MASK ((uchar)0x3f) 135 : /* Mask of the "slot complete" bit in shred.data.flags 136 : Indicates the last shred in a slot. */ 137 : #define FD_SHRED_DATA_FLAG_SLOT_COMPLETE ((uchar)0x80) 138 : /* Mask of the "data batch complete" bit in shred.data.flags */ 139 : #define FD_SHRED_DATA_FLAG_DATA_COMPLETE ((uchar)0x40) 140 : 141 : /* Maximum number of data shreds in a slot, also maximum number of parity shreds in a slot */ 142 0 : #define FD_SHRED_MAX_PER_SLOT (1 << 15UL) /* 32,768 shreds */ 143 : 144 : /* Offset of the shred variant. Used for parsing. */ 145 : #define FD_SHRED_VARIANT_OFF 0x40 146 : 147 : /* Firedancer-specific internal error codes. 148 : 149 : These are not part of the Solana protocol. */ 150 : 151 0 : #define FD_SHRED_EBATCH 0x4000 /* End of batch reached (success) 152 : no more shreds and found FD_SHRED_DATA_FLAG_DATA_COMPLETE */ 153 0 : #define FD_SHRED_ESLOT 0x8000 /* End of slot reached (success) 154 : no more shreds and found FD_SHRED_DATA_FLAG_SLOT_COMPLETE */ 155 0 : #define FD_SHRED_ENOMEM 12 /* Error: Target buffer too small */ 156 0 : #define FD_SHRED_EINVAL 22 /* Error: Invalid shred data */ 157 0 : #define FD_SHRED_EPIPE 32 /* Error: Expected data in source buffer, got EOF */ 158 : 159 : /* Primary shred data structure. 160 : Relies heavily on packed fields and unaligned memory accesses. */ 161 : struct __attribute__((packed)) fd_shred { 162 : /* Ed25519 signature over the shred 163 : 164 : For legacy type shreds, signs over content of the shred structure past this signature field. 165 : For merkle type shreds, signs over the first node of the inclusion proof (merkle root). */ 166 : /* 0x00 */ fd_ed25519_sig_t signature; 167 : 168 : /* Shred variant specifier 169 : Consists of two four bit fields. (Deliberately not using bit fields here) 170 : 171 : The high four bits indicate the shred type: 172 : - 0101: legacy code 173 : - 1010: legacy data 174 : - 0100: merkle code 175 : - 0110: merkle code (chained) 176 : - 0111: merkle code (chained resigned) 177 : - 1000: merkle data 178 : - 1001: merkle data (chained) 179 : - 1011: merkle data (chained resigned) 180 : 181 : For legacy type shreds, the low four bits are set to static patterns. 182 : For merkle type shreds, the low four bits are set to the number of non-root nodes in the inclusion proof. 183 : For merkle code type shreds, the 3rd highest bit represents if the merkle tree is chained. 184 : For merkle data type shreds, the 4th highest bit represents if the merkle tree is chained. 185 : For merkle code type shreds, the 4th highest bit represents if the shred is resigned. 186 : For merkle data type shreds, the 3th highest bit represents if the shred is resigned. 187 : */ 188 : /* 0x40 */ uchar variant; 189 : 190 : /* Slot number that this shred is part of */ 191 : /* 0x41 */ ulong slot; 192 : 193 : /* Index of this shred within the slot */ 194 : /* 0x49 */ uint idx; 195 : 196 : /* Hash of the genesis version and historical hard forks of the current chain */ 197 : /* 0x4d */ ushort version; 198 : 199 : /* Index into the vector of FEC sets for this slot. For data shreds, fec_set_idx<=idx. */ 200 : /* 0x4f */ uint fec_set_idx; 201 : 202 : union { 203 : /* Common data shred header */ 204 : struct __attribute__((packed)) fd_shred_data { 205 : /* Slot number difference between this block and the parent block. 206 : parent_off <= slot. 207 : Always greater than zero, except for slot 0, in which case the 208 : previous invariant forces this to be 0. */ 209 : /* 0x53 */ ushort parent_off; 210 : 211 : /* Bit field (MSB first) 212 : See FD_SHRED_DATA_FLAG_* 213 : 214 : [XX.. ....] Block complete? 0b00=no 0b01=no 0b11=yes (implies Entry batch complete) 215 : [.X.. ....] Entry batch complete? 0b0=no 0b1=yes 216 : [..XX XXXX] Reference tick number */ 217 : /* 0x55 */ uchar flags; 218 : 219 : /* Shred size: size of data shred headers (88 bytes) + payload length */ 220 : /* 0x56 */ ushort size; 221 : } data; 222 : 223 : /* Common coding shred header */ 224 : struct __attribute__((packed)) fd_shred_code { 225 : /* Total number of data shreds in slot. Must be positive. */ 226 : /* 0x53 */ ushort data_cnt; 227 : 228 : /* Total number of coding shreds in slot. Must be positive. */ 229 : /* 0x55 */ ushort code_cnt; 230 : 231 : /* Index within the vector of coding shreds in slot. In [0, 232 : code_cnt). Also, shred.code.idx <= shred.idx. */ 233 : /* 0x57 */ ushort idx; 234 : } code; 235 : }; 236 : }; 237 : typedef struct fd_shred_data fd_shred_data_t; 238 : typedef struct fd_shred_code fd_shred_code_t; 239 : typedef struct fd_shred fd_shred_t; 240 : 241 : FD_PROTOTYPES_BEGIN 242 : 243 : /* fd_shred_parse: Parses and validates an untrusted shred stored in 244 : bytes buf[i] for i in [0, sz). sz must be at least FD_SHRED_MIN_SZ 245 : bytes. Allows trailing data. 246 : 247 : The returned pointer either equals the input pointer or is NULL if 248 : the given shred is malformed or violates any invariants described 249 : above. */ 250 : FD_FN_PURE fd_shred_t const * 251 : fd_shred_parse( uchar const * buf, 252 : ulong sz ); 253 : 254 : /* fd_shred_type: Returns the value of the shred's type field. (FD_SHRED_TYPE_*) */ 255 : FD_FN_CONST static inline uchar 256 308909103 : fd_shred_type( uchar variant ) { 257 308909103 : return variant & 0xf0; 258 308909103 : } 259 : 260 : /* fd_shred_variant: Returns the encoded variant field 261 : given the shred type and merkle proof length. */ 262 : FD_FN_CONST static inline uchar 263 : fd_shred_variant( uchar type, 264 101837526 : uchar merkle_cnt ) { 265 101837526 : if( FD_LIKELY( type==FD_SHRED_TYPE_LEGACY_DATA ) ) 266 3 : merkle_cnt = 0x05; 267 101837526 : if( FD_LIKELY( type==FD_SHRED_TYPE_LEGACY_CODE ) ) 268 3 : merkle_cnt = 0x0a; 269 101837526 : return (uchar)(type | merkle_cnt); 270 101837526 : } 271 : 272 : FD_FN_PURE static inline ulong 273 101455980 : fd_shred_sz( fd_shred_t const * shred ) { 274 101455980 : uchar type = fd_shred_type( shred->variant ); 275 101455980 : return fd_ulong_if( 276 101455980 : type & FD_SHRED_TYPEMASK_CODE, 277 101455980 : FD_SHRED_MAX_SZ, 278 101455980 : fd_ulong_if( type==FD_SHRED_TYPE_LEGACY_DATA, shred->data.size, FD_SHRED_MIN_SZ) 279 101455980 : ); /* Legacy data */ 280 101455980 : } 281 : 282 : /* fd_shred_header_sz: Returns the header size of a shred. 283 : Returns zero if the shred has an invalid variant. 284 : 285 : Accesses offsets up to FD_SHRED_HEADER_MIN_SZ. */ 286 : FD_FN_CONST static inline ulong 287 9909 : fd_shred_header_sz( uchar variant ) { 288 9909 : uchar type = fd_shred_type( variant ); 289 9909 : if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) 290 4524 : return FD_SHRED_DATA_HEADER_SZ; 291 5385 : if( FD_LIKELY( type & FD_SHRED_TYPEMASK_CODE ) ) 292 5385 : return FD_SHRED_CODE_HEADER_SZ; 293 0 : return 0; 294 5385 : } 295 : 296 : /* fd_shred_merkle_cnt: Returns number of nodes in the merkle inclusion 297 : proof. Note that this excludes the root. Returns zero if the given 298 : shred is not a merkle variant. */ 299 : FD_FN_CONST static inline uint 300 101470254 : fd_shred_merkle_cnt( uchar variant ) { 301 101470254 : uchar type = fd_shred_type( variant ); 302 101470254 : if( FD_UNLIKELY( ( type == FD_SHRED_TYPE_LEGACY_DATA ) | ( type == FD_SHRED_TYPE_LEGACY_CODE ) ) ) 303 45 : return 0; 304 101470209 : return (variant&0xfU); 305 101470254 : } 306 : 307 : /* fd_shred_merkle_sz: Returns the size in bytes of the merkle inclusion proof. 308 : Returns zero if the given shred is not a merkle variant. */ 309 : FD_FN_CONST static inline ulong 310 101461065 : fd_shred_merkle_sz( uchar variant ) { 311 101461065 : return fd_shred_merkle_cnt( variant ) * FD_SHRED_MERKLE_NODE_SZ; 312 101461065 : } 313 : 314 : 315 : /* fd_shred_is_chained: Returns true if the shred is a chained merkle data or code shred. */ 316 : FD_FN_CONST static inline uchar 317 18831 : fd_shred_is_chained( ulong type ) { 318 18831 : return (uchar)( 319 18831 : ( type == FD_SHRED_TYPE_MERKLE_DATA_CHAINED ) 320 18831 : | ( type == FD_SHRED_TYPE_MERKLE_CODE_CHAINED ) 321 18831 : | ( type == FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED ) 322 18831 : | ( type == FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED ) ); 323 18831 : } 324 : 325 : /* fd_shred_is_resigned: Returns true if the shred is resigned by the retransmitter */ 326 : FD_FN_CONST static inline uchar 327 101464284 : fd_shred_is_resigned( ulong type ) { 328 101464284 : return ( type == FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED ) 329 101464284 : | ( type == FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED ); 330 101464284 : } 331 : 332 : /* fd_shred_is_{data,code} return 1 if the provided shred type is one of 333 : the data (or code, respectively) types, and 0 if not. The value 334 : provided for type must be a valid shred type (one of the 335 : FD_SHRED_TYPE_* values). For the purposes of these functions, 336 : properties beyond data/code are ignored; e.g. a chained resigned 337 : Merkle data shred is considered a data shred. */ 338 3522 : FD_FN_CONST static inline uchar fd_shred_is_data( ulong type ) { return (type & 0xC0UL)==0x80UL; } 339 294 : FD_FN_CONST static inline uchar fd_shred_is_code( ulong type ) { return (type & 0xC0UL)==0x40UL; } 340 : 341 : /* fd_shred_swap_type: changes data into code or vice versa without 342 : affecting leagacy, merkle, chained, or resigned status. For example, 343 : fd_shred_swap_type( chained resigned data ) == chained resigned code. 344 : fd_shred_swap_type( merkle code ) == merkle data. */ 345 : FD_FN_CONST static inline uchar 346 3057 : fd_shred_swap_type( ulong type ) { 347 : /* Swap bits 4 and 5. Swap bits 6 and 7. */ 348 3057 : return (uchar)(((type & 0x50UL)<<1) | ((type&0xA0UL)>>1)); 349 3057 : } 350 : 351 : /* fd_shred_payload_sz: Returns the payload size of a shred. 352 : Undefined behavior if the shred has not passed `fd_shred_parse`. */ 353 : FD_FN_PURE static inline ulong 354 294 : fd_shred_payload_sz( fd_shred_t const * shred ) { 355 294 : ulong type = fd_shred_type( shred->variant ); 356 294 : if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) { 357 147 : return shred->data.size - FD_SHRED_DATA_HEADER_SZ; 358 147 : } else { 359 147 : return fd_shred_sz( shred ) - FD_SHRED_CODE_HEADER_SZ 360 147 : - fd_shred_merkle_sz( shred->variant ) 361 147 : - fd_ulong_if( fd_shred_is_chained( type ), FD_SHRED_MERKLE_ROOT_SZ, 0 ) 362 147 : - fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0 ); 363 147 : } 364 294 : } 365 : 366 : /* fd_shred_merkle_off: Returns the byte offset of the merkle inclusion proof of a shred. 367 : 368 : The provided shred must have passed validation in fd_shred_parse(). */ 369 : FD_FN_PURE static inline ulong 370 101449563 : fd_shred_merkle_off( fd_shred_t const * shred ) { 371 101449563 : ulong type = fd_shred_type( shred->variant ); 372 101449563 : return fd_shred_sz( shred ) 373 101449563 : - fd_shred_merkle_sz( shred->variant ) 374 101449563 : - fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0 ); 375 101449563 : } 376 : 377 : /* fd_shred_merkle_nodes: Returns a pointer to the shred's merkle proof data. 378 : 379 : The provided shred must have passed validation in fd_shred_parse(). */ 380 : FD_FN_PURE static inline fd_shred_merkle_t const * 381 3009 : fd_shred_merkle_nodes( fd_shred_t const * shred ) { 382 3009 : uchar const * ptr = (uchar const *)shred; 383 3009 : ptr += fd_shred_merkle_off( shred ); 384 3009 : return (fd_shred_merkle_t const *)ptr; 385 3009 : } 386 : 387 : /* fd_shred_merkle_root: Assuming that `shred` is a Merkle variant, 388 : reconstructs the merkle root from a shred and populates it in 389 : root_out. Returns 1 on success, 0 on failure. The output value must 390 : be ignored if a failure is returned. U.B. if the shred is not a 391 : merkle variant. */ 392 : FD_FN_PURE int 393 : fd_shred_merkle_root( fd_shred_t const * shred, void * bmtree_mem, fd_bmtree_node_t * root_out ); 394 : 395 : /* fd_shred_data_payload: Returns a pointer to a data shred payload. 396 : 397 : The provided shred must have passed validation in fd_shred_parse(), 398 : and must satisfy `type&FD_SHRED_TYPEMASK_DATA` 399 : where `uchar type = fd_shred_type( shred->variant )`. */ 400 : FD_FN_CONST static inline uchar const * 401 3 : fd_shred_data_payload( fd_shred_t const * shred ) { 402 3 : return (uchar const *)shred + FD_SHRED_DATA_HEADER_SZ; 403 3 : } 404 : 405 : /* fd_shred_code_payload: Returns a pointer to a coding shred payload. 406 : 407 : The provided shred must have passed validation in fd_shred_parse(), 408 : and must satisfy `type&FD_SHRED_TYPEMASK_CODE` 409 : where `uchar type = fd_shred_type( shred->variant )`. */ 410 : FD_FN_CONST static inline uchar const * 411 0 : fd_shred_code_payload( fd_shred_t const * shred ) { 412 0 : return (uchar const *)shred + FD_SHRED_CODE_HEADER_SZ; 413 0 : } 414 : 415 : /* fd_shred_chain_offset: Assuming that `shred` is a chained Merkle 416 : variant, compute the offset from the start of the shred to the start 417 : of the chained Merkle root. U.B. if the shred is not a chained 418 : variant. */ 419 : FD_FN_CONST static inline ulong 420 1446 : fd_shred_chain_off( uchar variant ) { 421 1446 : ulong type = fd_shred_type( variant ); 422 1446 : return fd_ulong_if( type & FD_SHRED_TYPEMASK_CODE, FD_SHRED_MAX_SZ, FD_SHRED_MIN_SZ ) 423 1446 : - FD_SHRED_MERKLE_ROOT_SZ 424 1446 : - fd_shred_merkle_sz( variant ) 425 1446 : - fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0 ); 426 1446 : } 427 : 428 : /* fd_shred_retrasmitter_sig_off: Assuming that `shred` is a resigned 429 : variant, compute the offset from the start of the shred to the start 430 : of the retransmitter signature. U.B if the shred is not a resigned 431 : chained type. */ 432 : FD_FN_PURE static inline ulong 433 192 : fd_shred_retransmitter_sig_off( fd_shred_t const * shred ) { 434 192 : return fd_shred_sz( shred )-FD_SHRED_SIGNATURE_SZ; 435 192 : } 436 : 437 : FD_PROTOTYPES_END 438 : 439 : #endif /* HEADER_fd_src_ballet_shred_fd_shred_h */