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_message.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 : /* zero means nothing to do (no proof has been verified) */ 37 : 38 12 : #define FD_EQVOC_SUCCESS (0) /* shreds do not equivocate */ 39 : 40 : /* positive error codes means there is a proof of equivocation */ 41 : 42 24 : #define FD_EQVOC_VERIFIED_MERKLE (1) 43 12 : #define FD_EQVOC_VERIFIED_META (2) 44 12 : #define FD_EQVOC_VERIFIED_LAST (3) 45 12 : #define FD_EQVOC_VERIFIED_OVERLAP (4) 46 12 : #define FD_EQVOC_VERIFIED_CHAINED (5) 47 : 48 : /* negative error codes mean the shreds in the proof were not valid inputs */ 49 : 50 0 : #define FD_EQVOC_ERR_SER (-1) /* invalid serialization */ 51 0 : #define FD_EQVOC_ERR_SLOT (-2) /* different slot */ 52 0 : #define FD_EQVOC_ERR_VERSION (-3) /* different shred version */ 53 0 : #define FD_EQVOC_ERR_TYPE (-4) /* wrong shred type (must be chained merkle) */ 54 0 : #define FD_EQVOC_ERR_MERKLE (-5) /* failed to derive merkle root */ 55 0 : #define FD_EQVOC_ERR_SIG (-6) /* failed to sigverify */ 56 : 57 : /* FD_EQVOC_CHUNK_CNT: the count of chunks is hardcoded because Agave 58 : discards any chunks where count != 3 (even though technically the 59 : schema supports it). 60 : 61 : See: https://github.com/anza-xyz/agave/blob/v3.1/gossip/src/duplicate_shred_handler.rs#L21 */ 62 : 63 0 : #define FD_EQVOC_CHUNK_CNT (3) 64 : 65 : /* FD_EQVOC_CHUNK_SZ: the size of data in each chunk Firedancer produces 66 : in a DuplicateShred message is derived below. 67 : 68 : IPv6 MTU - IP / UDP headers = 1232 69 : DuplicateShredMaxPayloadSize = 1232 - 115 70 : DuplicateShred headers = 63 71 : 72 : This is not enforce on receive (Firedancer will accept smaller chunk 73 : payloads). 74 : 75 : See: https://github.com/anza-xyz/agave/blob/v2.0.3/gossip/src/cluster_info.rs#L113 */ 76 : 77 0 : #define FD_EQVOC_CHUNK_SZ (1232UL - 115UL - 63UL) 78 : FD_STATIC_ASSERT( FD_EQVOC_CHUNK_SZ<=sizeof(((fd_gossip_duplicate_shred_t*)0)->chunk), "DuplicateShred chunk max mismatch" ); 79 : 80 : typedef struct fd_eqvoc fd_eqvoc_t; 81 : typedef struct fd_eqvoc_proof fd_eqvoc_proof_t; 82 : 83 : /* fd_eqvoc_{align,footprint} return the required alignment and 84 : footprint of a memory region suitable for use as eqvoc with up to 85 : shred_max shreds and chunk_max chunks. */ 86 : 87 : FD_FN_CONST ulong 88 : fd_eqvoc_align( void ); 89 : 90 : FD_FN_CONST ulong 91 : fd_eqvoc_footprint( ulong shred_max, 92 : ulong slot_max, 93 : ulong from_max ); 94 : 95 : /* fd_eqvoc_new formats an unused memory region for use as a eqvoc. 96 : mem is a non-NULL pointer to this region in the local address space 97 : with the required footprint and alignment. */ 98 : 99 : void * 100 : fd_eqvoc_new( void * shmem, 101 : ulong shred_max, 102 : ulong cache_max, 103 : ulong proof_max, 104 : ulong seed ); 105 : 106 : /* fd_eqvoc_join joins the caller to the eqvoc. eqvoc points to the 107 : first byte of the memory region backing the eqvoc in the caller's 108 : address space. 109 : 110 : Returns a pointer in the local address space to eqvoc on success. */ 111 : 112 : fd_eqvoc_t * 113 : fd_eqvoc_join( void * sheqvoc ); 114 : 115 : /* fd_eqvoc_leave leaves a current local join. Returns a pointer to the 116 : underlying shared memory region on success and NULL on failure (logs 117 : details). Reasons for failure include eqvoc is NULL. */ 118 : 119 : void * 120 : fd_eqvoc_leave( fd_eqvoc_t const * eqvoc ); 121 : 122 : /* fd_eqvoc_delete unformats a memory region used as a eqvoc. Assumes 123 : only the nobody is joined to the region. Returns a pointer to the 124 : underlying shared memory region or NULL if used obviously in error 125 : (e.g. eqvoc is obviously not a eqvoc ... logs details). The 126 : ownership of the memory region is transferred to the caller. */ 127 : 128 : void * 129 : fd_eqvoc_delete( void * sheqvoc ); 130 : 131 : /* fd_eqvoc_set_shred_version sets the shred version on eqvoc. */ 132 : 133 : void 134 : fd_eqvoc_set_shred_version( fd_eqvoc_t * eqvoc, 135 : ushort version ); 136 : 137 : /* fd_eqvoc_set_leader_schedule sets the leader schedule on eqvoc. */ 138 : 139 : void 140 : fd_eqvoc_set_leader_schedule( fd_eqvoc_t * eqvoc, 141 : fd_epoch_leaders_t const * lsched ); 142 : 143 : /* fd_eqvoc_shred_insert inserts the shred into eqvoc. Returns an error 144 : code (FD_EQVOC_{SUCCESS,PROOF_{...},ERR_{...}}) indicating whether 145 : eqvoc found a shred that conflicts with another shred, indicating 146 : equivocation. If the error code is positive, chunks_out will be 147 : populated with a DuplicateShred proof that can be sent over gossip. 148 : Assumes shred has already been validated by the shred tile. */ 149 : 150 : int 151 : fd_eqvoc_shred_insert( fd_eqvoc_t * eqvoc, 152 : fd_shred_t const * shred, 153 : fd_gossip_duplicate_shred_t chunks_out[static FD_EQVOC_CHUNK_CNT] ); 154 : 155 : /* fd_eqvoc_chunk_insert inserts the DuplicateShred chunk from gossip 156 : into eqvoc. Returns one of FD_EQVOC_{SUCCESS,PROOF_{...},ERR_{...}}, 157 : an error code indicating whether eqvoc was able to verify the proof. 158 : If eqvoc hasn't received all the chunks, returns FD_EQVOC_SUCCESS. */ 159 : 160 : int 161 : fd_eqvoc_chunk_insert( fd_eqvoc_t * eqvoc, 162 : fd_pubkey_t const * from, 163 : fd_gossip_duplicate_shred_t const * chunk ); 164 : 165 : #endif /* HEADER_fd_src_choreo_eqvoc_fd_eqvoc_h */