Line data Source code
1 : #include "fd_crds.h"
2 :
3 : #include "fd_active_set.h"
4 : #include "fd_gossip_hset.h"
5 : #include "../../ballet/sha256/fd_sha256.h"
6 : #include "../../util/fd_hash32.h" /* for fd_hash32, which we use for CRDS eviction */
7 :
8 : #include <string.h>
9 :
10 : struct fd_crds_contact_info_entry {
11 : fd_gossip_contact_info_t contact_info[1];
12 : long received_wallclock_nanos;
13 : uchar is_me;
14 : uchar is_entrypoint;
15 :
16 : fd_crds_entry_t * crds_entry; /* Back-pointer to CRDS pool entry */
17 :
18 : /* A list of "fresh" contact info entries is maintained, holding
19 : entries that have been refreshed/inserted in the last 60s in
20 : upsertion order (oldest first).
21 :
22 : fd_crds_advance periodically checks for and removes peers from
23 : this list if they exceed the threshold. Peers removed in this
24 : loop are also re-scored in the peer sampler. This is different
25 : from dropping the CRDS entry entirely, which also removes the
26 : entry from this list. To avoid double-popping an entry we use
27 : in_list as a presence check prior to removing */
28 : struct {
29 : ulong prev;
30 : ulong next;
31 : uchar in_list; /* 1 if in the fresh list, 0 otherwise */
32 : } fresh_dlist;
33 :
34 : /* Similar to fresh_dlist, but with a 15s timeout instead.
35 : Additionally, fresh_dlist explicitly excludes our own contact info
36 : while fresh_15s_dlist includes it. */
37 : struct {
38 : ulong prev;
39 : ulong next;
40 : uchar in_list; /* 1 if in the fresh list, 0 otherwise */
41 : } fresh_15s_dlist;
42 :
43 : /* The contact info side table has a separate size limit, so
44 : we maintain a separate evict treap sorted by
45 : (stake ASC, wallclock ASC) to evict the lowest-stake
46 : (and oldest among equally-staked) contact info first. */
47 : struct {
48 : ulong parent;
49 : ulong left;
50 : ulong right;
51 : ulong prio;
52 : ulong next;
53 : ulong prev;
54 : } ci_evict_treap;
55 :
56 : struct {
57 : ulong next;
58 : } pool;
59 : };
60 :
61 : typedef struct fd_crds_contact_info_entry fd_crds_contact_info_entry_t;
62 :
63 : #define POOL_NAME crds_contact_info_pool
64 0 : #define POOL_T fd_crds_contact_info_entry_t
65 0 : #define POOL_NEXT pool.next
66 : #include "../../util/tmpl/fd_pool.c"
67 :
68 : struct fd_crds_key {
69 : uchar tag;
70 : uchar pubkey[ 32UL ];
71 : union {
72 : uchar vote_index;
73 : uchar epoch_slots_index;
74 : ushort duplicate_shred_index;
75 : };
76 : };
77 :
78 : typedef struct fd_crds_key fd_crds_key_t;
79 :
80 : /* The CRDS at a high level is just a list of all the messages we have
81 : received over gossip. These are called the CRDS values. Values
82 : are not arbitrary, and must conform to a strictly typed schema of
83 : around 10 different messages. */
84 :
85 : struct fd_crds_entry_private {
86 : /* The core operation of the CRDS is to "upsert" a value. Basically,
87 : all of the message types are keyed by the originators public key,
88 : and we only want to store the most recent message of each type.
89 :
90 : This key field is the key for the hash table. */
91 : fd_crds_key_t key;
92 :
93 : union {
94 : fd_crds_contact_info_entry_t * ci;
95 : ulong node_instance_token;
96 : };
97 :
98 : /* When an originator creates a CRDS message, they attach their local
99 : wallclock time to it. This time is used to determine when a
100 : message should be upserted. If messages have the same key, the
101 : newer one (as created by the originator) is used. */
102 : ulong wallclock;
103 :
104 : ushort value_sz;
105 : uchar value_bytes[ FD_GOSSIP_VALUE_MAX_SZ ];
106 : uchar value_hash[ 32UL ];
107 :
108 : ulong num_push_recv;
109 : ulong stake;
110 :
111 : struct {
112 : uint next;
113 : } pool;
114 :
115 : /* The CRDS needs to perform a variety of actions on the message table
116 : quickly, so there are various indexes woven through them values to
117 : support these actions. They are ...
118 :
119 : lookup is used to enable the core map<key, value> functionality
120 : described for upserts defined by value->key. */
121 : struct {
122 : uint next;
123 : uint prev;
124 : } lookup;
125 :
126 : /* The table has a fixed size message capacity, and supports eviction
127 : so insertion never fails. If the table is full and we wish to
128 : insert a new value, the "lowest priority" message is evicted to
129 : make room. This is accomplished with a treap sorted by stake, so
130 : the lowest stake message is removed. */
131 : struct {
132 : uint parent;
133 : uint left;
134 : uint right;
135 : uint prio;
136 : uint next;
137 : uint prev;
138 : } evict;
139 :
140 : /* Values in the table expire after a pre-determined amount of time,
141 : so we also keep a linked list of values sorted by creation time.
142 : The time used here is our nodes wallclock when we received the
143 : CRDS, not the originators local wallclock, which they could skew
144 : to cause their values to live longer.
145 :
146 : There are actually two lists that reuse the same pointers here,
147 : and a value will be in exactly one of the lists. One is for staked
148 : nodes, which values expire after 48 hours, and one is for unstaked
149 : nodes, which expire after 15 seconds (or also 48hours if the node
150 : is configured as unstaked). */
151 : struct {
152 : long wallclock_nanos;
153 : uint prev;
154 : uint next;
155 : } expire;
156 :
157 : /* Relays of one key are coalesced to one per FD_CRDS_RELAY_COALESCE_NANOS:
158 : a version arriving sooner waits in relay_treap, ordered by due time,
159 : and only the latest one is pushed. nanos is the last relay time, or
160 : the due time while pending. */
161 : struct {
162 : long nanos;
163 : uint parent;
164 : uint left;
165 : uint right;
166 : uint prio;
167 : int pending;
168 : } relay;
169 : };
170 :
171 : FD_STATIC_ASSERT( sizeof(fd_crds_entry_t)==1384UL, crds_entry_footprint );
172 :
173 0 : #define FD_CRDS_RELAY_COALESCE_NANOS (100L*1000L*1000L)
174 :
175 : #define POOL_NAME crds_pool
176 0 : #define POOL_T fd_crds_entry_t
177 : #define POOL_IDX_T uint
178 0 : #define POOL_NEXT pool.next
179 :
180 : #include "../../util/tmpl/fd_pool.c"
181 :
182 : #define TREAP_NAME evict_treap
183 : #define TREAP_T fd_crds_entry_t
184 : #define TREAP_QUERY_T void * /* We don't use query ... */
185 : #define TREAP_CMP(q,e) (__extension__({ (void)(q); (void)(e); -1; })) /* which means we don't need to give a real
186 : implementation to cmp either */
187 0 : #define TREAP_IDX_T uint
188 0 : #define TREAP_LT(e0,e1) ((e0)->stake<(e1)->stake)
189 0 : #define TREAP_PARENT evict.parent
190 0 : #define TREAP_LEFT evict.left
191 0 : #define TREAP_RIGHT evict.right
192 0 : #define TREAP_PRIO evict.prio
193 : #define TREAP_OPTIMIZE_ITERATION 1
194 0 : #define TREAP_NEXT evict.next
195 0 : #define TREAP_PREV evict.prev
196 :
197 : #include "../../util/tmpl/fd_treap.c"
198 :
199 : /* staked_expire_dlist tracks contact info crds entries inserted in the
200 : last 432000L*SLOT_DURATION_NANOS nanoseconds with nonzero active
201 : stake according to their epoch stake at the time they are inserted. */
202 : #define DLIST_NAME staked_expire_dlist
203 : #define DLIST_ELE_T fd_crds_entry_t
204 : #define DLIST_IDX_T uint
205 0 : #define DLIST_PREV expire.prev
206 0 : #define DLIST_NEXT expire.next
207 :
208 : #include "../../util/tmpl/fd_dlist.c"
209 :
210 : /* unstaked_expire_dlist tracks contact info crds entries from the last
211 : 432000L*SLOT_DURATION_NANOS nanoseconds (or from the last 15 seconds,
212 : if this node is itself running as unstaked) with zero active stake
213 : according to their epoch stake at the time they are inserted. */
214 : #define DLIST_NAME unstaked_expire_dlist
215 : #define DLIST_ELE_T fd_crds_entry_t
216 : #define DLIST_IDX_T uint
217 0 : #define DLIST_PREV expire.prev
218 0 : #define DLIST_NEXT expire.next
219 :
220 : #include "../../util/tmpl/fd_dlist.c"
221 :
222 : #define TREAP_NAME relay_treap
223 : #define TREAP_T fd_crds_entry_t
224 : #define TREAP_QUERY_T void *
225 : #define TREAP_CMP(q,e) (__extension__({ (void)(q); (void)(e); -1; }))
226 0 : #define TREAP_IDX_T uint
227 0 : #define TREAP_LT(e0,e1) ((e0)->relay.nanos<(e1)->relay.nanos)
228 0 : #define TREAP_PARENT relay.parent
229 0 : #define TREAP_LEFT relay.left
230 0 : #define TREAP_RIGHT relay.right
231 0 : #define TREAP_PRIO relay.prio
232 :
233 : #include "../../util/tmpl/fd_treap.c"
234 :
235 : /* fresh_15s_dlist tracks all contact info crds entries from the last
236 : 15 seconds. */
237 : #define DLIST_NAME ci_fresh_15s_dlist
238 : #define DLIST_ELE_T fd_crds_contact_info_entry_t
239 0 : #define DLIST_PREV fresh_15s_dlist.prev
240 0 : #define DLIST_NEXT fresh_15s_dlist.next
241 : #include "../../util/tmpl/fd_dlist.c"
242 :
243 : /* crds_contact_info_fresh_list tracks all contact info crds entries
244 : from the last 60 seconds. */
245 : #define DLIST_NAME crds_contact_info_fresh_list
246 : #define DLIST_ELE_T fd_crds_contact_info_entry_t
247 0 : #define DLIST_PREV fresh_dlist.prev
248 0 : #define DLIST_NEXT fresh_dlist.next
249 : #include "../../util/tmpl/fd_dlist.c"
250 :
251 : #define TREAP_NAME ci_evict_treap
252 : #define TREAP_T fd_crds_contact_info_entry_t
253 : #define TREAP_QUERY_T void *
254 : #define TREAP_CMP(q,e) (__extension__({ (void)(q); (void)(e); -1; }))
255 0 : #define TREAP_IDX_T ulong
256 :
257 : #if FD_DCHECK_STYLE>0
258 : #define TREAP_LT(a,b) (__extension__({ \
259 : FD_TEST( (a)->crds_entry ); \
260 : FD_TEST( (b)->crds_entry ); \
261 : ((a)->crds_entry->stake<(b)->crds_entry->stake) | (((a)->crds_entry->stake==(b)->crds_entry->stake) & ((a)->crds_entry->expire.wallclock_nanos<(b)->crds_entry->expire.wallclock_nanos)); \
262 : }))
263 : #else
264 0 : #define TREAP_LT(a,b) ((a)->crds_entry->stake<(b)->crds_entry->stake) | (((a)->crds_entry->stake==(b)->crds_entry->stake) & ((a)->crds_entry->expire.wallclock_nanos<(b)->crds_entry->expire.wallclock_nanos))
265 : #endif
266 :
267 0 : #define TREAP_PARENT ci_evict_treap.parent
268 0 : #define TREAP_LEFT ci_evict_treap.left
269 0 : #define TREAP_RIGHT ci_evict_treap.right
270 0 : #define TREAP_PRIO ci_evict_treap.prio
271 : #define TREAP_OPTIMIZE_ITERATION 1
272 0 : #define TREAP_NEXT ci_evict_treap.next
273 0 : #define TREAP_PREV ci_evict_treap.prev
274 : #include "../../util/tmpl/fd_treap.c"
275 :
276 : static inline ulong
277 : lookup_hash( fd_crds_key_t const * key,
278 0 : ulong seed ) {
279 0 : ulong hash_fn = ((ulong)key->tag)<<16;
280 0 : switch( key->tag ) {
281 0 : case FD_GOSSIP_VALUE_VOTE:
282 0 : hash_fn ^= key->vote_index;
283 0 : break;
284 0 : case FD_GOSSIP_VALUE_EPOCH_SLOTS:
285 0 : hash_fn ^= key->epoch_slots_index;
286 0 : break;
287 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
288 0 : hash_fn ^= key->duplicate_shred_index;
289 0 : break;
290 0 : default:
291 0 : break;
292 0 : }
293 0 : return fd_hash32( key->pubkey, seed^hash_fn );
294 0 : }
295 :
296 : static inline int
297 : lookup_eq( fd_crds_key_t const * key0,
298 0 : fd_crds_key_t const * key1 ) {
299 0 : if( FD_UNLIKELY( key0->tag!=key1->tag ) ) return 0;
300 0 : if( FD_UNLIKELY( !!memcmp( key0->pubkey, key1->pubkey, 32UL ) ) ) return 0;
301 0 : switch( key0->tag ) {
302 0 : case FD_GOSSIP_VALUE_VOTE:
303 0 : return key0->vote_index==key1->vote_index;
304 0 : case FD_GOSSIP_VALUE_EPOCH_SLOTS:
305 0 : return key0->epoch_slots_index==key1->epoch_slots_index;
306 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
307 0 : return key0->duplicate_shred_index==key1->duplicate_shred_index;
308 0 : default:
309 0 : break;
310 0 : }
311 0 : return 1;
312 0 : }
313 :
314 : #define MAP_NAME lookup_map
315 : #define MAP_ELE_T fd_crds_entry_t
316 : #define MAP_KEY_T fd_crds_key_t
317 0 : #define MAP_KEY key
318 0 : #define MAP_IDX_T uint
319 0 : #define MAP_NEXT lookup.next
320 0 : #define MAP_PREV lookup.prev
321 0 : #define MAP_KEY_HASH(k,s) (lookup_hash( k, s ))
322 0 : #define MAP_KEY_EQ(k0,k1) (lookup_eq( k0, k1 ))
323 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
324 :
325 : #include "../../util/tmpl/fd_map_chain.c"
326 :
327 : struct fd_crds_private {
328 : fd_gossip_out_ctx_t * gossip_update;
329 :
330 : fd_gossip_activity_update_fn activity_update_fn;
331 : void * activity_update_fn_ctx;
332 :
333 : fd_sha256_t sha256[1];
334 :
335 : int has_staked_node;
336 :
337 : fd_ip4_port_t entrypoints[ 16UL ];
338 : ulong entrypoints_cnt;
339 : ulong entrypoint_fresh_cnt;
340 :
341 : fd_crds_entry_t * pool;
342 : fd_crds_contact_info_entry_t * ci_pool;
343 :
344 : evict_treap_t * evict_treap;
345 : staked_expire_dlist_t * staked_expire_dlist;
346 : unstaked_expire_dlist_t * unstaked_expire_dlist;
347 : relay_treap_t * relay_treap;
348 : ci_fresh_15s_dlist_t * ci_fresh_15s_dlist;
349 : lookup_map_t * lookup_map;
350 : fd_gossip_hset_t * hset;
351 :
352 : fd_gossip_purged_t * purged;
353 :
354 : crds_contact_info_fresh_list_t * ci_fresh_dlist;
355 : ci_evict_treap_t * ci_evict_treap;
356 :
357 : fd_gossip_wsample_t * wsample;
358 : fd_active_set_t * active_set;
359 :
360 : fd_crds_metrics_t metrics[1];
361 :
362 : ulong magic;
363 : };
364 :
365 : FD_FN_CONST ulong
366 0 : fd_crds_align( void ) {
367 0 : return FD_CRDS_ALIGN;
368 0 : }
369 :
370 : FD_FN_CONST ulong
371 0 : fd_crds_footprint( ulong ele_max ) {
372 0 : ulong l;
373 0 : l = FD_LAYOUT_INIT;
374 0 : l = FD_LAYOUT_APPEND( l, FD_CRDS_ALIGN, sizeof(fd_crds_t) );
375 0 : l = FD_LAYOUT_APPEND( l, crds_pool_align(), crds_pool_footprint( ele_max ) );
376 0 : l = FD_LAYOUT_APPEND( l, evict_treap_align(), evict_treap_footprint( ele_max ) );
377 0 : l = FD_LAYOUT_APPEND( l, staked_expire_dlist_align(), staked_expire_dlist_footprint() );
378 0 : l = FD_LAYOUT_APPEND( l, unstaked_expire_dlist_align(), unstaked_expire_dlist_footprint() );
379 0 : l = FD_LAYOUT_APPEND( l, relay_treap_align(), relay_treap_footprint( ele_max ) );
380 0 : l = FD_LAYOUT_APPEND( l, ci_fresh_15s_dlist_align(), ci_fresh_15s_dlist_footprint() );
381 0 : l = FD_LAYOUT_APPEND( l, lookup_map_align(), lookup_map_footprint( ele_max ) );
382 0 : l = FD_LAYOUT_APPEND( l, fd_gossip_hset_align(), fd_gossip_hset_footprint( ele_max ) );
383 0 : l = FD_LAYOUT_APPEND( l, crds_contact_info_pool_align(), crds_contact_info_pool_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
384 0 : l = FD_LAYOUT_APPEND( l, crds_contact_info_fresh_list_align(), crds_contact_info_fresh_list_footprint() );
385 0 : l = FD_LAYOUT_APPEND( l, ci_evict_treap_align(), ci_evict_treap_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
386 0 : return FD_LAYOUT_FINI( l, FD_CRDS_ALIGN );
387 0 : }
388 :
389 : void *
390 : fd_crds_new( void * shmem,
391 : fd_ip4_port_t const * entrypoints,
392 : ulong entrypoints_cnt,
393 : fd_gossip_wsample_t * wsample,
394 : fd_active_set_t * active_set, /* TODO: Remove .. circular dep */
395 : fd_rng_t * rng,
396 : ulong ele_max,
397 : fd_gossip_purged_t * purged,
398 : fd_gossip_activity_update_fn activity_update_fn,
399 : void * activity_update_fn_ctx,
400 0 : fd_gossip_out_ctx_t * gossip_update_out ) {
401 0 : if( FD_UNLIKELY( !shmem ) ) {
402 0 : FD_LOG_WARNING(( "NULL shmem" ));
403 0 : return NULL;
404 0 : }
405 :
406 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_crds_align() ) ) ) {
407 0 : FD_LOG_WARNING(( "misaligned shmem" ));
408 0 : return NULL;
409 0 : }
410 :
411 0 : if( FD_UNLIKELY( !fd_ulong_is_pow2( ele_max ) ) ) {
412 0 : FD_LOG_WARNING(( "ele_max must be a power of 2" ));
413 0 : return NULL;
414 0 : }
415 :
416 0 : if( FD_UNLIKELY( !rng ) ) {
417 0 : FD_LOG_WARNING(( "NULL rng" ));
418 0 : return NULL;
419 0 : }
420 :
421 0 : if( FD_UNLIKELY( !purged ) ) {
422 0 : FD_LOG_WARNING(( "NULL purged" ));
423 0 : return NULL;
424 0 : }
425 :
426 0 : if( FD_UNLIKELY( !gossip_update_out ) ) {
427 0 : FD_LOG_WARNING(( "NULL gossip_out" ));
428 0 : return NULL;
429 0 : }
430 :
431 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
432 0 : fd_crds_t * crds = FD_SCRATCH_ALLOC_APPEND( l, FD_CRDS_ALIGN, sizeof(fd_crds_t) );
433 0 : void * _pool = FD_SCRATCH_ALLOC_APPEND( l, crds_pool_align(), crds_pool_footprint( ele_max ) );
434 0 : void * _evict_treap = FD_SCRATCH_ALLOC_APPEND( l, evict_treap_align(), evict_treap_footprint( ele_max ) );
435 0 : void * _staked_expire_dlist = FD_SCRATCH_ALLOC_APPEND( l, staked_expire_dlist_align(), staked_expire_dlist_footprint() );
436 0 : void * _unstaked_expire_dlist = FD_SCRATCH_ALLOC_APPEND( l, unstaked_expire_dlist_align(), unstaked_expire_dlist_footprint() );
437 0 : void * _relay_treap = FD_SCRATCH_ALLOC_APPEND( l, relay_treap_align(), relay_treap_footprint( ele_max ) );
438 0 : void * _ci_fresh_15s_dlist = FD_SCRATCH_ALLOC_APPEND( l, ci_fresh_15s_dlist_align(), ci_fresh_15s_dlist_footprint() );
439 0 : void * _lookup_map = FD_SCRATCH_ALLOC_APPEND( l, lookup_map_align(), lookup_map_footprint( ele_max ) );
440 0 : void * _hset = FD_SCRATCH_ALLOC_APPEND( l, fd_gossip_hset_align(), fd_gossip_hset_footprint( ele_max ) );
441 0 : void * _ci_pool = FD_SCRATCH_ALLOC_APPEND( l, crds_contact_info_pool_align(), crds_contact_info_pool_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
442 0 : void * _ci_dlist = FD_SCRATCH_ALLOC_APPEND( l, crds_contact_info_fresh_list_align(), crds_contact_info_fresh_list_footprint() );
443 0 : void * _ci_evict_treap = FD_SCRATCH_ALLOC_APPEND( l, ci_evict_treap_align(), ci_evict_treap_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
444 :
445 0 : crds->activity_update_fn = activity_update_fn;
446 0 : FD_TEST( crds->activity_update_fn );
447 :
448 0 : crds->activity_update_fn_ctx = activity_update_fn_ctx;
449 :
450 0 : crds->pool = crds_pool_join( crds_pool_new( _pool, ele_max ) );
451 0 : FD_TEST( crds->pool );
452 :
453 0 : crds->evict_treap = evict_treap_join( evict_treap_new( _evict_treap, ele_max ) );
454 0 : FD_TEST( crds->evict_treap );
455 0 : evict_treap_seed( crds->pool, ele_max, fd_rng_ulong( rng ) );
456 :
457 0 : crds->staked_expire_dlist = staked_expire_dlist_join( staked_expire_dlist_new( _staked_expire_dlist ) );
458 0 : FD_TEST( crds->staked_expire_dlist );
459 :
460 0 : crds->unstaked_expire_dlist = unstaked_expire_dlist_join( unstaked_expire_dlist_new( _unstaked_expire_dlist ) );
461 0 : FD_TEST( crds->unstaked_expire_dlist );
462 :
463 0 : crds->relay_treap = relay_treap_join( relay_treap_new( _relay_treap, ele_max ) );
464 0 : FD_TEST( crds->relay_treap );
465 0 : relay_treap_seed( crds->pool, ele_max, fd_rng_ulong( rng ) );
466 :
467 0 : crds->ci_fresh_15s_dlist = ci_fresh_15s_dlist_join( ci_fresh_15s_dlist_new( _ci_fresh_15s_dlist ) );
468 0 : FD_TEST( crds->ci_fresh_15s_dlist );
469 :
470 0 : crds->lookup_map = lookup_map_join( lookup_map_new( _lookup_map, ele_max, fd_rng_ulong( rng ) ) );
471 0 : FD_TEST( crds->lookup_map );
472 :
473 0 : crds->hset = fd_gossip_hset_join( fd_gossip_hset_new( _hset, ele_max ) );
474 0 : FD_TEST( crds->hset );
475 :
476 0 : crds->purged = purged;
477 :
478 0 : crds->ci_pool = crds_contact_info_pool_join( crds_contact_info_pool_new( _ci_pool, FD_CONTACT_INFO_TABLE_SIZE ) );
479 0 : FD_TEST( crds->ci_pool );
480 :
481 0 : crds->ci_fresh_dlist = crds_contact_info_fresh_list_join( crds_contact_info_fresh_list_new( _ci_dlist ) );
482 0 : FD_TEST( crds->ci_fresh_dlist );
483 :
484 0 : crds->ci_evict_treap = ci_evict_treap_join( ci_evict_treap_new( _ci_evict_treap, FD_CONTACT_INFO_TABLE_SIZE ) );
485 0 : FD_TEST( crds->ci_evict_treap );
486 0 : ci_evict_treap_seed( crds->ci_pool, FD_CONTACT_INFO_TABLE_SIZE, fd_rng_ulong( rng ) );
487 :
488 0 : FD_TEST( fd_sha256_join( fd_sha256_new( crds->sha256 ) ) );
489 :
490 0 : crds->wsample = wsample;
491 0 : crds->active_set = active_set;
492 :
493 0 : FD_TEST( entrypoints_cnt<=16UL );
494 0 : for( ulong i=0UL; i<entrypoints_cnt; i++ ) crds->entrypoints[ i ] = entrypoints[ i ];
495 0 : crds->entrypoints_cnt = entrypoints_cnt;
496 0 : crds->entrypoint_fresh_cnt = 0UL;
497 :
498 0 : memset( crds->metrics, 0, sizeof(fd_crds_metrics_t) );
499 :
500 0 : crds->gossip_update = gossip_update_out;
501 0 : crds->has_staked_node = 0;
502 :
503 0 : FD_COMPILER_MFENCE();
504 0 : FD_VOLATILE( crds->magic ) = FD_CRDS_MAGIC;
505 0 : FD_COMPILER_MFENCE();
506 :
507 0 : return (void *)crds;
508 0 : }
509 :
510 : fd_crds_t *
511 0 : fd_crds_join( void * shcrds ) {
512 0 : if( FD_UNLIKELY( !shcrds ) ) {
513 0 : FD_LOG_WARNING(( "NULL shcrds" ));
514 0 : return NULL;
515 0 : }
516 :
517 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shcrds, fd_crds_align() ) ) ) {
518 0 : FD_LOG_WARNING(( "misaligned shcrds" ));
519 0 : return NULL;
520 0 : }
521 :
522 0 : fd_crds_t * crds = (fd_crds_t *)shcrds;
523 :
524 0 : if( FD_UNLIKELY( crds->magic!=FD_CRDS_MAGIC ) ) {
525 0 : FD_LOG_WARNING(( "bad magic" ));
526 0 : return NULL;
527 0 : }
528 :
529 0 : return crds;
530 0 : }
531 :
532 : fd_crds_metrics_t const *
533 0 : fd_crds_metrics( fd_crds_t const * crds ) {
534 0 : return crds->metrics;
535 0 : }
536 :
537 : ulong
538 0 : fd_crds_len( fd_crds_t const * crds ) {
539 0 : return crds_pool_used( crds->pool );
540 0 : }
541 :
542 : static inline void
543 : crds_unindex( fd_crds_t * crds,
544 0 : fd_crds_entry_t * entry ) {
545 0 : if( FD_LIKELY( entry->stake ) ) staked_expire_dlist_ele_remove( crds->staked_expire_dlist, entry, crds->pool );
546 0 : else unstaked_expire_dlist_ele_remove( crds->unstaked_expire_dlist, entry, crds->pool );
547 :
548 0 : evict_treap_ele_remove( crds->evict_treap, entry, crds->pool );
549 0 : fd_gossip_hset_remove( crds->hset, crds_pool_idx( crds->pool, entry ) );
550 0 : lookup_map_ele_remove( crds->lookup_map, &entry->key, NULL, crds->pool );
551 :
552 0 : if( FD_UNLIKELY( entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
553 0 : if( FD_LIKELY( entry->stake ) ) crds->metrics->peer_staked_cnt--;
554 0 : else crds->metrics->peer_unstaked_cnt--;
555 0 : crds->metrics->peer_visible_stake -= entry->stake;
556 :
557 0 : if( FD_LIKELY( entry->ci->fresh_dlist.in_list ) ) {
558 0 : crds_contact_info_fresh_list_ele_remove( crds->ci_fresh_dlist, entry->ci, crds->ci_pool );
559 0 : crds->entrypoint_fresh_cnt -= entry->ci->is_entrypoint;
560 0 : }
561 0 : if( FD_LIKELY( entry->ci->fresh_15s_dlist.in_list ) ) {
562 0 : ci_fresh_15s_dlist_ele_remove( crds->ci_fresh_15s_dlist, entry->ci, crds->ci_pool );
563 0 : crds->activity_update_fn( crds->activity_update_fn_ctx, (fd_pubkey_t const *)entry->key.pubkey, entry->ci->contact_info, FD_GOSSIP_ACTIVITY_CHANGE_TYPE_INACTIVE );
564 0 : }
565 0 : ci_evict_treap_ele_remove( crds->ci_evict_treap, entry->ci, crds->ci_pool );
566 0 : }
567 :
568 0 : crds->metrics->count[ entry->key.tag ]--;
569 0 : }
570 :
571 : static inline void
572 : crds_index( fd_crds_t * crds,
573 0 : fd_crds_entry_t * entry ) {
574 0 : if( FD_LIKELY( entry->stake ) ) staked_expire_dlist_ele_push_tail( crds->staked_expire_dlist, entry, crds->pool );
575 0 : else unstaked_expire_dlist_ele_push_tail( crds->unstaked_expire_dlist, entry, crds->pool );
576 :
577 0 : evict_treap_ele_insert( crds->evict_treap, entry, crds->pool );
578 0 : fd_gossip_hset_insert( crds->hset, crds_pool_idx( crds->pool, entry ), entry->value_hash );
579 0 : lookup_map_ele_insert( crds->lookup_map, entry, crds->pool );
580 :
581 0 : if( FD_UNLIKELY( entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
582 0 : if( FD_LIKELY( entry->stake ) ) crds->metrics->peer_staked_cnt++;
583 0 : else crds->metrics->peer_unstaked_cnt++;
584 0 : crds->metrics->peer_visible_stake += entry->stake;
585 :
586 0 : fd_gossip_socket_t const * sock = &entry->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ];
587 0 : entry->ci->is_entrypoint = 0;
588 0 : for( ulong i=0UL; !entry->ci->is_me && i<crds->entrypoints_cnt; i++ ) {
589 0 : entry->ci->is_entrypoint |= (uchar)( !sock->is_ipv6 && sock->ip4==crds->entrypoints[ i ].addr && sock->port==crds->entrypoints[ i ].port );
590 0 : }
591 0 : crds->entrypoint_fresh_cnt += entry->ci->is_entrypoint;
592 :
593 0 : ci_evict_treap_ele_insert( crds->ci_evict_treap, entry->ci, crds->ci_pool );
594 0 : crds_contact_info_fresh_list_ele_push_tail( crds->ci_fresh_dlist, entry->ci, crds->ci_pool );
595 0 : ci_fresh_15s_dlist_ele_push_tail( crds->ci_fresh_15s_dlist, entry->ci, crds->ci_pool );
596 0 : entry->ci->fresh_dlist.in_list = 1;
597 0 : entry->ci->fresh_15s_dlist.in_list = 1;
598 0 : crds->activity_update_fn( crds->activity_update_fn_ctx, (fd_pubkey_t const *)entry->key.pubkey, entry->ci->contact_info, FD_GOSSIP_ACTIVITY_CHANGE_TYPE_ACTIVE );
599 0 : }
600 :
601 0 : crds->metrics->count[ entry->key.tag ]++;
602 0 : }
603 :
604 : /* publish_ci_seen mirrors a CONTACT_INFO update already published on
605 : gossip_ciaddr onto gossip_ciseen, which carries every contact info
606 : update including refreshes of unchanged content. */
607 :
608 : static inline void
609 : publish_ci_seen( fd_crds_t * crds,
610 : fd_gossip_update_message_t const * msg,
611 : ulong sz,
612 : long now,
613 0 : fd_stem_context_t * stem ) {
614 0 : fd_gossip_out_ctx_t * out = crds->gossip_update + FD_GOSSIP_UPDATE_LINK_CI_SEEN;
615 0 : if( FD_UNLIKELY( out->idx==ULONG_MAX ) ) return; /* no gui */
616 :
617 0 : fd_memcpy( fd_gossip_out_get_chunk( out ), msg, sz );
618 0 : fd_gossip_tx_publish_chunk( out, stem, (ulong)msg->tag, sz, now );
619 0 : }
620 :
621 : static inline void
622 : crds_release( fd_crds_t * crds,
623 : fd_crds_entry_t * entry,
624 : long now,
625 : int evicting,
626 0 : fd_stem_context_t * stem ) {
627 0 : crds_unindex( crds, entry );
628 0 : if( FD_UNLIKELY( entry->relay.pending ) ) relay_treap_ele_remove( crds->relay_treap, entry, crds->pool );
629 0 : fd_gossip_purged_insert_replaced( crds->purged, entry->value_hash, now );
630 :
631 0 : if( FD_UNLIKELY( entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
632 0 : if( FD_UNLIKELY( evicting ) ) crds->metrics->peer_evicted_cnt++;
633 :
634 0 : fd_gossip_out_ctx_t * out = crds->gossip_update + FD_GOSSIP_UPDATE_LINK_CI_ADDR;
635 0 : fd_gossip_update_message_t * msg = fd_gossip_out_get_chunk( out );
636 0 : msg->tag = FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE;
637 0 : msg->wallclock = (ulong)FD_NANOSEC_TO_MILLI( now );
638 0 : msg->contact_info_remove->idx = crds_contact_info_pool_idx( crds->ci_pool, entry->ci );
639 0 : fd_memcpy( msg->origin, entry->key.pubkey, 32UL );
640 0 : fd_gossip_tx_publish_chunk( out, stem, (ulong)msg->tag, FD_GOSSIP_UPDATE_SZ_CONTACT_INFO_REMOVE, now );
641 0 : publish_ci_seen( crds, msg, FD_GOSSIP_UPDATE_SZ_CONTACT_INFO_REMOVE, now, stem );
642 :
643 0 : ulong ci_idx = crds_contact_info_pool_idx( crds->ci_pool, entry->ci );
644 0 : fd_active_set_remove_peer( crds->active_set, ci_idx );
645 0 : fd_gossip_wsample_remove( crds->wsample, ci_idx );
646 :
647 0 : crds_contact_info_pool_ele_release( crds->ci_pool, entry->ci );
648 0 : }
649 :
650 0 : if( FD_UNLIKELY( evicting ) ) crds->metrics->evicted_cnt++;
651 0 : else crds->metrics->expired_cnt++;
652 :
653 0 : crds_pool_ele_release( crds->pool, entry );
654 0 : }
655 :
656 : static inline fd_crds_entry_t *
657 : crds_acquire( fd_crds_t * crds,
658 : int is_contact_info,
659 : long now,
660 0 : fd_stem_context_t * stem ) {
661 0 : if( FD_UNLIKELY( is_contact_info ) ) {
662 0 : if( FD_UNLIKELY( !crds_contact_info_pool_free( crds->ci_pool ) ) ) {
663 0 : ci_evict_treap_fwd_iter_t ci_it = ci_evict_treap_fwd_iter_init( crds->ci_evict_treap, crds->ci_pool );
664 0 : FD_TEST( !ci_evict_treap_fwd_iter_done( ci_it ) );
665 0 : fd_crds_contact_info_entry_t * ci_evict = ci_evict_treap_fwd_iter_ele( ci_it, crds->ci_pool );
666 0 : crds_release( crds, ci_evict->crds_entry, now, 1, stem );
667 0 : } else if( FD_UNLIKELY( !crds_pool_free( crds->pool ) ) ) {
668 0 : evict_treap_fwd_iter_t it = evict_treap_fwd_iter_init( crds->evict_treap, crds->pool );
669 0 : FD_TEST( !evict_treap_fwd_iter_done( it ) );
670 0 : crds_release( crds, evict_treap_fwd_iter_ele( it, crds->pool ), now, 1, stem );
671 0 : }
672 0 : fd_crds_contact_info_entry_t * ci = crds_contact_info_pool_ele_acquire( crds->ci_pool );
673 0 : fd_crds_entry_t * entry = crds_pool_ele_acquire( crds->pool );
674 0 : entry->ci = ci;
675 0 : entry->ci->crds_entry = entry;
676 0 : return entry;
677 0 : } else {
678 0 : if( FD_UNLIKELY( !crds_pool_free( crds->pool ) ) ) {
679 0 : evict_treap_fwd_iter_t it = evict_treap_fwd_iter_init( crds->evict_treap, crds->pool );
680 0 : FD_TEST( !evict_treap_fwd_iter_done( it ) );
681 0 : crds_release( crds, evict_treap_fwd_iter_ele( it, crds->pool ), now, 1, stem );
682 0 : }
683 0 : return crds_pool_ele_acquire( crds->pool );
684 0 : }
685 0 : }
686 :
687 : static inline void
688 : expire( fd_crds_t * crds,
689 : long now,
690 : fd_stem_context_t * stem,
691 0 : int * charge_busy ){
692 : /* Gossip's slot time does not change with the reduce_slot_time
693 : feature gates. */
694 0 : static const long SLOT_DURATION_NANOS = 400L*1000L*1000L;
695 0 : static const long STAKED_EXPIRE_DURATION_NANOS = 432000L*SLOT_DURATION_NANOS;
696 0 : static const long UNSTAKED_EXPIRE_DURATION_NANOS = 15L*1000L*1000L*1000L;
697 :
698 0 : while( !staked_expire_dlist_is_empty( crds->staked_expire_dlist, crds->pool ) ) {
699 0 : fd_crds_entry_t * head = staked_expire_dlist_ele_peek_head( crds->staked_expire_dlist, crds->pool );
700 :
701 0 : if( FD_LIKELY( head->expire.wallclock_nanos>now-STAKED_EXPIRE_DURATION_NANOS ) ) break;
702 0 : crds_release( crds, head, now, 0, stem );
703 0 : if( charge_busy ) *charge_busy = 1;
704 0 : }
705 :
706 0 : long unstaked_expire_duration_nanos = fd_long_if( crds->has_staked_node,
707 0 : UNSTAKED_EXPIRE_DURATION_NANOS,
708 0 : STAKED_EXPIRE_DURATION_NANOS );
709 :
710 0 : while( !unstaked_expire_dlist_is_empty( crds->unstaked_expire_dlist, crds->pool ) ) {
711 0 : fd_crds_entry_t * head = unstaked_expire_dlist_ele_peek_head( crds->unstaked_expire_dlist, crds->pool );
712 :
713 0 : if( FD_LIKELY( head->expire.wallclock_nanos>now-unstaked_expire_duration_nanos ) ) break;
714 0 : crds_release( crds, head, now, 0, stem );
715 0 : if( charge_busy ) *charge_busy = 1;
716 0 : }
717 0 : }
718 :
719 : static void
720 : unfresh( fd_crds_t * crds,
721 : long now,
722 0 : int * charge_busy ) {
723 0 : while( !crds_contact_info_fresh_list_is_empty( crds->ci_fresh_dlist, crds->ci_pool ) ) {
724 0 : fd_crds_contact_info_entry_t * head = crds_contact_info_fresh_list_ele_peek_head( crds->ci_fresh_dlist, crds->ci_pool );
725 :
726 0 : if( FD_LIKELY( head->received_wallclock_nanos>now-60L*1000L*1000L*1000L ) ) break;
727 0 : head = crds_contact_info_fresh_list_ele_pop_head( crds->ci_fresh_dlist, crds->ci_pool );
728 0 : FD_TEST( head->fresh_dlist.in_list );
729 0 : head->fresh_dlist.in_list = 0;
730 0 : crds->entrypoint_fresh_cnt -= head->is_entrypoint;
731 :
732 0 : fd_gossip_wsample_fresh( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, head ), 0 );
733 0 : if( charge_busy ) *charge_busy = 1;
734 0 : }
735 :
736 0 : while( !ci_fresh_15s_dlist_is_empty( crds->ci_fresh_15s_dlist, crds->ci_pool ) ) {
737 0 : fd_crds_contact_info_entry_t * head = ci_fresh_15s_dlist_ele_peek_head( crds->ci_fresh_15s_dlist, crds->ci_pool );
738 :
739 0 : if( FD_LIKELY( head->received_wallclock_nanos>now-15L*1000L*1000L*1000L ) ) break;
740 :
741 0 : head = ci_fresh_15s_dlist_ele_pop_head( crds->ci_fresh_15s_dlist, crds->ci_pool );
742 :
743 0 : FD_TEST( head->fresh_15s_dlist.in_list );
744 0 : head->fresh_15s_dlist.in_list = 0U;
745 0 : crds->activity_update_fn( crds->activity_update_fn_ctx, (fd_pubkey_t const *)head->crds_entry->key.pubkey, head->contact_info, FD_GOSSIP_ACTIVITY_CHANGE_TYPE_INACTIVE );
746 0 : if( charge_busy ) *charge_busy = 1;
747 0 : }
748 0 : }
749 :
750 : /* uncoalesce pushes the latest version of each key whose relay was
751 : held back and is now due. */
752 :
753 : static inline void
754 : uncoalesce( fd_crds_t * crds,
755 : long now,
756 : fd_stem_context_t * stem,
757 0 : int * charge_busy ) {
758 0 : ulong now_ms = (ulong)FD_NANOSEC_TO_MILLI( now );
759 0 : while( relay_treap_ele_cnt( crds->relay_treap ) ) {
760 0 : fd_crds_entry_t * head = relay_treap_fwd_iter_ele( relay_treap_fwd_iter_init( crds->relay_treap, crds->pool ), crds->pool );
761 0 : if( FD_LIKELY( head->relay.nanos>now ) ) break;
762 0 : relay_treap_ele_remove( crds->relay_treap, head, crds->pool );
763 0 : head->relay.pending = 0;
764 0 : if( FD_UNLIKELY( head->wallclock+15000UL<now_ms || head->wallclock>now_ms+15000UL ) ) {
765 0 : head->relay.nanos -= FD_CRDS_RELAY_COALESCE_NANOS; /* back to the last relay, none went out */
766 0 : continue;
767 0 : }
768 0 : head->relay.nanos = now;
769 0 : fd_active_set_push( crds->active_set, head->value_bytes, head->value_sz, head->key.pubkey, head->stake, stem, now, 0 );
770 0 : if( charge_busy ) *charge_busy = 1;
771 0 : }
772 0 : }
773 :
774 : void
775 : fd_crds_advance( fd_crds_t * crds,
776 : long now,
777 : fd_stem_context_t * stem,
778 0 : int * charge_busy ) {
779 0 : expire( crds, now, stem, charge_busy );
780 0 : unfresh( crds, now, charge_busy );
781 0 : uncoalesce( crds, now, stem, charge_busy );
782 0 : }
783 :
784 : static inline void
785 : publish_update_msg( fd_crds_t * crds,
786 : fd_crds_entry_t * entry,
787 : fd_gossip_value_t const * entry_view,
788 : int ci_changed,
789 : long now,
790 0 : fd_stem_context_t * stem ) {
791 0 : FD_TEST( stem );
792 0 : if( FD_LIKELY( entry->key.tag!=FD_GOSSIP_VALUE_CONTACT_INFO &&
793 0 : entry->key.tag!=FD_GOSSIP_VALUE_VOTE &&
794 0 : entry->key.tag!=FD_GOSSIP_VALUE_DUPLICATE_SHRED &&
795 0 : entry->key.tag!=FD_GOSSIP_VALUE_SNAPSHOT_HASHES ) ) {
796 0 : return;
797 0 : }
798 :
799 0 : int tag;
800 0 : switch( entry->key.tag ) {
801 0 : case FD_GOSSIP_VALUE_CONTACT_INFO: tag = FD_GOSSIP_UPDATE_TAG_CONTACT_INFO; break;
802 0 : case FD_GOSSIP_VALUE_VOTE: tag = FD_GOSSIP_UPDATE_TAG_VOTE; break;
803 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED: tag = FD_GOSSIP_UPDATE_TAG_DUPLICATE_SHRED; break;
804 0 : default: tag = FD_GOSSIP_UPDATE_TAG_SNAPSHOT_HASHES; break;
805 0 : }
806 0 : ulong link = fd_gossip_update_link( (ulong)tag );
807 0 : if( FD_UNLIKELY( tag==FD_GOSSIP_UPDATE_TAG_CONTACT_INFO && !ci_changed ) ) link = FD_GOSSIP_UPDATE_LINK_CI_SEEN;
808 0 : fd_gossip_out_ctx_t * out = crds->gossip_update + link;
809 0 : if( FD_UNLIKELY( out->idx==ULONG_MAX ) ) return; /* nobody reads this kind */
810 :
811 0 : fd_gossip_update_message_t * msg = fd_gossip_out_get_chunk( out );
812 0 : msg->tag = tag;
813 0 : msg->wallclock = entry->wallclock;
814 0 : fd_memcpy( msg->origin, entry->key.pubkey, 32UL );
815 :
816 0 : ulong sz;
817 0 : switch( entry->key.tag ) {
818 0 : case FD_GOSSIP_VALUE_CONTACT_INFO:
819 0 : *msg->contact_info->value = *entry->ci->contact_info;
820 0 : msg->contact_info->idx = crds_contact_info_pool_idx( crds->ci_pool, entry->ci );
821 0 : sz = FD_GOSSIP_UPDATE_SZ_CONTACT_INFO;
822 0 : break;
823 0 : case FD_GOSSIP_VALUE_VOTE:
824 : /* TODO: dynamic sizing */
825 0 : sz = FD_GOSSIP_UPDATE_SZ_VOTE;
826 0 : fd_crds_key_t lookup_ci;
827 0 : lookup_ci.tag = FD_GOSSIP_VALUE_CONTACT_INFO;
828 0 : fd_memcpy( &lookup_ci.pubkey, entry->key.pubkey, sizeof(fd_pubkey_t) );
829 0 : fd_crds_entry_t * ci = lookup_map_ele_query( crds->lookup_map, &lookup_ci, NULL, crds->pool );
830 :
831 0 : if( FD_LIKELY( ci && ci->key.tag == FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
832 0 : msg->vote->socket->is_ipv6 = ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].is_ipv6;
833 0 : if( msg->vote->socket->is_ipv6 ) {
834 0 : fd_memcpy( msg->vote->socket->ip6, ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip6, 16UL );
835 0 : } else {
836 0 : msg->vote->socket->ip4 = ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4;
837 0 : }
838 0 : msg->vote->socket->port = ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].port;
839 0 : } else {
840 0 : msg->vote->socket->is_ipv6 = 0;
841 0 : msg->vote->socket->ip4 = 0;
842 0 : msg->vote->socket->port = 0;
843 0 : }
844 :
845 0 : msg->vote->first_seen_nanos = now;
846 0 : msg->vote->value->index = entry->key.vote_index;
847 0 : msg->vote->value->transaction_len = entry_view->vote->transaction_len;
848 0 : fd_memcpy( msg->vote->value->transaction, entry_view->vote->transaction, entry_view->vote->transaction_len );
849 0 : break;
850 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
851 : /* TODO: dynamic sizing */
852 0 : sz = FD_GOSSIP_UPDATE_SZ_DUPLICATE_SHRED;
853 0 : {
854 0 : fd_gossip_duplicate_shred_t const * ds = entry_view->duplicate_shred;
855 0 : fd_gossip_duplicate_shred_t * ds_msg = msg->duplicate_shred;
856 :
857 0 : ds_msg->index = ds->index;
858 0 : ds_msg->slot = ds->slot;
859 0 : ds_msg->num_chunks = ds->num_chunks;
860 0 : ds_msg->chunk_index = ds->chunk_index;
861 0 : ds_msg->chunk_len = ds->chunk_len;
862 0 : fd_memcpy( ds_msg->chunk, ds->chunk, ds->chunk_len );
863 0 : }
864 0 : break;
865 0 : case FD_GOSSIP_VALUE_SNAPSHOT_HASHES:
866 : /* TODO: dynamic sizing */
867 0 : sz = FD_GOSSIP_UPDATE_SZ_SNAPSHOT_HASHES;
868 0 : {
869 0 : fd_gossip_snapshot_hashes_t const * sh = entry_view->snapshot_hashes;
870 0 : fd_gossip_snapshot_hashes_t * sh_msg = msg->snapshot_hashes;
871 :
872 0 : sh_msg->full_slot = sh->full_slot;
873 0 : fd_memcpy( sh_msg->full_hash, sh->full_hash, 32UL );
874 0 : sh_msg->incremental_len = sh->incremental_len;
875 0 : for( ulong i=0; i<sh->incremental_len; i++ ) {
876 0 : sh_msg->incremental[ i ].slot = sh->incremental[ i ].slot;
877 0 : fd_memcpy( sh_msg->incremental[ i ].hash, sh->incremental[ i ].hash, 32UL );
878 0 : }
879 0 : }
880 0 : break;
881 0 : default:
882 0 : FD_LOG_ERR(( "impossible" ));
883 0 : }
884 0 : fd_gossip_tx_publish_chunk( out,
885 0 : stem,
886 0 : (ulong)msg->tag,
887 0 : sz,
888 0 : now );
889 :
890 0 : if( FD_UNLIKELY( link==FD_GOSSIP_UPDATE_LINK_CI_ADDR && tag==FD_GOSSIP_UPDATE_TAG_CONTACT_INFO ) ) {
891 0 : publish_ci_seen( crds, msg, sz, now, stem );
892 0 : }
893 0 : }
894 :
895 : static int
896 : crds_compare( fd_crds_entry_t const * incumbent,
897 0 : fd_gossip_value_t const * candidate ){
898 0 : int compare = 0;
899 0 : switch( candidate->tag ) {
900 0 : case FD_GOSSIP_VALUE_CONTACT_INFO:
901 0 : if( FD_UNLIKELY( candidate->contact_info->outset<incumbent->ci->contact_info->outset ) ) compare = 1;
902 0 : else if( FD_UNLIKELY( candidate->contact_info->outset>incumbent->ci->contact_info->outset ) ) compare = -1;
903 0 : break;
904 : /* NodeInstance has no special override logic in Agave — it uses
905 : the default wallclock + hash tiebreaker like all other types. */
906 0 : default:
907 0 : break;
908 0 : }
909 :
910 0 : if( FD_UNLIKELY( compare ) ) return compare;
911 :
912 0 : if( FD_UNLIKELY( candidate->wallclock<incumbent->wallclock ) ) return 1;
913 0 : else if( FD_UNLIKELY( candidate->wallclock>incumbent->wallclock ) ) return -1;
914 0 : else return 0;
915 0 : }
916 :
917 : long
918 : fd_crds_insert( fd_crds_t * crds,
919 : fd_gossip_value_t const * value,
920 : uchar const * value_bytes,
921 : ulong value_bytes_len,
922 : ulong origin_stake,
923 : int origin_ping_tracked,
924 : int is_me,
925 : int is_push,
926 : long now,
927 : fd_stem_context_t * stem,
928 0 : int * opt_relay ) {
929 0 : if( opt_relay ) *opt_relay = 0;
930 0 : fd_crds_key_t candidate_key = {
931 0 : .tag = (uchar)value->tag,
932 0 : };
933 0 : switch( candidate_key.tag ) {
934 0 : case FD_GOSSIP_VALUE_VOTE: candidate_key.vote_index = value->vote->index; break;
935 0 : case FD_GOSSIP_VALUE_EPOCH_SLOTS: candidate_key.epoch_slots_index = value->epoch_slots->index; break;
936 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED: candidate_key.duplicate_shred_index = value->duplicate_shred->index; break;
937 0 : default: break;
938 0 : }
939 0 : fd_memcpy( candidate_key.pubkey, value->origin, 32UL );
940 :
941 0 : fd_crds_entry_t * incumbent = lookup_map_ele_query( crds->lookup_map, &candidate_key, NULL, crds->pool );
942 0 : int replacing = !!incumbent;
943 0 : int ci_changed = 1;
944 :
945 0 : uchar value_hash[ 32UL ];
946 0 : if( FD_UNLIKELY( !replacing ) ) {
947 0 : fd_sha256_hash( value_bytes, value_bytes_len, value_hash );
948 :
949 0 : incumbent = crds_acquire( crds, value->tag==FD_GOSSIP_VALUE_CONTACT_INFO, now, stem );
950 0 : incumbent->key = candidate_key;
951 0 : incumbent->relay.nanos = now-FD_CRDS_RELAY_COALESCE_NANOS;
952 0 : incumbent->relay.pending = 0;
953 0 : if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
954 0 : fd_gossip_wsample_add( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), origin_stake, origin_ping_tracked, is_me );
955 0 : }
956 0 : } else {
957 : /* Fast duplicate check by signature before computing expensive
958 : sha256 hash. */
959 0 : if( FD_UNLIKELY( fd_ulong_load_8( incumbent->value_bytes )==fd_ulong_load_8( value->signature ) ) ) return is_push ? (long)(1UL+incumbent->num_push_recv++) : 1L;
960 :
961 0 : fd_sha256_hash( value_bytes, value_bytes_len, value_hash );
962 0 : switch( crds_compare( incumbent, value ) ) {
963 0 : case -1: break; /* upserting */
964 0 : case 0: {
965 0 : int result = memcmp( value_hash, incumbent->value_hash, 32UL );
966 0 : if( FD_UNLIKELY( !result ) ) return is_push ? (long)(1UL+incumbent->num_push_recv++) : 1L;
967 0 : else if( FD_UNLIKELY( result<0 ) ) {
968 0 : fd_gossip_purged_insert_failed_insert( crds->purged, value_hash, now );
969 0 : return -1L; /* stale */
970 0 : }
971 0 : else break; /* upserting */
972 0 : }
973 0 : case 1: {
974 0 : fd_gossip_purged_insert_failed_insert( crds->purged, value_hash, now );
975 0 : return -1L; /* stale */
976 0 : }
977 0 : }
978 :
979 0 : fd_gossip_purged_insert_replaced( crds->purged, incumbent->value_hash, now );
980 0 : crds_unindex( crds, incumbent );
981 :
982 0 : if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
983 0 : ci_changed = !fd_gossip_contact_info_eq( incumbent->ci->contact_info, value->contact_info );
984 0 : fd_gossip_wsample_fresh( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), 1 );
985 0 : fd_gossip_wsample_stake( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), origin_stake );
986 0 : fd_gossip_wsample_ping_tracked( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), origin_ping_tracked );
987 0 : fd_gossip_wsample_is_me( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), is_me );
988 0 : }
989 0 : }
990 :
991 0 : incumbent->wallclock = value->wallclock;
992 0 : incumbent->stake = origin_stake;
993 0 : incumbent->num_push_recv = (ulong)!!is_push;
994 0 : incumbent->expire.wallclock_nanos = now;
995 0 : incumbent->value_sz = (ushort)value_bytes_len;
996 0 : fd_memcpy( incumbent->value_bytes, value_bytes, value_bytes_len );
997 0 : fd_memcpy( incumbent->value_hash, value_hash, 32UL );
998 :
999 0 : if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_NODE_INSTANCE ) ) {
1000 0 : incumbent->node_instance_token = value->node_instance->token;
1001 0 : } else if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
1002 0 : *incumbent->ci->contact_info = *value->contact_info;
1003 0 : incumbent->ci->received_wallclock_nanos = now;
1004 0 : incumbent->ci->is_me = (uchar)!!is_me;
1005 0 : }
1006 :
1007 0 : crds_index( crds, incumbent );
1008 :
1009 0 : crds->has_staked_node |= incumbent->stake ? 1 : 0;
1010 :
1011 0 : publish_update_msg( crds, incumbent, value, ci_changed, now, stem );
1012 :
1013 0 : if( FD_UNLIKELY( incumbent->relay.pending ) ) {
1014 0 : crds->metrics->relay_coalesced_cnt++;
1015 0 : return 0L;
1016 0 : }
1017 0 : ulong now_ms = (ulong)FD_NANOSEC_TO_MILLI( now );
1018 0 : if( FD_UNLIKELY( !opt_relay || value->wallclock+15000UL<now_ms || value->wallclock>now_ms+15000UL ) ) return 0L;
1019 0 : if( FD_LIKELY( now-incumbent->relay.nanos>=FD_CRDS_RELAY_COALESCE_NANOS ) ) {
1020 0 : incumbent->relay.nanos = now;
1021 0 : *opt_relay = 1;
1022 0 : } else {
1023 0 : incumbent->relay.pending = 1;
1024 0 : incumbent->relay.nanos += FD_CRDS_RELAY_COALESCE_NANOS;
1025 0 : relay_treap_ele_insert( crds->relay_treap, incumbent, crds->pool );
1026 0 : }
1027 0 : return 0L;
1028 0 : }
1029 :
1030 : void
1031 : fd_crds_entry_value( fd_crds_entry_t const * entry,
1032 : uchar const ** value_bytes,
1033 0 : ulong * value_sz ) {
1034 0 : *value_bytes = entry->value_bytes;
1035 0 : *value_sz = entry->value_sz;
1036 0 : }
1037 :
1038 : ulong
1039 0 : fd_crds_entry_wallclock( fd_crds_entry_t const * entry ) {
1040 0 : return entry->wallclock;
1041 0 : }
1042 :
1043 : uchar const *
1044 0 : fd_crds_entry_hash( fd_crds_entry_t const * entry ) {
1045 0 : return entry->value_hash;
1046 0 : }
1047 :
1048 : fd_gossip_hset_t const *
1049 0 : fd_crds_hset( fd_crds_t const * crds ) {
1050 0 : return crds->hset;
1051 0 : }
1052 :
1053 : ulong
1054 0 : fd_crds_peer_count( fd_crds_t const * crds ){
1055 0 : return crds_contact_info_pool_used( crds->ci_pool );
1056 0 : }
1057 :
1058 : ulong
1059 0 : fd_crds_entrypoint_fresh_cnt( fd_crds_t const * crds ) {
1060 0 : return crds->entrypoint_fresh_cnt;
1061 0 : }
1062 :
1063 : fd_gossip_contact_info_t const *
1064 : fd_crds_ci( fd_crds_t const * crds,
1065 0 : ulong ci_idx ) {
1066 0 : fd_crds_contact_info_entry_t const * ci = crds_contact_info_pool_ele_const( crds->ci_pool, ci_idx );
1067 0 : FD_TEST( ci );
1068 0 : return ci->contact_info;
1069 0 : }
1070 :
1071 : uchar const *
1072 : fd_crds_ci_pubkey( fd_crds_t const * crds,
1073 0 : ulong ci_idx ) {
1074 0 : fd_crds_contact_info_entry_t const * ci = crds_contact_info_pool_ele_const( crds->ci_pool, ci_idx );
1075 0 : FD_TEST( ci );
1076 0 : return ci->crds_entry->key.pubkey;
1077 0 : }
1078 :
1079 : ulong
1080 : fd_crds_ci_idx( fd_crds_t const * crds,
1081 0 : uchar const * pubkey ) {
1082 0 : fd_crds_key_t lookup_ci = {
1083 0 : .tag = FD_GOSSIP_VALUE_CONTACT_INFO,
1084 0 : };
1085 0 : fd_memcpy( lookup_ci.pubkey, pubkey, 32UL );
1086 :
1087 0 : fd_crds_entry_t const * ci_entry = lookup_map_ele_query( crds->lookup_map, &lookup_ci, NULL, crds->pool );
1088 0 : if( FD_UNLIKELY( !ci_entry ) ) return ULONG_MAX;
1089 0 : FD_TEST( ci_entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO );
1090 0 : return crds_contact_info_pool_idx( crds->ci_pool, ci_entry->ci );
1091 0 : }
1092 :
1093 : fd_crds_entry_t const *
1094 : fd_crds_entry_at( fd_crds_t const * crds,
1095 0 : ulong idx ) {
1096 0 : return crds_pool_ele_const( crds->pool, idx );
1097 0 : }
|