diff --git a/src/app/rpcserver/main.c b/src/app/rpcserver/main.c index da1f764f49..d7636902c8 100644 --- a/src/app/rpcserver/main.c +++ b/src/app/rpcserver/main.c @@ -19,7 +19,7 @@ #include "sham_link.h" #define SHAM_LINK_CONTEXT fd_rpc_ctx_t -#define SHAM_LINK_STATE fd_stake_ci_t +#define SHAM_LINK_STATE fd_multi_epoch_leaders_t #define SHAM_LINK_NAME stake_sham_link #include "sham_link.h" @@ -61,11 +61,8 @@ init_args( int * argc, char *** argv, fd_rpcserver_args_t * args ) { FD_LOG_NOTICE(( "blockstore has slot root=%lu", args->blockstore->shmem->wmk )); fd_wksp_mprotect( wksp, 1 ); - fd_pubkey_t identity_key[1]; /* Just the public key */ - memset( identity_key, 0xa5, sizeof(fd_pubkey_t) ); - args->stake_ci = fd_stake_ci_join( fd_stake_ci_new( aligned_alloc( fd_stake_ci_align(), fd_stake_ci_footprint() ), identity_key ) ); - - args->port = (ushort)fd_env_strip_cmdline_ulong( argc, argv, "--port", NULL, 8899 ); + args->leaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( aligned_alloc( fd_multi_epoch_leaders_align(), fd_multi_epoch_leaders_footprint() ) ) ); + args->port = (ushort)fd_env_strip_cmdline_ulong( argc, argv, "--port", NULL, 8899 ); args->params.max_connection_cnt = fd_env_strip_cmdline_ulong( argc, argv, "--max-connection-cnt", NULL, 30 ); args->params.max_ws_connection_cnt = fd_env_strip_cmdline_ulong( argc, argv, "--max-ws-connection-cnt", NULL, 10 ); @@ -221,7 +218,7 @@ int main( int argc, char ** argv ) { fd_replay_notif_msg_t msg; replay_sham_link_poll( rep_notify, ctx, &msg ); - stake_sham_link_poll( stake_notify, ctx, args.stake_ci ); + stake_sham_link_poll( stake_notify, ctx, args.leaders ); fd_rpc_ws_poll( ctx ); } @@ -241,11 +238,11 @@ replay_sham_link_after_frag(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg) { } static void -stake_sham_link_during_frag(fd_rpc_ctx_t * ctx, fd_stake_ci_t * state, void const * msg, int sz) { +stake_sham_link_during_frag(fd_rpc_ctx_t * ctx, fd_multi_epoch_leaders_t * state, void const * msg, int sz) { fd_rpc_stake_during_frag( ctx, state, msg, sz ); } static void -stake_sham_link_after_frag(fd_rpc_ctx_t * ctx, fd_stake_ci_t * state) { +stake_sham_link_after_frag(fd_rpc_ctx_t * ctx, fd_multi_epoch_leaders_t * state) { fd_rpc_stake_after_frag( ctx, state ); } diff --git a/src/disco/pack/fd_pack_tile.c b/src/disco/pack/fd_pack_tile.c index 4dd87ad025..0590234bc8 100644 --- a/src/disco/pack/fd_pack_tile.c +++ b/src/disco/pack/fd_pack_tile.c @@ -25,8 +25,6 @@ #define IN_KIND_BANK (2UL) #define IN_KIND_SIGN (3UL) -#define MAX_SLOTS_PER_EPOCH 432000UL - /* Pace microblocks, but only slightly. This helps keep performance more stable. This limit is 2,000 microblocks/second/bank. At 31 transactions/microblock, that's 62k txn/sec/bank. */ diff --git a/src/disco/shred/fd_shred_tile.c b/src/disco/shred/fd_shred_tile.c index 2fbbb8ffb8..583c5b3bce 100644 --- a/src/disco/shred/fd_shred_tile.c +++ b/src/disco/shred/fd_shred_tile.c @@ -86,10 +86,6 @@ have a max and use statically sized arrays than alloca. */ #define MAX_BANK_CNT 64UL -/* MAX_SHRED_DESTS indicates the maximum number of destinations (i.e. a - pubkey -> ip, port) that the shred tile can keep track of. */ -#define MAX_SHRED_DESTS 40200UL - #define FD_SHRED_TILE_SCRATCH_ALIGN 128UL #define IN_KIND_CONTACT (0UL) @@ -102,8 +98,6 @@ #define NET_OUT_IDX 1 #define SIGN_OUT_IDX 2 -#define MAX_SLOTS_PER_EPOCH 432000UL - #define DCACHE_ENTRIES_PER_FEC_SET (4UL) FD_STATIC_ASSERT( sizeof(fd_shred34_t) < USHORT_MAX, shred_34 ); FD_STATIC_ASSERT( 34*DCACHE_ENTRIES_PER_FEC_SET >= FD_REEDSOL_DATA_SHREDS_MAX+FD_REEDSOL_PARITY_SHREDS_MAX, shred_34 ); @@ -397,7 +391,7 @@ during_frag( fd_shred_ctx_t * ctx, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark )); uchar const * dcache_entry = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ); - fd_stake_ci_stake_msg_init( ctx->stake_ci, dcache_entry ); + fd_stake_ci_stake_msg_init( ctx->stake_ci, fd_type_pun_const( dcache_entry ) ); return; } diff --git a/src/disco/shred/fd_stake_ci.c b/src/disco/shred/fd_stake_ci.c index bb2d824516..396ef01df2 100644 --- a/src/disco/shred/fd_stake_ci.c +++ b/src/disco/shred/fd_stake_ci.c @@ -44,27 +44,19 @@ void * fd_stake_ci_delete( void * mem ) { return mem; } void -fd_stake_ci_stake_msg_init( fd_stake_ci_t * info, - uchar const * new_message ) { - ulong const * hdr = fd_type_pun_const( new_message ); - - ulong epoch = hdr[ 0 ]; - ulong staked_cnt = hdr[ 1 ]; - ulong start_slot = hdr[ 2 ]; - ulong slot_cnt = hdr[ 3 ]; - ulong excluded_stake = hdr[ 4 ]; - - if( FD_UNLIKELY( staked_cnt > MAX_SHRED_DESTS ) ) +fd_stake_ci_stake_msg_init( fd_stake_ci_t * info, + fd_stake_weight_msg_t const * msg ) { + if( FD_UNLIKELY( msg->staked_cnt > MAX_SHRED_DESTS ) ) FD_LOG_ERR(( "The stakes -> Firedancer splice sent a malformed update with %lu stakes in it," - " but the maximum allowed is %lu", staked_cnt, MAX_SHRED_DESTS )); + " but the maximum allowed is %lu", msg->staked_cnt, MAX_SHRED_DESTS )); - info->scratch->epoch = epoch; - info->scratch->start_slot = start_slot; - info->scratch->slot_cnt = slot_cnt; - info->scratch->staked_cnt = staked_cnt; - info->scratch->excluded_stake = excluded_stake; + info->scratch->epoch = msg->epoch; + info->scratch->start_slot = msg->start_slot; + info->scratch->slot_cnt = msg->slot_cnt; + info->scratch->staked_cnt = msg->staked_cnt; + info->scratch->excluded_stake = msg->excluded_stake; - fd_memcpy( info->stake_weight, hdr+5UL, sizeof(fd_stake_weight_t)*staked_cnt ); + fd_memcpy( info->stake_weight, msg->weights, msg->staked_cnt*sizeof(fd_stake_weight_t) ); } static inline void diff --git a/src/disco/shred/fd_stake_ci.h b/src/disco/shred/fd_stake_ci.h index 20bd41d538..6921618b0a 100644 --- a/src/disco/shred/fd_stake_ci.h +++ b/src/disco/shred/fd_stake_ci.h @@ -15,8 +15,7 @@ #include "fd_shred_dest.h" #include "../../flamenco/leaders/fd_leaders.h" -#define MAX_SHRED_DESTS 40200UL -#define MAX_SLOTS_PER_EPOCH 432000UL +#define MAX_SHRED_DESTS MAX_STAKED_LEADERS /* staked+unstaked <= MAX_SHRED_DESTS implies MAX_SHRED_DEST_FOOTPRINT>=fd_shred_dest_footprint( staked, unstaked ) This is asserted in the tests. The size of fd_shred_dest_t, varies @@ -119,12 +118,11 @@ void * fd_stake_ci_delete( void * mem ); need to cancel an operation that begun but didn't finish. Calling init multiple times without calling fini will not leak any resources. - new_message should be a pointer to the first byte of the dcache entry - containing the stakes update. new_message will be accessed - new_message[i] for i in [0, FD_STAKE_CI_STAKE_MSG_SZ). new_message - must contain at least one staked pubkey, and the pubkeys must be - sorted in the usual way (by stake descending, ties broken by pubkey - ascending). + msg should be a pointer to the first byte of the dcache entry + containing the stakes update. msg will be accessed msg->weights[i] + for i in [0, msg->staked_cnt). msg must contain at least one + staked pubkey, and the pubkeys must be sorted in the usual way (by + stake descending, ties broken by pubkey ascending). fd_stake_ci_dest_add_init behaves slightly differently and returns a pointer to the first element of an array of size MAX_SHRED_DESTS-1 to @@ -148,10 +146,10 @@ void * fd_stake_ci_delete( void * mem ); contact info will be preserved. If a stake message doesn't have contact info for an unstaked node, on the other hand, that node will be deleted from the list. */ -void fd_stake_ci_stake_msg_init( fd_stake_ci_t * info, uchar const * new_message ); -void fd_stake_ci_stake_msg_fini( fd_stake_ci_t * info ); -fd_shred_dest_weighted_t * fd_stake_ci_dest_add_init ( fd_stake_ci_t * info ); -void fd_stake_ci_dest_add_fini ( fd_stake_ci_t * info, ulong cnt ); +void fd_stake_ci_stake_msg_init( fd_stake_ci_t * info, fd_stake_weight_msg_t const * msg ); +void fd_stake_ci_stake_msg_fini( fd_stake_ci_t * info ); +fd_shred_dest_weighted_t * fd_stake_ci_dest_add_init ( fd_stake_ci_t * info ); +void fd_stake_ci_dest_add_fini ( fd_stake_ci_t * info, ulong cnt ); /* fd_stake_ci_set_identity changes the identity of the locally running diff --git a/src/disco/shred/test_stake_ci.c b/src/disco/shred/test_stake_ci.c index a766d8d4bb..cae8f13931 100644 --- a/src/disco/shred/test_stake_ci.c +++ b/src/disco/shred/test_stake_ci.c @@ -1,5 +1,4 @@ #include "fd_stake_ci.h" - #define SLOTS_PER_EPOCH 1000 /* Just for testing */ fd_stake_ci_t _info[1]; @@ -8,20 +7,11 @@ uchar stake_msg[ FD_STAKE_CI_STAKE_MSG_SZ ]; fd_pubkey_t identity_key[1]; -typedef struct { - ulong epoch; - ulong staked_cnt; - ulong start_slot; - ulong slot_cnt; - ulong excluded_stake; - fd_stake_weight_t weights[]; - } stake_msg_hdr_t; - -static uchar * +static fd_stake_weight_msg_t * generate_stake_msg( uchar * _buf, ulong epoch, char const * stakers ) { - stake_msg_hdr_t *buf = (stake_msg_hdr_t *)_buf; + fd_stake_weight_msg_t *buf = fd_type_pun( _buf ); buf->epoch = epoch; buf->start_slot = epoch * SLOTS_PER_EPOCH; @@ -34,7 +24,7 @@ generate_stake_msg( uchar * _buf, memset( buf->weights[i].key.uc, *stakers, sizeof(fd_pubkey_t) ); buf->weights[i].stake = 1000UL/(i+1UL); } - return _buf; + return fd_type_pun( _buf ); } static ulong @@ -346,12 +336,12 @@ test_limits( void ) { fd_stake_ci_t * info = fd_stake_ci_join( fd_stake_ci_new( _info, identity_key ) ); for( ulong stake_weight_cnt=40198UL; stake_weight_cnt<=40201UL; stake_weight_cnt++ ) { - stake_msg_hdr_t * buf = (stake_msg_hdr_t *)stake_msg; - buf->epoch = stake_weight_cnt; - buf->start_slot = stake_weight_cnt * SLOTS_PER_EPOCH; - buf->slot_cnt = SLOTS_PER_EPOCH; - buf->staked_cnt = 0UL; - buf->excluded_stake = 0UL; + fd_stake_weight_msg_t * buf = fd_type_pun( stake_msg ); + buf->epoch = stake_weight_cnt; + buf->start_slot = stake_weight_cnt * SLOTS_PER_EPOCH; + buf->slot_cnt = SLOTS_PER_EPOCH; + buf->staked_cnt = 0UL; + buf->excluded_stake = 0UL; for( ulong i=0UL; iexcluded_stake += stake; } } - fd_stake_ci_stake_msg_init( info, stake_msg ); + fd_stake_ci_stake_msg_init( info, buf ); fd_stake_ci_stake_msg_fini( info ); for( ulong cluster_info_cnt=40198UL; cluster_info_cnt<=40201UL; cluster_info_cnt++ ) { diff --git a/src/discof/poh/fd_poh_tile.c b/src/discof/poh/fd_poh_tile.c index 9ce1d1ed0b..35fd251644 100644 --- a/src/discof/poh/fd_poh_tile.c +++ b/src/discof/poh/fd_poh_tile.c @@ -315,12 +315,11 @@ #include "../../disco/metrics/fd_metrics.h" #include "../../util/pod/fd_pod_format.h" #include "../../disco/shred/fd_shredder.h" -#include "../../disco/shred/fd_stake_ci.h" #include "../../disco/keyguard/fd_keyload.h" #include "../../disco/keyguard/fd_keyswitch.h" #include "../../disco/metrics/generated/fd_metrics_poh.h" #include "../../disco/plugin/fd_plugin.h" -#include "../../flamenco/leaders/fd_leaders.h" +#include "../../flamenco/leaders/fd_multi_epoch_leaders.h" #include @@ -501,7 +500,7 @@ typedef struct { fd_sha256_t * sha256; - fd_stake_ci_t * stake_ci; + fd_multi_epoch_leaders_t * mleaders; /* The last sequence number of an outgoing fragment to the shred tile, or ULONG max if no such fragment. See fd_keyswitch.h for details @@ -548,6 +547,8 @@ typedef struct { ulong parent_slot; uchar parent_block_id[ 32 ]; + + uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ]; } fd_poh_ctx_t; /* The PoH recorder is implemented in Firedancer but for now needs to @@ -1136,19 +1137,7 @@ next_leader_slot( fd_poh_ctx_t * ctx ) { /* If we have published anything in a particular slot, then we should never become leader for that slot again. */ ulong min_leader_slot = fd_ulong_max( ctx->slot, fd_ulong_if( ctx->highwater_leader_slot==ULONG_MAX, 0UL, ctx->highwater_leader_slot ) ); - - for(;;) { - fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, min_leader_slot ); /* Safe to call from Rust */ - if( FD_UNLIKELY( !leaders ) ) break; - - while( min_leader_slot<(leaders->slot0+leaders->slot_cnt) ) { - fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, min_leader_slot ); /* Safe to call from Rust */ - if( FD_UNLIKELY( !memcmp( leader->key, ctx->identity_key.key, 32UL ) ) ) return min_leader_slot; - min_leader_slot++; - } - } - - return ULONG_MAX; + return fd_multi_epoch_leaders_get_next_slot( ctx->mleaders, min_leader_slot, &ctx->identity_key ); } extern int @@ -1180,7 +1169,6 @@ maybe_change_identity( fd_poh_ctx_t * ctx, } memcpy( ctx->identity_key.uc, ctx->keyswitch->bytes+32UL, 32UL ); - fd_stake_ci_set_identity( ctx->stake_ci, &ctx->identity_key ); /* When we switch key, we might have ticked part way through a slot that we are now leader in. This violates the contract of the @@ -1350,15 +1338,12 @@ fd_ext_poh_get_leader_after_n_slots( ulong n, uchar out_pubkey[ static 32 ] ) { fd_poh_ctx_t * ctx = fd_ext_poh_write_lock(); ulong slot = ctx->slot + n; - fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, slot ); /* Safe to call from Rust */ + fd_pubkey_t const * leader = fd_multi_epoch_leaders_get_leader_for_slot( ctx->mleaders, slot ); int copied = 0; - if( FD_LIKELY( leaders ) ) { - fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot ); /* Safe to call from Rust */ - if( FD_LIKELY( leader ) ) { - memcpy( out_pubkey, leader, 32UL ); - copied = 1; - } + if( FD_LIKELY( leader ) ) { + memcpy( out_pubkey, leader, 32UL ); + copied = 1; } fd_ext_poh_write_unlock(); return copied; @@ -1374,7 +1359,6 @@ scratch_footprint( fd_topo_tile_t const * tile ) { (void)tile; ulong l = FD_LAYOUT_INIT; l = FD_LAYOUT_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) ); - l = FD_LAYOUT_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); l = FD_LAYOUT_APPEND( l, FD_SHA256_ALIGN, FD_SHA256_FOOTPRINT ); return FD_LAYOUT_FINI( l, scratch_align() ); } @@ -1821,7 +1805,7 @@ during_frag( fd_poh_ctx_t * ctx, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark )); uchar const * dcache_entry = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ); - fd_stake_ci_stake_msg_init( ctx->stake_ci, dcache_entry ); + fd_multi_epoch_leaders_stake_msg_init( ctx->mleaders, fd_type_pun_const( dcache_entry ) ); return; } @@ -1957,7 +1941,7 @@ after_frag( fd_poh_ctx_t * ctx, if( FD_UNLIKELY( ctx->skip_frag ) ) return; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_STAKE ) ) { - fd_stake_ci_stake_msg_fini( ctx->stake_ci ); + fd_multi_epoch_leaders_stake_msg_fini( ctx->mleaders ); /* It might seem like we do not need to do state transitions in and out of being the leader here, since leader schedule updates are always one epoch in advance (whether we are leader or not would @@ -2215,7 +2199,6 @@ unprivileged_init( fd_topo_t * topo, FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_poh_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) ); - void * stake_ci = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); void * sha256 = FD_SCRATCH_ALLOC_APPEND( l, FD_SHA256_ALIGN, FD_SHA256_FOOTPRINT ); #define NONNULL( x ) (__extension__({ \ @@ -2223,8 +2206,8 @@ unprivileged_init( fd_topo_t * topo, if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \ __x; })) - ctx->stake_ci = NONNULL( fd_stake_ci_join( fd_stake_ci_new( stake_ci, &ctx->identity_key ) ) ); - ctx->sha256 = NONNULL( fd_sha256_join( fd_sha256_new( sha256 ) ) ); + ctx->mleaders = NONNULL( fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( ctx->mleaders_mem ) ) ); + ctx->sha256 = NONNULL( fd_sha256_join( fd_sha256_new( sha256 ) ) ); ctx->current_leader_bank = NULL; ctx->signal_leader_change = NULL; diff --git a/src/discof/repair/fd_repair_tile.c b/src/discof/repair/fd_repair_tile.c index 4705cf8222..7e615f1a79 100644 --- a/src/discof/repair/fd_repair_tile.c +++ b/src/discof/repair/fd_repair_tile.c @@ -7,12 +7,13 @@ #include "../../flamenco/repair/fd_repair.h" #include "../../flamenco/runtime/fd_blockstore.h" +#include "../../flamenco/leaders/fd_leaders_base.h" #include "../../disco/fd_disco.h" #include "../../disco/keyguard/fd_keyload.h" #include "../../disco/keyguard/fd_keyguard_client.h" #include "../../disco/keyguard/fd_keyguard.h" #include "../../disco/net/fd_net_tile.h" -#include "../../disco/shred/fd_stake_ci.h" +#include "../../discof/replay/fd_exec.h" #include "../../util/pod/fd_pod_format.h" #include "../../choreo/fd_choreo_base.h" #include "../../util/net/fd_net_headers.h" @@ -141,8 +142,6 @@ struct fd_repair_tile_ctx { fd_ip4_udp_hdrs_t intake_hdr[1]; fd_ip4_udp_hdrs_t serve_hdr [1]; - fd_stake_ci_t * stake_ci; - fd_stem_context_t * stem; fd_wksp_t * blockstore_wksp; @@ -179,7 +178,6 @@ scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED) { l = FD_LAYOUT_APPEND( l, fd_fec_chainer_align(), fd_fec_chainer_footprint( 1 << 20 ) ); // TODO: fix this l = FD_LAYOUT_APPEND( l, fd_scratch_smem_align(), fd_scratch_smem_footprint( FD_REPAIR_SCRATCH_MAX ) ); l = FD_LAYOUT_APPEND( l, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( FD_REPAIR_SCRATCH_DEPTH ) ); - l = FD_LAYOUT_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); return FD_LAYOUT_FINI( l, scratch_align() ); } @@ -252,18 +250,6 @@ handle_new_cluster_contact_info( fd_repair_tile_ctx_t * ctx, } } -static inline void -handle_new_stake_weights( fd_repair_tile_ctx_t * ctx ) { - ulong stakes_cnt = ctx->stake_ci->scratch->staked_cnt; - - if( stakes_cnt >= MAX_REPAIR_PEERS ) { - FD_LOG_ERR(( "Cluster nodes had %lu stake weights, which was more than the max of %lu", stakes_cnt, MAX_REPAIR_PEERS )); - } - - fd_stake_weight_t const * in_stake_weights = ctx->stake_ci->stake_weight; - fd_repair_set_stake_weights( ctx->repair, in_stake_weights, stakes_cnt ); -} - ulong fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx, fd_repair_t * glob, @@ -476,7 +462,8 @@ during_frag( fd_repair_tile_ctx_t * ctx, FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, in_ctx->chunk0, in_ctx->wmark )); } dcache_entry = fd_chunk_to_laddr_const( in_ctx->mem, chunk ); - fd_stake_ci_stake_msg_init( ctx->stake_ci, dcache_entry ); + fd_stake_weight_msg_t const * msg = fd_type_pun_const( dcache_entry ); + fd_repair_set_stake_weights_init( ctx->repair, msg->weights, msg->staked_cnt ); return; } else if( FD_LIKELY( in_kind==IN_KIND_SHRED ) ) { @@ -764,8 +751,7 @@ after_frag( fd_repair_tile_ctx_t * ctx, } if( FD_UNLIKELY( in_kind==IN_KIND_STAKE ) ) { - fd_stake_ci_stake_msg_fini( ctx->stake_ci ); - handle_new_stake_weights( ctx ); + fd_repair_set_stake_weights_fini( ctx->repair ); return; } @@ -1206,9 +1192,6 @@ unprivileged_init( fd_topo_t * topo, ctx->repair_intake_listen_port = tile->repair.repair_intake_listen_port; ctx->repair_serve_listen_port = tile->repair.repair_serve_listen_port; - void * _stake_ci = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); - ctx->stake_ci = fd_stake_ci_join( fd_stake_ci_new( _stake_ci , &ctx->identity_public_key ) ); - ctx->net_id = (ushort)0; fd_ip4_udp_hdr_init( ctx->intake_hdr, FD_REPAIR_MAX_PACKET_SIZE, 0, ctx->repair_intake_listen_port ); diff --git a/src/discof/replay/fd_exec.h b/src/discof/replay/fd_exec.h index 86acf56096..026bfcd126 100644 --- a/src/discof/replay/fd_exec.h +++ b/src/discof/replay/fd_exec.h @@ -2,22 +2,13 @@ #define HEADER_fd_src_discof_replay_fd_exec_h #include "../../flamenco/fd_flamenco_base.h" +#include "../../flamenco/leaders/fd_leaders_base.h" #include "../../flamenco/runtime/context/fd_exec_epoch_ctx.h" #include "../../flamenco/runtime/context/fd_exec_slot_ctx.h" #include "../../flamenco/runtime/fd_runtime_public.h" #include "../../flamenco/stakes/fd_stakes.h" #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h" -/* Follows message structure in fd_stake_ci_stake_msg_init */ -struct fd_stake_weight_msg_t { - ulong epoch; /* Epoch for which the stake weights are valid */ - ulong staked_cnt; /* Number of staked nodes */ - ulong start_slot; /* Start slot of the epoch */ - ulong slot_cnt; /* Number of slots in the epoch */ - ulong excluded_stake; /* Total stake that is excluded from leader selection */ -}; -typedef struct fd_stake_weight_msg_t fd_stake_weight_msg_t; - /* Replay tile msg link formatting. The following take a pointer into a dcache region and formats it as a specific message type. */ @@ -28,10 +19,9 @@ generate_stake_weight_msg( fd_exec_slot_ctx_t * slot_ctx, ulong * stake_weight_msg_out ) { fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - fd_stake_weight_msg_t * stake_weight_msg = (fd_stake_weight_msg_t *)fd_type_pun( stake_weight_msg_out ); - fd_stake_weight_t * stake_weights = (fd_stake_weight_t *)&stake_weight_msg_out[5]; + fd_stake_weight_msg_t * stake_weight_msg = fd_type_pun( stake_weight_msg_out ); ulong stake_weight_idx = fd_stake_weights_by_node( &slot_ctx->slot_bank.epoch_stakes, - stake_weights, + stake_weight_msg->weights, runtime_spad ); stake_weight_msg->epoch = epoch; diff --git a/src/discof/rpcserver/fd_rpc_service.c b/src/discof/rpcserver/fd_rpc_service.c index b9e2fd4479..47ae91272c 100644 --- a/src/discof/rpcserver/fd_rpc_service.c +++ b/src/discof/rpcserver/fd_rpc_service.c @@ -76,7 +76,7 @@ struct fd_rpc_global_ctx { fd_perf_sample_t * perf_samples; fd_perf_sample_t perf_sample_snapshot; long perf_sample_ts; - fd_stake_ci_t * stake_ci; + fd_multi_epoch_leaders_t * leaders; ulong acct_age; fd_rpc_history_t * history; }; @@ -517,18 +517,27 @@ method_getBlockProduction(struct json_values* values, fd_rpc_ctx_t * ctx) { FD_SPAD_FRAME_BEGIN( ctx->global->spad ) { ulong startslot = blockstore->shmem->wmk; ulong endslot = blockstore->shmem->lps; - fd_per_epoch_info_t const * ei = glob->stake_ci->epoch_info; - startslot = fd_ulong_max( startslot, fd_ulong_min( ei[0].start_slot, ei[1].start_slot ) ); - ulong n = (endslot - startslot)/4U + 1U; - void * shmem = fd_spad_alloc( glob->spad, product_rb_align(), product_rb_footprint( n ) ); - product_rb_node_t * pool = product_rb_join( product_rb_new( shmem, n ) ); + fd_multi_epoch_leaders_lsched_sorted_t lscheds = fd_multi_epoch_leaders_get_sorted_lscheds( glob->leaders ); + + ulong slots_in_leaders = (lscheds.lscheds[0] ? lscheds.lscheds[0]->slot_cnt : 0UL) + + (lscheds.lscheds[1] ? lscheds.lscheds[1]->slot_cnt : 0UL); + ulong worst_case_n = fd_ulong_min( slots_in_leaders, (endslot - startslot) / 4UL + 1UL ); + + void * shmem = fd_spad_alloc( glob->spad, product_rb_align(), product_rb_footprint( worst_case_n ) ); + product_rb_node_t * pool = product_rb_join( product_rb_new( shmem, worst_case_n ) ); product_rb_node_t * root = NULL; - fd_epoch_leaders_t const * lsched = fd_stake_ci_get_lsched_for_slot( glob->stake_ci, startslot ); - if( lsched ) { - for ( ulong i = startslot; i <= endslot; ++i ) { - fd_pubkey_t const * slot_leader = fd_epoch_leaders_get( lsched, i ); + for( ulong i=0UL; i<2UL; i++ ) { + const fd_epoch_leaders_t * lsched = lscheds.lscheds[i]; + if( !lsched ) continue; + + ulong const start_slot_in_epoch = fd_ulong_max( startslot, lsched->slot0 ); + ulong const end_slot_in_epoch = fd_ulong_min( endslot+1, lsched->slot0 + lsched->slot_cnt ); + /* we're guaranteed start_slot_in_epoch <= end_slot_in_epoch */ + + for( ulong j=start_slot_in_epoch; juc, sizeof(fd_pubkey_t) ); @@ -540,7 +549,7 @@ method_getBlockProduction(struct json_values* values, fd_rpc_ctx_t * ctx) { product_rb_insert( pool, &root, nd ); } nd->nleader++; - if( fd_rpc_history_get_block_info(ctx->global->history, i) ) { + if( fd_rpc_history_get_block_info(ctx->global->history, j) ) { nd->nproduced++; } } @@ -963,9 +972,12 @@ method_getLeaderSchedule(struct json_values* values, fd_rpc_ctx_t * ctx) { fd_method_error(ctx, -1, "unable to read epoch_bank"); return 0; } - ulong slot_index; - ulong epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot, &slot_index ); - fd_epoch_leaders_t * leaders = ctx->global->stake_ci->epoch_info[epoch%2].lsched; + + fd_epoch_leaders_t const * leaders = fd_multi_epoch_leaders_get_lsched_for_slot( ctx->global->leaders, slot ); + if( FD_UNLIKELY( !leaders ) ) { + fd_method_error(ctx, -1, "unable to get leaders for slot %lu", slot); + return 0; + } /* Reorganize the map to index on sorted leader key */ void * shmem = fd_spad_alloc( ctx->global->spad, leader_rb_align(), leader_rb_footprint( leaders->pub_cnt ) ); @@ -1325,8 +1337,7 @@ method_getSlotLeader(struct json_values* values, fd_rpc_ctx_t * ctx) { fd_webserver_t * ws = &ctx->global->ws; fd_web_reply_sprintf(ws, "{\"jsonrpc\":\"2.0\",\"result\":"); ulong slot = get_slot_from_commitment_level( values, ctx ); - fd_epoch_leaders_t const * lsched = fd_stake_ci_get_lsched_for_slot( ctx->global->stake_ci, slot ); - fd_pubkey_t const * slot_leader = fd_epoch_leaders_get( lsched, slot ); + fd_pubkey_t const * slot_leader = fd_multi_epoch_leaders_get_leader_for_slot( ctx->global->leaders, slot ); if( slot_leader ) { char str[50]; fd_base58_encode_32(slot_leader->uc, 0, str); @@ -1370,7 +1381,7 @@ method_getSlotLeaders(struct json_values* values, fd_rpc_ctx_t * ctx) { limitn = 5000; fd_web_reply_sprintf(ws, "{\"jsonrpc\":\"2.0\",\"result\":["); - fd_epoch_leaders_t const * lsched = fd_stake_ci_get_lsched_for_slot( ctx->global->stake_ci, startslotn ); + fd_epoch_leaders_t const * lsched = fd_multi_epoch_leaders_get_lsched_for_slot( ctx->global->leaders, startslotn ); if( lsched ) { for ( ulong i = startslotn; i < startslotn + limitn; ++i ) { if( i > startslotn ) EMIT_SIMPLE(","); @@ -2361,9 +2372,9 @@ fd_rpc_create_ctx(fd_rpcserver_args_t * args, fd_rpc_ctx_t ** ctx_p) { fd_memset(ctx, 0, sizeof(fd_rpc_ctx_t)); fd_memset(gctx, 0, sizeof(fd_rpc_global_ctx_t)); - ctx->global = gctx; - gctx->spad = args->spad; - gctx->stake_ci = args->stake_ci; + ctx->global = gctx; + gctx->spad = args->spad; + gctx->leaders = args->leaders; if( !args->offline ) { gctx->tpu_socket = socket(AF_INET, SOCK_DGRAM, 0); @@ -2488,13 +2499,13 @@ fd_rpc_replay_after_frag(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg) { } void -fd_rpc_stake_during_frag( fd_rpc_ctx_t * ctx, fd_stake_ci_t * state, void const * msg, int sz ) { +fd_rpc_stake_during_frag( fd_rpc_ctx_t * ctx, fd_multi_epoch_leaders_t * state, void const * msg, int sz ) { (void)ctx; (void)sz; - fd_stake_ci_stake_msg_init( state, msg ); + fd_multi_epoch_leaders_stake_msg_init( state, msg ); } void -fd_rpc_stake_after_frag(fd_rpc_ctx_t * ctx, fd_stake_ci_t * state) { +fd_rpc_stake_after_frag(fd_rpc_ctx_t * ctx, fd_multi_epoch_leaders_t * state) { (void)ctx; - fd_stake_ci_stake_msg_fini( state ); + fd_multi_epoch_leaders_stake_msg_fini( state ); } diff --git a/src/discof/rpcserver/fd_rpc_service.h b/src/discof/rpcserver/fd_rpc_service.h index 214aab3e50..6be147bf62 100644 --- a/src/discof/rpcserver/fd_rpc_service.h +++ b/src/discof/rpcserver/fd_rpc_service.h @@ -5,7 +5,7 @@ #include "../replay/fd_replay_notif.h" #include "../../disco/topo/fd_topo.h" -#include "../../disco/shred/fd_stake_ci.h" +#include "../../flamenco/leaders/fd_multi_epoch_leaders.h" #include "../../flamenco/runtime/fd_blockstore.h" #include "../../waltz/http/fd_http_server.h" @@ -14,22 +14,22 @@ typedef struct fd_rpc_ctx fd_rpc_ctx_t; struct fd_rpcserver_args { - int offline; - fd_funk_t funk[1]; - fd_blockstore_t blockstore_ljoin; - fd_blockstore_t * blockstore; - int blockstore_fd; - fd_stake_ci_t * stake_ci; - ushort port; - fd_http_server_params_t params; - struct sockaddr_in tpu_addr; - uint block_index_max; - uint txn_index_max; - uint acct_index_max; - char history_file[ PATH_MAX ]; + int offline; + fd_funk_t funk[1]; + fd_blockstore_t blockstore_ljoin; + fd_blockstore_t * blockstore; + int blockstore_fd; + fd_multi_epoch_leaders_t * leaders; + ushort port; + fd_http_server_params_t params; + struct sockaddr_in tpu_addr; + uint block_index_max; + uint txn_index_max; + uint acct_index_max; + char history_file[ PATH_MAX ]; /* Bump allocator */ - fd_spad_t * spad; + fd_spad_t * spad; }; typedef struct fd_rpcserver_args fd_rpcserver_args_t; @@ -45,8 +45,8 @@ void fd_rpc_replay_during_frag(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * state void fd_rpc_replay_after_frag(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg); -void fd_rpc_stake_during_frag(fd_rpc_ctx_t * ctx, fd_stake_ci_t * state, void const * msg, int sz); +void fd_rpc_stake_during_frag(fd_rpc_ctx_t * ctx, fd_multi_epoch_leaders_t * state, void const * msg, int sz); -void fd_rpc_stake_after_frag(fd_rpc_ctx_t * ctx, fd_stake_ci_t * state); +void fd_rpc_stake_after_frag(fd_rpc_ctx_t * ctx, fd_multi_epoch_leaders_t * state); #endif /* HEADER_fd_src_discof_rpcserver_fd_rpc_service_h */ diff --git a/src/discof/rpcserver/fd_rpcserv_tile.c b/src/discof/rpcserver/fd_rpcserv_tile.c index 7de9feccdc..1e63f4ae0e 100644 --- a/src/discof/rpcserver/fd_rpcserv_tile.c +++ b/src/discof/rpcserver/fd_rpcserv_tile.c @@ -12,7 +12,7 @@ #include "../../flamenco/fd_flamenco.h" #include "../../util/fd_util.h" #include "../../disco/fd_disco.h" -#include "../../disco/shred/fd_stake_ci.h" +#include "../../flamenco/leaders/fd_multi_epoch_leaders.h" #include "../../util/pod/fd_pod_format.h" #include "../../funk/fd_funk_filemap.h" #include "../../disco/keyguard/fd_keyload.h" @@ -22,7 +22,7 @@ #include #define REPLAY_NOTIF_IDX 0 -#define STAKE_CI_IN_IDX 1 +#define STAKE_IN_IDX 1 struct fd_rpcserv_tile_ctx { fd_rpcserver_args_t args; @@ -39,11 +39,13 @@ struct fd_rpcserv_tile_ctx { ulong replay_notif_in_wmark; fd_replay_notif_msg_t replay_notif_in_state; - fd_wksp_t * stake_ci_in_mem; - ulong stake_ci_in_chunk0; - ulong stake_ci_in_wmark; + fd_wksp_t * stake_in_mem; + ulong stake_in_chunk0; + ulong stake_in_wmark; int blockstore_fd; + + uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ]; }; typedef struct fd_rpcserv_tile_ctx fd_rpcserv_tile_ctx_t; @@ -67,7 +69,6 @@ FD_FN_PURE static inline ulong scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED) { ulong l = FD_LAYOUT_INIT; l = FD_LAYOUT_APPEND( l, alignof(fd_rpcserv_tile_ctx_t), sizeof(fd_rpcserv_tile_ctx_t) ); - l = FD_LAYOUT_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); l = FD_LAYOUT_APPEND( l, fd_spad_align(), fd_spad_footprint( FD_RPC_SCRATCH_MAX ) ); return FD_LAYOUT_FINI( l, scratch_align() ); } @@ -106,12 +107,12 @@ during_frag( fd_rpcserv_tile_ctx_t * ctx, } fd_rpc_replay_during_frag( ctx->ctx, &ctx->replay_notif_in_state, fd_chunk_to_laddr_const( ctx->replay_notif_in_mem, chunk ), (int)sz ); - } else if( FD_UNLIKELY( in_idx==STAKE_CI_IN_IDX ) ) { - if( FD_UNLIKELY( chunkstake_ci_in_chunk0 || chunk>ctx->stake_ci_in_wmark ) ) { + } else if( FD_UNLIKELY( in_idx==STAKE_IN_IDX ) ) { + if( FD_UNLIKELY( chunkstake_in_chunk0 || chunk>ctx->stake_in_wmark ) ) { FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, - ctx->stake_ci_in_chunk0, ctx->stake_ci_in_wmark )); + ctx->stake_in_chunk0, ctx->stake_in_wmark )); } - fd_rpc_stake_during_frag( ctx->ctx, ctx->args.stake_ci, fd_chunk_to_laddr_const( ctx->stake_ci_in_mem, chunk ), (int)sz ); + fd_rpc_stake_during_frag( ctx->ctx, ctx->args.leaders, fd_chunk_to_laddr_const( ctx->stake_in_mem, chunk ), (int)sz ); } else { FD_LOG_ERR(("Unknown in_idx %lu for rpc", in_idx)); @@ -149,8 +150,8 @@ after_frag( fd_rpcserv_tile_ctx_t * ctx, fd_rpc_replay_after_frag( ctx->ctx, &ctx->replay_notif_in_state ); - } else if( FD_UNLIKELY( in_idx==STAKE_CI_IN_IDX ) ) { - fd_rpc_stake_after_frag( ctx->ctx, ctx->args.stake_ci ); + } else if( FD_UNLIKELY( in_idx==STAKE_IN_IDX ) ) { + fd_rpc_stake_after_frag( ctx->ctx, ctx->args.leaders ); } else { FD_LOG_ERR(("Unknown in_idx %lu for rpc", in_idx)); @@ -164,7 +165,6 @@ privileged_init( fd_topo_t * topo, FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_rpcserv_tile_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_rpcserv_tile_ctx_t), sizeof(fd_rpcserv_tile_ctx_t) ); - void * stake_ci_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); void * spad_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_spad_align(), fd_spad_footprint( FD_RPC_SCRATCH_MAX ) ); FD_SCRATCH_ALLOC_FINI( l, scratch_align() ); @@ -184,7 +184,7 @@ privileged_init( fd_topo_t * topo, args->tpu_addr.sin_addr.s_addr = tile->rpcserv.tpu_ip_addr; args->tpu_addr.sin_port = htons( (ushort)tile->rpcserv.tpu_port ); - args->stake_ci = fd_stake_ci_join( fd_stake_ci_new( stake_ci_mem, ctx->identity_key ) ); + args->leaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( ctx->mleaders_mem) ); uchar * spad_mem_cur = spad_mem; args->spad = fd_spad_join( fd_spad_new( spad_mem_cur, FD_RPC_SCRATCH_MAX ) ); @@ -223,7 +223,7 @@ unprivileged_init( fd_topo_t * topo, if( FD_UNLIKELY( tile->in_cnt != 2 || strcmp( topo->links[ tile->in_link_id[ REPLAY_NOTIF_IDX ] ].name, "replay_notif") || - strcmp( topo->links[ tile->in_link_id[ STAKE_CI_IN_IDX ] ].name, "stake_out" ) ) ) { + strcmp( topo->links[ tile->in_link_id[ STAKE_IN_IDX ] ].name, "stake_out" ) ) ) { FD_LOG_ERR(( "repair tile has none or unexpected input links %lu %s %s", tile->in_cnt, topo->links[ tile->in_link_id[ 0 ] ].name, topo->links[ tile->in_link_id[ 1 ] ].name )); } @@ -237,7 +237,6 @@ unprivileged_init( fd_topo_t * topo, FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_rpcserv_tile_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_rpcserv_tile_ctx_t), sizeof(fd_rpcserv_tile_ctx_t) ); - FD_SCRATCH_ALLOC_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); FD_SCRATCH_ALLOC_APPEND( l, fd_spad_align(), fd_ulong_align_up( FD_RPC_SCRATCH_MAX, fd_spad_align() ) ); ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() ); if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) ) @@ -250,10 +249,10 @@ unprivileged_init( fd_topo_t * topo, ctx->replay_notif_in_chunk0 = fd_dcache_compact_chunk0( ctx->replay_notif_in_mem, replay_notif_in_link->dcache ); ctx->replay_notif_in_wmark = fd_dcache_compact_wmark ( ctx->replay_notif_in_mem, replay_notif_in_link->dcache, replay_notif_in_link->mtu ); - fd_topo_link_t * stake_ci_in_link = &topo->links[ tile->in_link_id[ STAKE_CI_IN_IDX ] ]; - ctx->stake_ci_in_mem = topo->workspaces[ topo->objs[ stake_ci_in_link->dcache_obj_id ].wksp_id ].wksp; - ctx->stake_ci_in_chunk0 = fd_dcache_compact_chunk0( ctx->stake_ci_in_mem, stake_ci_in_link->dcache ); - ctx->stake_ci_in_wmark = fd_dcache_compact_wmark ( ctx->stake_ci_in_mem, stake_ci_in_link->dcache, stake_ci_in_link->mtu ); + fd_topo_link_t * stake_in_link = &topo->links[ tile->in_link_id[ STAKE_IN_IDX ] ]; + ctx->stake_in_mem = topo->workspaces[ topo->objs[ stake_in_link->dcache_obj_id ].wksp_id ].wksp; + ctx->stake_in_chunk0 = fd_dcache_compact_chunk0( ctx->stake_in_mem, stake_in_link->dcache ); + ctx->stake_in_wmark = fd_dcache_compact_wmark ( ctx->stake_in_mem, stake_in_link->dcache, stake_in_link->mtu ); } static ulong diff --git a/src/discof/send/fd_send_tile.c b/src/discof/send/fd_send_tile.c index 0186458b38..f6dbd5ade8 100644 --- a/src/discof/send/fd_send_tile.c +++ b/src/discof/send/fd_send_tile.c @@ -223,7 +223,7 @@ during_frag( fd_send_tile_ctx_t * ctx, if( sz>sizeof(fd_stake_weight_t)*(MAX_SHRED_DESTS+1UL) ) { FD_LOG_ERR(( "sz %lu >= max expected stake update size %lu", sz, sizeof(fd_stake_weight_t) * (MAX_SHRED_DESTS+1UL) )); } - fd_stake_ci_stake_msg_init( ctx->stake_ci, dcache_entry ); + fd_stake_ci_stake_msg_init( ctx->stake_ci, fd_type_pun_const( dcache_entry ) ); } if( FD_UNLIKELY( kind==IN_KIND_GOSSIP ) ) { diff --git a/src/discoh/poh/fd_poh_tile.c b/src/discoh/poh/fd_poh_tile.c index 40b4208d18..9b00e29fb5 100644 --- a/src/discoh/poh/fd_poh_tile.c +++ b/src/discoh/poh/fd_poh_tile.c @@ -315,12 +315,11 @@ #include "../../disco/metrics/fd_metrics.h" #include "../../util/pod/fd_pod_format.h" #include "../../disco/shred/fd_shredder.h" -#include "../../disco/shred/fd_stake_ci.h" #include "../../disco/keyguard/fd_keyload.h" #include "../../disco/keyguard/fd_keyswitch.h" #include "../../disco/metrics/generated/fd_metrics_poh.h" #include "../../disco/plugin/fd_plugin.h" -#include "../../flamenco/leaders/fd_leaders.h" +#include "../../flamenco/leaders/fd_multi_epoch_leaders.h" #include @@ -501,7 +500,7 @@ typedef struct { fd_sha256_t * sha256; - fd_stake_ci_t * stake_ci; + fd_multi_epoch_leaders_t * mleaders; /* The last sequence number of an outgoing fragment to the shred tile, or ULONG max if no such fragment. See fd_keyswitch.h for details @@ -548,6 +547,8 @@ typedef struct { ulong parent_slot; uchar parent_block_id[ 32 ]; + + uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ]; } fd_poh_ctx_t; /* The PoH recorder is implemented in Firedancer but for now needs to @@ -1136,19 +1137,7 @@ next_leader_slot( fd_poh_ctx_t * ctx ) { /* If we have published anything in a particular slot, then we should never become leader for that slot again. */ ulong min_leader_slot = fd_ulong_max( ctx->slot, fd_ulong_if( ctx->highwater_leader_slot==ULONG_MAX, 0UL, ctx->highwater_leader_slot ) ); - - for(;;) { - fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, min_leader_slot ); /* Safe to call from Rust */ - if( FD_UNLIKELY( !leaders ) ) break; - - while( min_leader_slot<(leaders->slot0+leaders->slot_cnt) ) { - fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, min_leader_slot ); /* Safe to call from Rust */ - if( FD_UNLIKELY( !memcmp( leader->key, ctx->identity_key.key, 32UL ) ) ) return min_leader_slot; - min_leader_slot++; - } - } - - return ULONG_MAX; + return fd_multi_epoch_leaders_get_next_slot( ctx->mleaders, min_leader_slot, &ctx->identity_key ); } extern int @@ -1180,7 +1169,6 @@ maybe_change_identity( fd_poh_ctx_t * ctx, } memcpy( ctx->identity_key.uc, ctx->keyswitch->bytes+32UL, 32UL ); - fd_stake_ci_set_identity( ctx->stake_ci, &ctx->identity_key ); /* When we switch key, we might have ticked part way through a slot that we are now leader in. This violates the contract of the @@ -1350,15 +1338,12 @@ fd_ext_poh_get_leader_after_n_slots( ulong n, uchar out_pubkey[ static 32 ] ) { fd_poh_ctx_t * ctx = fd_ext_poh_write_lock(); ulong slot = ctx->slot + n; - fd_epoch_leaders_t * leaders = fd_stake_ci_get_lsched_for_slot( ctx->stake_ci, slot ); /* Safe to call from Rust */ + fd_pubkey_t const * leader = fd_multi_epoch_leaders_get_leader_for_slot( ctx->mleaders, slot ); int copied = 0; - if( FD_LIKELY( leaders ) ) { - fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot ); /* Safe to call from Rust */ - if( FD_LIKELY( leader ) ) { - memcpy( out_pubkey, leader, 32UL ); - copied = 1; - } + if( FD_LIKELY( leader ) ) { + memcpy( out_pubkey, leader, 32UL ); + copied = 1; } fd_ext_poh_write_unlock(); return copied; @@ -1374,7 +1359,6 @@ scratch_footprint( fd_topo_tile_t const * tile ) { (void)tile; ulong l = FD_LAYOUT_INIT; l = FD_LAYOUT_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) ); - l = FD_LAYOUT_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); l = FD_LAYOUT_APPEND( l, FD_SHA256_ALIGN, FD_SHA256_FOOTPRINT ); return FD_LAYOUT_FINI( l, scratch_align() ); } @@ -1821,7 +1805,7 @@ during_frag( fd_poh_ctx_t * ctx, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark )); uchar const * dcache_entry = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ); - fd_stake_ci_stake_msg_init( ctx->stake_ci, dcache_entry ); + fd_multi_epoch_leaders_stake_msg_init( ctx->mleaders, fd_type_pun_const( dcache_entry ) ); return; } @@ -1957,7 +1941,7 @@ after_frag( fd_poh_ctx_t * ctx, if( FD_UNLIKELY( ctx->skip_frag ) ) return; if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_STAKE ) ) { - fd_stake_ci_stake_msg_fini( ctx->stake_ci ); + fd_multi_epoch_leaders_stake_msg_fini( ctx->mleaders ); /* It might seem like we do not need to do state transitions in and out of being the leader here, since leader schedule updates are always one epoch in advance (whether we are leader or not would @@ -2215,7 +2199,6 @@ unprivileged_init( fd_topo_t * topo, FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_poh_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) ); - void * stake_ci = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_ci_align(), fd_stake_ci_footprint() ); void * sha256 = FD_SCRATCH_ALLOC_APPEND( l, FD_SHA256_ALIGN, FD_SHA256_FOOTPRINT ); #define NONNULL( x ) (__extension__({ \ @@ -2223,8 +2206,8 @@ unprivileged_init( fd_topo_t * topo, if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \ __x; })) - ctx->stake_ci = NONNULL( fd_stake_ci_join( fd_stake_ci_new( stake_ci, &ctx->identity_key ) ) ); - ctx->sha256 = NONNULL( fd_sha256_join( fd_sha256_new( sha256 ) ) ); + ctx->mleaders = NONNULL( fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( ctx->mleaders_mem ) ) ); + ctx->sha256 = NONNULL( fd_sha256_join( fd_sha256_new( sha256 ) ) ); ctx->current_leader_bank = NULL; ctx->signal_leader_change = NULL; diff --git a/src/flamenco/leaders/Local.mk b/src/flamenco/leaders/Local.mk index 438babd11d..75b55beb85 100644 --- a/src/flamenco/leaders/Local.mk +++ b/src/flamenco/leaders/Local.mk @@ -1,6 +1,8 @@ ifdef FD_HAS_INT128 -$(call add-hdrs,fd_leaders.h) -$(call add-objs,fd_leaders,fd_flamenco) +$(call add-hdrs,fd_leaders.h fd_multi_epoch_leaders.h) +$(call add-objs,fd_leaders fd_multi_epoch_leaders,fd_flamenco) $(call make-unit-test,test_leaders,test_leaders,fd_flamenco fd_ballet fd_util) +$(call make-unit-test,test_multi_leaders,test_multi_leaders,fd_flamenco fd_ballet fd_util) $(call run-unit-test,test_leaders,) +$(call run-unit-test,test_multi_leaders) endif diff --git a/src/flamenco/leaders/fd_leaders.c b/src/flamenco/leaders/fd_leaders.c index 400b54b864..0b78ae4d93 100644 --- a/src/flamenco/leaders/fd_leaders.c +++ b/src/flamenco/leaders/fd_leaders.c @@ -39,7 +39,7 @@ fd_epoch_leaders_new( void * shmem, /* The eventual layout that we want is: struct (align=8, footprint=48) list of indices (align=4, footprint=4*ceil(slot_cnt/4)) - (up to 56 bytes of padding to align to 64) + (up to 60 bytes of padding to align to 64) list of pubkeys (align=32, footprint=32*pub_cnt) the indeterminate pubkey (align=32, footprint=32) (possibly 32 bytes of padding to align to 64) diff --git a/src/flamenco/leaders/fd_leaders.h b/src/flamenco/leaders/fd_leaders.h index 927c801105..ecf05d871e 100644 --- a/src/flamenco/leaders/fd_leaders.h +++ b/src/flamenco/leaders/fd_leaders.h @@ -27,13 +27,10 @@ dedup pubkeys into a lookup table and only store an index for each rotation. */ -#include "../fd_flamenco_base.h" +#include "fd_leaders_base.h" #include "../types/fd_types.h" #include "../../ballet/wsample/fd_wsample.h" -#define MAX_SLOTS_CNT 432000UL -#define MAX_PUB_CNT 50000UL - #define FD_ULONG_MAX( a, b ) (__builtin_choose_expr( __builtin_constant_p( a ) & __builtin_constant_p( b ), \ ((ulong )(a))>=((ulong )(b)) ? ((ulong )(a)) : ((ulong )(b)), \ fd_ulong_max( (a), (b) ) )) diff --git a/src/flamenco/leaders/fd_leaders_base.h b/src/flamenco/leaders/fd_leaders_base.h new file mode 100644 index 0000000000..b81b90b7c8 --- /dev/null +++ b/src/flamenco/leaders/fd_leaders_base.h @@ -0,0 +1,22 @@ +#ifndef HEADER_fd_src_flamenco_leaders_fd_leaders_base_h +#define HEADER_fd_src_flamenco_leaders_fd_leaders_base_h + +#include "../fd_flamenco_base.h" +#include "../types/fd_types.h" + +#define MAX_SLOTS_PER_EPOCH 432000UL +#define MAX_PUB_CNT 50000UL +#define MAX_STAKED_LEADERS 40200UL + +/* Follows message structure in fd_stake_ci_stake_msg_init */ +struct fd_stake_weight_msg_t { + ulong epoch; /* Epoch for which the stake weights are valid */ + ulong staked_cnt; /* Number of staked nodes */ + ulong start_slot; /* Start slot of the epoch */ + ulong slot_cnt; /* Number of slots in the epoch */ + ulong excluded_stake; /* Total stake that is excluded from leader selection */ + fd_stake_weight_t weights[]; /* Stake weights for each staked node */ +}; +typedef struct fd_stake_weight_msg_t fd_stake_weight_msg_t; + +#endif /* HEADER_fd_src_flamenco_leaders_fd_leaders_base_h */ diff --git a/src/flamenco/leaders/fd_multi_epoch_leaders.c b/src/flamenco/leaders/fd_multi_epoch_leaders.c new file mode 100644 index 0000000000..8ad34f5121 --- /dev/null +++ b/src/flamenco/leaders/fd_multi_epoch_leaders.c @@ -0,0 +1,149 @@ +#include "fd_multi_epoch_leaders.h" + +void * +fd_multi_epoch_leaders_new( void * shmem ) { + if( FD_UNLIKELY( !shmem ) ) { + FD_LOG_WARNING(( "NULL shmem" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_multi_epoch_leaders_align() ) ) ) { + FD_LOG_WARNING(( "misaligned shmem" )); + return NULL; + } + + fd_multi_epoch_leaders_t * leaders = (fd_multi_epoch_leaders_t *)shmem; + + /* Initialize all epochs to satisfy invariants */ + fd_stake_weight_t dummy_stakes[ 1 ] = {{ .key = {{0}}, .stake = 1UL }}; + for( ulong i=0UL; ilsched[i] = fd_epoch_leaders_join( fd_epoch_leaders_new( leaders->_lsched[i], i, 0UL, 1UL, 1UL, dummy_stakes, 0UL ) ); + FD_TEST( leaders->lsched[i] ); + leaders->init_done[i] = 0; + } + + return shmem; +} + +fd_multi_epoch_leaders_t * +fd_multi_epoch_leaders_join( void * shleaders ) { return shleaders; } + +void * +fd_multi_epoch_leaders_leave( fd_multi_epoch_leaders_t * mleaders ) { return mleaders; } + +void * +fd_multi_epoch_leaders_delete( void * shleaders ) { return shleaders; } + +fd_epoch_leaders_t const * +fd_multi_epoch_leaders_get_lsched_for_epoch( fd_multi_epoch_leaders_t const * mleaders, + ulong epoch ) { + fd_epoch_leaders_t const * even_lsched = fd_ptr_if( mleaders->init_done[0] & !!(mleaders->lsched[0]->epoch==epoch), mleaders->lsched[0], NULL ); + fd_epoch_leaders_t const * odd_lsched = fd_ptr_if( mleaders->init_done[1] & !!(mleaders->lsched[1]->epoch==epoch), mleaders->lsched[1], NULL ); + return fd_ptr_if( !!even_lsched, even_lsched, odd_lsched ); +} + +static inline ulong +fd_multi_epoch_leaders_get_epoch_idx( fd_multi_epoch_leaders_t const * mleaders, + ulong slot ) { + fd_epoch_leaders_t const * even_lsched = mleaders->lsched[0]; + fd_epoch_leaders_t const * odd_lsched = mleaders->lsched[1]; + + ulong even_match = fd_ulong_if( mleaders->init_done[0] & !!(even_lsched->slot0<=slot) & !!(slotslot0+even_lsched->slot_cnt), 0UL, ULONG_MAX ); + ulong odd_match = fd_ulong_if( mleaders->init_done[1] & !!(odd_lsched->slot0<=slot) & !!(slotslot0+odd_lsched->slot_cnt), 1UL, ULONG_MAX ); + + return fd_ulong_if( even_match!=ULONG_MAX, even_match, odd_match ); +} + +fd_epoch_leaders_t const * +fd_multi_epoch_leaders_get_lsched_for_slot( fd_multi_epoch_leaders_t const * mleaders, + ulong slot ) { + const ulong epoch_idx = fd_multi_epoch_leaders_get_epoch_idx( mleaders, slot ); + if( FD_UNLIKELY( epoch_idx==ULONG_MAX ) ) return NULL; + return mleaders->lsched[epoch_idx]; +} + +ulong +fd_multi_epoch_leaders_get_next_slot( fd_multi_epoch_leaders_t const * mleaders, + ulong start_slot, + fd_pubkey_t const * leader_q ) { + + /* Find epoch containing start_slot */ + ulong epoch_idx = fd_multi_epoch_leaders_get_epoch_idx( mleaders, start_slot ); + if( FD_UNLIKELY( epoch_idx==ULONG_MAX ) ) return ULONG_MAX; + + /* Find the leader in the epoch */ + fd_epoch_leaders_t const * epoch_lsched = mleaders->lsched[ epoch_idx ]; + ulong slot0 = epoch_lsched->slot0; + ulong slot_end = slot0 + epoch_lsched->slot_cnt; + + for( ulong slot=start_slot; slotkey, leader_q->key, 32UL ) ) ) return slot; + } + + return ULONG_MAX; +} + +void +fd_multi_epoch_leaders_stake_msg_init( fd_multi_epoch_leaders_t * mleaders, + fd_stake_weight_msg_t const * msg ) { + if( FD_UNLIKELY( msg->staked_cnt > MAX_STAKED_LEADERS ) ) + FD_LOG_ERR(( "Multi-epoch leaders received a malformed update with %lu stakes in it," + " but the maximum allowed is %lu", msg->staked_cnt, MAX_STAKED_LEADERS )); + + mleaders->scratch->epoch = msg->epoch; + mleaders->scratch->start_slot = msg->start_slot; + mleaders->scratch->slot_cnt = msg->slot_cnt; + mleaders->scratch->staked_cnt = msg->staked_cnt; + mleaders->scratch->excluded_stake = msg->excluded_stake; + + fd_memcpy( mleaders->stake_weight, msg->weights, msg->staked_cnt*sizeof(fd_stake_weight_t) ); +} + +void +fd_multi_epoch_leaders_stake_msg_fini( fd_multi_epoch_leaders_t * mleaders ) { + const ulong epoch = mleaders->scratch->epoch; + const ulong slot0 = mleaders->scratch->start_slot; + const ulong slot_cnt = mleaders->scratch->slot_cnt; + const ulong pub_cnt = mleaders->scratch->staked_cnt; + const ulong excluded_stake = mleaders->scratch->excluded_stake; + const ulong epoch_idx = epoch % MULTI_EPOCH_LEADERS_EPOCH_CNT; + + fd_stake_weight_t const * stakes = mleaders->stake_weight; + + /* Clear old data */ + fd_epoch_leaders_delete( fd_epoch_leaders_leave( mleaders->lsched[epoch_idx] ) ); + + /* Populate new lsched */ + uchar * lsched_mem = mleaders->_lsched[epoch_idx]; + mleaders->lsched[epoch_idx] = fd_epoch_leaders_join( fd_epoch_leaders_new( + lsched_mem, epoch, slot0, slot_cnt, + pub_cnt, stakes, excluded_stake ) ); + mleaders->init_done[epoch_idx] = 1; +} + +fd_pubkey_t const * +fd_multi_epoch_leaders_get_leader_for_slot( fd_multi_epoch_leaders_t const * mleaders, + ulong slot ) { + const ulong epoch_idx = fd_multi_epoch_leaders_get_epoch_idx( mleaders, slot ); + if( FD_UNLIKELY( epoch_idx==ULONG_MAX ) ) return NULL; + return fd_epoch_leaders_get( mleaders->lsched[epoch_idx], slot ); +} + +fd_multi_epoch_leaders_lsched_sorted_t +fd_multi_epoch_leaders_get_sorted_lscheds( fd_multi_epoch_leaders_t const * mleaders ) { + fd_multi_epoch_leaders_lsched_sorted_t ret = { .lscheds = { NULL, NULL } }; + fd_epoch_leaders_t * even_option = fd_ptr_if( mleaders->init_done[0], mleaders->lsched[0], NULL ); + fd_epoch_leaders_t * odd_option = fd_ptr_if( mleaders->init_done[1], mleaders->lsched[1], NULL ); + + /* Sort by epoch if both non-null, null comes first */ + if( even_option && odd_option ) { + ret.lscheds[0] = fd_ptr_if( even_option->epoch < odd_option->epoch, even_option, odd_option ); + ret.lscheds[1] = fd_ptr_if( even_option->epoch < odd_option->epoch, odd_option, even_option ); + } else { + /* if one non-null, this will pick it up. Else, both null and this no-ops */ + ret.lscheds[0] = fd_ptr_if( !!even_option, even_option, odd_option ); + } + + return ret; +} diff --git a/src/flamenco/leaders/fd_multi_epoch_leaders.h b/src/flamenco/leaders/fd_multi_epoch_leaders.h new file mode 100644 index 0000000000..d6dbc99466 --- /dev/null +++ b/src/flamenco/leaders/fd_multi_epoch_leaders.h @@ -0,0 +1,170 @@ +#ifndef HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h +#define HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h + +#include "fd_leaders.h" + +/* fd_multi_epoch_leaders is a wrapper around multiple fd_epoch_leaders + objects. It simplifies tracking leader schedules for multiple epochs, + and querying to find the leader for a given slot. While maintaining + the leader schedule for the current epoch i, you can also prepare the + schedule for epoch i+1 and send to the next epoch's leader as you + approach the boundary. */ + +typedef uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) + _lsched_t[FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKED_LEADERS, MAX_SLOTS_PER_EPOCH)]; + +#define MULTI_EPOCH_LEADERS_EPOCH_CNT (2UL) +FD_STATIC_ASSERT(MULTI_EPOCH_LEADERS_EPOCH_CNT == 2UL, "This implementation depends on epoch_cnt==2"); + +struct fd_multi_epoch_leaders_priv { + fd_epoch_leaders_t * lsched [ MULTI_EPOCH_LEADERS_EPOCH_CNT ]; + fd_stake_weight_t stake_weight [ MAX_STAKED_LEADERS ]; + + /* has that epoch's mem experienced a stake_msg_fini? */ + int init_done [ MULTI_EPOCH_LEADERS_EPOCH_CNT ]; + struct { + ulong epoch; + ulong start_slot; + ulong slot_cnt; + ulong staked_cnt; + ulong excluded_stake; + } scratch[1]; + + _lsched_t _lsched[MULTI_EPOCH_LEADERS_EPOCH_CNT]; +}; +typedef struct fd_multi_epoch_leaders_priv fd_multi_epoch_leaders_priv_t; + +typedef fd_multi_epoch_leaders_priv_t fd_multi_epoch_leaders_t; + + +FD_PROTOTYPES_BEGIN + +/* ******** OBJECT LIFECYCLE FUNCTIONS ******** */ + +/* fd_epoch_leaders_{align,footprint} describe the required footprint + and alignment of the leader schedule object. They have compile friendly + versions for static allocation of underlying mem */ + +#define FD_MULTI_EPOCH_LEADERS_ALIGN \ + FD_ULONG_MAX( FD_EPOCH_LEADERS_ALIGN, alignof(fd_multi_epoch_leaders_t) ) + +#define FD_MULTI_EPOCH_LEADERS_FOOTPRINT \ + sizeof(fd_multi_epoch_leaders_t) + +FD_FN_CONST static inline ulong +fd_multi_epoch_leaders_align( void ) { + return FD_MULTI_EPOCH_LEADERS_ALIGN; +} + +FD_FN_CONST static inline ulong +fd_multi_epoch_leaders_footprint( void ) { + return FD_MULTI_EPOCH_LEADERS_FOOTPRINT; +} + +/* fd_multi_epoch_leaders_new formats a memory region for use as a multi-epoch + leader schedule object. shmem points to the first byte of a memory + region with matching alignment and footprint requirements. Returns NULL + if shmem is NULL or misaligned. Else returns pointer to formatted memory. + Does not join. */ + +void * +fd_multi_epoch_leaders_new( void * shmem ); + +/* fd_multi_epoch_leaders_join joins the caller to the leader schedule object. + fd_multi_epoch_leaders_leave undoes an existing join. */ + +fd_multi_epoch_leaders_t * +fd_multi_epoch_leaders_join( void * shleaders ); + +void * +fd_multi_epoch_leaders_leave( fd_multi_epoch_leaders_t * mleaders ); + +/* fd_multi_epoch_leaders_delete unformats a memory region and returns owner- + ship back to the caller. */ + +void * +fd_multi_epoch_leaders_delete( void * shleaders ); + +/* ******** LEADER INFO GETTER FUNCTIONS ******** */ + +/* fd_multi_epoch_leaders_get_leader_for_slot returns a pointer to the selected + public key given a slot. Returns NULL if slot is not in epochs tracked + by multi-epoch leader object. If the leader for slot is part of the + excluded_stake for that epoch, instead of returning the correct value + (which is not known), returns a pointer to a pubkey with value + FD_INDETERMINATE_LEADER. */ + +FD_FN_PURE fd_pubkey_t const * +fd_multi_epoch_leaders_get_leader_for_slot( fd_multi_epoch_leaders_t const * mleaders, + ulong slot ); + +/* fd_multi_epoch_leaders_get_lsched_for_{epoch,slot} return the leader + schedule for epoch or epoch containing slot, respectively. Returns + NULL if not tracked by mleaders. */ + +FD_FN_PURE fd_epoch_leaders_t const * +fd_multi_epoch_leaders_get_lsched_for_epoch( fd_multi_epoch_leaders_t const * mleaders, + ulong epoch ); +FD_FN_PURE fd_epoch_leaders_t const * +fd_multi_epoch_leaders_get_lsched_for_slot( fd_multi_epoch_leaders_t const * mleaders, + ulong slot ); + +/* fd_multi_epoch_leaders_get_sorted_lscheds returns up to two lscheds, + sorted in increasing epoch order. If we only have data for one epoch, + the first element will be the corresponding lsched. If no lsched data, + both will be null. Lifetime of returned pointers is until next call to + fd_multi_epoch_leaders_stake_msg_fini. */ +typedef struct { + fd_epoch_leaders_t const * lscheds[2]; +} fd_multi_epoch_leaders_lsched_sorted_t; + +FD_FN_PURE fd_multi_epoch_leaders_lsched_sorted_t +fd_multi_epoch_leaders_get_sorted_lscheds( fd_multi_epoch_leaders_t const * mleaders ); + + +/* fd_multi_epoch_leaders_get_next_slot returns the first slot on or after + start_slot that 'leader' will be leader. It only checks the epoch containing + start_slot. If it can't find one, returns ULONG_MAX. + + Failures cases include: + - mleaders does not track the epoch containing start_slot + - It was either never initialized with that epoch information, or + - It was overwritten by another epoch with the same parity + - leader_q does not have a leader slot in the epoch containing start_slot + - leader_q was part of the excluded_stake for that epoch, and the lsched + returns FD_INDETERMINATE_LEADER as the leader for leader_q's slots. +*/ + +FD_FN_PURE ulong +fd_multi_epoch_leaders_get_next_slot( fd_multi_epoch_leaders_t const * mleaders, + ulong start_slot, + fd_pubkey_t const * leader_q ); + +/* ******** STAKE INFO UPDATE METHODS ******** */ + +/* fd_stake_ci_stake_msg_{init, fini} are used to handle messages + containing stake weight updates from the Rust side of the splice,. + Since these messages arrive on a dcache and can get overrun, both + expose a init/fini model. Calling init multiple times without calling + fini will not leak any resources. + + msg should be a pointer to the first byte of the dcache entry + containing the stakes update. msg will be accessed + msg->weights[i] for i in [0, msg->staked_cnt). msg->weights + must contain at least one staked pubkey, and the pubkeys must be + sorted in the usual way (by stake descending, ties broken by pubkey + ascending). multi_epoch_leaders will only use the staked node. + + init does not maintain a read interest in msg after returning. */ + +void +fd_multi_epoch_leaders_stake_msg_init( fd_multi_epoch_leaders_t * mleaders, + fd_stake_weight_msg_t const * msg ); + +void +fd_multi_epoch_leaders_stake_msg_fini( fd_multi_epoch_leaders_t * mleaders ); + + +FD_PROTOTYPES_END + +#endif /* HEADER_fd_src_flamenco_leaders_fd_multi_epoch_leaders_h */ diff --git a/src/flamenco/leaders/test_multi_leaders.c b/src/flamenco/leaders/test_multi_leaders.c new file mode 100644 index 0000000000..610a3ab568 --- /dev/null +++ b/src/flamenco/leaders/test_multi_leaders.c @@ -0,0 +1,315 @@ +#include "fd_multi_epoch_leaders.h" + +FD_STATIC_ASSERT( alignof(fd_multi_epoch_leaders_t)<=FD_MULTI_EPOCH_LEADERS_ALIGN, alignment ); + +static uchar mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ] + __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))); + +#define SLOTS_PER_EPOCH 1000 /* Just for testing */ +#define STAKE_MSG_SZ ( 40UL + MAX_STAKED_LEADERS * 40UL ) /* for testing, at most 16 nodes */ +uchar stake_msg[ STAKE_MSG_SZ ]; + +static fd_stake_weight_msg_t * +generate_stake_msg( uchar * _buf, + ulong epoch, + char const * stakers ) { + fd_stake_weight_msg_t *buf = fd_type_pun( _buf ); + + buf->epoch = epoch; + buf->start_slot = epoch * SLOTS_PER_EPOCH; + buf->slot_cnt = SLOTS_PER_EPOCH; + buf->staked_cnt = strlen(stakers); + buf->excluded_stake = 0UL; + + ulong i = 0UL; + for(; *stakers; stakers++, i++ ) { + memset( buf->weights[i].key.uc, *stakers, sizeof(fd_pubkey_t) ); + buf->weights[i].stake = 1000UL/(i+1UL); + } + return fd_type_pun( _buf ); +} + +static void +check_leaders( fd_multi_epoch_leaders_t const * mleaders, + ulong epoch, + char const * staked_leaders ) { + ulong min_slot = epoch * SLOTS_PER_EPOCH; + ulong max_slot = (epoch + 1UL) * SLOTS_PER_EPOCH - 1UL; + + fd_epoch_leaders_t const * lsched = fd_multi_epoch_leaders_get_lsched_for_slot( mleaders, min_slot ); + + if( !staked_leaders ) { + FD_TEST( !lsched ); + return; + } + + FD_TEST( lsched ); + FD_TEST( lsched == fd_multi_epoch_leaders_get_lsched_for_slot( mleaders, max_slot ) ); + FD_TEST( !fd_epoch_leaders_get( lsched, min_slot-1UL ) ); + FD_TEST( !fd_epoch_leaders_get( lsched, max_slot+1UL ) ); + + ulong leader_cnt[ 26 ]={ 0UL }; + for( ulong s=min_slot; s<=max_slot; s++ ) { + fd_pubkey_t const * leader = fd_multi_epoch_leaders_get_leader_for_slot( mleaders, s ); + FD_TEST( leader ); + ulong c = (ulong)leader->uc[ 0 ] - (ulong)'A'; + leader_cnt[ c ]++; + } + + ulong unaccounted = max_slot-min_slot+1UL; + for( char const * c=staked_leaders; *c; c++ ) { + /* The stake distribution this test uses is such that given the + large number of slots per epoch and small number of validators, + with high probability every staked validator will get at least + one leader slot. */ + FD_TEST( leader_cnt[ *c-'A' ] ); + unaccounted -= leader_cnt[ *c-'A' ]; + } + FD_TEST( unaccounted==0UL ); +} + +static void +test_staked_only( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABC" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 1UL, "ABCDE" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); + check_leaders( mleaders, 1UL, "ABCDE" ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 2UL, "ABCF" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 2UL, "ABCF" ); + check_leaders( mleaders, 1UL, "ABCDE" ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 3UL, "I" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 2UL, "ABCF" ); + check_leaders( mleaders, 3UL, "I" ); + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_transitions( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABCD" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABCD" ); + + /* Transition to different set */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 1UL, "ABCDEF" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABCD" ); + check_leaders( mleaders, 1UL, "ABCDEF" ); + + /* Transition them back */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 2UL, "AB" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 1UL, "ABCDEF" ); + check_leaders( mleaders, 2UL, "AB" ); + + /* Completely swap */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 3UL, "GI" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 2UL, "AB" ); + check_leaders( mleaders, 3UL, "GI" ); + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_skip_ahead( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABC" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); + + /* Skip ahead several epochs */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 5UL, "DEF" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); /* Should remain because diff parity */ + check_leaders( mleaders, 1UL, NULL ); + check_leaders( mleaders, 4UL, NULL ); + check_leaders( mleaders, 5UL, "DEF" ); + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_cancel( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABC" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); + + /* Start init but don't finish */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 1UL, "DEF" ) ); + /* Don't call fini */ + + /* Start another init - should cancel the previous one */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 1UL, "GHI" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + check_leaders( mleaders, 0UL, "ABC" ); + check_leaders( mleaders, 1UL, "GHI" ); /* Should be GHI, not DEF */ + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_ordering( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABC" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 1UL, "BCA" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + check_leaders( mleaders, 0UL, "ABC" ); + check_leaders( mleaders, 1UL, "BCA" ); + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_next_slot( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABC" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + fd_epoch_leaders_t const * lsched = fd_multi_epoch_leaders_get_lsched_for_epoch( mleaders, 0UL ); + FD_TEST( lsched ); + FD_TEST( lsched->slot0 == 0UL ); + FD_TEST( lsched->slot_cnt == SLOTS_PER_EPOCH ); + + /* Test finding next slot for each leader */ + fd_pubkey_t test_key; + for( char leader='A'; leader<'D'; leader++ ) { + memset( test_key.uc, leader, sizeof(fd_pubkey_t) ); + ulong next_slot = fd_multi_epoch_leaders_get_next_slot( mleaders, 0UL, &test_key ); + FD_TEST( next_slot >= lsched->slot0 ); + FD_TEST( next_slot < lsched->slot0 + lsched->slot_cnt ); + FD_TEST( fd_multi_epoch_leaders_get_leader_for_slot( mleaders, next_slot )->uc[0] == leader ); + } + + /* Test with non-existent leader */ + memset( test_key.uc, 'Z', sizeof(fd_pubkey_t) ); + ulong next_slot = fd_multi_epoch_leaders_get_next_slot( mleaders, 0UL, &test_key ); + FD_TEST( next_slot == ULONG_MAX ); + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_limits( void ) { + /* Test with maximum number of staked leaders */ + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + for( ulong stake_weight_cnt=MAX_STAKED_LEADERS-2; stake_weight_cnt<=MAX_STAKED_LEADERS+2; stake_weight_cnt++ ) { + fd_stake_weight_msg_t * buf = fd_type_pun( stake_msg ); + buf->epoch = stake_weight_cnt; + buf->start_slot = stake_weight_cnt * SLOTS_PER_EPOCH; + buf->slot_cnt = SLOTS_PER_EPOCH; + buf->staked_cnt = 0UL; + buf->excluded_stake = 0UL; + + for( ulong i=0UL; iweights[i].key.uc, 127-((int)i%96), sizeof(fd_pubkey_t) ); + FD_STORE( ulong, buf->weights[i].key.uc, fd_ulong_bswap( i ) ); + buf->weights[i].stake = stake; + buf->staked_cnt++; + } else { + buf->excluded_stake += stake; + } + } + fd_multi_epoch_leaders_stake_msg_init( mleaders, buf ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + FD_TEST( fd_multi_epoch_leaders_get_lsched_for_slot( mleaders, stake_weight_cnt*SLOTS_PER_EPOCH ) ); + } + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +static void +test_get_sorted_lscheds( void ) { + fd_multi_epoch_leaders_t * mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders_mem ) ); + + /* No epochs initialized - both should be NULL */ + fd_multi_epoch_leaders_lsched_sorted_t result = fd_multi_epoch_leaders_get_sorted_lscheds( mleaders ); + FD_TEST( result.lscheds[0] == NULL ); + FD_TEST( result.lscheds[1] == NULL ); + + /* Initialize one epoch (epoch 0) */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 0UL, "ABC" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + result = fd_multi_epoch_leaders_get_sorted_lscheds( mleaders ); + FD_TEST( result.lscheds[0] != NULL ); + FD_TEST( result.lscheds[0]->epoch == 0UL ); + FD_TEST( result.lscheds[1] == NULL ); + + /* Initialize second epoch (epoch 1) - should be sorted by epoch */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 1UL, "DEF" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + result = fd_multi_epoch_leaders_get_sorted_lscheds( mleaders ); + FD_TEST( result.lscheds[0] != NULL ); + FD_TEST( result.lscheds[1] != NULL ); + FD_TEST( result.lscheds[0]->epoch == 0UL ); + FD_TEST( result.lscheds[1]->epoch == 1UL ); + + /* Initialize epoch 3 (overwrites epoch 1) - should maintain sorting */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 3UL, "GHI" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + result = fd_multi_epoch_leaders_get_sorted_lscheds( mleaders ); + FD_TEST( result.lscheds[0] != NULL ); + FD_TEST( result.lscheds[1] != NULL ); + FD_TEST( result.lscheds[0]->epoch == 0UL ); + FD_TEST( result.lscheds[1]->epoch == 3UL ); + + /* Initialize a much higher epoch (epoch 10) to test large gap */ + fd_multi_epoch_leaders_stake_msg_init( mleaders, generate_stake_msg( stake_msg, 10UL, "MNO" ) ); + fd_multi_epoch_leaders_stake_msg_fini( mleaders ); + + result = fd_multi_epoch_leaders_get_sorted_lscheds( mleaders ); + FD_TEST( result.lscheds[0] != NULL ); + FD_TEST( result.lscheds[1] != NULL ); + /* Now we have epoch 3 (odd parity) and epoch 10 (even parity) */ + FD_TEST( result.lscheds[0]->epoch == 3UL ); /* epoch 3 is smaller */ + FD_TEST( result.lscheds[1]->epoch == 10UL ); /* epoch 10 is larger */ + + fd_multi_epoch_leaders_delete( fd_multi_epoch_leaders_leave( mleaders ) ); +} + +int +main( int argc, + char ** argv ) { + fd_boot( &argc, &argv ); + + test_staked_only(); + test_transitions(); + test_skip_ahead(); + test_cancel(); + test_ordering(); + test_next_slot(); + test_limits(); + test_get_sorted_lscheds(); + + FD_LOG_NOTICE(( "pass" )); + fd_halt(); + return 0; +} diff --git a/src/flamenco/repair/fd_repair.c b/src/flamenco/repair/fd_repair.c index 617c8beeac..293f5c9268 100644 --- a/src/flamenco/repair/fd_repair.c +++ b/src/flamenco/repair/fd_repair.c @@ -26,6 +26,8 @@ fd_repair_new ( void * shmem, ulong seed ) { shm = FD_SCRATCH_ALLOC_APPEND( l, fd_pinged_table_align(), fd_pinged_table_footprint(FD_REPAIR_PINGED_MAX) ); glob->pinged = fd_pinged_table_join(fd_pinged_table_new(shm, FD_REPAIR_PINGED_MAX, seed)); glob->stake_weights = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stake_weight_t), FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) ); + glob->stake_weights_temp = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stake_weight_t), FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) ); + glob->stake_weights_temp_cnt = 0; glob->stake_weights_cnt = 0; glob->last_decay = 0; glob->last_print = 0; @@ -497,9 +499,9 @@ void fd_repair_add_sticky( fd_repair_t * glob, fd_pubkey_t const * id ) { } void -fd_repair_set_stake_weights( fd_repair_t * repair, - fd_stake_weight_t const * stake_weights, - ulong stake_weights_cnt ) { +fd_repair_set_stake_weights_init( fd_repair_t * repair, + fd_stake_weight_t const * stake_weights, + ulong stake_weights_cnt ) { if( stake_weights == NULL ) { FD_LOG_ERR(( "stake weights NULL" )); } @@ -507,9 +509,14 @@ fd_repair_set_stake_weights( fd_repair_t * repair, FD_LOG_ERR(( "too many stake weights" )); } - fd_memset( repair->stake_weights, 0, FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) ); - fd_memcpy( repair->stake_weights, stake_weights, stake_weights_cnt * sizeof(fd_stake_weight_t) ); - repair->stake_weights_cnt = stake_weights_cnt; + fd_memcpy( repair->stake_weights_temp, stake_weights, stake_weights_cnt * sizeof(fd_stake_weight_t) ); + repair->stake_weights_temp_cnt = stake_weights_cnt; +} + +void +fd_repair_set_stake_weights_fini( fd_repair_t * repair ) { + fd_swap( repair->stake_weights, repair->stake_weights_temp ); + repair->stake_weights_cnt = repair->stake_weights_temp_cnt; } diff --git a/src/flamenco/repair/fd_repair.h b/src/flamenco/repair/fd_repair.h index aa010c7cbf..348df9b263 100644 --- a/src/flamenco/repair/fd_repair.h +++ b/src/flamenco/repair/fd_repair.h @@ -232,6 +232,8 @@ struct fd_repair { /* Stake weights */ ulong stake_weights_cnt; fd_stake_weight_t * stake_weights; + ulong stake_weights_temp_cnt; + fd_stake_weight_t * stake_weights_temp; /* Path to the file where we write the cache of known good repair peers, to make cold booting faster */ int good_peer_cache_file_fd; /* Metrics */ @@ -249,6 +251,8 @@ fd_repair_footprint( void ) { l = FD_LAYOUT_APPEND( l, fd_active_table_align(), fd_active_table_footprint(FD_ACTIVE_KEY_MAX) ); l = FD_LAYOUT_APPEND( l, fd_inflight_table_align(), fd_inflight_table_footprint(FD_NEEDED_KEY_MAX) ); l = FD_LAYOUT_APPEND( l, fd_pinged_table_align(), fd_pinged_table_footprint(FD_REPAIR_PINGED_MAX) ); + /* regular and temp stake weights */ + l = FD_LAYOUT_APPEND( l, alignof(fd_stake_weight_t), FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) ); l = FD_LAYOUT_APPEND( l, alignof(fd_stake_weight_t), FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) ); return FD_LAYOUT_FINI(l, fd_repair_align() ); } @@ -315,11 +319,11 @@ fd_repair_construct_request_protocol( fd_repair_t * glob, void fd_repair_add_sticky( fd_repair_t * glob, fd_pubkey_t const * id ); -void fd_repair_set_stake_weights( fd_repair_t * repair, - fd_stake_weight_t const * stake_weights, - ulong stake_weights_cnt ); - +void fd_repair_set_stake_weights_init( fd_repair_t * repair, + fd_stake_weight_t const * stake_weights, + ulong stake_weights_cnt ); +void fd_repair_set_stake_weights_fini( fd_repair_t * repair ); fd_repair_metrics_t * fd_repair_get_metrics( fd_repair_t * repair ); diff --git a/src/flamenco/runtime/context/fd_exec_epoch_ctx.c b/src/flamenco/runtime/context/fd_exec_epoch_ctx.c index f7ba385bc6..843da1920c 100644 --- a/src/flamenco/runtime/context/fd_exec_epoch_ctx.c +++ b/src/flamenco/runtime/context/fd_exec_epoch_ctx.c @@ -23,7 +23,7 @@ fd_exec_epoch_ctx_footprint_ext( fd_exec_epoch_ctx_layout_t * layout, ulong stake_votes_sz = fd_vote_accounts_pair_t_map_footprint( vote_acc_max ); if( !stake_votes_sz ) return 0UL; ulong stake_delegations_sz = fd_delegation_pair_t_map_footprint ( vote_acc_max ); if( !stake_delegations_sz ) return 0UL; ulong next_epoch_stakes_sz = fd_vote_accounts_pair_t_map_footprint( vote_acc_max ); if( !next_epoch_stakes_sz ) return 0UL; - ulong leaders_sz = fd_epoch_leaders_footprint( MAX_PUB_CNT, MAX_SLOTS_CNT ); if( !leaders_sz ) FD_LOG_CRIT(( "invalid fd_epoch_leaders footprint" )); + ulong leaders_sz = fd_epoch_leaders_footprint( MAX_PUB_CNT, MAX_SLOTS_PER_EPOCH ); if( !leaders_sz ) FD_LOG_CRIT(( "invalid fd_epoch_leaders footprint" )); FD_SCRATCH_ALLOC_INIT( l, 0 ); FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_epoch_ctx_t), sizeof(fd_exec_epoch_ctx_t) ); @@ -220,7 +220,7 @@ fd_exec_epoch_ctx_bank_mem_setup( fd_exec_epoch_ctx_t * self ) { fd_vote_accounts_pair_t_map_join( fd_vote_accounts_pair_t_map_new( next_epoch_stakes_mem, layout->vote_acc_max ) ); //TODO support separate epoch leaders new and init - //fd_epoch_leaders_new ( leaders_mem, MAX_PUB_CNT, MAX_SLOTS_CNT ); + //fd_epoch_leaders_new ( leaders_mem, MAX_PUB_CNT, MAX_SLOTS_PER_EPOCH ); return epoch_bank; } @@ -247,7 +247,7 @@ fd_exec_epoch_ctx_from_prev( fd_exec_epoch_ctx_t * self, fd_bincode_encode_ctx_t encode = {.data = buf, .dataend = buf + sz }; fd_epoch_bank_encode( old_epoch_bank, &encode ); - sz = fd_ulong_align_up( fd_epoch_leaders_footprint( MAX_PUB_CNT, MAX_SLOTS_CNT ), fd_epoch_leaders_align() ); + sz = fd_ulong_align_up( fd_epoch_leaders_footprint( MAX_PUB_CNT, MAX_SLOTS_PER_EPOCH ), fd_epoch_leaders_align() ); fd_memcpy( fd_exec_epoch_ctx_leaders( self ), fd_exec_epoch_ctx_leaders( prev ), sz ); } FD_SPAD_FRAME_END; diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index 4e4ff0955d..e71c95780d 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -188,7 +188,7 @@ fd_runtime_update_leaders( fd_exec_slot_ctx_t * slot_ctx, if( FD_UNLIKELY( stake_weight_cnt>MAX_PUB_CNT ) ) { FD_LOG_ERR(( "Stake weight count exceeded max" )); } - if( FD_UNLIKELY( slot_cnt>MAX_SLOTS_CNT ) ) { + if( FD_UNLIKELY( slot_cnt>MAX_SLOTS_PER_EPOCH ) ) { FD_LOG_ERR(( "Slot count exceeeded max" )); }