From 38c319324ad83923a835e68af5ddb9b17db65ca4 Mon Sep 17 00:00:00 2001 From: Ishan Bhatt Date: Thu, 12 Feb 2026 01:37:16 +0000 Subject: [PATCH 1/6] wip --- src/flamenco/runtime/fd_runtime_const.h | 2 + src/flamenco/stakes/Local.mk | 5 + src/flamenco/stakes/fd_vote_timestamps.c | 508 +++++++++++++++++++++ src/flamenco/stakes/fd_vote_timestamps.h | 67 +++ src/flamenco/stakes/test_vote_timestamps.c | 25 + 5 files changed, 607 insertions(+) create mode 100644 src/flamenco/stakes/fd_vote_timestamps.c create mode 100644 src/flamenco/stakes/fd_vote_timestamps.h create mode 100644 src/flamenco/stakes/test_vote_timestamps.c diff --git a/src/flamenco/runtime/fd_runtime_const.h b/src/flamenco/runtime/fd_runtime_const.h index 34cc0066a5c..bc8f275869f 100644 --- a/src/flamenco/runtime/fd_runtime_const.h +++ b/src/flamenco/runtime/fd_runtime_const.h @@ -292,6 +292,8 @@ FD_STATIC_ASSERT( BPF_LOADER_SERIALIZATION_FOOTPRINT==FD_BPF_LOADER_INPUT_REGION #define FD_VOTE_LOCKOUTS_ALIGN (32UL) #define FD_VOTE_LOCKOUTS_FOOTPRINT (FD_VOTE_STATE_VERSIONED_FOOTPRINT) +#define FD_MAX_WRITALBE_ACCOUNTS_IN_SLOT (42000UL) + static const FD_FN_UNUSED fd_account_meta_t FD_ACCOUNT_META_DEFAULT = {0}; FD_PROTOTYPES_END diff --git a/src/flamenco/stakes/Local.mk b/src/flamenco/stakes/Local.mk index 6892777f8e6..2a43da972a9 100644 --- a/src/flamenco/stakes/Local.mk +++ b/src/flamenco/stakes/Local.mk @@ -12,7 +12,12 @@ endif $(call add-hdrs,fd_vote_states.h) $(call add-objs,fd_vote_states,fd_flamenco) +$(call add-hdrs,fd_vote_timestamps.h) +$(call add-objs,fd_vote_timestamps,fd_flamenco) ifdef FD_HAS_HOSTED $(call make-unit-test,test_vote_states,test_vote_states,fd_flamenco fd_funk fd_ballet fd_util) $(call run-unit-test,test_vote_states) + +$(call make-unit-test,test_vote_timestamps,test_vote_timestamps,fd_flamenco fd_funk fd_ballet fd_util) +$(call run-unit-test,test_vote_timestamps) endif diff --git a/src/flamenco/stakes/fd_vote_timestamps.c b/src/flamenco/stakes/fd_vote_timestamps.c new file mode 100644 index 00000000000..a0d03cc422b --- /dev/null +++ b/src/flamenco/stakes/fd_vote_timestamps.c @@ -0,0 +1,508 @@ +#include "fd_vote_timestamps.h" + +struct fd_vote_timestamp_index_ele { + fd_pubkey_t pubkey; + ushort refcnt; + uint next; +}; +typedef struct fd_vote_timestamp_index_ele fd_vote_timestamp_index_ele_t; + +#define POOL_NAME fd_vote_timestamp_index_pool +#define POOL_T fd_vote_timestamp_index_ele_t +#define POOL_NEXT next +#define POOL_IDX_T uint +#include "../../util/tmpl/fd_pool.c" + +#define MAP_NAME fd_vote_timestamp_index_map +#define MAP_KEY_T fd_pubkey_t +#define MAP_ELE_T fd_vote_timestamp_index_ele_t +#define MAP_KEY pubkey +#define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 )) +#define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) )) +#define MAP_NEXT next +#define MAP_IDX_T uint +#include "../../util/tmpl/fd_map_chain.c" + +/**********************************************************************/ + +struct snapshot_ele { + ulong slot_age : 19; + ulong timestamp : 45; + uint idx; + uint next; +}; +typedef struct snapshot_ele snapshot_ele_t; + +#define MAP_NAME snapshot_ele_map +#define MAP_KEY_T uint +#define MAP_ELE_T snapshot_ele_t +#define MAP_KEY idx +#define MAP_NEXT next +#define MAP_IDX_T uint +#include "../../util/tmpl/fd_map_chain.c" + +struct snapshot_key { + uchar prev; + uchar next; + ulong offset; + ulong map_offset; +}; +typedef struct snapshot_key snapshot_key_ele_t; + +#define DLIST_NAME snapshot_key_dlist +#define DLIST_ELE_T snapshot_key_ele_t +#define DLIST_IDX_T uchar +#include "../../util/tmpl/fd_dlist.c" + +#define POOL_NAME snapshot_key_pool +#define POOL_T snapshot_key_ele_t +#define POOL_IDX_T uchar +#include "../../util/tmpl/fd_pool.c" + +/*********************************************************************/ + +struct fd_vote_timestamp_delta_ele { + ulong timestamp; + uint pubkey_idx; +}; +typedef struct fd_vote_timestamp_delta_ele fd_vote_timestamp_delta_ele_t; + +struct fd_vote_timestamps { + ulong fork_pool_offset; + + ulong index_pool_offset; + ulong index_map_offset; + + ushort root_idx; + + ulong snapshot_max; + ulong snapshot_cnt; + ulong snapshot_keys_dlist_offset; + ulong snapshot_keys_pool_offset; +}; +typedef struct fd_vote_timestamps fd_vote_timestamps_t; + +struct fd_vote_timestamp_ele { + ulong slot; + /* left child, right sibling tree pointers */ + ushort parent_idx; + ushort child_idx; + ushort sibling_idx; + ushort next; + + uchar snapshot_idx; + + ushort deltas_cnt; + /* TODO: Const for this or make it paramterizable */ + fd_vote_timestamp_delta_ele_t deltas[ 42000UL ]; +}; +typedef struct fd_vote_timestamp_ele fd_vote_timestamp_ele_t; + +#define POOL_NAME fd_vote_timestamp_pool +#define POOL_T fd_vote_timestamp_ele_t +#define POOL_IDX_T ushort +#include "../../util/tmpl/fd_pool.c" + +static inline fd_vote_timestamp_ele_t * +fd_vote_timestamps_get_fork_pool( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->fork_pool_offset ); +} + +static inline fd_vote_timestamp_index_ele_t * +fd_vote_timestamps_get_index_pool( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->index_pool_offset ); +} + +static inline fd_vote_timestamp_index_map_t * +fd_vote_timestamps_get_index_map( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->index_map_offset ); +} + +static inline snapshot_key_dlist_t * +fd_vote_timestamps_get_snapshot_keys_dlist( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->snapshot_keys_dlist_offset ); +} + +static inline snapshot_key_ele_t * +fd_vote_timestamps_get_snapshot_keys_pool( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->snapshot_keys_pool_offset ); +} + +static inline snapshot_ele_t * +fd_vote_timestamps_get_snapshot( fd_vote_timestamps_t * vote_ts, + uchar snapshot_idx ) { + /* TODO:FIXME: this is pretty hacky. */ + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); + return fd_type_pun( (uchar *)vote_ts + key->offset ); +} + +static inline snapshot_ele_map_t * +fd_vote_timestamps_get_snapshot_ele_map( fd_vote_timestamps_t * vote_ts, + uchar snapshot_idx ) { + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); + return fd_type_pun( (uchar *)vote_ts + key->map_offset ); +} + +ulong +fd_vote_timestamps_align( void ) { + return 128UL; +} + +ulong +fd_vote_timestamps_footprint( ulong max_live_slots, + uchar max_snaps, + ulong max_vote_accs ) { + ulong map_chain_cnt = 2048UL; + + + ulong l = FD_LAYOUT_INIT; + l = FD_LAYOUT_APPEND( l, fd_vote_timestamps_align(), sizeof(fd_vote_timestamps_t) ); + l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_pool_align(), fd_vote_timestamp_pool_footprint( max_live_slots ) ); + l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_index_pool_align(), fd_vote_timestamp_index_pool_footprint( max_vote_accs ) ); + l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_index_map_align(), fd_vote_timestamp_index_map_footprint( map_chain_cnt ) ); + l = FD_LAYOUT_APPEND( l, alignof(snapshot_ele_t), sizeof(snapshot_ele_t)*max_vote_accs*max_snaps ); + l = FD_LAYOUT_APPEND( l, snapshot_key_dlist_align(), snapshot_key_dlist_footprint() ); + l = FD_LAYOUT_APPEND( l, snapshot_key_pool_align(), snapshot_key_pool_footprint( max_snaps ) ); + for( uchar i=0; ifork_pool_offset = (ulong)fork_pool - (ulong)shmem; + vote_timestamps->index_pool_offset = (ulong)index_pool - (ulong)shmem; + vote_timestamps->index_map_offset = (ulong)index_map - (ulong)shmem; + + + snapshot_key_ele_t * snapshot_keys_pool = snapshot_key_pool_join( snapshot_key_pool_new( snapshot_keys_pool_mem, max_snaps ) ); + if( FD_UNLIKELY( !snapshot_keys_pool ) ) { + FD_LOG_WARNING(( "Failed to create vote timestamp snapshot keys pool" )); + return NULL; + } + + + snapshot_key_dlist_t * snapshot_keys = snapshot_key_dlist_join( snapshot_key_dlist_new( snapshot_keys_dlist_mem ) ); + if( FD_UNLIKELY( !snapshot_keys ) ) { + FD_LOG_WARNING(( "Failed to create vote timestamp snapshot keys" )); + return NULL; + } + + for( uchar i=0; ioffset = (ulong)snapshots_mem - (ulong)vote_timestamps; + + snapshot_ele_map_t * snapshot_ele_map = snapshot_ele_map_join( snapshot_ele_map_new( snapshots_ele_map_mem, map_chain_cnt, seed ) ); + if( FD_UNLIKELY( !snapshot_ele_map ) ) { + FD_LOG_WARNING(( "Failed to create vote timestamp snapshot ele map" )); + return NULL; + } + key->map_offset = (ulong)snapshot_ele_map - (ulong)vote_timestamps; + snapshot_key_pool_ele_release( snapshot_keys_pool_mem, key ); + } + FD_SCRATCH_ALLOC_FINI( l, fd_vote_timestamps_align() ); + + vote_timestamps->snapshot_keys_dlist_offset = (ulong)snapshot_keys - (ulong)shmem; + vote_timestamps->snapshot_keys_pool_offset = (ulong)snapshot_keys_pool - (ulong)shmem; + + return shmem; +} + +fd_vote_timestamps_t * +fd_vote_timestamps_join( void * shmem ) { + return (fd_vote_timestamps_t *)shmem; +} + +ushort +fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, + ushort parent_fork_idx, + ulong slot ) { + + fd_vote_timestamp_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + + FD_CRIT( fd_vote_timestamp_pool_free( pool )!=0UL, "No free slots in vote timestamp pool" ); + + fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele_acquire( pool ); + ushort child_idx = (ushort)fd_vote_timestamp_pool_idx( pool, child ); + + /* If there is no root yet, this is the root*/ + if( FD_UNLIKELY( parent_fork_idx==USHORT_MAX ) ) { + FD_CRIT( vote_ts->root_idx==USHORT_MAX, "Root already exists" ); + child->parent_idx = USHORT_MAX; + vote_ts->root_idx = child_idx; + } else { + fd_vote_timestamp_ele_t * parent = fd_vote_timestamp_pool_ele( pool, parent_fork_idx ); + FD_CRIT( parent, "parent fork idx not found" ); + + fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele_acquire( pool ); + ushort child_idx = (ushort)fd_vote_timestamp_pool_idx( pool, child ); + child->parent_idx = parent_fork_idx; + + if( FD_LIKELY( parent->child_idx==USHORT_MAX ) ) { + parent->child_idx = child_idx; + } else { + fd_vote_timestamp_ele_t * curr = fd_vote_timestamp_pool_ele( pool, parent->child_idx ); + /* Assign child as the sibling pointer of rightmost child. */ + while( curr->sibling_idx!=USHORT_MAX ) { + curr = fd_vote_timestamp_pool_ele( pool, curr->sibling_idx ); + } + curr->sibling_idx = child_idx; + } + } + + child->sibling_idx = USHORT_MAX; + child->child_idx = USHORT_MAX; + child->slot = slot; + child->deltas_cnt = 0UL; + child->snapshot_idx = UCHAR_MAX; + + return child_idx; +} + +void +fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, + ushort new_root_idx ) { + fd_vote_timestamp_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * new_root = fd_vote_timestamp_pool_ele( pool, new_root_idx ); + fd_vote_timestamp_ele_t * head = fd_vote_timestamp_pool_ele( pool, vote_ts->root_idx ); + + head->next = USHORT_MAX; + fd_vote_timestamp_ele_t * tail = head; + while( head ) { + fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele( pool, head->child_idx ); + + while( FD_LIKELY( child ) ) { + + if( FD_LIKELY( child!=new_root ) ) { + + /* Update tail pointers */ + tail->next = (ushort)fd_vote_timestamp_pool_idx( pool, child ); + tail = fd_vote_timestamp_pool_ele( pool, tail->next ); + tail->next = USHORT_MAX; + } + + child = fd_vote_timestamp_pool_ele( pool, child->sibling_idx ); + } + + fd_vote_timestamp_ele_t * next = fd_vote_timestamp_pool_ele( pool, head->next ); + fd_vote_timestamp_pool_ele_release( pool, head ); + head = next; + } + + new_root->parent_idx = USHORT_MAX; + vote_ts->root_idx = new_root_idx; +} + +void +fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, + ushort fork_idx, + fd_pubkey_t pubkey, + ulong timestamp ) { + /* First update and query index. Figure out pubkey index if not one + exists, otherwise allocate a new entry in the index. */ + fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + fd_vote_timestamp_index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); + + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_map_ele_query( index_map, &pubkey, NULL, index_pool ); + if( FD_LIKELY( ele ) ) { + ele->refcnt++; + } else { + ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); + ele->pubkey = pubkey; + ele->refcnt = 1UL; + + FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); + } + + uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); + + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + + /* Now just add the entry to the delta list. */ + fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[ fork->deltas_cnt++ ]; + delta->timestamp = timestamp; + delta->pubkey_idx = pubkey_idx; +} + +static uchar +prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, + ushort fork_idx, + ushort * parent_snapshot_path, + ushort * parent_snapshot_path_cnt ) { + /* A reasonable eviction policy here is LRU eviction with some tweaks: + 1. Never evict the root snapshot + 2. Don't evict the "best" snapshot (closest to the fork idx) */ + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); + + /* Find best snapshot to build off of. Always prioritize the least + amount of deltas. This is purely a policy decision. */ + parent_snapshot_path[ *parent_snapshot_path_cnt ] = (ushort)fd_vote_timestamp_pool_idx( fork_pool, fork ); + (*parent_snapshot_path_cnt)++; + + fd_vote_timestamp_ele_t * curr = fork; + while( curr->snapshot_idx!=UCHAR_MAX ) { + curr = fd_vote_timestamp_pool_ele( fork_pool, curr->parent_idx ); + parent_snapshot_path[*parent_snapshot_path_cnt] = (ushort)fd_vote_timestamp_pool_idx( fork_pool, curr ); + (*parent_snapshot_path_cnt)++; + } + + uchar best_snapshot_idx = curr->snapshot_idx; + uchar root_snapshot_idx = root->snapshot_idx; + + snapshot_key_dlist_t * snapshot_keys_dlist = fd_vote_timestamps_get_snapshot_keys_dlist( vote_ts ); + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + + if( FD_UNLIKELY( snapshot_key_pool_free( snapshot_keys_pool )==0UL ) ) { + /* If there are no free slots in the pool, we need to evict. */ + + snapshot_key_ele_t * key = snapshot_key_dlist_ele_pop_head( snapshot_keys_dlist, snapshot_keys_pool ); + uchar idx = (uchar)snapshot_key_pool_idx( snapshot_keys_pool, key ); + /* TODO: MAKE IT SO THE ROOTED BANK ISN'T IN THE DLIST */ + if( idx==root_snapshot_idx || idx==best_snapshot_idx ) { + snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, key, snapshot_keys_pool ); + key = snapshot_key_dlist_ele_pop_head( snapshot_keys_dlist, snapshot_keys_pool ); + idx = (uchar)snapshot_key_pool_idx( snapshot_keys_pool, key ); + } + if( idx==root_snapshot_idx || idx==best_snapshot_idx ) { + snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, key, snapshot_keys_pool ); + key = snapshot_key_dlist_ele_pop_head( snapshot_keys_dlist, snapshot_keys_pool ); + idx = (uchar)snapshot_key_pool_idx( snapshot_keys_pool, key ); + } + snapshot_key_pool_ele_release( snapshot_keys_pool, key ); + } + + snapshot_key_ele_t * new_key = snapshot_key_pool_ele_acquire( snapshot_keys_pool ); + snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, new_key, snapshot_keys_pool ); + return (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ); +} + +static void +apply_delta( ulong base_slot, + snapshot_ele_t * snapshot, + snapshot_ele_map_t * snapshot_map, + fd_vote_timestamp_ele_t * fork ) { + for( ushort i=0; ideltas_cnt; i++ ) { + /* We have the property that timestamps are always increasing for + the same pubkey. When a pubkey is evicted from the index, then + we will clear all entries for that pubkey in all snapshots in the + case it gets renewed. */ + fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[i]; + snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); + if( FD_LIKELY( snapshot_ele ) ) { + /* If it is already found do nothing */ + } else { + snapshot_ele = &snapshot[delta->pubkey_idx]; + snapshot_ele->idx = delta->pubkey_idx; + snapshot_ele->timestamp = delta->timestamp; + snapshot_ele->slot_age = base_slot - fork->slot; + snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + } + } +} + +static void +apply_snapshot( snapshot_ele_t * snapshot, + snapshot_ele_map_t * snapshot_map, + ulong base_slot, + snapshot_ele_t * prev_snapshot, + snapshot_ele_map_t * prev_snapshot_map, + ulong prev_slot ) { + + for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( prev_snapshot_map, prev_snapshot ); + !snapshot_ele_map_iter_done( iter, prev_snapshot_map, prev_snapshot ); + iter = snapshot_ele_map_iter_next( iter, prev_snapshot_map, prev_snapshot ) ) { + uint ele_idx = (uint)snapshot_ele_map_iter_idx( iter, prev_snapshot_map, prev_snapshot ); + + snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &ele_idx, NULL, snapshot ); + if( FD_LIKELY( snapshot_ele ) ) continue; + snapshot_ele = &snapshot[ele_idx]; + snapshot_ele->idx = (uint)ele_idx; + snapshot_ele->timestamp = prev_snapshot[ele_idx].timestamp; + snapshot_ele->slot_age = base_slot - prev_slot; + snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + } +} + +void +fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, + ushort fork_idx ) { + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + + ushort path[ USHORT_MAX ]; + ushort path_cnt = 0; + fork->snapshot_idx = prune_and_get_snapshot( vote_ts, fork_idx, path, &path_cnt ); + + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); + snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); + + /* We now have the path of all of the vote timestamp entries that we + have to apply. We also have the snapshot index that we can use to + get the timestamp. We want to iterate backwards through the fork + indices and apply the deltas */ + fd_vote_timestamp_ele_t * curr_fork = NULL; + for( ushort i=0; islot, snapshot, snapshot_map, curr_fork ); + } + FD_TEST( curr_fork ); + + /* Finally, we need to apply the delta from the previous snapshot */ + snapshot_ele_t * prev_snapshot = fd_vote_timestamps_get_snapshot( vote_ts, curr_fork->snapshot_idx ); + snapshot_ele_map_t * prev_snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, curr_fork->snapshot_idx ); + apply_snapshot( snapshot, snapshot_map, fork->slot, prev_snapshot, prev_snapshot_map, curr_fork->slot ); + + +} diff --git a/src/flamenco/stakes/fd_vote_timestamps.h b/src/flamenco/stakes/fd_vote_timestamps.h new file mode 100644 index 00000000000..b3e3b5c3f4a --- /dev/null +++ b/src/flamenco/stakes/fd_vote_timestamps.h @@ -0,0 +1,67 @@ +#ifndef HEADER_fd_src_flamenco_stakes_fd_vote_timestamps_h +#define HEADER_fd_src_flamenco_stakes_fd_vote_timestamps_h + +#include "../../util/fd_util_base.h" +#include "../../util/tmpl/fd_map.h" +#include "../types/fd_types_custom.h" + +FD_PROTOTYPES_BEGIN + +// STATIC: SIZED FOR 2^25 PUBKEYS MAX, 1.25GiB +// PUBKEYS: Array<(Pubkey, ushort: refcnt)> +// PUBKEY_IDX: uint into pubkeys array +// PUBKEY_MAP: Map + +// attach_child( fork_id: ushort, parent_fork_id: ushort ); +// advance_root( fork_id: ushort ) -> ensure snapshot child +// insert( fork_id: ushort, pubkey: [u8; 32], slot: u64, timestamp: i64 ); +// timestamp( fork_id: ushort ) -> i64; +// +// SNAPSHOTS: 8*2^25*8 bytes in gib ~ 2 GiB +// SNAPSHOTS: Pool> // slot_age 19 bits, timestamp 45 bits +// +// DELTAS: List<(PUBKEY_IDX, timestamp)> // 4096 * 40000 * 4 bytes in gib ~ 0.61 GiB + +struct fd_vote_timestamps; +typedef struct fd_vote_timestamps fd_vote_timestamps_t; + +ulong +fd_vote_timestamps_align( void ); + +ulong +fd_vote_timestamps_footprint( ulong max_live_slots, + uchar max_snaps, + ulong max_vote_accs ); + +void * +fd_vote_timestamps_new( void * shmem, + ulong max_live_slots, + ulong max_snaps, + ulong max_vote_accs, + ulong seed ); + +fd_vote_timestamps_t * +fd_vote_timestamps_join( void * shmem ); + +ushort +fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, + ushort parent_fork_idx, + ulong slot ); + +void +fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, + ushort new_root_idx ); + +void +fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, + ushort fork_idx, + fd_pubkey_t pubkey, + ulong timestamp ); + +void +fd_vote_times_stamps_get_timestamp( fd_vote_timestamps_t * vote_ts, + ushort fork_idx ); + +FD_PROTOTYPES_END + +#endif /* HEADER_fd_src_flamenco_stakes_fd_vote_timestamps_h */ diff --git a/src/flamenco/stakes/test_vote_timestamps.c b/src/flamenco/stakes/test_vote_timestamps.c new file mode 100644 index 00000000000..645a5ec9513 --- /dev/null +++ b/src/flamenco/stakes/test_vote_timestamps.c @@ -0,0 +1,25 @@ +#include "fd_vote_timestamps.h" + +int main( int argc, char ** argv ) { + fd_boot( &argc, &argv ); + + char const * name = fd_env_strip_cmdline_cstr ( &argc, &argv, "--wksp", NULL, NULL ); + char const * _page_sz = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz", NULL, "gigantic" ); + ulong page_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt", NULL, 1UL ); + ulong near_cpu = fd_env_strip_cmdline_ulong( &argc, &argv, "--near-cpu", NULL, fd_log_cpu_id() ); + ulong wksp_tag = fd_env_strip_cmdline_ulong( &argc, &argv, "--wksp-tag", NULL, 1234UL ); + + fd_wksp_t * wksp; + if( name ) { + FD_LOG_NOTICE(( "Attaching to --wksp %s", name )); + wksp = fd_wksp_attach( name ); + } else { + FD_LOG_NOTICE(( "--wksp not specified, using an anonymous local workspace, --page-sz %s, --page-cnt %lu, --near-cpu %lu", + _page_sz, page_cnt, near_cpu )); + wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz( _page_sz ), page_cnt, near_cpu, "wksp", 0UL ); + } + + (void)wksp; + (void)wksp_tag; + FD_LOG_NOTICE(( "pass" )); +} From ff2ee4c9102439c18e49504c2f9e03ab42badb99 Mon Sep 17 00:00:00 2001 From: Ishan Bhatt Date: Thu, 12 Feb 2026 16:06:32 +0000 Subject: [PATCH 2/6] wip --- src/flamenco/stakes/fd_vote_timestamps.c | 89 ++++++++++++++++++++-- src/flamenco/stakes/fd_vote_timestamps.h | 18 ++++- src/flamenco/stakes/test_vote_timestamps.c | 45 ++++++++++- 3 files changed, 144 insertions(+), 8 deletions(-) diff --git a/src/flamenco/stakes/fd_vote_timestamps.c b/src/flamenco/stakes/fd_vote_timestamps.c index a0d03cc422b..36cc5e8d697 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.c +++ b/src/flamenco/stakes/fd_vote_timestamps.c @@ -162,7 +162,6 @@ fd_vote_timestamps_footprint( ulong max_live_slots, l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_pool_align(), fd_vote_timestamp_pool_footprint( max_live_slots ) ); l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_index_pool_align(), fd_vote_timestamp_index_pool_footprint( max_vote_accs ) ); l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_index_map_align(), fd_vote_timestamp_index_map_footprint( map_chain_cnt ) ); - l = FD_LAYOUT_APPEND( l, alignof(snapshot_ele_t), sizeof(snapshot_ele_t)*max_vote_accs*max_snaps ); l = FD_LAYOUT_APPEND( l, snapshot_key_dlist_align(), snapshot_key_dlist_footprint() ); l = FD_LAYOUT_APPEND( l, snapshot_key_pool_align(), snapshot_key_pool_footprint( max_snaps ) ); for( uchar i=0; ioffset = (ulong)snapshots_mem - (ulong)vote_timestamps; @@ -246,7 +245,7 @@ fd_vote_timestamps_new( void * shmem, return NULL; } key->map_offset = (ulong)snapshot_ele_map - (ulong)vote_timestamps; - snapshot_key_pool_ele_release( snapshot_keys_pool_mem, key ); + snapshot_key_pool_ele_release( snapshot_keys_pool, key ); } FD_SCRATCH_ALLOC_FINI( l, fd_vote_timestamps_align() ); @@ -261,6 +260,39 @@ fd_vote_timestamps_join( void * shmem ) { return (fd_vote_timestamps_t *)shmem; } +ushort +fd_vote_timestamps_init( fd_vote_timestamps_t * vote_ts, + ulong slot ) { + /* Assign a fork node on the fork pool */ + fd_vote_timestamp_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele_acquire( pool ); + ushort fork_idx = (ushort)fd_vote_timestamp_pool_idx( pool, fork ); + + vote_ts->root_idx = fork_idx; + fork->parent_idx = USHORT_MAX; + fork->child_idx = USHORT_MAX; + fork->sibling_idx = USHORT_MAX; + fork->slot = slot; + + /* Setup the snapshot key for the root fork. */ + + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + + snapshot_key_dlist_t * snapshot_keys_dlist = fd_vote_timestamps_get_snapshot_keys_dlist( vote_ts ); + snapshot_key_ele_t * new_key = snapshot_key_pool_ele_acquire( snapshot_keys_pool ); + ulong sidx = snapshot_key_pool_idx( snapshot_keys_pool, new_key ); + fork->snapshot_idx = (uchar)sidx; + + snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, new_key, snapshot_keys_pool ); + + /* Now that the node is on the tracking dlist and is allocated we + need to initialize the map for the snapshot. */ + snapshot_ele_map_t * snapshot_ele_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); + snapshot_ele_map_reset( snapshot_ele_map ); + + return fork_idx; +} + ushort fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, ushort parent_fork_idx, @@ -373,6 +405,41 @@ fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, delta->pubkey_idx = pubkey_idx; } +void +fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, + fd_pubkey_t pubkey, + ulong timestamp ) { + + /* First update and query index. Figure out pubkey index if not one + exists, otherwise allocate a new entry in the index. */ + fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + fd_vote_timestamp_index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); + + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_map_ele_query( index_map, &pubkey, NULL, index_pool ); + if( FD_LIKELY( ele ) ) { + FD_LOG_CRIT(( "pubkey already exists in index" )); + } else { + ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); + ele->pubkey = pubkey; + ele->refcnt = 1UL; + + FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); + } + uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); + + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); + + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); + snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); + + snapshot_ele_t * snapshot_ele = &snapshot[pubkey_idx]; + snapshot_ele->idx = pubkey_idx; + snapshot_ele->timestamp = timestamp; + snapshot_ele->slot_age = 0; + snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); +} + static uchar prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, ushort fork_idx, @@ -477,7 +544,7 @@ apply_snapshot( snapshot_ele_t * snapshot, void fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, - ushort fork_idx ) { + ushort fork_idx ) { fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); @@ -491,7 +558,7 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, /* We now have the path of all of the vote timestamp entries that we have to apply. We also have the snapshot index that we can use to get the timestamp. We want to iterate backwards through the fork - indices and apply the deltas */ + indices and apply the deltas. */ fd_vote_timestamp_ele_t * curr_fork = NULL; for( ushort i=0; isnapshot_idx ); snapshot_ele_map_t * prev_snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, curr_fork->snapshot_idx ); apply_snapshot( snapshot, snapshot_map, fork->slot, prev_snapshot, prev_snapshot_map, curr_fork->slot ); +} +ushort +fd_vote_timestamps_slot_votes_cnt( fd_vote_timestamps_t * vote_ts, + ushort fork_idx ) { + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + return fork->deltas_cnt; +} +uint +fd_vote_timestamps_index_cnt( fd_vote_timestamps_t * vote_ts ) { + fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + return (uint)fd_vote_timestamp_index_pool_used( index_pool ); } diff --git a/src/flamenco/stakes/fd_vote_timestamps.h b/src/flamenco/stakes/fd_vote_timestamps.h index b3e3b5c3f4a..3bfc055bbac 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.h +++ b/src/flamenco/stakes/fd_vote_timestamps.h @@ -43,6 +43,10 @@ fd_vote_timestamps_new( void * shmem, fd_vote_timestamps_t * fd_vote_timestamps_join( void * shmem ); +ushort +fd_vote_timestamps_init( fd_vote_timestamps_t * vote_ts, + ulong slot ); + ushort fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, ushort parent_fork_idx, @@ -59,9 +63,21 @@ fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, ulong timestamp ); void -fd_vote_times_stamps_get_timestamp( fd_vote_timestamps_t * vote_ts, +fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, + fd_pubkey_t pubkey, + ulong timestamp ); + +void +fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ushort fork_idx ); +ushort +fd_vote_timestamps_slot_votes_cnt( fd_vote_timestamps_t * vote_ts, + ushort fork_idx ); + +uint +fd_vote_timestamps_index_cnt( fd_vote_timestamps_t * vote_ts ); + FD_PROTOTYPES_END #endif /* HEADER_fd_src_flamenco_stakes_fd_vote_timestamps_h */ diff --git a/src/flamenco/stakes/test_vote_timestamps.c b/src/flamenco/stakes/test_vote_timestamps.c index 645a5ec9513..362f7ff8b81 100644 --- a/src/flamenco/stakes/test_vote_timestamps.c +++ b/src/flamenco/stakes/test_vote_timestamps.c @@ -9,6 +9,15 @@ int main( int argc, char ** argv ) { ulong near_cpu = fd_env_strip_cmdline_ulong( &argc, &argv, "--near-cpu", NULL, fd_log_cpu_id() ); ulong wksp_tag = fd_env_strip_cmdline_ulong( &argc, &argv, "--wksp-tag", NULL, 1234UL ); + fd_pubkey_t pubkey_A = {.ul = {0}}; + fd_pubkey_t pubkey_B = {.ul = {1}}; + fd_pubkey_t pubkey_C = {.ul = {2}}; + fd_pubkey_t pubkey_D = {.ul = {3}}; + fd_pubkey_t pubkey_E = {.ul = {4}}; + fd_pubkey_t pubkey_F = {.ul = {5}}; (void)pubkey_F; + fd_pubkey_t pubkey_G = {.ul = {6}}; (void)pubkey_G; + fd_pubkey_t pubkey_H = {.ul = {7}}; (void)pubkey_H; + fd_wksp_t * wksp; if( name ) { FD_LOG_NOTICE(( "Attaching to --wksp %s", name )); @@ -19,7 +28,39 @@ int main( int argc, char ** argv ) { wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz( _page_sz ), page_cnt, near_cpu, "wksp", 0UL ); } - (void)wksp; - (void)wksp_tag; + ulong footprint = fd_vote_timestamps_footprint( 16UL, 4, 128UL ); + FD_LOG_NOTICE(( "footprint: %lu", footprint )); + + uchar * mem = fd_wksp_alloc_laddr( wksp, fd_vote_timestamps_align(), footprint, wksp_tag ); + FD_TEST( mem ); + + + fd_vote_timestamps_t * vote_timestamps = fd_vote_timestamps_join( fd_vote_timestamps_new( mem, 16UL, 4, 128UL, 0UL ) ); + FD_TEST( vote_timestamps ); + + ushort fork_idx = fd_vote_timestamps_init( vote_timestamps, 0UL ); + FD_TEST( fork_idx==0 ); + + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_A, 10 ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_B, 11 ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_C, 10 ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_D, 10 ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_E, 10 ); + + FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); + FD_TEST( 0==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, fork_idx ) ); + + + ushort child_idx = fd_vote_timestamps_attach_child( vote_timestamps, fork_idx, 1UL ); + (void)child_idx; + + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_A, 11 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_B, 11 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_C, 11 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_D, 11 ); + FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); + FD_TEST( 4==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx ) ); + FD_LOG_NOTICE(( "pass" )); + } From ed800dfc69c563c084bbffda00a3a408ca89a1c7 Mon Sep 17 00:00:00 2001 From: Ishan Bhatt Date: Thu, 12 Feb 2026 21:36:59 +0000 Subject: [PATCH 3/6] kind of working --- src/flamenco/stakes/fd_vote_timestamps.c | 124 +++++++++++++++++---- src/flamenco/stakes/fd_vote_timestamps.h | 13 ++- src/flamenco/stakes/test_vote_timestamps.c | 17 +-- 3 files changed, 123 insertions(+), 31 deletions(-) diff --git a/src/flamenco/stakes/fd_vote_timestamps.c b/src/flamenco/stakes/fd_vote_timestamps.c index 36cc5e8d697..253a15fb269 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.c +++ b/src/flamenco/stakes/fd_vote_timestamps.c @@ -2,6 +2,7 @@ struct fd_vote_timestamp_index_ele { fd_pubkey_t pubkey; + ulong epoch_stakes[ 2UL ]; ushort refcnt; uint next; }; @@ -61,6 +62,21 @@ typedef struct snapshot_key snapshot_key_ele_t; /*********************************************************************/ +/* ts_est_ele_t is a temporary struct used for sorting vote accounts by + last vote timestamp for clock sysvar calculation. */ + struct ts_est_ele { + ulong timestamp; + fd_w_u128_t stake; /* should really be fine as ulong, but we match Agave */ + }; + typedef struct ts_est_ele ts_est_ele_t; + +#define SORT_NAME sort_stake_ts +#define SORT_KEY_T ts_est_ele_t +#define SORT_BEFORE(a,b) ( (a).timestamp < (b).timestamp ) +#include "../../util/tmpl/fd_sort.c" + +/* ***************************/ + struct fd_vote_timestamp_delta_ele { ulong timestamp; uint pubkey_idx; @@ -79,11 +95,14 @@ struct fd_vote_timestamps { ulong snapshot_cnt; ulong snapshot_keys_dlist_offset; ulong snapshot_keys_pool_offset; + + ts_est_ele_t ts_eles[ 40200 ]; /* TODO:FIXME: this has to be configurable */ }; typedef struct fd_vote_timestamps fd_vote_timestamps_t; struct fd_vote_timestamp_ele { ulong slot; + ushort epoch; /* left child, right sibling tree pointers */ ushort parent_idx; ushort child_idx; @@ -244,8 +263,11 @@ fd_vote_timestamps_new( void * shmem, FD_LOG_WARNING(( "Failed to create vote timestamp snapshot ele map" )); return NULL; } + key->map_offset = (ulong)snapshot_ele_map - (ulong)vote_timestamps; - snapshot_key_pool_ele_release( snapshot_keys_pool, key ); + } + for( uchar i=0; ichild_idx = USHORT_MAX; fork->sibling_idx = USHORT_MAX; fork->slot = slot; + fork->epoch = epoch; /* Setup the snapshot key for the root fork. */ @@ -296,7 +320,8 @@ fd_vote_timestamps_init( fd_vote_timestamps_t * vote_ts, ushort fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, ushort parent_fork_idx, - ulong slot ) { + ulong slot, + ushort epoch ) { fd_vote_timestamp_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); @@ -333,6 +358,7 @@ fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, child->sibling_idx = USHORT_MAX; child->child_idx = USHORT_MAX; child->slot = slot; + child->epoch = epoch; child->deltas_cnt = 0UL; child->snapshot_idx = UCHAR_MAX; @@ -400,36 +426,34 @@ fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); /* Now just add the entry to the delta list. */ - fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[ fork->deltas_cnt++ ]; + fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[ fork->deltas_cnt ]; delta->timestamp = timestamp; delta->pubkey_idx = pubkey_idx; + fork->deltas_cnt++; } void fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, fd_pubkey_t pubkey, - ulong timestamp ) { + ulong timestamp, + ulong stake ) { /* First update and query index. Figure out pubkey index if not one exists, otherwise allocate a new entry in the index. */ fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); fd_vote_timestamp_index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); - fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_map_ele_query( index_map, &pubkey, NULL, index_pool ); - if( FD_LIKELY( ele ) ) { - FD_LOG_CRIT(( "pubkey already exists in index" )); - } else { - ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); - ele->pubkey = pubkey; - ele->refcnt = 1UL; - - FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); - } - uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); + ele->pubkey = pubkey; + ele->refcnt = 1UL; + ele->epoch_stakes[ fork->epoch % 2UL ] = stake; + + FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); + uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); @@ -450,6 +474,7 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, 2. Don't evict the "best" snapshot (closest to the fork idx) */ fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + FD_LOG_NOTICE(("FORK IDX: %hu", fork_idx)); fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); @@ -459,15 +484,21 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, (*parent_snapshot_path_cnt)++; fd_vote_timestamp_ele_t * curr = fork; - while( curr->snapshot_idx!=UCHAR_MAX ) { + while( curr->snapshot_idx==UCHAR_MAX ) { + FD_LOG_NOTICE(("PATH IDX")); curr = fd_vote_timestamp_pool_ele( fork_pool, curr->parent_idx ); parent_snapshot_path[*parent_snapshot_path_cnt] = (ushort)fd_vote_timestamp_pool_idx( fork_pool, curr ); (*parent_snapshot_path_cnt)++; } + FD_LOG_NOTICE(("PARENT SNAPSHOT PATH CNT: %hu", *parent_snapshot_path_cnt)); + uchar best_snapshot_idx = curr->snapshot_idx; uchar root_snapshot_idx = root->snapshot_idx; + FD_LOG_NOTICE(("BEST SNAPSHOT IDX: %u", best_snapshot_idx)); + FD_LOG_NOTICE(("ROOT SNAPSHOT IDX: %u", root_snapshot_idx)); + snapshot_key_dlist_t * snapshot_keys_dlist = fd_vote_timestamps_get_snapshot_keys_dlist( vote_ts ); snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); @@ -487,10 +518,12 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, key = snapshot_key_dlist_ele_pop_head( snapshot_keys_dlist, snapshot_keys_pool ); idx = (uchar)snapshot_key_pool_idx( snapshot_keys_pool, key ); } + FD_LOG_NOTICE(("EVICTED KEY IDX: %u", idx)); snapshot_key_pool_ele_release( snapshot_keys_pool, key ); } snapshot_key_ele_t * new_key = snapshot_key_pool_ele_acquire( snapshot_keys_pool ); + FD_LOG_NOTICE(("NEW KEY IDX: %u", (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ))); snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, new_key, snapshot_keys_pool ); return (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ); } @@ -500,6 +533,8 @@ apply_delta( ulong base_slot, snapshot_ele_t * snapshot, snapshot_ele_map_t * snapshot_map, fd_vote_timestamp_ele_t * fork ) { + + FD_LOG_NOTICE(("APPLYING DELTAS %u", fork->deltas_cnt)); for( ushort i=0; ideltas_cnt; i++ ) { /* We have the property that timestamps are always increasing for the same pubkey. When a pubkey is evicted from the index, then @@ -542,7 +577,7 @@ apply_snapshot( snapshot_ele_t * snapshot, } } -void +ulong fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ushort fork_idx ) { fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); @@ -554,6 +589,7 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); + FD_LOG_NOTICE(("SNAPSHOT CNT: %u", (uint)snapshot_ele_map_verify( snapshot_map, 0, snapshot ))); /* We now have the path of all of the vote timestamp entries that we have to apply. We also have the snapshot index that we can use to @@ -562,6 +598,7 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, fd_vote_timestamp_ele_t * curr_fork = NULL; for( ushort i=0; islot, snapshot, snapshot_map, curr_fork ); } FD_TEST( curr_fork ); @@ -570,6 +607,55 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, snapshot_ele_t * prev_snapshot = fd_vote_timestamps_get_snapshot( vote_ts, curr_fork->snapshot_idx ); snapshot_ele_map_t * prev_snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, curr_fork->snapshot_idx ); apply_snapshot( snapshot, snapshot_map, fork->slot, prev_snapshot, prev_snapshot_map, curr_fork->slot ); + + + fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + + /* Iterate through the snapshot to get the stake for each pubkey. */ + + ulong ts_ele_cnt = 0UL; + uint128 total_stake = 0UL; + for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( snapshot_map, snapshot ); + !snapshot_ele_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_ele_map_iter_next( iter, snapshot_map, snapshot ) ) { + uint ele_idx = (uint)snapshot_ele_map_iter_idx( iter, snapshot_map, snapshot ); + snapshot_ele_t * snapshot_ele = snapshot_ele_map_iter_ele( iter, snapshot_map, snapshot ); + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele( index_pool, ele_idx ); + + ulong stake = ele->epoch_stakes[ fork->epoch % 2UL ]; + ulong timestamp = snapshot_ele->timestamp; + ulong slot_delta = snapshot_ele->slot_age; + FD_LOG_NOTICE(("ITER %u %lu", ele_idx, slot_delta)); + + + /* TODO:FIXME: get the right slot duration on boot */ + ulong offset = fd_ulong_sat_mul( 400e9, slot_delta ); + ulong estimate = timestamp + (offset / ((ulong)1e9)); + + vote_ts->ts_eles[ ts_ele_cnt ] = (ts_est_ele_t){ + .timestamp = estimate, + .stake = { .ud=stake }, + }; + ts_ele_cnt++; + + total_stake += stake; + } + + sort_stake_ts_inplace( vote_ts->ts_eles, ts_ele_cnt ); + + /* Populate estimate with the stake-weighted median timestamp. + https://github.com/anza-xyz/agave/blob/v2.3.7/runtime/src/stake_weighted_timestamp.rs#L59-L68 */ + uint128 stake_accumulator = 0; + ulong estimate = 0UL; + for( ulong i=0UL; its_eles[i].stake.ud ); + if( stake_accumulator>(total_stake/2UL) ) { + estimate = vote_ts->ts_eles[ i ].timestamp; + break; + } + } + return estimate; + } ushort diff --git a/src/flamenco/stakes/fd_vote_timestamps.h b/src/flamenco/stakes/fd_vote_timestamps.h index 3bfc055bbac..51a6f640e35 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.h +++ b/src/flamenco/stakes/fd_vote_timestamps.h @@ -45,12 +45,14 @@ fd_vote_timestamps_join( void * shmem ); ushort fd_vote_timestamps_init( fd_vote_timestamps_t * vote_ts, - ulong slot ); + ulong slot, + ushort epoch ); ushort fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, ushort parent_fork_idx, - ulong slot ); + ulong slot, + ushort epoch ); void fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, @@ -65,11 +67,12 @@ fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, void fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, fd_pubkey_t pubkey, - ulong timestamp ); + ulong timestamp, + ulong stake ); -void +ulong fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, - ushort fork_idx ); + ushort fork_idx ); ushort fd_vote_timestamps_slot_votes_cnt( fd_vote_timestamps_t * vote_ts, diff --git a/src/flamenco/stakes/test_vote_timestamps.c b/src/flamenco/stakes/test_vote_timestamps.c index 362f7ff8b81..dd4d2f17a53 100644 --- a/src/flamenco/stakes/test_vote_timestamps.c +++ b/src/flamenco/stakes/test_vote_timestamps.c @@ -38,20 +38,20 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_t * vote_timestamps = fd_vote_timestamps_join( fd_vote_timestamps_new( mem, 16UL, 4, 128UL, 0UL ) ); FD_TEST( vote_timestamps ); - ushort fork_idx = fd_vote_timestamps_init( vote_timestamps, 0UL ); + ushort fork_idx = fd_vote_timestamps_init( vote_timestamps, 0UL, 0 ); FD_TEST( fork_idx==0 ); - fd_vote_timestamps_insert_root( vote_timestamps, pubkey_A, 10 ); - fd_vote_timestamps_insert_root( vote_timestamps, pubkey_B, 11 ); - fd_vote_timestamps_insert_root( vote_timestamps, pubkey_C, 10 ); - fd_vote_timestamps_insert_root( vote_timestamps, pubkey_D, 10 ); - fd_vote_timestamps_insert_root( vote_timestamps, pubkey_E, 10 ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_A, 10, 100UL ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_B, 10, 200UL ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_C, 10, 300UL ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_D, 10, 400UL ); + fd_vote_timestamps_insert_root( vote_timestamps, pubkey_E, 10, 500UL ); FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); FD_TEST( 0==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, fork_idx ) ); - ushort child_idx = fd_vote_timestamps_attach_child( vote_timestamps, fork_idx, 1UL ); + ushort child_idx = fd_vote_timestamps_attach_child( vote_timestamps, fork_idx, 1UL, 0 ); (void)child_idx; fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_A, 11 ); @@ -61,6 +61,9 @@ int main( int argc, char ** argv ) { FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); FD_TEST( 4==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx ) ); + ulong timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx ); + FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + FD_LOG_NOTICE(( "pass" )); } From 9e488394610d4d0765ad8f96eab857017f4cf66a Mon Sep 17 00:00:00 2001 From: Ishan Bhatt Date: Thu, 12 Feb 2026 22:35:18 +0000 Subject: [PATCH 4/6] wip --- src/flamenco/stakes/fd_vote_timestamps.c | 240 +++--------------- src/flamenco/stakes/fd_vote_timestamps.h | 3 +- .../stakes/fd_vote_timestamps_private.h | 166 ++++++++++++ src/flamenco/stakes/test_vote_timestamps.c | 44 +++- 4 files changed, 239 insertions(+), 214 deletions(-) create mode 100644 src/flamenco/stakes/fd_vote_timestamps_private.h diff --git a/src/flamenco/stakes/fd_vote_timestamps.c b/src/flamenco/stakes/fd_vote_timestamps.c index 253a15fb269..eee0193c549 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.c +++ b/src/flamenco/stakes/fd_vote_timestamps.c @@ -1,168 +1,5 @@ #include "fd_vote_timestamps.h" - -struct fd_vote_timestamp_index_ele { - fd_pubkey_t pubkey; - ulong epoch_stakes[ 2UL ]; - ushort refcnt; - uint next; -}; -typedef struct fd_vote_timestamp_index_ele fd_vote_timestamp_index_ele_t; - -#define POOL_NAME fd_vote_timestamp_index_pool -#define POOL_T fd_vote_timestamp_index_ele_t -#define POOL_NEXT next -#define POOL_IDX_T uint -#include "../../util/tmpl/fd_pool.c" - -#define MAP_NAME fd_vote_timestamp_index_map -#define MAP_KEY_T fd_pubkey_t -#define MAP_ELE_T fd_vote_timestamp_index_ele_t -#define MAP_KEY pubkey -#define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 )) -#define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) )) -#define MAP_NEXT next -#define MAP_IDX_T uint -#include "../../util/tmpl/fd_map_chain.c" - -/**********************************************************************/ - -struct snapshot_ele { - ulong slot_age : 19; - ulong timestamp : 45; - uint idx; - uint next; -}; -typedef struct snapshot_ele snapshot_ele_t; - -#define MAP_NAME snapshot_ele_map -#define MAP_KEY_T uint -#define MAP_ELE_T snapshot_ele_t -#define MAP_KEY idx -#define MAP_NEXT next -#define MAP_IDX_T uint -#include "../../util/tmpl/fd_map_chain.c" - -struct snapshot_key { - uchar prev; - uchar next; - ulong offset; - ulong map_offset; -}; -typedef struct snapshot_key snapshot_key_ele_t; - -#define DLIST_NAME snapshot_key_dlist -#define DLIST_ELE_T snapshot_key_ele_t -#define DLIST_IDX_T uchar -#include "../../util/tmpl/fd_dlist.c" - -#define POOL_NAME snapshot_key_pool -#define POOL_T snapshot_key_ele_t -#define POOL_IDX_T uchar -#include "../../util/tmpl/fd_pool.c" - -/*********************************************************************/ - -/* ts_est_ele_t is a temporary struct used for sorting vote accounts by - last vote timestamp for clock sysvar calculation. */ - struct ts_est_ele { - ulong timestamp; - fd_w_u128_t stake; /* should really be fine as ulong, but we match Agave */ - }; - typedef struct ts_est_ele ts_est_ele_t; - -#define SORT_NAME sort_stake_ts -#define SORT_KEY_T ts_est_ele_t -#define SORT_BEFORE(a,b) ( (a).timestamp < (b).timestamp ) -#include "../../util/tmpl/fd_sort.c" - -/* ***************************/ - -struct fd_vote_timestamp_delta_ele { - ulong timestamp; - uint pubkey_idx; -}; -typedef struct fd_vote_timestamp_delta_ele fd_vote_timestamp_delta_ele_t; - -struct fd_vote_timestamps { - ulong fork_pool_offset; - - ulong index_pool_offset; - ulong index_map_offset; - - ushort root_idx; - - ulong snapshot_max; - ulong snapshot_cnt; - ulong snapshot_keys_dlist_offset; - ulong snapshot_keys_pool_offset; - - ts_est_ele_t ts_eles[ 40200 ]; /* TODO:FIXME: this has to be configurable */ -}; -typedef struct fd_vote_timestamps fd_vote_timestamps_t; - -struct fd_vote_timestamp_ele { - ulong slot; - ushort epoch; - /* left child, right sibling tree pointers */ - ushort parent_idx; - ushort child_idx; - ushort sibling_idx; - ushort next; - - uchar snapshot_idx; - - ushort deltas_cnt; - /* TODO: Const for this or make it paramterizable */ - fd_vote_timestamp_delta_ele_t deltas[ 42000UL ]; -}; -typedef struct fd_vote_timestamp_ele fd_vote_timestamp_ele_t; - -#define POOL_NAME fd_vote_timestamp_pool -#define POOL_T fd_vote_timestamp_ele_t -#define POOL_IDX_T ushort -#include "../../util/tmpl/fd_pool.c" - -static inline fd_vote_timestamp_ele_t * -fd_vote_timestamps_get_fork_pool( fd_vote_timestamps_t * vote_ts ) { - return fd_type_pun( (uchar *)vote_ts + vote_ts->fork_pool_offset ); -} - -static inline fd_vote_timestamp_index_ele_t * -fd_vote_timestamps_get_index_pool( fd_vote_timestamps_t * vote_ts ) { - return fd_type_pun( (uchar *)vote_ts + vote_ts->index_pool_offset ); -} - -static inline fd_vote_timestamp_index_map_t * -fd_vote_timestamps_get_index_map( fd_vote_timestamps_t * vote_ts ) { - return fd_type_pun( (uchar *)vote_ts + vote_ts->index_map_offset ); -} - -static inline snapshot_key_dlist_t * -fd_vote_timestamps_get_snapshot_keys_dlist( fd_vote_timestamps_t * vote_ts ) { - return fd_type_pun( (uchar *)vote_ts + vote_ts->snapshot_keys_dlist_offset ); -} - -static inline snapshot_key_ele_t * -fd_vote_timestamps_get_snapshot_keys_pool( fd_vote_timestamps_t * vote_ts ) { - return fd_type_pun( (uchar *)vote_ts + vote_ts->snapshot_keys_pool_offset ); -} - -static inline snapshot_ele_t * -fd_vote_timestamps_get_snapshot( fd_vote_timestamps_t * vote_ts, - uchar snapshot_idx ) { - /* TODO:FIXME: this is pretty hacky. */ - snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); - snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); - return fd_type_pun( (uchar *)vote_ts + key->offset ); -} - -static inline snapshot_ele_map_t * -fd_vote_timestamps_get_snapshot_ele_map( fd_vote_timestamps_t * vote_ts, - uchar snapshot_idx ) { - snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); - snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); - return fd_type_pun( (uchar *)vote_ts + key->map_offset ); -} +#include "fd_vote_timestamps_private.h" ulong fd_vote_timestamps_align( void ) { @@ -306,6 +143,7 @@ fd_vote_timestamps_init( fd_vote_timestamps_t * vote_ts, snapshot_key_ele_t * new_key = snapshot_key_pool_ele_acquire( snapshot_keys_pool ); ulong sidx = snapshot_key_pool_idx( snapshot_keys_pool, new_key ); fork->snapshot_idx = (uchar)sidx; + FD_LOG_WARNING(("ROOT SNAPSHOT KEY IDX: %u", (uchar)sidx)); snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, new_key, snapshot_keys_pool ); @@ -330,29 +168,22 @@ fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele_acquire( pool ); ushort child_idx = (ushort)fd_vote_timestamp_pool_idx( pool, child ); - /* If there is no root yet, this is the root*/ - if( FD_UNLIKELY( parent_fork_idx==USHORT_MAX ) ) { - FD_CRIT( vote_ts->root_idx==USHORT_MAX, "Root already exists" ); - child->parent_idx = USHORT_MAX; - vote_ts->root_idx = child_idx; - } else { - fd_vote_timestamp_ele_t * parent = fd_vote_timestamp_pool_ele( pool, parent_fork_idx ); - FD_CRIT( parent, "parent fork idx not found" ); - fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele_acquire( pool ); - ushort child_idx = (ushort)fd_vote_timestamp_pool_idx( pool, child ); - child->parent_idx = parent_fork_idx; + fd_vote_timestamp_ele_t * parent = fd_vote_timestamp_pool_ele( pool, parent_fork_idx ); + FD_CRIT( parent, "parent fork idx not found" ); - if( FD_LIKELY( parent->child_idx==USHORT_MAX ) ) { - parent->child_idx = child_idx; - } else { - fd_vote_timestamp_ele_t * curr = fd_vote_timestamp_pool_ele( pool, parent->child_idx ); - /* Assign child as the sibling pointer of rightmost child. */ - while( curr->sibling_idx!=USHORT_MAX ) { - curr = fd_vote_timestamp_pool_ele( pool, curr->sibling_idx ); - } - curr->sibling_idx = child_idx; + child->parent_idx = parent_fork_idx; + FD_LOG_NOTICE(("CHILD IDX %u PARENT IDX %u", child_idx, child->parent_idx)); + + if( FD_LIKELY( parent->child_idx==USHORT_MAX ) ) { + parent->child_idx = child_idx; + } else { + fd_vote_timestamp_ele_t * curr = fd_vote_timestamp_pool_ele( pool, parent->child_idx ); + /* Assign child as the sibling pointer of rightmost child. */ + while( curr->sibling_idx!=USHORT_MAX ) { + curr = fd_vote_timestamp_pool_ele( pool, curr->sibling_idx ); } + curr->sibling_idx = child_idx; } child->sibling_idx = USHORT_MAX; @@ -403,28 +234,31 @@ void fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, ushort fork_idx, fd_pubkey_t pubkey, - ulong timestamp ) { + ulong timestamp, + ulong stake ) { /* First update and query index. Figure out pubkey index if not one exists, otherwise allocate a new entry in the index. */ fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); fd_vote_timestamp_index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_map_ele_query( index_map, &pubkey, NULL, index_pool ); if( FD_LIKELY( ele ) ) { ele->refcnt++; } else { + FD_LOG_NOTICE(("INSERTING NEW ELE")); ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); ele->pubkey = pubkey; ele->refcnt = 1UL; + ele->epoch_stakes[ fork->epoch % 2UL ] = stake; /* TODO:FIXME: this probably isn't right. */ FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); } uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); - /* Now just add the entry to the delta list. */ fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[ fork->deltas_cnt ]; delta->timestamp = timestamp; @@ -473,10 +307,8 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, 1. Never evict the root snapshot 2. Don't evict the "best" snapshot (closest to the fork idx) */ fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - - FD_LOG_NOTICE(("FORK IDX: %hu", fork_idx)); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); - fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); /* Find best snapshot to build off of. Always prioritize the least amount of deltas. This is purely a policy decision. */ @@ -485,19 +317,15 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, fd_vote_timestamp_ele_t * curr = fork; while( curr->snapshot_idx==UCHAR_MAX ) { - FD_LOG_NOTICE(("PATH IDX")); curr = fd_vote_timestamp_pool_ele( fork_pool, curr->parent_idx ); parent_snapshot_path[*parent_snapshot_path_cnt] = (ushort)fd_vote_timestamp_pool_idx( fork_pool, curr ); (*parent_snapshot_path_cnt)++; } - FD_LOG_NOTICE(("PARENT SNAPSHOT PATH CNT: %hu", *parent_snapshot_path_cnt)); - uchar best_snapshot_idx = curr->snapshot_idx; uchar root_snapshot_idx = root->snapshot_idx; - FD_LOG_NOTICE(("BEST SNAPSHOT IDX: %u", best_snapshot_idx)); - FD_LOG_NOTICE(("ROOT SNAPSHOT IDX: %u", root_snapshot_idx)); + FD_LOG_NOTICE(("PATH CNT %u BEST %u, ROOT %u", *parent_snapshot_path_cnt, best_snapshot_idx, root_snapshot_idx)); snapshot_key_dlist_t * snapshot_keys_dlist = fd_vote_timestamps_get_snapshot_keys_dlist( vote_ts ); snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); @@ -523,7 +351,7 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, } snapshot_key_ele_t * new_key = snapshot_key_pool_ele_acquire( snapshot_keys_pool ); - FD_LOG_NOTICE(("NEW KEY IDX: %u", (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ))); + FD_LOG_NOTICE(("SNAPSHOT KEY IDX: %u", (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ))); snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, new_key, snapshot_keys_pool ); return (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ); } @@ -589,19 +417,16 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); - FD_LOG_NOTICE(("SNAPSHOT CNT: %u", (uint)snapshot_ele_map_verify( snapshot_map, 0, snapshot ))); + snapshot_ele_map_reset( snapshot_map ); /* We now have the path of all of the vote timestamp entries that we have to apply. We also have the snapshot index that we can use to get the timestamp. We want to iterate backwards through the fork indices and apply the deltas. */ - fd_vote_timestamp_ele_t * curr_fork = NULL; - for( ushort i=0; islot, snapshot, snapshot_map, curr_fork ); + for( ushort i=0; islot, snapshot, snapshot_map, fd_vote_timestamp_pool_ele( fork_pool, path[i] ) ); } - FD_TEST( curr_fork ); + fd_vote_timestamp_ele_t * curr_fork = fd_vote_timestamp_pool_ele( fork_pool, path[path_cnt-1] ); /* Finally, we need to apply the delta from the previous snapshot */ snapshot_ele_t * prev_snapshot = fd_vote_timestamps_get_snapshot( vote_ts, curr_fork->snapshot_idx ); @@ -625,12 +450,11 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ulong stake = ele->epoch_stakes[ fork->epoch % 2UL ]; ulong timestamp = snapshot_ele->timestamp; ulong slot_delta = snapshot_ele->slot_age; - FD_LOG_NOTICE(("ITER %u %lu", ele_idx, slot_delta)); - /* TODO:FIXME: get the right slot duration on boot */ ulong offset = fd_ulong_sat_mul( 400e9, slot_delta ); ulong estimate = timestamp + (offset / ((ulong)1e9)); + FD_LOG_NOTICE(("IDX %u DISTANCE %lu TIMESTAMP %lu ESTIMATE %lu STAKE %lu", ele_idx, slot_delta, timestamp, estimate, stake)); vote_ts->ts_eles[ ts_ele_cnt ] = (ts_est_ele_t){ .timestamp = estimate, @@ -656,6 +480,8 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, } return estimate; + /* TODO: Let the runtime handle the timestamp adjusting. */ + } ushort diff --git a/src/flamenco/stakes/fd_vote_timestamps.h b/src/flamenco/stakes/fd_vote_timestamps.h index 51a6f640e35..3031e940079 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.h +++ b/src/flamenco/stakes/fd_vote_timestamps.h @@ -62,7 +62,8 @@ void fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, ushort fork_idx, fd_pubkey_t pubkey, - ulong timestamp ); + ulong timestamp, + ulong stake ); void fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, diff --git a/src/flamenco/stakes/fd_vote_timestamps_private.h b/src/flamenco/stakes/fd_vote_timestamps_private.h new file mode 100644 index 00000000000..a16f061624f --- /dev/null +++ b/src/flamenco/stakes/fd_vote_timestamps_private.h @@ -0,0 +1,166 @@ +#include "../../util/fd_util_base.h" +#include "../types/fd_types_custom.h" + +struct fd_vote_timestamp_index_ele { + fd_pubkey_t pubkey; + ulong epoch_stakes[ 2UL ]; + ushort refcnt; + uint next; +}; +typedef struct fd_vote_timestamp_index_ele fd_vote_timestamp_index_ele_t; + +#define POOL_NAME fd_vote_timestamp_index_pool +#define POOL_T fd_vote_timestamp_index_ele_t +#define POOL_NEXT next +#define POOL_IDX_T uint +#include "../../util/tmpl/fd_pool.c" + +#define MAP_NAME fd_vote_timestamp_index_map +#define MAP_KEY_T fd_pubkey_t +#define MAP_ELE_T fd_vote_timestamp_index_ele_t +#define MAP_KEY pubkey +#define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 )) +#define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) )) +#define MAP_NEXT next +#define MAP_IDX_T uint +#include "../../util/tmpl/fd_map_chain.c" + +/**********************************************************************/ + +struct snapshot_ele { + ulong slot_age : 19; + ulong timestamp : 45; + uint idx; + uint next; +}; +typedef struct snapshot_ele snapshot_ele_t; + +#define MAP_NAME snapshot_ele_map +#define MAP_KEY_T uint +#define MAP_ELE_T snapshot_ele_t +#define MAP_KEY idx +#define MAP_NEXT next +#define MAP_IDX_T uint +#include "../../util/tmpl/fd_map_chain.c" + +struct snapshot_key { + uchar prev; + uchar next; + ulong offset; + ulong map_offset; +}; +typedef struct snapshot_key snapshot_key_ele_t; + +#define DLIST_NAME snapshot_key_dlist +#define DLIST_ELE_T snapshot_key_ele_t +#define DLIST_IDX_T uchar +#include "../../util/tmpl/fd_dlist.c" + +#define POOL_NAME snapshot_key_pool +#define POOL_T snapshot_key_ele_t +#define POOL_IDX_T uchar +#include "../../util/tmpl/fd_pool.c" + +/*********************************************************************/ + +/* ts_est_ele_t is a temporary struct used for sorting vote accounts by + last vote timestamp for clock sysvar calculation. */ + struct ts_est_ele { + ulong timestamp; + fd_w_u128_t stake; /* should really be fine as ulong, but we match Agave */ + }; + typedef struct ts_est_ele ts_est_ele_t; + +#define SORT_NAME sort_stake_ts +#define SORT_KEY_T ts_est_ele_t +#define SORT_BEFORE(a,b) ( (a).timestamp < (b).timestamp ) +#include "../../util/tmpl/fd_sort.c" + +/* ***************************/ + +struct fd_vote_timestamp_delta_ele { + ulong timestamp; + uint pubkey_idx; +}; +typedef struct fd_vote_timestamp_delta_ele fd_vote_timestamp_delta_ele_t; + +struct fd_vote_timestamps { + ulong fork_pool_offset; + + ulong index_pool_offset; + ulong index_map_offset; + + ushort root_idx; + + ulong snapshot_max; + ulong snapshot_cnt; + ulong snapshot_keys_dlist_offset; + ulong snapshot_keys_pool_offset; + + ts_est_ele_t ts_eles[ 40200 ]; /* TODO:FIXME: this has to be configurable */ +}; +typedef struct fd_vote_timestamps fd_vote_timestamps_t; + +struct fd_vote_timestamp_ele { + ulong slot; + ushort epoch; + /* left child, right sibling tree pointers */ + ushort parent_idx; + ushort child_idx; + ushort sibling_idx; + ushort next; + + uchar snapshot_idx; + + ushort deltas_cnt; + /* TODO: Const for this or make it paramterizable */ + fd_vote_timestamp_delta_ele_t deltas[ 42000UL ]; +}; +typedef struct fd_vote_timestamp_ele fd_vote_timestamp_ele_t; + +#define POOL_NAME fd_vote_timestamp_pool +#define POOL_T fd_vote_timestamp_ele_t +#define POOL_IDX_T ushort +#include "../../util/tmpl/fd_pool.c" + +static inline fd_vote_timestamp_ele_t * +fd_vote_timestamps_get_fork_pool( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->fork_pool_offset ); +} + +static inline fd_vote_timestamp_index_ele_t * +fd_vote_timestamps_get_index_pool( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->index_pool_offset ); +} + +static inline fd_vote_timestamp_index_map_t * +fd_vote_timestamps_get_index_map( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->index_map_offset ); +} + +static inline snapshot_key_dlist_t * +fd_vote_timestamps_get_snapshot_keys_dlist( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->snapshot_keys_dlist_offset ); +} + +static inline snapshot_key_ele_t * +fd_vote_timestamps_get_snapshot_keys_pool( fd_vote_timestamps_t * vote_ts ) { + return fd_type_pun( (uchar *)vote_ts + vote_ts->snapshot_keys_pool_offset ); +} + +static inline snapshot_ele_t * +fd_vote_timestamps_get_snapshot( fd_vote_timestamps_t * vote_ts, + uchar snapshot_idx ) { + /* TODO:FIXME: this is pretty hacky. */ + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); + return fd_type_pun( (uchar *)vote_ts + key->offset ); +} + +static inline snapshot_ele_map_t * +fd_vote_timestamps_get_snapshot_ele_map( fd_vote_timestamps_t * vote_ts, + uchar snapshot_idx ) { + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); + return fd_type_pun( (uchar *)vote_ts + key->map_offset ); +} diff --git a/src/flamenco/stakes/test_vote_timestamps.c b/src/flamenco/stakes/test_vote_timestamps.c index dd4d2f17a53..485c4550505 100644 --- a/src/flamenco/stakes/test_vote_timestamps.c +++ b/src/flamenco/stakes/test_vote_timestamps.c @@ -1,4 +1,5 @@ #include "fd_vote_timestamps.h" +#include "fd_vote_timestamps_private.h" int main( int argc, char ** argv ) { fd_boot( &argc, &argv ); @@ -38,6 +39,8 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_t * vote_timestamps = fd_vote_timestamps_join( fd_vote_timestamps_new( mem, 16UL, 4, 128UL, 0UL ) ); FD_TEST( vote_timestamps ); + fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_timestamps ); + ushort fork_idx = fd_vote_timestamps_init( vote_timestamps, 0UL, 0 ); FD_TEST( fork_idx==0 ); @@ -49,21 +52,50 @@ int main( int argc, char ** argv ) { FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); FD_TEST( 0==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, fork_idx ) ); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, vote_timestamps->root_idx ); + FD_TEST( fork->snapshot_idx!=UCHAR_MAX ); + ushort child_idx = fd_vote_timestamps_attach_child( vote_timestamps, fork_idx, 1UL, 0 ); - (void)child_idx; - fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_A, 11 ); - fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_B, 11 ); - fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_C, 11 ); - fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_D, 11 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_A, 11, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_B, 11, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_C, 11, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_D, 11, 0UL ); FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); FD_TEST( 4==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx ) ); ulong timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx ); FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); - FD_LOG_NOTICE(( "pass" )); + ushort child_idx2 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 2UL, 0 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_F, 11, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_A, 15, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_B, 15, 0UL ); + FD_TEST( 6UL==fd_vote_timestamps_index_cnt( vote_timestamps ) ); + FD_TEST( 3==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx2 ) ); + timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx2 ); + FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + ushort child_idx3 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 3UL, 0 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_F, 11, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_A, 15, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_B, 15, 0UL ); + FD_TEST( 6UL==fd_vote_timestamps_index_cnt( vote_timestamps ) ); + FD_TEST( 3==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx3 ) ); + timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx3 ); + FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + + /* Make sure the eviction policy is working. */ + ushort child_idx4 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx2, 4UL, 0 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_C, 16, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_A, 15, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_B, 15, 0UL ); + FD_TEST( 6UL==fd_vote_timestamps_index_cnt( vote_timestamps ) ); + FD_TEST( 3==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx4 ) ); + timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx4 ); + FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + + FD_LOG_NOTICE(( "pass" )); } From 0ca4692a7d022375d7b2863f1fae734293bb95cb Mon Sep 17 00:00:00 2001 From: Ishan Bhatt Date: Fri, 13 Feb 2026 16:03:53 +0000 Subject: [PATCH 5/6] rooting works without index eviction --- src/flamenco/stakes/fd_vote_timestamps.c | 165 +++++++++++++----- src/flamenco/stakes/fd_vote_timestamps.h | 7 - .../stakes/fd_vote_timestamps_private.h | 9 +- src/flamenco/stakes/test_vote_timestamps.c | 112 +++++++++--- 4 files changed, 218 insertions(+), 75 deletions(-) diff --git a/src/flamenco/stakes/fd_vote_timestamps.c b/src/flamenco/stakes/fd_vote_timestamps.c index eee0193c549..75400ea1fb7 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.c +++ b/src/flamenco/stakes/fd_vote_timestamps.c @@ -12,7 +12,6 @@ fd_vote_timestamps_footprint( ulong max_live_slots, ulong max_vote_accs ) { ulong map_chain_cnt = 2048UL; - ulong l = FD_LAYOUT_INIT; l = FD_LAYOUT_APPEND( l, fd_vote_timestamps_align(), sizeof(fd_vote_timestamps_t) ); l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_pool_align(), fd_vote_timestamp_pool_footprint( max_live_slots ) ); @@ -82,7 +81,6 @@ fd_vote_timestamps_new( void * shmem, return NULL; } - snapshot_key_dlist_t * snapshot_keys = snapshot_key_dlist_join( snapshot_key_dlist_new( snapshot_keys_dlist_mem ) ); if( FD_UNLIKELY( !snapshot_keys ) ) { FD_LOG_WARNING(( "Failed to create vote timestamp snapshot keys" )); @@ -196,6 +194,46 @@ fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, return child_idx; } +static void +apply_root_delta( fd_vote_timestamps_t * vote_ts, + fd_vote_timestamp_ele_t * new_root, + fd_vote_timestamp_ele_t * old_root ) { + + fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + + snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, old_root->snapshot_idx ); + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, old_root->snapshot_idx ); + + /* NOTE: After this point, the snapshot has incorrect slot age, but + I think this is okay because slot age gets overriden anyway? + TODO:FIXME: confirm that this is true. */ + for( ushort i=0; ideltas_cnt; i++ ) { + /* We have the property that timestamps are always increasing for + the same pubkey. When a pubkey is evicted from the index, then + we will clear all entries for that pubkey in all snapshots in the + case it gets renewed. */ + fd_vote_timestamp_delta_ele_t * delta = &new_root->deltas[i]; + + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele( index_pool, delta->pubkey_idx ); + ele->refcnt--; + + snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); + if( FD_LIKELY( snapshot_ele ) ) { + snapshot_ele->idx = delta->pubkey_idx; + snapshot_ele->timestamp = delta->timestamp; + snapshot_ele->slot_age = new_root->slot - old_root->slot; + } else { + snapshot_ele = &snapshot[delta->pubkey_idx]; + snapshot_ele->idx = delta->pubkey_idx; + snapshot_ele->timestamp = delta->timestamp; + snapshot_ele->slot_age = new_root->slot - old_root->slot; + snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + } + } + /* We no longer need the deltas here so we can clear them. */ + new_root->deltas_cnt = 0; +} + void fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, ushort new_root_idx ) { @@ -203,6 +241,30 @@ fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, fd_vote_timestamp_ele_t * new_root = fd_vote_timestamp_pool_ele( pool, new_root_idx ); fd_vote_timestamp_ele_t * head = fd_vote_timestamp_pool_ele( pool, vote_ts->root_idx ); + FD_CRIT( new_root->parent_idx==vote_ts->root_idx, "new root is not a child of the current root" ); + + /* The new root node can either have a snapshot or not. If the new + root does, we can go ahead and free the snapshot the root has. + We can also update the root pointer. */ + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); + snapshot_key_ele_t * old_root_key = snapshot_key_pool_ele( snapshot_keys_pool, head->snapshot_idx ); + if( FD_UNLIKELY( new_root->snapshot_idx!=UCHAR_MAX ) ) { + FD_LOG_WARNING(("ADVANCING ROOT TO NODE WITH SNAPSHOT")); + old_root_key->fork_idx = USHORT_MAX; + head->snapshot_idx = UCHAR_MAX; + snapshot_key_pool_ele_release( snapshot_keys_pool, old_root_key ); + } else { + /* This means that we need to apply the delta from the new root onto + the old root's snapshot. Then transfer ownership of the snapshot + to the new_root. */ + FD_LOG_WARNING(("ADVANCING ROOT TO NODE WITHOUT SNAPSHOT")); + apply_root_delta( vote_ts, new_root, head ); + + old_root_key->fork_idx = new_root_idx; + new_root->snapshot_idx = head->snapshot_idx; + head->snapshot_idx = UCHAR_MAX; + } + head->next = USHORT_MAX; fd_vote_timestamp_ele_t * tail = head; while( head ) { @@ -222,12 +284,36 @@ fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, } fd_vote_timestamp_ele_t * next = fd_vote_timestamp_pool_ele( pool, head->next ); + + if( FD_UNLIKELY( head->snapshot_idx!=UCHAR_MAX ) ) { + snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, head->snapshot_idx ); + key->fork_idx = USHORT_MAX; + snapshot_key_pool_ele_release( snapshot_keys_pool, key ); + head->snapshot_idx = UCHAR_MAX; + snapshot_key_dlist_t * dlist = fd_vote_timestamps_get_snapshot_keys_dlist( vote_ts ); + snapshot_key_dlist_idx_remove( dlist, head->snapshot_idx, snapshot_keys_pool ); + } + + fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + for( ushort i=0; ideltas_cnt; i++ ) { + fd_vote_timestamp_delta_ele_t * delta = &head->deltas[i]; + fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele( index_pool, delta->pubkey_idx ); + ele->refcnt--; + } + head->deltas_cnt = 0; + + FD_LOG_WARNING(("ELEMENT RELEASE")); + fd_vote_timestamp_pool_ele_release( pool, head ); head = next; } new_root->parent_idx = USHORT_MAX; vote_ts->root_idx = new_root_idx; + + /* Now clear all evictable entries from the index. + TODO:FIXME: implement another map ontop of the index pool to track + entries that are candidates for eviction. */ } void @@ -252,8 +338,7 @@ fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); ele->pubkey = pubkey; ele->refcnt = 1UL; - ele->epoch_stakes[ fork->epoch % 2UL ] = stake; /* TODO:FIXME: this probably isn't right. */ - + ele->epoch_stakes[ fork->epoch % 2UL ] = stake; FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); } @@ -282,7 +367,7 @@ fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); ele->pubkey = pubkey; - ele->refcnt = 1UL; + ele->refcnt = 0UL; ele->epoch_stakes[ fork->epoch % 2UL ] = stake; FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); @@ -347,15 +432,42 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, idx = (uchar)snapshot_key_pool_idx( snapshot_keys_pool, key ); } FD_LOG_NOTICE(("EVICTED KEY IDX: %u", idx)); + fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, key->fork_idx ); + fork->snapshot_idx = UCHAR_MAX; + key->fork_idx = USHORT_MAX; snapshot_key_pool_ele_release( snapshot_keys_pool, key ); } snapshot_key_ele_t * new_key = snapshot_key_pool_ele_acquire( snapshot_keys_pool ); + new_key->fork_idx = fork_idx; FD_LOG_NOTICE(("SNAPSHOT KEY IDX: %u", (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ))); snapshot_key_dlist_ele_push_tail( snapshot_keys_dlist, new_key, snapshot_keys_pool ); return (uchar)snapshot_key_pool_idx( snapshot_keys_pool, new_key ); } +static void +apply_snapshot( snapshot_ele_t * snapshot, + snapshot_ele_map_t * snapshot_map, + ulong base_slot, + snapshot_ele_t * prev_snapshot, + snapshot_ele_map_t * prev_snapshot_map, + ulong prev_slot ) { + + for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( prev_snapshot_map, prev_snapshot ); + !snapshot_ele_map_iter_done( iter, prev_snapshot_map, prev_snapshot ); + iter = snapshot_ele_map_iter_next( iter, prev_snapshot_map, prev_snapshot ) ) { + uint ele_idx = (uint)snapshot_ele_map_iter_idx( iter, prev_snapshot_map, prev_snapshot ); + + snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &ele_idx, NULL, snapshot ); + if( FD_LIKELY( snapshot_ele ) ) continue; + snapshot_ele = &snapshot[ele_idx]; + snapshot_ele->idx = (uint)ele_idx; + snapshot_ele->timestamp = prev_snapshot[ele_idx].timestamp; + snapshot_ele->slot_age = base_slot - prev_slot; + snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + } +} + static void apply_delta( ulong base_slot, snapshot_ele_t * snapshot, @@ -370,7 +482,7 @@ apply_delta( ulong base_slot, case it gets renewed. */ fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[i]; snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); - if( FD_LIKELY( snapshot_ele ) ) { + if( FD_LIKELY( !snapshot_ele ) ) { /* If it is already found do nothing */ } else { snapshot_ele = &snapshot[delta->pubkey_idx]; @@ -382,29 +494,6 @@ apply_delta( ulong base_slot, } } -static void -apply_snapshot( snapshot_ele_t * snapshot, - snapshot_ele_map_t * snapshot_map, - ulong base_slot, - snapshot_ele_t * prev_snapshot, - snapshot_ele_map_t * prev_snapshot_map, - ulong prev_slot ) { - - for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( prev_snapshot_map, prev_snapshot ); - !snapshot_ele_map_iter_done( iter, prev_snapshot_map, prev_snapshot ); - iter = snapshot_ele_map_iter_next( iter, prev_snapshot_map, prev_snapshot ) ) { - uint ele_idx = (uint)snapshot_ele_map_iter_idx( iter, prev_snapshot_map, prev_snapshot ); - - snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &ele_idx, NULL, snapshot ); - if( FD_LIKELY( snapshot_ele ) ) continue; - snapshot_ele = &snapshot[ele_idx]; - snapshot_ele->idx = (uint)ele_idx; - snapshot_ele->timestamp = prev_snapshot[ele_idx].timestamp; - snapshot_ele->slot_age = base_slot - prev_slot; - snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); - } -} - ulong fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ushort fork_idx ) { @@ -451,6 +540,10 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ulong timestamp = snapshot_ele->timestamp; ulong slot_delta = snapshot_ele->slot_age; + if( FD_UNLIKELY( slot_delta>432000UL ) ) { + /* TODO:FIXME: Schedule entry for eviction. */ + } + /* TODO:FIXME: get the right slot duration on boot */ ulong offset = fd_ulong_sat_mul( 400e9, slot_delta ); ulong estimate = timestamp + (offset / ((ulong)1e9)); @@ -483,17 +576,3 @@ fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, /* TODO: Let the runtime handle the timestamp adjusting. */ } - -ushort -fd_vote_timestamps_slot_votes_cnt( fd_vote_timestamps_t * vote_ts, - ushort fork_idx ) { - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); - return fork->deltas_cnt; -} - -uint -fd_vote_timestamps_index_cnt( fd_vote_timestamps_t * vote_ts ) { - fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); - return (uint)fd_vote_timestamp_index_pool_used( index_pool ); -} diff --git a/src/flamenco/stakes/fd_vote_timestamps.h b/src/flamenco/stakes/fd_vote_timestamps.h index 3031e940079..fb4adc65a4b 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.h +++ b/src/flamenco/stakes/fd_vote_timestamps.h @@ -75,13 +75,6 @@ ulong fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ushort fork_idx ); -ushort -fd_vote_timestamps_slot_votes_cnt( fd_vote_timestamps_t * vote_ts, - ushort fork_idx ); - -uint -fd_vote_timestamps_index_cnt( fd_vote_timestamps_t * vote_ts ); - FD_PROTOTYPES_END #endif /* HEADER_fd_src_flamenco_stakes_fd_vote_timestamps_h */ diff --git a/src/flamenco/stakes/fd_vote_timestamps_private.h b/src/flamenco/stakes/fd_vote_timestamps_private.h index a16f061624f..eb9856b5624 100644 --- a/src/flamenco/stakes/fd_vote_timestamps_private.h +++ b/src/flamenco/stakes/fd_vote_timestamps_private.h @@ -44,10 +44,11 @@ typedef struct snapshot_ele snapshot_ele_t; #include "../../util/tmpl/fd_map_chain.c" struct snapshot_key { - uchar prev; - uchar next; - ulong offset; - ulong map_offset; + ushort fork_idx; + uchar prev; + uchar next; + ulong offset; + ulong map_offset; }; typedef struct snapshot_key snapshot_key_ele_t; diff --git a/src/flamenco/stakes/test_vote_timestamps.c b/src/flamenco/stakes/test_vote_timestamps.c index 485c4550505..df7a97d25f2 100644 --- a/src/flamenco/stakes/test_vote_timestamps.c +++ b/src/flamenco/stakes/test_vote_timestamps.c @@ -41,8 +41,8 @@ int main( int argc, char ** argv ) { fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_timestamps ); - ushort fork_idx = fd_vote_timestamps_init( vote_timestamps, 0UL, 0 ); - FD_TEST( fork_idx==0 ); + ushort root_idx = fd_vote_timestamps_init( vote_timestamps, 0UL, 0 ); + FD_TEST( root_idx==0 ); fd_vote_timestamps_insert_root( vote_timestamps, pubkey_A, 10, 100UL ); fd_vote_timestamps_insert_root( vote_timestamps, pubkey_B, 10, 200UL ); @@ -50,22 +50,27 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_insert_root( vote_timestamps, pubkey_D, 10, 400UL ); fd_vote_timestamps_insert_root( vote_timestamps, pubkey_E, 10, 500UL ); - FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); - FD_TEST( 0==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, fork_idx ) ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, vote_timestamps->root_idx ); - FD_TEST( fork->snapshot_idx!=UCHAR_MAX ); - - - - ushort child_idx = fd_vote_timestamps_attach_child( vote_timestamps, fork_idx, 1UL, 0 ); + FD_TEST( 5U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_timestamps->root_idx ); + FD_TEST( root->deltas_cnt==0 ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + FD_TEST( root->parent_idx==USHORT_MAX ); + FD_TEST( root->child_idx==USHORT_MAX ); + FD_TEST( root->sibling_idx==USHORT_MAX ); + ushort child_idx = fd_vote_timestamps_attach_child( vote_timestamps, root_idx, 1UL, 0 ); fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_A, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_B, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_C, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_D, 11, 0UL ); - FD_TEST( 5U==fd_vote_timestamps_index_cnt( vote_timestamps ) ); - FD_TEST( 4==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx ) ); - + FD_TEST( 5U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + fd_vote_timestamp_ele_t * child_fork = fd_vote_timestamp_pool_ele( fork_pool, child_idx ); + FD_TEST( child_fork->deltas_cnt==4 ); + FD_TEST( root->child_idx==child_idx ); + FD_TEST( child_fork->parent_idx==root_idx ); + FD_TEST( child_fork->child_idx==USHORT_MAX ); + FD_TEST( child_fork->sibling_idx==USHORT_MAX ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); ulong timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx ); FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); @@ -73,29 +78,94 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_F, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_A, 15, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_B, 15, 0UL ); - FD_TEST( 6UL==fd_vote_timestamps_index_cnt( vote_timestamps ) ); - FD_TEST( 3==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx2 ) ); + FD_TEST( 6U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx2 ); FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + fd_vote_timestamp_ele_t * child_fork2 = fd_vote_timestamp_pool_ele( fork_pool, child_idx2 ); + FD_TEST( child_fork->child_idx==child_idx2 ); + FD_TEST( child_fork2->parent_idx==child_idx ); + FD_TEST( child_fork2->child_idx==USHORT_MAX ); + FD_TEST( child_fork2->sibling_idx==USHORT_MAX ); + FD_TEST( child_fork2->deltas_cnt==3 ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); ushort child_idx3 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 3UL, 0 ); fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_F, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_A, 15, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_B, 15, 0UL ); - FD_TEST( 6UL==fd_vote_timestamps_index_cnt( vote_timestamps ) ); - FD_TEST( 3==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx3 ) ); + FD_TEST( 6U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx3 ); FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); - - /* Make sure the eviction policy is working. */ + fd_vote_timestamp_ele_t * child_fork3 = fd_vote_timestamp_pool_ele( fork_pool, child_idx3 ); + FD_TEST( child_fork->child_idx==child_idx2 ); + FD_TEST( child_fork2->sibling_idx==child_idx3 ); + FD_TEST( child_fork3->parent_idx==child_idx ); + FD_TEST( child_fork3->child_idx==USHORT_MAX ); + FD_TEST( child_fork3->sibling_idx==USHORT_MAX ); + FD_TEST( child_fork3->deltas_cnt==3 ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + + /* Make sure the eviction policy is working. At this point we expect + LRU eviction to kick in excluding the root and the best option. + In this case the best option is child_idx_2, so we expect child_idx + to have its snapshot evicted. */ ushort child_idx4 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx2, 4UL, 0 ); fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_C, 16, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_A, 15, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_B, 15, 0UL ); - FD_TEST( 6UL==fd_vote_timestamps_index_cnt( vote_timestamps ) ); - FD_TEST( 3==fd_vote_timestamps_slot_votes_cnt( vote_timestamps, child_idx4 ) ); timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx4 ); FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + fd_vote_timestamp_ele_t * child_fork4 = fd_vote_timestamp_pool_ele( fork_pool, child_idx4 ); + FD_TEST( child_fork4->parent_idx==child_idx2 ); + FD_TEST( child_fork4->snapshot_idx!=UCHAR_MAX ); + FD_TEST( child_fork->snapshot_idx==UCHAR_MAX ); + FD_TEST( child_fork4->deltas_cnt==3 ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + + /* Now try to make a child off of child_idx and see if the skipped + delta gets applied correctly. We also should expect to see + child_idx2's snapshot to be evicted. Make sure that the root's + snapshot idx does not get evicted. */ + ushort child_idx5 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 5UL, 0 ); + fd_vote_timestamps_insert( vote_timestamps, child_idx5, pubkey_A, 20, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx5, pubkey_B, 20, 0UL ); + fd_vote_timestamps_insert( vote_timestamps, child_idx5, pubkey_C, 20, 0UL ); + timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx5 ); + FD_TEST( child_fork2->snapshot_idx==UCHAR_MAX ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + + ushort child_idx6 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx2, 6UL, 0 ); + timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx6 ); + FD_TEST( child_fork3->snapshot_idx==UCHAR_MAX ); + FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + + /* Advance the root to a node that does not have a snapshot: in this + case move to child_idx. This node also has no siblings so only + the old root will get pruned. */ + fd_vote_timestamps_advance_root( vote_timestamps, child_idx ); + /* TODO: Asserts here. Make sure that the values in the snapshot are + what we expect them to be. Also validate the fork structure at + this point. */ + fd_vote_timestamp_ele_t * new_root = fd_vote_timestamp_pool_ele( fork_pool, vote_timestamps->root_idx ); + FD_TEST( new_root->deltas_cnt==0 ); + FD_TEST( new_root->snapshot_idx!=UCHAR_MAX ); + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, new_root->snapshot_idx ); + snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_timestamps, new_root->snapshot_idx ); + ulong ts_10_cnt = 0UL; + ulong ts_11_cnt = 0UL; + for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( snapshot_map, snapshot ); + !snapshot_ele_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_ele_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_ele_map_iter_ele( iter, snapshot_map, snapshot ); + FD_TEST( ele->timestamp==10 || ele->timestamp==11 ); + if( ele->timestamp==10 ) ts_10_cnt++; + else ts_11_cnt++; + } + FD_TEST( ts_10_cnt==1 ); + FD_TEST( ts_11_cnt==4 ); + + FD_LOG_NOTICE(( "pass" )); } From 2c8f51f822f044055d098f52bd21724800394a7f Mon Sep 17 00:00:00 2001 From: Ishan Bhatt Date: Fri, 13 Feb 2026 22:35:53 +0000 Subject: [PATCH 6/6] wip --- src/flamenco/stakes/fd_vote_timestamps.c | 242 ++++++++++-------- .../stakes/fd_vote_timestamps_private.h | 40 +-- src/flamenco/stakes/test_vote_timestamps.c | 175 +++++++++++-- 3 files changed, 303 insertions(+), 154 deletions(-) diff --git a/src/flamenco/stakes/fd_vote_timestamps.c b/src/flamenco/stakes/fd_vote_timestamps.c index 75400ea1fb7..be55d79d0a4 100644 --- a/src/flamenco/stakes/fd_vote_timestamps.c +++ b/src/flamenco/stakes/fd_vote_timestamps.c @@ -13,14 +13,14 @@ fd_vote_timestamps_footprint( ulong max_live_slots, ulong map_chain_cnt = 2048UL; ulong l = FD_LAYOUT_INIT; - l = FD_LAYOUT_APPEND( l, fd_vote_timestamps_align(), sizeof(fd_vote_timestamps_t) ); - l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_pool_align(), fd_vote_timestamp_pool_footprint( max_live_slots ) ); - l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_index_pool_align(), fd_vote_timestamp_index_pool_footprint( max_vote_accs ) ); - l = FD_LAYOUT_APPEND( l, fd_vote_timestamp_index_map_align(), fd_vote_timestamp_index_map_footprint( map_chain_cnt ) ); - l = FD_LAYOUT_APPEND( l, snapshot_key_dlist_align(), snapshot_key_dlist_footprint() ); - l = FD_LAYOUT_APPEND( l, snapshot_key_pool_align(), snapshot_key_pool_footprint( max_snaps ) ); + l = FD_LAYOUT_APPEND( l, fd_vote_timestamps_align(), sizeof(fd_vote_timestamps_t) ); + l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( max_live_slots ) ); + l = FD_LAYOUT_APPEND( l, index_pool_align(), index_pool_footprint( max_vote_accs ) ); + l = FD_LAYOUT_APPEND( l, index_map_align(), index_map_footprint( map_chain_cnt ) ); + l = FD_LAYOUT_APPEND( l, snapshot_key_dlist_align(), snapshot_key_dlist_footprint() ); + l = FD_LAYOUT_APPEND( l, snapshot_key_pool_align(), snapshot_key_pool_footprint( max_snaps ) ); for( uchar i=0; ioffset = (ulong)snapshots_mem - (ulong)vote_timestamps; - snapshot_ele_map_t * snapshot_ele_map = snapshot_ele_map_join( snapshot_ele_map_new( snapshots_ele_map_mem, map_chain_cnt, seed ) ); - if( FD_UNLIKELY( !snapshot_ele_map ) ) { + snapshot_map_t * snapshot_map = snapshot_map_join( snapshot_map_new( snapshots_ele_map_mem, map_chain_cnt, seed ) ); + if( FD_UNLIKELY( !snapshot_map ) ) { FD_LOG_WARNING(( "Failed to create vote timestamp snapshot ele map" )); return NULL; } - key->map_offset = (ulong)snapshot_ele_map - (ulong)vote_timestamps; + key->map_offset = (ulong)snapshot_map - (ulong)vote_timestamps; } for( uchar i=0; iroot_idx = fork_idx; fork->parent_idx = USHORT_MAX; @@ -147,8 +147,8 @@ fd_vote_timestamps_init( fd_vote_timestamps_t * vote_ts, /* Now that the node is on the tracking dlist and is allocated we need to initialize the map for the snapshot. */ - snapshot_ele_map_t * snapshot_ele_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); - snapshot_ele_map_reset( snapshot_ele_map ); + snapshot_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, fork->snapshot_idx ); + snapshot_map_reset( snapshot_map ); return fork_idx; } @@ -159,15 +159,15 @@ fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, ulong slot, ushort epoch ) { - fd_vote_timestamp_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fork_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - FD_CRIT( fd_vote_timestamp_pool_free( pool )!=0UL, "No free slots in vote timestamp pool" ); + FD_CRIT( fork_pool_free( pool )!=0UL, "No free slots in vote timestamp pool" ); - fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele_acquire( pool ); - ushort child_idx = (ushort)fd_vote_timestamp_pool_idx( pool, child ); + fork_ele_t * child = fork_pool_ele_acquire( pool ); + ushort child_idx = (ushort)fork_pool_idx( pool, child ); - fd_vote_timestamp_ele_t * parent = fd_vote_timestamp_pool_ele( pool, parent_fork_idx ); + fork_ele_t * parent = fork_pool_ele( pool, parent_fork_idx ); FD_CRIT( parent, "parent fork idx not found" ); child->parent_idx = parent_fork_idx; @@ -176,10 +176,10 @@ fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, if( FD_LIKELY( parent->child_idx==USHORT_MAX ) ) { parent->child_idx = child_idx; } else { - fd_vote_timestamp_ele_t * curr = fd_vote_timestamp_pool_ele( pool, parent->child_idx ); + fork_ele_t * curr = fork_pool_ele( pool, parent->child_idx ); /* Assign child as the sibling pointer of rightmost child. */ while( curr->sibling_idx!=USHORT_MAX ) { - curr = fd_vote_timestamp_pool_ele( pool, curr->sibling_idx ); + curr = fork_pool_ele( pool, curr->sibling_idx ); } curr->sibling_idx = child_idx; } @@ -196,12 +196,12 @@ fd_vote_timestamps_attach_child( fd_vote_timestamps_t * vote_ts, static void apply_root_delta( fd_vote_timestamps_t * vote_ts, - fd_vote_timestamp_ele_t * new_root, - fd_vote_timestamp_ele_t * old_root ) { + fork_ele_t * new_root, + fork_ele_t * old_root ) { - fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); - snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, old_root->snapshot_idx ); + snapshot_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, old_root->snapshot_idx ); snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, old_root->snapshot_idx ); /* NOTE: After this point, the snapshot has incorrect slot age, but @@ -212,12 +212,12 @@ apply_root_delta( fd_vote_timestamps_t * vote_ts, the same pubkey. When a pubkey is evicted from the index, then we will clear all entries for that pubkey in all snapshots in the case it gets renewed. */ - fd_vote_timestamp_delta_ele_t * delta = &new_root->deltas[i]; + delta_ele_t * delta = &new_root->deltas[i]; - fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele( index_pool, delta->pubkey_idx ); + index_ele_t * ele = index_pool_ele( index_pool, delta->pubkey_idx ); ele->refcnt--; - snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); + snapshot_ele_t * snapshot_ele = snapshot_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); if( FD_LIKELY( snapshot_ele ) ) { snapshot_ele->idx = delta->pubkey_idx; snapshot_ele->timestamp = delta->timestamp; @@ -227,7 +227,7 @@ apply_root_delta( fd_vote_timestamps_t * vote_ts, snapshot_ele->idx = delta->pubkey_idx; snapshot_ele->timestamp = delta->timestamp; snapshot_ele->slot_age = new_root->slot - old_root->slot; - snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + snapshot_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); } } /* We no longer need the deltas here so we can clear them. */ @@ -237,9 +237,9 @@ apply_root_delta( fd_vote_timestamps_t * vote_ts, void fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, ushort new_root_idx ) { - fd_vote_timestamp_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * new_root = fd_vote_timestamp_pool_ele( pool, new_root_idx ); - fd_vote_timestamp_ele_t * head = fd_vote_timestamp_pool_ele( pool, vote_ts->root_idx ); + fork_ele_t * pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fork_ele_t * new_root = fork_pool_ele( pool, new_root_idx ); + fork_ele_t * head = fork_pool_ele( pool, vote_ts->root_idx ); FD_CRIT( new_root->parent_idx==vote_ts->root_idx, "new root is not a child of the current root" ); @@ -249,9 +249,13 @@ fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); snapshot_key_ele_t * old_root_key = snapshot_key_pool_ele( snapshot_keys_pool, head->snapshot_idx ); if( FD_UNLIKELY( new_root->snapshot_idx!=UCHAR_MAX ) ) { - FD_LOG_WARNING(("ADVANCING ROOT TO NODE WITH SNAPSHOT")); old_root_key->fork_idx = USHORT_MAX; head->snapshot_idx = UCHAR_MAX; + + new_root->deltas_cnt = 0; + + snapshot_key_dlist_t * dlist = fd_vote_timestamps_get_snapshot_keys_dlist( vote_ts ); + snapshot_key_dlist_idx_remove( dlist, head->snapshot_idx, snapshot_keys_pool ); snapshot_key_pool_ele_release( snapshot_keys_pool, old_root_key ); } else { /* This means that we need to apply the delta from the new root onto @@ -265,25 +269,28 @@ fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, head->snapshot_idx = UCHAR_MAX; } + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, new_root->snapshot_idx ); + snapshot_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, new_root->snapshot_idx ); + head->next = USHORT_MAX; - fd_vote_timestamp_ele_t * tail = head; + fork_ele_t * tail = head; while( head ) { - fd_vote_timestamp_ele_t * child = fd_vote_timestamp_pool_ele( pool, head->child_idx ); + fork_ele_t * child = fork_pool_ele( pool, head->child_idx ); while( FD_LIKELY( child ) ) { if( FD_LIKELY( child!=new_root ) ) { /* Update tail pointers */ - tail->next = (ushort)fd_vote_timestamp_pool_idx( pool, child ); - tail = fd_vote_timestamp_pool_ele( pool, tail->next ); + tail->next = (ushort)fork_pool_idx( pool, child ); + tail = fork_pool_ele( pool, tail->next ); tail->next = USHORT_MAX; } - child = fd_vote_timestamp_pool_ele( pool, child->sibling_idx ); + child = fork_pool_ele( pool, child->sibling_idx ); } - fd_vote_timestamp_ele_t * next = fd_vote_timestamp_pool_ele( pool, head->next ); + fork_ele_t * next = fork_pool_ele( pool, head->next ); if( FD_UNLIKELY( head->snapshot_idx!=UCHAR_MAX ) ) { snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, head->snapshot_idx ); @@ -294,23 +301,41 @@ fd_vote_timestamps_advance_root( fd_vote_timestamps_t * vote_ts, snapshot_key_dlist_idx_remove( dlist, head->snapshot_idx, snapshot_keys_pool ); } - fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); for( ushort i=0; ideltas_cnt; i++ ) { - fd_vote_timestamp_delta_ele_t * delta = &head->deltas[i]; - fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele( index_pool, delta->pubkey_idx ); + delta_ele_t * delta = &head->deltas[i]; + index_ele_t * ele = index_pool_ele( index_pool, delta->pubkey_idx ); ele->refcnt--; + /* Release the index entry if it's not in the root's snapshot and + no longer has any references. */ + if( FD_UNLIKELY( ele->refcnt==0 && snapshot_map_idx_query( snapshot_map, &delta->pubkey_idx, UINT_MAX, snapshot )==UINT_MAX ) ) { + index_pool_ele_release( index_pool, ele ); + } } head->deltas_cnt = 0; FD_LOG_WARNING(("ELEMENT RELEASE")); - fd_vote_timestamp_pool_ele_release( pool, head ); + fork_pool_ele_release( pool, head ); head = next; } new_root->parent_idx = USHORT_MAX; vote_ts->root_idx = new_root_idx; + FD_LOG_WARNING(("VOTE TS %u", vote_ts->root_idx)); + + snapshot = fd_vote_timestamps_get_snapshot( vote_ts, new_root->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, new_root->snapshot_idx ); + + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + FD_LOG_NOTICE(( "timestamp: %lu", ele->timestamp )); + } + + /* Now clear all evictable entries from the index. TODO:FIXME: implement another map ontop of the index pool to track entries that are candidates for eviction. */ @@ -324,31 +349,32 @@ fd_vote_timestamps_insert( fd_vote_timestamps_t * vote_ts, ulong stake ) { /* First update and query index. Figure out pubkey index if not one exists, otherwise allocate a new entry in the index. */ - fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); - fd_vote_timestamp_index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); + index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + fork_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fork_ele_t * fork = fork_pool_ele( fork_pool, fork_idx ); - fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_map_ele_query( index_map, &pubkey, NULL, index_pool ); + index_ele_t * ele = index_map_ele_query( index_map, &pubkey, NULL, index_pool ); if( FD_LIKELY( ele ) ) { ele->refcnt++; } else { FD_LOG_NOTICE(("INSERTING NEW ELE")); - ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); + ele = index_pool_ele_acquire( index_pool ); ele->pubkey = pubkey; ele->refcnt = 1UL; ele->epoch_stakes[ fork->epoch % 2UL ] = stake; - FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); + FD_TEST( index_map_ele_insert( index_map, ele, index_pool ) ); } - uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); + uint pubkey_idx = (uint)index_pool_idx( index_pool, ele ); /* Now just add the entry to the delta list. */ - fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[ fork->deltas_cnt ]; + delta_ele_t * delta = &fork->deltas[ fork->deltas_cnt ]; delta->timestamp = timestamp; delta->pubkey_idx = pubkey_idx; fork->deltas_cnt++; + } void @@ -359,28 +385,28 @@ fd_vote_timestamps_insert_root( fd_vote_timestamps_t * vote_ts, /* First update and query index. Figure out pubkey index if not one exists, otherwise allocate a new entry in the index. */ - fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); - fd_vote_timestamp_index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); + index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + index_map_t * index_map = fd_vote_timestamps_get_index_map( vote_ts ); - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); + fork_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fork_ele_t * fork = fork_pool_ele( fork_pool, vote_ts->root_idx ); - fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele_acquire( index_pool ); + index_ele_t * ele = index_pool_ele_acquire( index_pool ); ele->pubkey = pubkey; ele->refcnt = 0UL; ele->epoch_stakes[ fork->epoch % 2UL ] = stake; - FD_TEST( fd_vote_timestamp_index_map_ele_insert( index_map, ele, index_pool ) ); - uint pubkey_idx = (uint)fd_vote_timestamp_index_pool_idx( index_pool, ele ); + FD_TEST( index_map_ele_insert( index_map, ele, index_pool ) ); + uint pubkey_idx = (uint)index_pool_idx( index_pool, ele ); snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); - snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); + snapshot_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, fork->snapshot_idx ); snapshot_ele_t * snapshot_ele = &snapshot[pubkey_idx]; snapshot_ele->idx = pubkey_idx; snapshot_ele->timestamp = timestamp; snapshot_ele->slot_age = 0; - snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + snapshot_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); } static uchar @@ -391,19 +417,19 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, /* A reasonable eviction policy here is LRU eviction with some tweaks: 1. Never evict the root snapshot 2. Don't evict the "best" snapshot (closest to the fork idx) */ - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); - fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_ts->root_idx ); + fork_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fork_ele_t * fork = fork_pool_ele( fork_pool, fork_idx ); + fork_ele_t * root = fork_pool_ele( fork_pool, vote_ts->root_idx ); /* Find best snapshot to build off of. Always prioritize the least amount of deltas. This is purely a policy decision. */ - parent_snapshot_path[ *parent_snapshot_path_cnt ] = (ushort)fd_vote_timestamp_pool_idx( fork_pool, fork ); + parent_snapshot_path[ *parent_snapshot_path_cnt ] = (ushort)fork_pool_idx( fork_pool, fork ); (*parent_snapshot_path_cnt)++; - fd_vote_timestamp_ele_t * curr = fork; + fork_ele_t * curr = fork; while( curr->snapshot_idx==UCHAR_MAX ) { - curr = fd_vote_timestamp_pool_ele( fork_pool, curr->parent_idx ); - parent_snapshot_path[*parent_snapshot_path_cnt] = (ushort)fd_vote_timestamp_pool_idx( fork_pool, curr ); + curr = fork_pool_ele( fork_pool, curr->parent_idx ); + parent_snapshot_path[*parent_snapshot_path_cnt] = (ushort)fork_pool_idx( fork_pool, curr ); (*parent_snapshot_path_cnt)++; } @@ -432,7 +458,7 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, idx = (uchar)snapshot_key_pool_idx( snapshot_keys_pool, key ); } FD_LOG_NOTICE(("EVICTED KEY IDX: %u", idx)); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, key->fork_idx ); + fork_ele_t * fork = fork_pool_ele( fork_pool, key->fork_idx ); fork->snapshot_idx = UCHAR_MAX; key->fork_idx = USHORT_MAX; snapshot_key_pool_ele_release( snapshot_keys_pool, key ); @@ -447,32 +473,32 @@ prune_and_get_snapshot( fd_vote_timestamps_t * vote_ts, static void apply_snapshot( snapshot_ele_t * snapshot, - snapshot_ele_map_t * snapshot_map, + snapshot_map_t * snapshot_map, ulong base_slot, snapshot_ele_t * prev_snapshot, - snapshot_ele_map_t * prev_snapshot_map, + snapshot_map_t * prev_snapshot_map, ulong prev_slot ) { - for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( prev_snapshot_map, prev_snapshot ); - !snapshot_ele_map_iter_done( iter, prev_snapshot_map, prev_snapshot ); - iter = snapshot_ele_map_iter_next( iter, prev_snapshot_map, prev_snapshot ) ) { - uint ele_idx = (uint)snapshot_ele_map_iter_idx( iter, prev_snapshot_map, prev_snapshot ); + for( snapshot_map_iter_t iter = snapshot_map_iter_init( prev_snapshot_map, prev_snapshot ); + !snapshot_map_iter_done( iter, prev_snapshot_map, prev_snapshot ); + iter = snapshot_map_iter_next( iter, prev_snapshot_map, prev_snapshot ) ) { + uint ele_idx = (uint)snapshot_map_iter_idx( iter, prev_snapshot_map, prev_snapshot ); - snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &ele_idx, NULL, snapshot ); + snapshot_ele_t * snapshot_ele = snapshot_map_ele_query( snapshot_map, &ele_idx, NULL, snapshot ); if( FD_LIKELY( snapshot_ele ) ) continue; snapshot_ele = &snapshot[ele_idx]; snapshot_ele->idx = (uint)ele_idx; snapshot_ele->timestamp = prev_snapshot[ele_idx].timestamp; snapshot_ele->slot_age = base_slot - prev_slot; - snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + snapshot_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); } } static void apply_delta( ulong base_slot, snapshot_ele_t * snapshot, - snapshot_ele_map_t * snapshot_map, - fd_vote_timestamp_ele_t * fork ) { + snapshot_map_t * snapshot_map, + fork_ele_t * fork ) { FD_LOG_NOTICE(("APPLYING DELTAS %u", fork->deltas_cnt)); for( ushort i=0; ideltas_cnt; i++ ) { @@ -480,16 +506,17 @@ apply_delta( ulong base_slot, the same pubkey. When a pubkey is evicted from the index, then we will clear all entries for that pubkey in all snapshots in the case it gets renewed. */ - fd_vote_timestamp_delta_ele_t * delta = &fork->deltas[i]; - snapshot_ele_t * snapshot_ele = snapshot_ele_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); - if( FD_LIKELY( !snapshot_ele ) ) { + delta_ele_t * delta = &fork->deltas[i]; + snapshot_ele_t * snapshot_ele = snapshot_map_ele_query( snapshot_map, &delta->pubkey_idx, NULL, snapshot ); + if( FD_UNLIKELY( snapshot_ele ) ) { /* If it is already found do nothing */ } else { snapshot_ele = &snapshot[delta->pubkey_idx]; snapshot_ele->idx = delta->pubkey_idx; snapshot_ele->timestamp = delta->timestamp; snapshot_ele->slot_age = base_slot - fork->slot; - snapshot_ele_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); + FD_LOG_WARNING(("APPLYING DELTAS %u %lu", delta->pubkey_idx, snapshot_ele->timestamp)); + snapshot_map_ele_insert( snapshot_map, snapshot_ele, snapshot ); } } } @@ -497,44 +524,45 @@ apply_delta( ulong base_slot, ulong fd_vote_timestamps_get_timestamp( fd_vote_timestamps_t * vote_ts, ushort fork_idx ) { - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); - fd_vote_timestamp_ele_t * fork = fd_vote_timestamp_pool_ele( fork_pool, fork_idx ); + fork_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_ts ); + fork_ele_t * fork = fork_pool_ele( fork_pool, fork_idx ); ushort path[ USHORT_MAX ]; ushort path_cnt = 0; fork->snapshot_idx = prune_and_get_snapshot( vote_ts, fork_idx, path, &path_cnt ); - - snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); - snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, fork->snapshot_idx ); - snapshot_ele_map_reset( snapshot_map ); + FD_LOG_NOTICE(("snapshot idx: %u", fork->snapshot_idx)); + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_ts, fork->snapshot_idx ); + snapshot_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, fork->snapshot_idx ); + snapshot_map_reset( snapshot_map ); /* We now have the path of all of the vote timestamp entries that we have to apply. We also have the snapshot index that we can use to get the timestamp. We want to iterate backwards through the fork indices and apply the deltas. */ for( ushort i=0; islot, snapshot, snapshot_map, fd_vote_timestamp_pool_ele( fork_pool, path[i] ) ); + FD_LOG_WARNING(("APPLYING DELTAS FOR INDEX %u", path[i])); + apply_delta( fork->slot, snapshot, snapshot_map, fork_pool_ele( fork_pool, path[i] ) ); } - fd_vote_timestamp_ele_t * curr_fork = fd_vote_timestamp_pool_ele( fork_pool, path[path_cnt-1] ); + fork_ele_t * curr_fork = fork_pool_ele( fork_pool, path[path_cnt-1] ); /* Finally, we need to apply the delta from the previous snapshot */ snapshot_ele_t * prev_snapshot = fd_vote_timestamps_get_snapshot( vote_ts, curr_fork->snapshot_idx ); - snapshot_ele_map_t * prev_snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_ts, curr_fork->snapshot_idx ); + snapshot_map_t * prev_snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_ts, curr_fork->snapshot_idx ); apply_snapshot( snapshot, snapshot_map, fork->slot, prev_snapshot, prev_snapshot_map, curr_fork->slot ); - fd_vote_timestamp_index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); + index_ele_t * index_pool = fd_vote_timestamps_get_index_pool( vote_ts ); /* Iterate through the snapshot to get the stake for each pubkey. */ ulong ts_ele_cnt = 0UL; uint128 total_stake = 0UL; - for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( snapshot_map, snapshot ); - !snapshot_ele_map_iter_done( iter, snapshot_map, snapshot ); - iter = snapshot_ele_map_iter_next( iter, snapshot_map, snapshot ) ) { - uint ele_idx = (uint)snapshot_ele_map_iter_idx( iter, snapshot_map, snapshot ); - snapshot_ele_t * snapshot_ele = snapshot_ele_map_iter_ele( iter, snapshot_map, snapshot ); - fd_vote_timestamp_index_ele_t * ele = fd_vote_timestamp_index_pool_ele( index_pool, ele_idx ); + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + uint ele_idx = (uint)snapshot_map_iter_idx( iter, snapshot_map, snapshot ); + snapshot_ele_t * snapshot_ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + index_ele_t * ele = index_pool_ele( index_pool, ele_idx ); ulong stake = ele->epoch_stakes[ fork->epoch % 2UL ]; ulong timestamp = snapshot_ele->timestamp; diff --git a/src/flamenco/stakes/fd_vote_timestamps_private.h b/src/flamenco/stakes/fd_vote_timestamps_private.h index eb9856b5624..989edafc79c 100644 --- a/src/flamenco/stakes/fd_vote_timestamps_private.h +++ b/src/flamenco/stakes/fd_vote_timestamps_private.h @@ -1,23 +1,23 @@ #include "../../util/fd_util_base.h" #include "../types/fd_types_custom.h" -struct fd_vote_timestamp_index_ele { +struct index_ele { fd_pubkey_t pubkey; ulong epoch_stakes[ 2UL ]; ushort refcnt; uint next; }; -typedef struct fd_vote_timestamp_index_ele fd_vote_timestamp_index_ele_t; +typedef struct index_ele index_ele_t; -#define POOL_NAME fd_vote_timestamp_index_pool -#define POOL_T fd_vote_timestamp_index_ele_t +#define POOL_NAME index_pool +#define POOL_T index_ele_t #define POOL_NEXT next #define POOL_IDX_T uint #include "../../util/tmpl/fd_pool.c" -#define MAP_NAME fd_vote_timestamp_index_map +#define MAP_NAME index_map #define MAP_KEY_T fd_pubkey_t -#define MAP_ELE_T fd_vote_timestamp_index_ele_t +#define MAP_ELE_T index_ele_t #define MAP_KEY pubkey #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 )) #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) )) @@ -35,7 +35,7 @@ struct snapshot_ele { }; typedef struct snapshot_ele snapshot_ele_t; -#define MAP_NAME snapshot_ele_map +#define MAP_NAME snapshot_map #define MAP_KEY_T uint #define MAP_ELE_T snapshot_ele_t #define MAP_KEY idx @@ -64,6 +64,8 @@ typedef struct snapshot_key snapshot_key_ele_t; /*********************************************************************/ +/* TODO:FIXME: this can be improved almost defintely */ + /* ts_est_ele_t is a temporary struct used for sorting vote accounts by last vote timestamp for clock sysvar calculation. */ struct ts_est_ele { @@ -79,11 +81,11 @@ typedef struct snapshot_key snapshot_key_ele_t; /* ***************************/ -struct fd_vote_timestamp_delta_ele { +struct delta_ele { ulong timestamp; uint pubkey_idx; }; -typedef struct fd_vote_timestamp_delta_ele fd_vote_timestamp_delta_ele_t; +typedef struct delta_ele delta_ele_t; struct fd_vote_timestamps { ulong fork_pool_offset; @@ -102,7 +104,7 @@ struct fd_vote_timestamps { }; typedef struct fd_vote_timestamps fd_vote_timestamps_t; -struct fd_vote_timestamp_ele { +struct fork_ele { ulong slot; ushort epoch; /* left child, right sibling tree pointers */ @@ -115,26 +117,26 @@ struct fd_vote_timestamp_ele { ushort deltas_cnt; /* TODO: Const for this or make it paramterizable */ - fd_vote_timestamp_delta_ele_t deltas[ 42000UL ]; + delta_ele_t deltas[ 42000UL ]; }; -typedef struct fd_vote_timestamp_ele fd_vote_timestamp_ele_t; +typedef struct fork_ele fork_ele_t; -#define POOL_NAME fd_vote_timestamp_pool -#define POOL_T fd_vote_timestamp_ele_t +#define POOL_NAME fork_pool +#define POOL_T fork_ele_t #define POOL_IDX_T ushort #include "../../util/tmpl/fd_pool.c" -static inline fd_vote_timestamp_ele_t * +static inline fork_ele_t * fd_vote_timestamps_get_fork_pool( fd_vote_timestamps_t * vote_ts ) { return fd_type_pun( (uchar *)vote_ts + vote_ts->fork_pool_offset ); } -static inline fd_vote_timestamp_index_ele_t * +static inline index_ele_t * fd_vote_timestamps_get_index_pool( fd_vote_timestamps_t * vote_ts ) { return fd_type_pun( (uchar *)vote_ts + vote_ts->index_pool_offset ); } -static inline fd_vote_timestamp_index_map_t * +static inline index_map_t * fd_vote_timestamps_get_index_map( fd_vote_timestamps_t * vote_ts ) { return fd_type_pun( (uchar *)vote_ts + vote_ts->index_map_offset ); } @@ -158,8 +160,8 @@ fd_vote_timestamps_get_snapshot( fd_vote_timestamps_t * vote_ts, return fd_type_pun( (uchar *)vote_ts + key->offset ); } -static inline snapshot_ele_map_t * -fd_vote_timestamps_get_snapshot_ele_map( fd_vote_timestamps_t * vote_ts, +static inline snapshot_map_t * +fd_vote_timestamps_get_snapshot_map( fd_vote_timestamps_t * vote_ts, uchar snapshot_idx ) { snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_ts ); snapshot_key_ele_t * key = snapshot_key_pool_ele( snapshot_keys_pool, snapshot_idx ); diff --git a/src/flamenco/stakes/test_vote_timestamps.c b/src/flamenco/stakes/test_vote_timestamps.c index df7a97d25f2..73f36f233e1 100644 --- a/src/flamenco/stakes/test_vote_timestamps.c +++ b/src/flamenco/stakes/test_vote_timestamps.c @@ -39,7 +39,7 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_t * vote_timestamps = fd_vote_timestamps_join( fd_vote_timestamps_new( mem, 16UL, 4, 128UL, 0UL ) ); FD_TEST( vote_timestamps ); - fd_vote_timestamp_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_timestamps ); + fork_ele_t * fork_pool = fd_vote_timestamps_get_fork_pool( vote_timestamps ); ushort root_idx = fd_vote_timestamps_init( vote_timestamps, 0UL, 0 ); FD_TEST( root_idx==0 ); @@ -50,8 +50,8 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_insert_root( vote_timestamps, pubkey_D, 10, 400UL ); fd_vote_timestamps_insert_root( vote_timestamps, pubkey_E, 10, 500UL ); - FD_TEST( 5U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); - fd_vote_timestamp_ele_t * root = fd_vote_timestamp_pool_ele( fork_pool, vote_timestamps->root_idx ); + FD_TEST( 5U==index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + fork_ele_t * root = fork_pool_ele( fork_pool, vote_timestamps->root_idx ); FD_TEST( root->deltas_cnt==0 ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); FD_TEST( root->parent_idx==USHORT_MAX ); @@ -63,40 +63,70 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_B, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_C, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx, pubkey_D, 11, 0UL ); - FD_TEST( 5U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); - fd_vote_timestamp_ele_t * child_fork = fd_vote_timestamp_pool_ele( fork_pool, child_idx ); + FD_TEST( 5U==index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + fork_ele_t * child_fork = fork_pool_ele( fork_pool, child_idx ); FD_TEST( child_fork->deltas_cnt==4 ); FD_TEST( root->child_idx==child_idx ); FD_TEST( child_fork->parent_idx==root_idx ); FD_TEST( child_fork->child_idx==USHORT_MAX ); FD_TEST( child_fork->sibling_idx==USHORT_MAX ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); - ulong timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx ); - FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx ); + snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, child_fork->snapshot_idx ); + snapshot_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, child_fork->snapshot_idx ); + + ulong t_10_cnt = 0UL; + ulong t_11_cnt = 0UL; + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + if( ele->timestamp==10 ) t_10_cnt++; + else if( ele->timestamp==11 ) t_11_cnt++; + else FD_TEST( 0 ); + } + FD_TEST( t_10_cnt==1 ); + FD_TEST( t_11_cnt==4 ); + ushort child_idx2 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 2UL, 0 ); fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_F, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_A, 15, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx2, pubkey_B, 15, 0UL ); - FD_TEST( 6U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); - timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx2 ); - FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); - fd_vote_timestamp_ele_t * child_fork2 = fd_vote_timestamp_pool_ele( fork_pool, child_idx2 ); + FD_TEST( 6U==index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx2 ); + fork_ele_t * child_fork2 = fork_pool_ele( fork_pool, child_idx2 ); FD_TEST( child_fork->child_idx==child_idx2 ); FD_TEST( child_fork2->parent_idx==child_idx ); FD_TEST( child_fork2->child_idx==USHORT_MAX ); FD_TEST( child_fork2->sibling_idx==USHORT_MAX ); FD_TEST( child_fork2->deltas_cnt==3 ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, child_fork2->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, child_fork2->snapshot_idx ); + t_10_cnt = 0UL; + t_11_cnt = 0UL; + ulong t_15_cnt = 0UL; + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + if( ele->timestamp==10 ) t_10_cnt++; + else if( ele->timestamp==11 ) t_11_cnt++; + else if( ele->timestamp==15 ) t_15_cnt++; + else FD_TEST( 0 ); + } + FD_TEST( t_10_cnt==1 ); + FD_TEST( t_11_cnt==3 ); + FD_TEST( t_15_cnt==2 ); ushort child_idx3 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 3UL, 0 ); fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_F, 11, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_A, 15, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx3, pubkey_B, 15, 0UL ); - FD_TEST( 6U==fd_vote_timestamp_index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); - timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx3 ); - FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); - fd_vote_timestamp_ele_t * child_fork3 = fd_vote_timestamp_pool_ele( fork_pool, child_idx3 ); + FD_TEST( 6U==index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx3 ); + fork_ele_t * child_fork3 = fork_pool_ele( fork_pool, child_idx3 ); FD_TEST( child_fork->child_idx==child_idx2 ); FD_TEST( child_fork2->sibling_idx==child_idx3 ); FD_TEST( child_fork3->parent_idx==child_idx ); @@ -104,6 +134,23 @@ int main( int argc, char ** argv ) { FD_TEST( child_fork3->sibling_idx==USHORT_MAX ); FD_TEST( child_fork3->deltas_cnt==3 ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, child_fork3->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, child_fork3->snapshot_idx ); + t_10_cnt = 0UL; + t_11_cnt = 0UL; + t_15_cnt = 0UL; + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + if( ele->timestamp==10 ) t_10_cnt++; + else if( ele->timestamp==11 ) t_11_cnt++; + else if( ele->timestamp==15 ) t_15_cnt++; + else FD_TEST( 0 ); + } + FD_TEST( t_10_cnt==1 ); + FD_TEST( t_11_cnt==3 ); + FD_TEST( t_15_cnt==2 ); /* Make sure the eviction policy is working. At this point we expect LRU eviction to kick in excluding the root and the best option. @@ -113,30 +160,70 @@ int main( int argc, char ** argv ) { fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_C, 16, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_A, 15, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx4, pubkey_B, 15, 0UL ); - timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx4 ); - FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); - fd_vote_timestamp_ele_t * child_fork4 = fd_vote_timestamp_pool_ele( fork_pool, child_idx4 ); + fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx4 ); + fork_ele_t * child_fork4 = fork_pool_ele( fork_pool, child_idx4 ); FD_TEST( child_fork4->parent_idx==child_idx2 ); FD_TEST( child_fork4->snapshot_idx!=UCHAR_MAX ); FD_TEST( child_fork->snapshot_idx==UCHAR_MAX ); FD_TEST( child_fork4->deltas_cnt==3 ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); + t_10_cnt = 0UL; + t_11_cnt = 0UL; + t_15_cnt = 0UL; + ulong t_16_cnt = 0UL; + snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, child_fork4->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, child_fork4->snapshot_idx ); + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + if( ele->timestamp==10 ) t_10_cnt++; + else if( ele->timestamp==11 ) t_11_cnt++; + else if( ele->timestamp==15 ) t_15_cnt++; + else if( ele->timestamp==16 ) t_16_cnt++; + else FD_TEST( 0 ); + } + FD_TEST( t_10_cnt==1 ); + FD_TEST( t_11_cnt==2 ); + FD_TEST( t_15_cnt==2 ); + FD_TEST( t_16_cnt==1 ); /* Now try to make a child off of child_idx and see if the skipped delta gets applied correctly. We also should expect to see child_idx2's snapshot to be evicted. Make sure that the root's snapshot idx does not get evicted. */ + ushort child_idx5 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx, 5UL, 0 ); fd_vote_timestamps_insert( vote_timestamps, child_idx5, pubkey_A, 20, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx5, pubkey_B, 20, 0UL ); fd_vote_timestamps_insert( vote_timestamps, child_idx5, pubkey_C, 20, 0UL ); - timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx5 ); + fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx5 ); + + fork_ele_t * child_fork5 = fork_pool_ele( fork_pool, child_idx5 ); FD_TEST( child_fork2->snapshot_idx==UCHAR_MAX ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); - FD_LOG_NOTICE(( "timestamp: %lu", timestamp )); + FD_TEST( child_fork5->snapshot_idx!=UCHAR_MAX ); + + ulong t_20_cnt = 0UL; + t_11_cnt = 0UL; + t_10_cnt = 0UL; + snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, child_fork5->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, child_fork5->snapshot_idx ); + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + if( ele->timestamp==20 ) t_20_cnt++; + else if( ele->timestamp==11 ) t_11_cnt++; + else if( ele->timestamp==10 ) t_10_cnt++; + else FD_TEST( 0 ); + } + FD_TEST( t_20_cnt==3 ); + FD_TEST( t_11_cnt==1 ); + FD_TEST( t_10_cnt==1 ); ushort child_idx6 = fd_vote_timestamps_attach_child( vote_timestamps, child_idx2, 6UL, 0 ); - timestamp = fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx6 ); + fd_vote_timestamps_get_timestamp( vote_timestamps, child_idx6 ); FD_TEST( child_fork3->snapshot_idx==UCHAR_MAX ); FD_TEST( root->snapshot_idx!=UCHAR_MAX ); @@ -147,17 +234,17 @@ int main( int argc, char ** argv ) { /* TODO: Asserts here. Make sure that the values in the snapshot are what we expect them to be. Also validate the fork structure at this point. */ - fd_vote_timestamp_ele_t * new_root = fd_vote_timestamp_pool_ele( fork_pool, vote_timestamps->root_idx ); + fork_ele_t * new_root = fork_pool_ele( fork_pool, vote_timestamps->root_idx ); FD_TEST( new_root->deltas_cnt==0 ); FD_TEST( new_root->snapshot_idx!=UCHAR_MAX ); - snapshot_ele_t * snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, new_root->snapshot_idx ); - snapshot_ele_map_t * snapshot_map = fd_vote_timestamps_get_snapshot_ele_map( vote_timestamps, new_root->snapshot_idx ); + snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, new_root->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, new_root->snapshot_idx ); ulong ts_10_cnt = 0UL; ulong ts_11_cnt = 0UL; - for( snapshot_ele_map_iter_t iter = snapshot_ele_map_iter_init( snapshot_map, snapshot ); - !snapshot_ele_map_iter_done( iter, snapshot_map, snapshot ); - iter = snapshot_ele_map_iter_next( iter, snapshot_map, snapshot ) ) { - snapshot_ele_t * ele = snapshot_ele_map_iter_ele( iter, snapshot_map, snapshot ); + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); FD_TEST( ele->timestamp==10 || ele->timestamp==11 ); if( ele->timestamp==10 ) ts_10_cnt++; else ts_11_cnt++; @@ -165,7 +252,39 @@ int main( int argc, char ** argv ) { FD_TEST( ts_10_cnt==1 ); FD_TEST( ts_11_cnt==4 ); + /* Now try advancing the root to the child_idx5 which has a snapshot. + Also now every other element in the tree will be pruned. */ + fd_vote_timestamps_advance_root( vote_timestamps, child_idx5 ); + new_root = fork_pool_ele( fork_pool, vote_timestamps->root_idx ); + FD_TEST( new_root->deltas_cnt==0 ); + FD_TEST( new_root->snapshot_idx!=UCHAR_MAX ); + FD_TEST( fork_pool_used( fork_pool )==1 ); + + snapshot_key_ele_t * snapshot_keys_pool = fd_vote_timestamps_get_snapshot_keys_pool( vote_timestamps ); + FD_TEST( snapshot_key_pool_used( snapshot_keys_pool )==1 ); + /* Make sure pubkey_F is pruned from the index. */ + FD_TEST( 5U==index_pool_used(fd_vote_timestamps_get_index_pool( vote_timestamps ) ) ); + snapshot = fd_vote_timestamps_get_snapshot( vote_timestamps, new_root->snapshot_idx ); + snapshot_map = fd_vote_timestamps_get_snapshot_map( vote_timestamps, new_root->snapshot_idx ); + + t_20_cnt = 0UL; + t_11_cnt = 0UL; + t_10_cnt = 0UL; + for( snapshot_map_iter_t iter = snapshot_map_iter_init( snapshot_map, snapshot ); + !snapshot_map_iter_done( iter, snapshot_map, snapshot ); + iter = snapshot_map_iter_next( iter, snapshot_map, snapshot ) ) { + snapshot_ele_t * ele = snapshot_map_iter_ele( iter, snapshot_map, snapshot ); + if( ele->timestamp==20 ) t_20_cnt++; + else if( ele->timestamp==11 ) t_11_cnt++; + else if( ele->timestamp==10 ) t_10_cnt++; + else FD_TEST( 0 ); + } + FD_TEST( t_20_cnt==3 ); + FD_TEST( t_11_cnt==1 ); + FD_TEST( t_10_cnt==1 ); + + /* TODO: The index currently leaks elements. */ FD_LOG_NOTICE(( "pass" )); }