Line data Source code
1 : #ifndef HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h 2 : #define HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h 3 : 4 : #include "../fd_choreo_base.h" 5 : #include "../../ballet/shred/fd_shred.h" 6 : #include "../../flamenco/leaders/fd_leaders.h" 7 : #include "../../flamenco/gossip/fd_gossip_types.h" 8 : 9 : /* fd_eqvoc presents an API for detecting and sending & receiving proofs 10 : of equivocation. 11 : 12 : APIs prefixed with `fd_eqvoc_proof` relate to constructing and 13 : verifying equivocation proofs from shreds. 14 : 15 : APIs prefixed with `fd_eqvoc_fec` relate to shred and FEC set 16 : metadata indexing to detect equivocating shreds. 17 : 18 : Equivocation is when a shred producer produces two or more versions 19 : of a shred for the same (slot, idx). An equivocation proof comprises 20 : two shreds that conflict in a way that imply the shreds' producer 21 : equivocated. 22 : 23 : The proof can be both direct and indirect (implied). A direct proof, 24 : for example, contains two shreds with the same slot and shred index 25 : but different data payloads. An indirect proof contains two shreds 26 : with different shred indices, and the metadata on the shreds implies 27 : there must be two or more versions of a block for that slot. See 28 : `construct_proof` or `verify_proof` in fd_eqvoc.c for more details. 29 : 30 : Every shred in a FEC set must have the same signature, so a different 31 : value in the signature field would indicate equivocation. Note in 32 : the case of merkle shreds, the shred signature is signed on the FEC 33 : set's merkle root, so every shred in the same FEC set must have the 34 : same signature. */ 35 : 36 : /* FD_EQVOC_USE_HANDHOLDING: Define this to non-zero at compile time 37 : to turn on additional runtime checks and logging. */ 38 : 39 : #ifndef FD_EQVOC_USE_HANDHOLDING 40 : #define FD_EQVOC_USE_HANDHOLDING 1 41 : #endif 42 : 43 : /* zero means nothing to do (no proof has been verified) */ 44 : 45 9 : #define FD_EQVOC_SUCCESS (0) /* shreds do not equivocate */ 46 : 47 : /* positive error codes means there is a proof of equivocation */ 48 : 49 18 : #define FD_EQVOC_VERIFIED_MERKLE (1) 50 9 : #define FD_EQVOC_VERIFIED_META (2) 51 9 : #define FD_EQVOC_VERIFIED_LAST (3) 52 9 : #define FD_EQVOC_VERIFIED_OVERLAP (4) 53 9 : #define FD_EQVOC_VERIFIED_CHAINED (5) 54 : 55 : /* negative error codes mean the shreds in the proof were not valid inputs */ 56 : 57 0 : #define FD_EQVOC_ERR_SER (-1) /* invalid serialization */ 58 0 : #define FD_EQVOC_ERR_SLOT (-2) /* different slot */ 59 0 : #define FD_EQVOC_ERR_VERSION (-3) /* different shred version */ 60 0 : #define FD_EQVOC_ERR_TYPE (-4) /* wrong shred type (must be chained merkle) */ 61 0 : #define FD_EQVOC_ERR_MERKLE (-5) /* failed to derive merkle root */ 62 0 : #define FD_EQVOC_ERR_SIG (-6) /* failed to sigverify */ 63 : 64 : /* FD_EQVOC_CHUNK_CNT: the count of chunks is hardcoded because Agave 65 : discards any chunks where count != 3 (even though technically the 66 : schema supports it). 67 : 68 : See: https://github.com/anza-xyz/agave/blob/v3.1/gossip/src/duplicate_shred_handler.rs#L21 */ 69 : 70 0 : #define FD_EQVOC_CHUNK_CNT (3) 71 : 72 : /* FD_EQVOC_CHUNK_SZ: the size of data in each chunk Firedancer produces 73 : in a DuplicateShred message is derived below. 74 : 75 : IPv6 MTU - IP / UDP headers = 1232 76 : DuplicateShredMaxPayloadSize = 1232 - 115 77 : DuplicateShred headers = 63 78 : 79 : This is not enforce on receive (Firedancer will accept smaller chunk 80 : payloads). 81 : 82 : See: https://github.com/anza-xyz/agave/blob/v2.0.3/gossip/src/cluster_info.rs#L113 */ 83 : 84 0 : #define FD_EQVOC_CHUNK_SZ (1232UL - 115UL - 63UL) 85 : FD_STATIC_ASSERT( FD_EQVOC_CHUNK_SZ==FD_GOSSIP_DUPLICATE_SHRED_MAX_CHUNKS, "DuplicateShred chunk max mismatch" ); 86 : 87 : typedef struct fd_eqvoc fd_eqvoc_t; 88 : typedef struct fd_eqvoc_proof fd_eqvoc_proof_t; 89 : 90 : /* fd_eqvoc_{align,footprint} return the required alignment and 91 : footprint of a memory region suitable for use as eqvoc with up to 92 : shred_max shreds and chunk_max chunks. */ 93 : 94 : FD_FN_CONST ulong 95 : fd_eqvoc_align( void ); 96 : 97 : FD_FN_CONST ulong 98 : fd_eqvoc_footprint( ulong shred_max, 99 : ulong slot_max, 100 : ulong from_max ); 101 : 102 : /* fd_eqvoc_new formats an unused memory region for use as a eqvoc. 103 : mem is a non-NULL pointer to this region in the local address space 104 : with the required footprint and alignment. */ 105 : 106 : void * 107 : fd_eqvoc_new( void * shmem, 108 : ulong shred_max, 109 : ulong cache_max, 110 : ulong proof_max, 111 : ulong seed ); 112 : 113 : /* fd_eqvoc_join joins the caller to the eqvoc. eqvoc points to the 114 : first byte of the memory region backing the eqvoc in the caller's 115 : address space. 116 : 117 : Returns a pointer in the local address space to eqvoc on success. */ 118 : 119 : fd_eqvoc_t * 120 : fd_eqvoc_join( void * sheqvoc ); 121 : 122 : /* fd_eqvoc_leave leaves a current local join. Returns a pointer to the 123 : underlying shared memory region on success and NULL on failure (logs 124 : details). Reasons for failure include eqvoc is NULL. */ 125 : 126 : void * 127 : fd_eqvoc_leave( fd_eqvoc_t const * eqvoc ); 128 : 129 : /* fd_eqvoc_delete unformats a memory region used as a eqvoc. Assumes 130 : only the nobody is joined to the region. Returns a pointer to the 131 : underlying shared memory region or NULL if used obviously in error 132 : (e.g. eqvoc is obviously not a eqvoc ... logs details). The 133 : ownership of the memory region is transferred to the caller. */ 134 : 135 : void * 136 : fd_eqvoc_delete( void * sheqvoc ); 137 : 138 : /* fd_eqvoc_set_shred_version sets the shred version on eqvoc. */ 139 : 140 : void 141 : fd_eqvoc_set_shred_version( fd_eqvoc_t * eqvoc, 142 : ushort version ); 143 : 144 : /* fd_eqvoc_set_leader_schedule sets the leader schedule on eqvoc. */ 145 : 146 : void 147 : fd_eqvoc_set_leader_schedule( fd_eqvoc_t * eqvoc, 148 : fd_epoch_leaders_t const * lsched ); 149 : 150 : /* fd_eqvoc_shred_insert inserts the shred into eqvoc. Returns an error 151 : code (FD_EQVOC_{SUCCESS,PROOF_{...},ERR_{...}}) indicating whether 152 : eqvoc found a shred that conflicts with another shred, indicating 153 : equivocation. If the error code is positive, chunks_out will be 154 : populated with a DuplicateShred proof that can be sent over gossip. 155 : Assumes shred has already been validated by the shred tile. */ 156 : 157 : int 158 : fd_eqvoc_shred_insert( fd_eqvoc_t * eqvoc, 159 : fd_shred_t const * shred, 160 : fd_gossip_duplicate_shred_t chunks_out[static FD_EQVOC_CHUNK_CNT] ); 161 : 162 : /* fd_eqvoc_chunk_insert inserts the DuplicateShred chunk from gossip 163 : into eqvoc. Returns one of FD_EQVOC_{SUCCESS,PROOF_{...},ERR_{...}}, 164 : an error code indicating whether eqvoc was able to verify the proof. 165 : If eqvoc hasn't received all the chunks, returns FD_EQVOC_SUCCESS. */ 166 : 167 : int 168 : fd_eqvoc_chunk_insert( fd_eqvoc_t * eqvoc, 169 : fd_pubkey_t const * from, 170 : fd_gossip_duplicate_shred_t const * chunk ); 171 : 172 : #endif /* HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h */