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