diff --git a/config/extra/with-handholding.mk b/config/extra/with-handholding.mk index c0c89ffcc6f..fb23ffeff0b 100644 --- a/config/extra/with-handholding.mk +++ b/config/extra/with-handholding.mk @@ -10,3 +10,4 @@ CPPFLAGS+=-DFD_FUNK_HANDHOLDING=1 CPPFLAGS+=-DFD_RUNTIME_ERR_HANDHOLDING=1 CPPFLAGS+=-DFD_FOREST_USE_HANDHOLDING=1 CPPFLAGS+=-DFD_STAKES_USE_HANDHOLDING=1 +CPPFLAGS+=-DFD_VOTE_STATES_USE_HANDHOLDING=1 diff --git a/src/discof/replay/fd_exec.h b/src/discof/replay/fd_exec.h index d533a17b4b6..e07faadd568 100644 --- a/src/discof/replay/fd_exec.h +++ b/src/discof/replay/fd_exec.h @@ -18,11 +18,11 @@ static inline ulong generate_stake_weight_msg( fd_exec_slot_ctx_t * slot_ctx, ulong epoch, - fd_vote_accounts_global_t const * vote_accounts, + fd_vote_states_t const * vote_states, ulong * stake_weight_msg_out ) { fd_stake_weight_msg_t * stake_weight_msg = (fd_stake_weight_msg_t *)fd_type_pun( stake_weight_msg_out ); fd_vote_stake_weight_t * stake_weights = stake_weight_msg->weights; - ulong staked_cnt = fd_stake_weights_by_node( vote_accounts, stake_weights ); + ulong staked_cnt = fd_stake_weights_by_node( vote_states, stake_weights ); fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank ); stake_weight_msg->epoch = epoch; diff --git a/src/discof/replay/fd_replay_tile.c b/src/discof/replay/fd_replay_tile.c index 42b8db06054..ca3da7f683d 100644 --- a/src/discof/replay/fd_replay_tile.c +++ b/src/discof/replay/fd_replay_tile.c @@ -358,33 +358,29 @@ publish_stake_weights( fd_replay_tile_ctx_t * ctx, fd_exec_slot_ctx_t * slot_ctx ) { fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank ); - fd_vote_accounts_global_t const * epoch_stakes = fd_bank_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( epoch_stakes ); - - if( epoch_stakes_root!=NULL ) { + fd_vote_states_t const * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( slot_ctx->bank ); + do { ulong * stake_weights_msg = fd_chunk_to_laddr( ctx->stake_out->mem, ctx->stake_out->chunk ); - ulong epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, fd_bank_slot_get( slot_ctx->bank ) ); - ulong stake_weights_sz = generate_stake_weight_msg( slot_ctx, epoch - 1, epoch_stakes, stake_weights_msg ); - ulong stake_weights_sig = 4UL; + ulong epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, fd_bank_slot_get( slot_ctx->bank ) ); + ulong stake_weights_sz = generate_stake_weight_msg( slot_ctx, epoch - 1, vote_states_prev_prev, stake_weights_msg ); + ulong stake_weights_sig = 4UL; fd_stem_publish( stem, 0UL, stake_weights_sig, ctx->stake_out->chunk, stake_weights_sz, 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) ); ctx->stake_out->chunk = fd_dcache_compact_next( ctx->stake_out->chunk, stake_weights_sz, ctx->stake_out->chunk0, ctx->stake_out->wmark ); FD_LOG_NOTICE(("sending current epoch stake weights - epoch: %lu, stake_weight_cnt: %lu, start_slot: %lu, slot_cnt: %lu", stake_weights_msg[0], stake_weights_msg[1], stake_weights_msg[2], stake_weights_msg[3])); - } - - fd_bank_epoch_stakes_end_locking_query( slot_ctx->bank ); - fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( next_epoch_stakes ); + } while( 0 ); + fd_bank_vote_states_prev_prev_end_locking_query( slot_ctx->bank ); - if( next_epoch_stakes_root!=NULL ) { + fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_locking_query( slot_ctx->bank ); + do { ulong * stake_weights_msg = fd_chunk_to_laddr( ctx->stake_out->mem, ctx->stake_out->chunk ); ulong epoch = fd_slot_to_leader_schedule_epoch( epoch_schedule, fd_bank_slot_get( slot_ctx->bank ) ); /* epoch */ - ulong stake_weights_sz = generate_stake_weight_msg( slot_ctx, epoch, next_epoch_stakes, stake_weights_msg ); - ulong stake_weights_sig = 4UL; + ulong stake_weights_sz = generate_stake_weight_msg( slot_ctx, epoch, vote_states_prev, stake_weights_msg ); + ulong stake_weights_sig = 4UL; fd_stem_publish( stem, 0UL, stake_weights_sig, ctx->stake_out->chunk, stake_weights_sz, 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) ); ctx->stake_out->chunk = fd_dcache_compact_next( ctx->stake_out->chunk, stake_weights_sz, ctx->stake_out->chunk0, ctx->stake_out->wmark ); FD_LOG_NOTICE(("sending next epoch stake weights - epoch: %lu, stake_weight_cnt: %lu, start_slot: %lu, slot_cnt: %lu", stake_weights_msg[0], stake_weights_msg[1], stake_weights_msg[2], stake_weights_msg[3])); - } - fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank ); + } while( 0 ); + fd_bank_vote_states_prev_end_locking_query( slot_ctx->bank ); } static void @@ -529,7 +525,6 @@ restore_slot_ctx( fd_replay_tile_ctx_t * ctx, fd_exec_slot_ctx_t * recovered_slot_ctx = fd_exec_slot_ctx_recover( ctx->slot_ctx, manifest_global ); - if( !recovered_slot_ctx ) { FD_LOG_ERR(( "Failed to restore slot context from snapshot manifest!" )); } @@ -692,20 +687,18 @@ init_after_snapshot( fd_replay_tile_ctx_t * ctx ) { memset( block_id, 0, sizeof(fd_hash_t) ); block_id->key[0] = UCHAR_MAX; /* TODO: would be good to have the actual block id of the snapshot slot */ - fd_vote_accounts_global_t const * vote_accounts = fd_bank_curr_epoch_stakes_locking_query( ctx->slot_ctx->bank ); - - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( ctx->slot_ctx->bank ); fd_bank_hash_cmp_t * bank_hash_cmp = ctx->bank_hash_cmp; - for( fd_vote_accounts_pair_global_t_mapnode_t * curr = fd_vote_accounts_pair_global_t_map_minimum( vote_accounts_pool, vote_accounts_root ); - curr; - curr = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_pool, curr ) ) { - bank_hash_cmp->total_stake += curr->elem.stake; + + fd_vote_states_iter_t iter[1]; + for( fd_vote_states_iter_init( vote_states, iter ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); + bank_hash_cmp->total_stake += vote_state->stake; } bank_hash_cmp->watermark = snapshot_slot; - fd_bank_curr_epoch_stakes_end_locking_query( ctx->slot_ctx->bank ); + fd_bank_vote_states_end_locking_query( ctx->slot_ctx->bank ); ulong root = snapshot_slot; if( FD_LIKELY( root > fd_fseq_query( ctx->published_wmark ) ) ) { @@ -1055,86 +1048,30 @@ publish_votes_to_plugin( fd_replay_tile_ctx_t * ctx, fd_fork_t * fork = fd_fork_frontier_ele_query( ctx->forks->frontier, &bank_slot, NULL, ctx->forks->pool ); if( FD_UNLIKELY ( !fork ) ) return; - fd_vote_accounts_global_t const * epoch_stakes = fd_bank_epoch_stakes_locking_query( ctx->slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( epoch_stakes ); - + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( ctx->slot_ctx->bank ); ulong i = 0; FD_SPAD_FRAME_BEGIN( ctx->runtime_spad ) { - for( fd_vote_accounts_pair_global_t_mapnode_t const * n = fd_vote_accounts_pair_global_t_map_minimum_const( epoch_stakes_pool, epoch_stakes_root ); - n && i < FD_CLUSTER_NODE_CNT; - n = fd_vote_accounts_pair_global_t_map_successor_const( epoch_stakes_pool, n ) ) { - if( n->elem.stake == 0 ) continue; - - uchar * data = (uchar *)&n->elem.value + n->elem.value.data_offset; - ulong data_len = n->elem.value.data_len; - - int err; - fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad( - vote_state_versioned, ctx->runtime_spad, - data, - data_len, - &err ); - if( FD_UNLIKELY( err ) ) { - FD_LOG_ERR(( "Unexpected failure in decoding vote state %d", err )); - } - - fd_pubkey_t node_pubkey; - ulong commission; - ulong epoch_credits; - fd_vote_epoch_credits_t const * _epoch_credits; - ulong root_slot; - - switch( vsv->discriminant ) { - case fd_vote_state_versioned_enum_v0_23_5: - node_pubkey = vsv->inner.v0_23_5.node_pubkey; - commission = vsv->inner.v0_23_5.commission; - _epoch_credits = deq_fd_vote_epoch_credits_t_cnt( vsv->inner.v0_23_5.epoch_credits ) == 0 ? NULL : deq_fd_vote_epoch_credits_t_peek_tail_const( vsv->inner.v0_23_5.epoch_credits ); - epoch_credits = _epoch_credits==NULL ? 0UL : _epoch_credits->credits - _epoch_credits->prev_credits; - root_slot = vsv->inner.v0_23_5.root_slot; - break; - case fd_vote_state_versioned_enum_v1_14_11: - node_pubkey = vsv->inner.v1_14_11.node_pubkey; - commission = vsv->inner.v1_14_11.commission; - _epoch_credits = deq_fd_vote_epoch_credits_t_cnt( vsv->inner.v1_14_11.epoch_credits ) == 0 ? NULL : deq_fd_vote_epoch_credits_t_peek_tail_const( vsv->inner.v1_14_11.epoch_credits ); - epoch_credits = _epoch_credits==NULL ? 0UL : _epoch_credits->credits - _epoch_credits->prev_credits; - root_slot = vsv->inner.v1_14_11.root_slot; - break; - case fd_vote_state_versioned_enum_current: - node_pubkey = vsv->inner.current.node_pubkey; - commission = vsv->inner.current.commission; - _epoch_credits = deq_fd_vote_epoch_credits_t_cnt( vsv->inner.current.epoch_credits ) == 0 ? NULL : deq_fd_vote_epoch_credits_t_peek_tail_const( vsv->inner.current.epoch_credits ); - epoch_credits = _epoch_credits==NULL ? 0UL : _epoch_credits->credits - _epoch_credits->prev_credits; - root_slot = vsv->inner.v0_23_5.root_slot; - break; - default: - __builtin_unreachable(); - } - - fd_clock_timestamp_vote_t_mapnode_t query; - memcpy( query.elem.pubkey.uc, n->elem.key.uc, 32UL ); - fd_clock_timestamp_votes_global_t const * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_query( ctx->slot_ctx->bank ); - fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_root = fd_clock_timestamp_votes_votes_root_join( clock_timestamp_votes ); - fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_pool = fd_clock_timestamp_votes_votes_pool_join( clock_timestamp_votes ); - - fd_clock_timestamp_vote_t_mapnode_t * res = fd_clock_timestamp_vote_t_map_find( timestamp_votes_pool, timestamp_votes_root, &query ); + fd_vote_states_iter_t iter[1]; + for( fd_vote_states_iter_init( vote_states, iter ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); fd_vote_update_msg_t * msg = (fd_vote_update_msg_t *)(dst + sizeof(ulong) + i*112U); memset( msg, 0, 112U ); - memcpy( msg->vote_pubkey, n->elem.key.uc, sizeof(fd_pubkey_t) ); - memcpy( msg->node_pubkey, node_pubkey.uc, sizeof(fd_pubkey_t) ); - msg->activated_stake = n->elem.stake; - msg->last_vote = res == NULL ? 0UL : res->elem.slot; - msg->root_slot = root_slot; - msg->epoch_credits = epoch_credits; - msg->commission = (uchar)commission; + memcpy( msg->vote_pubkey, &vote_state->vote_account, sizeof(fd_pubkey_t) ); + memcpy( msg->node_pubkey, &vote_state->node_account, sizeof(fd_pubkey_t) ); + msg->activated_stake = vote_state->stake; + msg->last_vote = vote_state->last_vote_slot; + msg->root_slot = 0UL; /* TODO: needs to be populated in vote_state */ + msg->epoch_credits = 0UL; /* TODO: needs to be populated in vote_state */ + msg->commission = (uchar)vote_state->commission; msg->is_delinquent = (uchar)fd_int_if(fd_bank_slot_get( ctx->slot_ctx->bank ) >= 128UL, msg->last_vote <= fd_bank_slot_get( ctx->slot_ctx->bank ) - 128UL, msg->last_vote == 0); + + ++i; - fd_bank_clock_timestamp_votes_end_locking_query( ctx->slot_ctx->bank ); } } FD_SPAD_FRAME_END; - fd_bank_epoch_stakes_end_locking_query( ctx->slot_ctx->bank ); + fd_bank_vote_states_end_locking_query( ctx->slot_ctx->bank ); *(ulong *)dst = i; diff --git a/src/discof/restore/utils/fd_ssload.c b/src/discof/restore/utils/fd_ssload.c index 5fcfd377a32..e87388abd41 100644 --- a/src/discof/restore/utils/fd_ssload.c +++ b/src/discof/restore/utils/fd_ssload.c @@ -136,23 +136,6 @@ fd_ssload_recover( fd_snapshot_manifest_t * manifest, fd_bank_execution_fees_set( slot_ctx->bank, manifest->collector_fees ); fd_bank_priority_fees_set( slot_ctx->bank, 0UL ); - /* FIXME: Remove the magic number here. */ - fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank ); - uchar * clock_pool_mem = (uchar *)fd_ulong_align_up( (ulong)clock_timestamp_votes + sizeof(fd_clock_timestamp_votes_global_t), fd_clock_timestamp_vote_t_map_align() ); - fd_clock_timestamp_vote_t_mapnode_t * clock_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new(clock_pool_mem, 30000UL ) ); - clock_timestamp_votes->votes_pool_offset = (ulong)fd_clock_timestamp_vote_t_map_leave( clock_pool) - (ulong)clock_timestamp_votes; - clock_timestamp_votes->votes_root_offset = 0UL; - fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank ); - - for( ulong i=0UL; ivote_accounts_len; i++ ) { - fd_snapshot_manifest_vote_account_t * account = &manifest->vote_accounts[ i ]; - fd_pubkey_t vote_account_pubkey; - fd_memcpy( vote_account_pubkey.uc, account->vote_account_pubkey, 32UL ); - if( FD_LIKELY( account->last_slot || account->stake ) ) { - fd_vote_record_timestamp_vote_with_slot( &vote_account_pubkey, account->last_timestamp, account->last_slot, slot_ctx->bank ); - } - } - /* Update last restart slot https://github.com/solana-labs/solana/blob/30531d7a5b74f914dde53bfbb0bc2144f2ac92bb/runtime/src/bank.rs#L2152 diff --git a/src/discof/writer/fd_writer_tile.c b/src/discof/writer/fd_writer_tile.c index f2975337724..abafa3d4a81 100644 --- a/src/discof/writer/fd_writer_tile.c +++ b/src/discof/writer/fd_writer_tile.c @@ -272,7 +272,6 @@ after_frag( fd_writer_tile_ctx_t * ctx, ctx->funk, ctx->funk_txn, txn_ctx, - ctx->spad, ctx->bank, ctx->capture_ctx ); diff --git a/src/flamenco/rewards/fd_rewards.c b/src/flamenco/rewards/fd_rewards.c index 08784bf6ce2..61de0021ae8 100644 --- a/src/flamenco/rewards/fd_rewards.c +++ b/src/flamenco/rewards/fd_rewards.c @@ -96,30 +96,14 @@ slot_in_year_for_inflation( fd_bank_t const * bank ) { static void calculate_stake_points_and_credits( fd_stake_history_t const * stake_history, fd_stake_delegation_t const * stake, - fd_vote_state_versioned_t * vote_state_versioned, + fd_vote_state_ele_t const * vote_state, ulong * new_rate_activation_epoch, fd_calculated_stake_points_t * result ) { ulong credits_in_stake = stake->credits_observed; - - fd_vote_epoch_credits_t * epoch_credits; - switch( vote_state_versioned->discriminant ) { - case fd_vote_state_versioned_enum_current: - epoch_credits = vote_state_versioned->inner.current.epoch_credits; - break; - case fd_vote_state_versioned_enum_v0_23_5: - epoch_credits = vote_state_versioned->inner.v0_23_5.epoch_credits; - break; - case fd_vote_state_versioned_enum_v1_14_11: - epoch_credits = vote_state_versioned->inner.v1_14_11.epoch_credits; - break; - default: - FD_LOG_ERR(( "invalid vote account, should never happen" )); - } - - ulong credits_in_vote = 0UL; - if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) { - credits_in_vote = deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->credits; + ulong credits_in_vote = 0UL; + if( FD_LIKELY( vote_state->credits_cnt>0UL ) ) { + credits_in_vote = vote_state->credits[vote_state->credits_cnt-1UL]; } /* If the Vote account has less credits observed than the Stake account, @@ -145,15 +129,12 @@ calculate_stake_points_and_credits( fd_stake_history_t const * stake_history } /* Calculate the points for each epoch credit */ - uint128 points = 0; - ulong new_credits_observed = credits_in_stake; - for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits ); - !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter ); - iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) { - - fd_vote_epoch_credits_t * ele = deq_fd_vote_epoch_credits_t_iter_ele( epoch_credits, iter ); - ulong final_epoch_credits = ele->credits; - ulong initial_epoch_credits = ele->prev_credits; + uint128 points = 0; + ulong new_credits_observed = credits_in_stake; + for( ulong i=0UL; icredits_cnt; i++ ) { + + ulong final_epoch_credits = vote_state->credits[i]; + ulong initial_epoch_credits = vote_state->prev_credits[i]; uint128 earned_credits = 0; if( FD_LIKELY( credits_in_stake < initial_epoch_credits ) ) { earned_credits = (uint128)(final_epoch_credits - initial_epoch_credits); @@ -171,11 +152,17 @@ calculate_stake_points_and_credits( fd_stake_history_t const * stake_history .warmup_cooldown_rate = stake->warmup_cooldown_rate, }; - ulong stake_amount = fd_stake_activating_and_deactivating( &delegation, ele->epoch, stake_history, new_rate_activation_epoch ).effective; + ulong stake_amount = fd_stake_activating_and_deactivating( + &delegation, + vote_state->epoch[i], + stake_history, + new_rate_activation_epoch ).effective; points += (uint128)stake_amount * earned_credits; + } + result->points = points; result->new_credits_observed = new_credits_observed; result->force_credits_update_with_skipped_reward = 0; @@ -185,14 +172,19 @@ calculate_stake_points_and_credits( fd_stake_history_t const * stake_history static int calculate_stake_rewards( fd_stake_history_t const * stake_history, fd_stake_delegation_t const * stake, - fd_vote_state_versioned_t * vote_state_versioned, + fd_vote_state_ele_t const * vote_state, ulong rewarded_epoch, fd_point_value_t * point_value, ulong * new_rate_activation_epoch, fd_calculated_stake_rewards_t * result ) { fd_calculated_stake_points_t stake_points_result = {0}; - calculate_stake_points_and_credits( stake_history, stake, vote_state_versioned, new_rate_activation_epoch, &stake_points_result); + calculate_stake_points_and_credits( + stake_history, + stake, + vote_state, + new_rate_activation_epoch, + &stake_points_result); // Drive credits_observed forward unconditionally when rewards are disabled // or when this is the stake's activation epoch @@ -218,7 +210,7 @@ calculate_stake_rewards( fd_stake_history_t const * stake_history, } fd_commission_split_t split_result; - fd_vote_commission_split( vote_state_versioned, rewards, &split_result ); + fd_vote_commission_split( vote_state->commission, rewards, &split_result ); if( split_result.is_split && (split_result.voter_portion == 0 || split_result.staker_portion == 0) ) { return 1; } @@ -233,7 +225,7 @@ calculate_stake_rewards( fd_stake_history_t const * stake_history, static int redeem_rewards( fd_stake_history_t const * stake_history, fd_stake_delegation_t const * stake, - fd_vote_state_versioned_t * vote_state_versioned, + fd_vote_state_ele_t const * vote_state, ulong rewarded_epoch, fd_point_value_t * point_value, ulong * new_rate_activation_epoch, @@ -242,7 +234,7 @@ redeem_rewards( fd_stake_history_t const * stake_history, int rc = calculate_stake_rewards( stake_history, stake, - vote_state_versioned, + vote_state, rewarded_epoch, point_value, new_rate_activation_epoch, @@ -257,12 +249,16 @@ redeem_rewards( fd_stake_history_t const * stake_history, /* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L70 */ static int calculate_points( fd_stake_delegation_t const * stake, - fd_vote_state_versioned_t * vote_state_versioned, + fd_vote_state_ele_t const * vote_state, fd_stake_history_t const * stake_history, ulong * new_rate_activation_epoch, uint128 * result ) { fd_calculated_stake_points_t stake_point_result; - calculate_stake_points_and_credits( stake_history, stake, vote_state_versioned, new_rate_activation_epoch, &stake_point_result ); + calculate_stake_points_and_credits( stake_history, + stake, + vote_state, + new_rate_activation_epoch, + &stake_point_result ); *result = stake_point_result.points; return FD_EXECUTOR_INSTR_SUCCESS; @@ -317,12 +313,11 @@ get_minimum_stake_delegation( fd_exec_slot_ctx_t const * slot_ctx ) { } static uint128 -calculate_points_all( fd_bank_t * bank, - fd_stake_history_t const * stake_history, - ulong * new_warmup_cooldown_rate_epoch, - ulong minimum_stake_delegation, - fd_vote_info_pair_t_mapnode_t * vote_states_pool, - fd_vote_info_pair_t_mapnode_t * vote_states_root ) { +calculate_points_all( fd_exec_slot_ctx_t const * slot_ctx, + fd_bank_t * bank, + fd_stake_history_t const * stake_history, + ulong * new_warmup_cooldown_rate_epoch, + ulong minimum_stake_delegation ) { uint128 total_points = 0; fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_locking_query( bank ); @@ -342,17 +337,22 @@ calculate_points_all( fd_bank_t * bank, continue; } - /* Check that the vote account is present in our cache */ - fd_vote_info_pair_t_mapnode_t query_key; - query_key.elem.account = stake_delegation->vote_account; - fd_vote_info_pair_t_mapnode_t * vote_state_info = fd_vote_info_pair_t_map_find( vote_states_pool, vote_states_root, &query_key ); - if( FD_UNLIKELY( vote_state_info==NULL ) ) { - FD_LOG_DEBUG(( "vote account missing from cache" )); + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( slot_ctx->bank ); + fd_vote_state_ele_t * vote_state_ele = fd_vote_states_query( vote_states, &stake_delegation->vote_account ); + if( FD_UNLIKELY( !vote_state_ele ) ) { + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); continue; } uint128 account_points; - int err = calculate_points( stake_delegation, &vote_state_info->elem.state, stake_history, new_warmup_cooldown_rate_epoch, &account_points ); + int err = calculate_points( + stake_delegation, + vote_state_ele, + stake_history, + new_warmup_cooldown_rate_epoch, &account_points ); + + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); + if( FD_UNLIKELY( err ) ) { FD_LOG_DEBUG(( "failed to calculate points" )); continue; @@ -372,8 +372,7 @@ static void calculate_reward_points_partitioned( fd_exec_slot_ctx_t * slot_ctx, fd_stake_history_t const * stake_history, ulong rewards, - fd_point_value_t * result, - fd_epoch_info_t * temp_info ) { + fd_point_value_t * result ) { ulong minimum_stake_delegation = get_minimum_stake_delegation( slot_ctx ); /* Calculate the points for each stake delegation */ @@ -391,12 +390,11 @@ calculate_reward_points_partitioned( fd_exec_slot_ctx_t * slot_ctx, } uint128 points = calculate_points_all( + slot_ctx, slot_ctx->bank, stake_history, new_warmup_cooldown_rate_epoch, - minimum_stake_delegation, - temp_info->vote_states_pool, - temp_info->vote_states_root ); + minimum_stake_delegation ); if( points > 0 ) { result->points = points; @@ -405,8 +403,7 @@ calculate_reward_points_partitioned( fd_exec_slot_ctx_t * slot_ctx, } static void -calculate_stake_vote_rewards_account( fd_epoch_info_t const * temp_info, - fd_exec_slot_ctx_t const * slot_ctx, +calculate_stake_vote_rewards_account( fd_exec_slot_ctx_t const * slot_ctx, fd_capture_ctx_t const * capture_ctx, fd_stake_history_t const * stake_history, ulong const rewarded_epoch, @@ -446,28 +443,28 @@ calculate_stake_vote_rewards_account( fd_epoch_info_t const * } } - fd_pubkey_t const * voter_acc = &stake_delegation->vote_account; - fd_vote_info_pair_t_mapnode_t key; - key.elem.account = *voter_acc; - fd_vote_info_pair_t_mapnode_t * vote_state_entry = fd_vote_info_pair_t_map_find( temp_info->vote_states_pool, - temp_info->vote_states_root, - &key ); - if( FD_UNLIKELY( vote_state_entry==NULL ) ) { + fd_pubkey_t const * voter_acc = &stake_delegation->vote_account; + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( slot_ctx->bank ); + fd_vote_state_ele_t * vote_state_ele = fd_vote_states_query( vote_states, voter_acc ); + if( FD_UNLIKELY( !vote_state_ele ) ) { + FD_LOG_WARNING(( "failed to query vote state" )); + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); continue; } - fd_vote_state_versioned_t * vote_state = &vote_state_entry->elem.state; - /* Note, this doesn't actually redeem any rewards.. this is a misnomer. */ fd_calculated_stake_rewards_t calculated_stake_rewards[1] = {0}; - int err = redeem_rewards( stake_history, - stake_delegation, - vote_state, - rewarded_epoch, - point_value, - new_warmup_cooldown_rate_epoch, - calculated_stake_rewards ); + int err = redeem_rewards( + stake_history, + stake_delegation, + vote_state_ele, + rewarded_epoch, + point_value, + new_warmup_cooldown_rate_epoch, + calculated_stake_rewards ); + + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); if( FD_UNLIKELY( err!=0 ) ) { FD_LOG_DEBUG(( "redeem_rewards failed for %s with error %d", FD_BASE58_ENC_32_ALLOCA( &stake_delegation->stake_account ), err )); continue; @@ -477,29 +474,12 @@ calculate_stake_vote_rewards_account( fd_epoch_info_t const * fd_solcap_write_stake_reward_event( capture_ctx->capture, &stake_delegation->stake_account, voter_acc, - fd_vote_account_commission( vote_state ), + vote_state_ele->commission, (long)calculated_stake_rewards->voter_rewards, (long)calculated_stake_rewards->staker_rewards, (long)calculated_stake_rewards->new_credits_observed ); } - /* Fetch the comission for the vote account */ - uchar commission = 0; - switch( vote_state->discriminant ) { - case fd_vote_state_versioned_enum_current: - commission = vote_state->inner.current.commission; - break; - case fd_vote_state_versioned_enum_v0_23_5: - commission = vote_state->inner.v0_23_5.commission; - break; - case fd_vote_state_versioned_enum_v1_14_11: - commission = vote_state->inner.v1_14_11.commission; - break; - default: - FD_LOG_DEBUG(( "unsupported vote account" )); - continue; - } - // Find and update the vote reward node in the local map fd_vote_reward_t_mapnode_t vote_map_key[1]; vote_map_key->elem.pubkey = *voter_acc; @@ -514,7 +494,7 @@ calculate_stake_vote_rewards_account( fd_epoch_info_t const * if( vote_reward_node==NULL ) { vote_reward_node = fd_vote_reward_t_map_acquire( vote_reward_map_pool ); vote_reward_node->elem.pubkey = *voter_acc; - vote_reward_node->elem.commission = commission; + vote_reward_node->elem.commission = vote_state_ele->commission; vote_reward_node->elem.vote_rewards = calculated_stake_rewards->voter_rewards; vote_reward_node->elem.needs_store = 1; fd_vote_reward_t_map_insert( vote_reward_map_pool, &vote_reward_map_root, vote_reward_node ); @@ -585,7 +565,6 @@ calculate_stake_vote_rewards( fd_exec_slot_ctx_t * slot_ct ulong rewarded_epoch, fd_point_value_t * point_value, fd_calculate_stake_vote_rewards_result_t * result, - fd_epoch_info_t * temp_info, fd_spad_t * runtime_spad ) { int _err[1]; @@ -620,8 +599,13 @@ calculate_stake_vote_rewards( fd_exec_slot_ctx_t * slot_ct fd_stake_reward_calculation_dlist_new( result->stake_reward_calculation.stake_rewards ); result->stake_reward_calculation.stake_rewards_len = 0UL; + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( slot_ctx->bank ); + if( FD_UNLIKELY( !vote_states ) ) { + FD_LOG_CRIT(( "vote_states is NULL" )); + } + /* Create the vote rewards map. This will be destroyed after the vote rewards have been distributed. */ - ulong vote_account_cnt = fd_vote_info_pair_t_map_size( temp_info->vote_states_pool, temp_info->vote_states_root ); + ulong vote_account_cnt = fd_vote_states_cnt( vote_states ); result->vote_reward_map_pool = fd_vote_reward_t_map_join( fd_vote_reward_t_map_new( fd_spad_alloc( runtime_spad, fd_vote_reward_t_map_align(), fd_vote_reward_t_map_footprint( vote_account_cnt )), @@ -629,26 +613,26 @@ calculate_stake_vote_rewards( fd_exec_slot_ctx_t * slot_ct result->vote_reward_map_root = NULL; /* Pre-fill the vote pubkeys in the vote rewards map pool */ - for( fd_vote_info_pair_t_mapnode_t * vote_info = fd_vote_info_pair_t_map_minimum( temp_info->vote_states_pool, temp_info->vote_states_root ); - vote_info; - vote_info = fd_vote_info_pair_t_map_successor( temp_info->vote_states_pool, vote_info ) ) { + fd_vote_states_iter_t iter[1]; + for( fd_vote_states_iter_init( vote_states, iter ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); - fd_pubkey_t const * voter_pubkey = &vote_info->elem.account; fd_vote_reward_t_mapnode_t * vote_reward_node = fd_vote_reward_t_map_acquire( result->vote_reward_map_pool ); - vote_reward_node->elem.pubkey = *voter_pubkey; + vote_reward_node->elem.pubkey = vote_state->vote_account; vote_reward_node->elem.vote_rewards = 0UL; vote_reward_node->elem.needs_store = 0; fd_vote_reward_t_map_insert( result->vote_reward_map_pool, &result->vote_reward_map_root, vote_reward_node ); } + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); + fd_bank_stake_delegations_end_locking_query( slot_ctx->bank ); /* Loop over all the delegations https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L367 */ calculate_stake_vote_rewards_account( - temp_info, slot_ctx, capture_ctx, stake_history, @@ -669,7 +653,6 @@ calculate_validator_rewards( fd_exec_slot_ctx_t * slot_ctx, ulong rewarded_epoch, ulong rewards, fd_calculate_validator_rewards_result_t * result, - fd_epoch_info_t * temp_info, fd_spad_t * runtime_spad ) { /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2759-L2786 */ fd_stake_history_t const * stake_history = fd_sysvar_stake_history_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad ); @@ -678,11 +661,11 @@ calculate_validator_rewards( fd_exec_slot_ctx_t * slot_ctx, } /* Calculate the epoch reward points from stake/vote accounts */ - calculate_reward_points_partitioned( slot_ctx, - stake_history, - rewards, - &result->point_value, - temp_info ); + calculate_reward_points_partitioned( + slot_ctx, + stake_history, + rewards, + &result->point_value ); if( capture_ctx ) { ulong const epoch = fd_bank_epoch_get( slot_ctx->bank ); @@ -694,14 +677,14 @@ calculate_validator_rewards( fd_exec_slot_ctx_t * slot_ctx, } /* Calculate the stake and vote rewards for each account */ - calculate_stake_vote_rewards( slot_ctx, - capture_ctx, - stake_history, - rewarded_epoch, - &result->point_value, - &result->calculate_stake_vote_rewards_result, - temp_info, - runtime_spad ); + calculate_stake_vote_rewards( + slot_ctx, + capture_ctx, + stake_history, + rewarded_epoch, + &result->point_value, + &result->calculate_stake_vote_rewards_result, + runtime_spad ); } /* Calculate the number of blocks required to distribute rewards to all stake accounts. @@ -775,7 +758,6 @@ calculate_rewards_for_partitioning( fd_exec_slot_ctx_t * slot_ ulong prev_epoch, const fd_hash_t * parent_blockhash, fd_partitioned_rewards_calculation_t * result, - fd_epoch_info_t * temp_info, fd_spad_t * runtime_spad ) { /* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L227 */ fd_prev_epoch_inflation_rewards_t rewards; @@ -791,7 +773,6 @@ calculate_rewards_for_partitioning( fd_exec_slot_ctx_t * slot_ prev_epoch, rewards.validator_rewards, validator_result, - temp_info, runtime_spad ); fd_stake_reward_calculation_t * stake_reward_calculation = &validator_result->calculate_stake_vote_rewards_result.stake_reward_calculation; @@ -828,7 +809,6 @@ calculate_rewards_and_distribute_vote_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx, ulong prev_epoch, fd_hash_t const * parent_blockhash, - fd_epoch_info_t * temp_info, fd_spad_t * runtime_spad ) { /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2406-L2492 */ @@ -838,7 +818,6 @@ calculate_rewards_and_distribute_vote_rewards( fd_exec_slot_ctx_t * slot_ctx, prev_epoch, parent_blockhash, rewards_calc_result, - temp_info, runtime_spad ); /* Iterate over all the vote reward nodes */ @@ -1121,7 +1100,6 @@ fd_begin_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx, fd_hash_t const * parent_blockhash, ulong parent_epoch, - fd_epoch_info_t * temp_info, fd_spad_t * runtime_spad ) { /* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L55 */ @@ -1130,7 +1108,6 @@ fd_begin_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, capture_ctx, parent_epoch, parent_blockhash, - temp_info, runtime_spad ); /* https://github.com/anza-xyz/agave/blob/9a7bf72940f4b3cd7fc94f54e005868ce707d53d/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L62 */ @@ -1206,9 +1183,6 @@ fd_rewards_recalculate_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( fd_bank_stake_delegations_locking_modify( slot_ctx->bank ) ); - fd_epoch_info_t epoch_info = {0}; - fd_epoch_info_new( &epoch_info ); - fd_stake_history_entry_t _accumulator = { .effective = 0UL, .activating = 0UL, @@ -1225,12 +1199,9 @@ fd_rewards_recalculate_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_bank_stake_delegations_end_locking_modify( slot_ctx->bank ); - /* NOTE: this is just a workaround for now to correctly populate epoch_info. */ fd_populate_vote_accounts( slot_ctx, stake_history, - new_warmup_cooldown_rate_epoch, - &epoch_info, - runtime_spad ); + new_warmup_cooldown_rate_epoch ); /* In future, the calculation will be cached in the snapshot, but for now we just re-calculate it (as Agave does). */ fd_calculate_stake_vote_rewards_result_t calculate_stake_vote_rewards_result[1]; @@ -1240,7 +1211,6 @@ fd_rewards_recalculate_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, rewarded_epoch, &point_value, calculate_stake_vote_rewards_result, - &epoch_info, runtime_spad ); /* The vote reward map isn't actually used in this code path and will only diff --git a/src/flamenco/rewards/fd_rewards.h b/src/flamenco/rewards/fd_rewards.h index 76e5653bbb8..006bf238761 100644 --- a/src/flamenco/rewards/fd_rewards.h +++ b/src/flamenco/rewards/fd_rewards.h @@ -31,7 +31,6 @@ fd_begin_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx, fd_hash_t const * parent_blockhash, ulong parent_epoch, - fd_epoch_info_t * temp_info, fd_spad_t * runtime_spad ); /* fd_rewards_recalculate_partitioned_rewards restores epoch bank stake diff --git a/src/flamenco/runtime/context/fd_exec_slot_ctx.c b/src/flamenco/runtime/context/fd_exec_slot_ctx.c index 454bc90e1ff..cc2c2846723 100644 --- a/src/flamenco/runtime/context/fd_exec_slot_ctx.c +++ b/src/flamenco/runtime/context/fd_exec_slot_ctx.c @@ -94,216 +94,133 @@ fd_exec_slot_ctx_recover( fd_exec_slot_ctx_t * slot_ctx, fd_vote_accounts_pair_global_t_mapnode_t * manifest_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( manifest_vote_accounts ); fd_vote_accounts_pair_global_t_mapnode_t * manifest_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( manifest_vote_accounts ); - fd_vote_accounts_global_t * curr_epoch_stakes = fd_bank_curr_epoch_stakes_locking_modify( slot_ctx->bank ); - uchar * curr_epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)curr_epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * curr_epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( curr_epoch_stakes_pool_mem, 50000UL ) ); - fd_vote_accounts_pair_global_t_mapnode_t * curr_epoch_stakes_root = NULL; - uchar * acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)curr_epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL ); - - for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( manifest_vote_accounts_pool, manifest_vote_accounts_root ); - n; - n = fd_vote_accounts_pair_global_t_map_successor( manifest_vote_accounts_pool, n ) ) { - - fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire( curr_epoch_stakes_pool ); - FD_TEST( elem ); - - elem->elem.stake = n->elem.stake; - elem->elem.key = n->elem.key; - - elem->elem.value.lamports = n->elem.value.lamports; - elem->elem.value.data_len = 0UL; - elem->elem.value.data_offset = 0UL; - elem->elem.value.owner = n->elem.value.owner; - elem->elem.value.executable = n->elem.value.executable; - elem->elem.value.rent_epoch = n->elem.value.rent_epoch; - - elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value);; - elem->elem.value.data_len = n->elem.value.data_len; - - uchar * manifest_data = fd_solana_account_data_join( &n->elem.value ); - memcpy( acc_region_curr, manifest_data, n->elem.value.data_len ); - acc_region_curr += n->elem.value.data_len; - - fd_vote_accounts_pair_global_t_map_insert( - curr_epoch_stakes_pool, - &curr_epoch_stakes_root, - elem ); - } - - fd_vote_accounts_vote_accounts_pool_update( curr_epoch_stakes, curr_epoch_stakes_pool ); - fd_vote_accounts_vote_accounts_root_update( curr_epoch_stakes, curr_epoch_stakes_root ); - fd_bank_curr_epoch_stakes_end_locking_modify( slot_ctx->bank ); - fd_bank_epoch_set( slot_ctx->bank, manifest->bank.epoch ); /* Move EpochStakes */ - do { - ulong epoch = fd_bank_epoch_get( slot_ctx->bank ); + ulong epoch = fd_bank_epoch_get( slot_ctx->bank ); - /* We need to save the vote accounts for the current epoch and the next - epoch as it is used to calculate the leader schedule at the epoch - boundary. */ + /* We need to save the vote accounts for the current epoch and the next + epoch as it is used to calculate the leader schedule at the epoch + boundary. */ - fd_vote_accounts_global_t * vote_accounts_curr_stakes = NULL; - fd_vote_accounts_global_t * vote_accounts_next_stakes = NULL; + fd_vote_accounts_global_t * vote_accounts_curr_stakes = NULL; + fd_vote_accounts_global_t * vote_accounts_next_stakes = NULL; - fd_epoch_epoch_stakes_pair_global_t * versioned_bank_epoch_stakes = fd_versioned_bank_epoch_stakes_join( &manifest->bank ); - for( ulong i=0UL; ibank.epoch_stakes_len; i++ ) { - if( versioned_bank_epoch_stakes[i].key == epoch ) { - vote_accounts_curr_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts; - } - if( versioned_bank_epoch_stakes[i].key == epoch+1UL ) { - vote_accounts_next_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts; - } - - /* When loading from a snapshot, Agave's stake caches mean that we have to special-case the epoch stakes - that are used for the second epoch E+2 after the snapshot epoch E. - - If the snapshot contains the epoch stakes for E+2, we should use those. - - If the snapshot does not, we should use the stakes at the end of the E-1 epoch, instead of E-2 as we do for - all other epochs. */ - } - - fd_versioned_epoch_stakes_pair_global_t * versioned_epoch_stakes = fd_solana_manifest_versioned_epoch_stakes_join( manifest ); - for( ulong i=0UL; iversioned_epoch_stakes_len; i++ ) { - - if( versioned_epoch_stakes[i].epoch == epoch ) { - vote_accounts_curr_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts; - } - if( versioned_epoch_stakes[i].epoch == epoch+1UL ) { - vote_accounts_next_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts; - - /* Save the initial value to be used for the get_epoch_stake - syscall. - - A note on Agave's indexing scheme for their epoch_stakes - structure: - - https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175 - - If we are loading a snapshot and replaying in the middle of - epoch 7, the syscall is supposed to return the total stake at - the end of epoch 6. The epoch_stakes structure is indexed in - Agave by the epoch number of the leader schedule that the - stakes are meant to determine. For instance, to get the - stakes at the end of epoch 6, we should query by 8, because - the leader schedule for epoch 8 is determined based on the - stakes at the end of epoch 6. Therefore, we save the total - epoch stake by querying for epoch+1. */ - fd_bank_total_epoch_stake_set( slot_ctx->bank, versioned_epoch_stakes[i].val.inner.Current.total_stake ); - } + fd_epoch_epoch_stakes_pair_global_t * versioned_bank_epoch_stakes = fd_versioned_bank_epoch_stakes_join( &manifest->bank ); + for( ulong i=0UL; ibank.epoch_stakes_len; i++ ) { + if( versioned_bank_epoch_stakes[i].key == epoch ) { + vote_accounts_curr_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts; } - - fd_bank_use_prev_epoch_stake_set( slot_ctx->bank, epoch + 2UL ); - - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_curr_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_curr_stakes ); - - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_next_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_next_stakes ); - - if( FD_UNLIKELY( (!vote_accounts_curr_stakes_pool) | (!vote_accounts_next_stakes_pool) ) ) { - FD_LOG_WARNING(( "snapshot missing EpochStakes for epochs %lu and/or %lu", epoch, epoch+1UL )); - return 0; + if( versioned_bank_epoch_stakes[i].key == epoch+1UL ) { + vote_accounts_next_stakes = &versioned_bank_epoch_stakes[i].value.stakes.vote_accounts; } - /* Move current EpochStakes */ - - fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank ); - uchar * epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( epoch_stakes_pool_mem, 50000UL ) ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = NULL; - - acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL ); - - for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( - vote_accounts_curr_stakes_pool, - vote_accounts_curr_stakes_root ); - n; - n = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_curr_stakes_pool, n ) ) { - - fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire( - epoch_stakes_pool ); - FD_TEST( elem ); - - elem->elem.stake = n->elem.stake; - elem->elem.key = n->elem.key; + /* When loading from a snapshot, Agave's stake caches mean that we have to special-case the epoch stakes + that are used for the second epoch E+2 after the snapshot epoch E. - elem->elem.value.lamports = n->elem.value.lamports; - elem->elem.value.data_len = 0UL; - elem->elem.value.data_offset = 0UL; - elem->elem.value.owner = n->elem.value.owner; - elem->elem.value.executable = n->elem.value.executable; - elem->elem.value.rent_epoch = n->elem.value.rent_epoch; + If the snapshot contains the epoch stakes for E+2, we should use those. - elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value); - elem->elem.value.data_len = n->elem.value.data_len; + If the snapshot does not, we should use the stakes at the end of the E-1 epoch, instead of E-2 as we do for + all other epochs. */ + } - uchar * manifest_data = fd_solana_account_data_join( &n->elem.value ); - memcpy( acc_region_curr, manifest_data, n->elem.value.data_len ); - acc_region_curr += n->elem.value.data_len; + fd_versioned_epoch_stakes_pair_global_t * versioned_epoch_stakes = fd_solana_manifest_versioned_epoch_stakes_join( manifest ); + for( ulong i=0UL; iversioned_epoch_stakes_len; i++ ) { - fd_vote_accounts_pair_global_t_map_insert( - epoch_stakes_pool, - &epoch_stakes_root, - elem ); + if( versioned_epoch_stakes[i].epoch == epoch ) { + vote_accounts_curr_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts; } + if( versioned_epoch_stakes[i].epoch == epoch+1UL ) { + vote_accounts_next_stakes = &versioned_epoch_stakes[i].val.inner.Current.stakes.vote_accounts; + + /* Save the initial value to be used for the get_epoch_stake + syscall. + + A note on Agave's indexing scheme for their epoch_stakes + structure: + + https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6175 + + If we are loading a snapshot and replaying in the middle of + epoch 7, the syscall is supposed to return the total stake at + the end of epoch 6. The epoch_stakes structure is indexed in + Agave by the epoch number of the leader schedule that the + stakes are meant to determine. For instance, to get the + stakes at the end of epoch 6, we should query by 8, because + the leader schedule for epoch 8 is determined based on the + stakes at the end of epoch 6. Therefore, we save the total + epoch stake by querying for epoch+1. */ + fd_bank_total_epoch_stake_set( slot_ctx->bank, versioned_epoch_stakes[i].val.inner.Current.total_stake ); + } + } - fd_vote_accounts_vote_accounts_pool_update( epoch_stakes, epoch_stakes_pool ); - fd_vote_accounts_vote_accounts_root_update( epoch_stakes, epoch_stakes_root ); - fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank ); - - /* Move next EpochStakes */ - - fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank ); - uchar * next_epoch_stakes_pool_mem = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( next_epoch_stakes_pool_mem, 50000UL ) ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = NULL; - - fd_vote_accounts_pair_global_t_mapnode_t * pool = vote_accounts_next_stakes_pool; - fd_vote_accounts_pair_global_t_mapnode_t * root = vote_accounts_next_stakes_root; - - acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL ); - - for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( pool, root ); - n; - n = fd_vote_accounts_pair_global_t_map_successor( pool, n ) ) { - - fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_acquire( next_epoch_stakes_pool ); - FD_TEST( elem ); + fd_bank_use_prev_epoch_stake_set( slot_ctx->bank, epoch + 2UL ); - elem->elem.stake = n->elem.stake; - elem->elem.key = n->elem.key; + fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_curr_stakes ); + fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_curr_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_curr_stakes ); - elem->elem.value.lamports = n->elem.value.lamports; - elem->elem.value.data_len = 0UL; - elem->elem.value.data_offset = 0UL; - elem->elem.value.owner = n->elem.value.owner; - elem->elem.value.executable = n->elem.value.executable; - elem->elem.value.rent_epoch = n->elem.value.rent_epoch; + fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts_next_stakes ); + fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_next_stakes_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts_next_stakes ); - elem->elem.value.data_offset = (ulong)(acc_region_curr - (uchar *)&elem->elem.value);; - elem->elem.value.data_len = n->elem.value.data_len; + if( FD_UNLIKELY( (!vote_accounts_curr_stakes_pool) | (!vote_accounts_next_stakes_pool) ) ) { + FD_LOG_WARNING(( "snapshot missing EpochStakes for epochs %lu and/or %lu", epoch, epoch+1UL )); + return 0; + } - uchar * manifest_data = fd_solana_account_data_join( &n->elem.value ); - memcpy( acc_region_curr, manifest_data, n->elem.value.data_len ); - acc_region_curr += n->elem.value.data_len; + /* Copy the vote states for the previous epoch E-2 */ + + fd_vote_states_t * vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( vote_accounts_curr_stakes_pool, vote_accounts_curr_stakes_root ); + n; + n = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_curr_stakes_pool, n ) ) { + uchar * account_data = fd_solana_account_data_join( &n->elem.value ); + ulong account_data_len = n->elem.value.data_len; + fd_vote_states_update_from_account( + vote_states_prev_prev, + &n->elem.key, + account_data, + account_data_len ); + fd_vote_states_update_stake( vote_states_prev_prev, &n->elem.key, n->elem.stake ); + } + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); + + /* Copy the vote states for the previous epoch E-1 */ + + fd_vote_states_t * vote_states_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( vote_accounts_next_stakes_pool, vote_accounts_next_stakes_root ); + n; + n = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_next_stakes_pool, n ) ) { + uchar * account_data = fd_solana_account_data_join( &n->elem.value ); + ulong account_data_len = n->elem.value.data_len; + fd_vote_states_update_from_account( + vote_states_prev, + &n->elem.key, + account_data, + account_data_len ); + fd_vote_states_update_stake( vote_states_prev, &n->elem.key, n->elem.stake ); + } - fd_vote_accounts_pair_global_t_map_insert( - next_epoch_stakes_pool, - &next_epoch_stakes_root, - elem ); + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); - } - fd_vote_accounts_vote_accounts_pool_update( next_epoch_stakes, next_epoch_stakes_pool ); - fd_vote_accounts_vote_accounts_root_update( next_epoch_stakes, next_epoch_stakes_root ); - fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank ); + /* Copy the vote states for the current epoch E */ - } while(0); + fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( manifest_vote_accounts_pool, manifest_vote_accounts_root ); + n; + n = fd_vote_accounts_pair_global_t_map_successor( manifest_vote_accounts_pool, n ) ) { + uchar * account_data = fd_solana_account_data_join( &n->elem.value ); + ulong account_data_len = n->elem.value.data_len; + fd_vote_states_update_from_account( + vote_states, + &n->elem.key, + account_data, + account_data_len ); + fd_vote_states_update_stake( vote_states, &n->elem.key, n->elem.stake ); + } + fd_bank_vote_states_end_locking_modify( slot_ctx->bank ); - /* Copy the stakes delegations */ + /* Copy the stake delegations for the current epoch E*/ fd_stake_delegations_t * stake_delegations = fd_bank_stake_delegations_locking_modify( slot_ctx->bank ); stake_delegations = fd_stake_delegations_join( fd_stake_delegations_new( stake_delegations, FD_RUNTIME_MAX_STAKE_ACCOUNTS ) ); @@ -329,7 +246,6 @@ fd_exec_slot_ctx_recover( fd_exec_slot_ctx_t * slot_ctx, } fd_bank_stake_delegations_end_locking_modify( slot_ctx->bank ); - return slot_ctx; } diff --git a/src/flamenco/runtime/fd_bank.h b/src/flamenco/runtime/fd_bank.h index 2991a775b80..1adc824d741 100644 --- a/src/flamenco/runtime/fd_bank.h +++ b/src/flamenco/runtime/fd_bank.h @@ -6,6 +6,7 @@ #include "../features/fd_features.h" #include "../rewards/fd_epoch_rewards.h" #include "../stakes/fd_stake_delegations.h" +#include "../stakes/fd_vote_states.h" #include "../fd_rwlock.h" #include "fd_runtime_const.h" #include "fd_blockhashes.h" @@ -142,7 +143,6 @@ FD_PROTOTYPES_BEGIN #define FD_BANKS_ITER(X) \ /* type, name, footprint, align, CoW, limit fork width, has lock */ \ - X(fd_clock_timestamp_votes_global_t, clock_timestamp_votes, 5000000UL, 128UL, 1, 0, 1 ) /* TODO: This needs to get sized out */ \ X(fd_blockhashes_t, block_hash_queue, sizeof(fd_blockhashes_t), alignof(fd_blockhashes_t), 0, 0, 0 ) /* Block hash queue */ \ X(fd_fee_rate_governor_t, fee_rate_governor, sizeof(fd_fee_rate_governor_t), alignof(fd_fee_rate_governor_t), 0, 0, 0 ) /* Fee rate governor */ \ X(ulong, capitalization, sizeof(ulong), alignof(ulong), 0, 0, 0 ) /* Capitalization */ \ @@ -179,14 +179,6 @@ FD_PROTOTYPES_BEGIN X(fd_rent_t, rent, sizeof(fd_rent_t), alignof(fd_rent_t), 0, 0, 0 ) /* Rent */ \ X(fd_slot_lthash_t, lthash, sizeof(fd_slot_lthash_t), alignof(fd_slot_lthash_t), 0, 0, 1 ) /* LTHash */ \ X(fd_sysvar_cache_t, sysvar_cache, sizeof(fd_sysvar_cache_t), alignof(fd_sysvar_cache_t), 0, 0, 0 ) /* Sysvar cache */ \ - X(fd_vote_accounts_global_t, next_epoch_stakes, 200000000UL, 128UL, 1, 0, 1 ) /* Next epoch stakes, ~4K per account * 50k vote accounts */ \ - /* These are the stakes that determine the leader */ \ - /* schedule for the upcoming epoch. If we are executing */ \ - /* in epoch E, these are the stakes at the end of epoch */ \ - /* E-1 and they determined the leader schedule for epoch */ \ - /* E+1. */ \ - X(fd_vote_accounts_global_t, epoch_stakes, 200000000UL, 128UL, 1, 0, 1 ) /* Epoch stakes ~4K per account * 50k vote accounts */ \ - X(fd_vote_accounts_global_t, curr_epoch_stakes, 200000000UL, 128UL, 1, 0, 1 ) /* Stakes being accumulated in current epoch */ \ X(fd_epoch_rewards_t, epoch_rewards, FD_EPOCH_REWARDS_FOOTPRINT, FD_EPOCH_REWARDS_ALIGN, 1, 1, 1 ) /* Epoch rewards */ \ X(fd_epoch_leaders_t, epoch_leaders, FD_RUNTIME_MAX_EPOCH_LEADERS, FD_EPOCH_LEADERS_ALIGN, 1, 1, 1 ) /* Epoch leaders. If our system supports 100k vote accs, */ \ /* then there can be 100k unique leaders in the worst */ \ @@ -201,7 +193,13 @@ FD_PROTOTYPES_BEGIN X(ulong, shred_cnt, sizeof(ulong), alignof(ulong), 0, 0, 0 ) /* Shred count */ \ X(int, enable_exec_recording, sizeof(int), alignof(int), 0, 0, 0 ) /* Enable exec recording */ \ X(fd_stake_delegations_t, stake_delegations, FD_STAKE_DELEGATIONS_FOOTPRINT, FD_STAKE_DELEGATIONS_ALIGN, 1, 0, 1 ) /* Stake delegations */ \ - X(ulong, epoch, sizeof(ulong), alignof(ulong), 0, 0, 0 ) /* Epoch */ + X(ulong, epoch, sizeof(ulong), alignof(ulong), 0, 0, 0 ) /* Epoch */ \ + X(fd_vote_states_t, vote_states, FD_VOTE_STATES_FOOTPRINT, FD_VOTE_STATES_ALIGN, 1, 0, 1 ) /* Vote states for all vote accounts as of epoch E if */ \ + /* epoch E is the one that is currently being executed */ \ + X(fd_vote_states_t, vote_states_prev, FD_VOTE_STATES_FOOTPRINT, FD_VOTE_STATES_ALIGN, 1, 1, 1 ) /* Vote states for all vote accounts as of of the end of */ \ + /* epoch E-1 if epoch E is currently being executed */ \ + X(fd_vote_states_t, vote_states_prev_prev, FD_VOTE_STATES_FOOTPRINT, FD_VOTE_STATES_ALIGN, 1, 1, 1 ) /* Vote states for all vote accounts as of the end of */ \ + /* epoch E-2 if epoch E is currently being executed */ /* Invariant Every CoW field must have a rw-lock */ #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \ @@ -236,47 +234,29 @@ FD_PROTOTYPES_BEGIN #undef HAS_COW_0 #undef HAS_COW_1 -#define POOL_NAME fd_bank_clock_timestamp_votes_pool -#define POOL_T fd_bank_clock_timestamp_votes_t -#include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T - -#define POOL_NAME fd_bank_next_epoch_stakes_pool -#define POOL_T fd_bank_next_epoch_stakes_t -#include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T - -#define POOL_NAME fd_bank_epoch_stakes_pool -#define POOL_T fd_bank_epoch_stakes_t -#include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T - #define POOL_NAME fd_bank_epoch_leaders_pool #define POOL_T fd_bank_epoch_leaders_t #include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T #define POOL_NAME fd_bank_epoch_rewards_pool #define POOL_T fd_bank_epoch_rewards_t #include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T #define POOL_NAME fd_bank_stake_delegations_pool #define POOL_T fd_bank_stake_delegations_t #include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T -#define POOL_NAME fd_bank_curr_epoch_stakes_pool -#define POOL_T fd_bank_curr_epoch_stakes_t +#define POOL_NAME fd_bank_vote_states_pool +#define POOL_T fd_bank_vote_states_t +#include "../../util/tmpl/fd_pool.c" + +#define POOL_NAME fd_bank_vote_states_prev_pool +#define POOL_T fd_bank_vote_states_prev_t +#include "../../util/tmpl/fd_pool.c" + +#define POOL_NAME fd_bank_vote_states_prev_prev_pool +#define POOL_T fd_bank_vote_states_prev_prev_t #include "../../util/tmpl/fd_pool.c" -#undef POOL_NAME -#undef POOL_T /* As mentioned above, the overall layout of the bank struct: - Fields used for internal pool/bank management diff --git a/src/flamenco/runtime/fd_cost_tracker.h b/src/flamenco/runtime/fd_cost_tracker.h index 1deed84e67b..07b7265480f 100644 --- a/src/flamenco/runtime/fd_cost_tracker.h +++ b/src/flamenco/runtime/fd_cost_tracker.h @@ -45,8 +45,8 @@ FD_PROTOTYPES_BEGIN /* Initializes the cost tracker and allocates enough memory for the map */ void -fd_cost_tracker_init( fd_cost_tracker_t * self, - fd_spad_t * spad ); +fd_cost_tracker_init( fd_cost_tracker_t * self, + fd_spad_t * spad ); /* Modeled after `CostModel::calculate_cost_for_executed_transaction()`. Used to compute transaction cost information for executed transactions. diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index 197c9891d7a..02d3f6620b7 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -128,25 +128,15 @@ fd_runtime_update_leaders( fd_bank_t * bank, fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank ); - fd_vote_accounts_global_t const * epoch_vaccs = fd_bank_epoch_stakes_locking_query( bank ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_vaccs ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_root = fd_vote_accounts_vote_accounts_root_join( epoch_vaccs ); - ulong epoch = fd_slot_to_epoch ( epoch_schedule, slot, NULL ); ulong slot0 = fd_epoch_slot0 ( epoch_schedule, epoch ); ulong slot_cnt = fd_epoch_slot_cnt( epoch_schedule, epoch ); - fd_runtime_update_slots_per_epoch( bank, fd_epoch_slot_cnt( epoch_schedule, epoch ) ); - - ulong vote_acc_cnt = fd_vote_accounts_pair_global_t_map_size( vote_acc_pool, vote_acc_root ); - fd_bank_epoch_stakes_end_locking_query( bank ); - - fd_vote_stake_weight_t * epoch_weights = fd_spad_alloc_check( runtime_spad, alignof(fd_vote_stake_weight_t), vote_acc_cnt * sizeof(fd_vote_stake_weight_t) ); - ulong stake_weight_cnt = fd_stake_weights_by_node( epoch_vaccs, epoch_weights ); - - if( FD_UNLIKELY( stake_weight_cnt == ULONG_MAX ) ) { - FD_LOG_ERR(( "fd_stake_weights_by_node() failed" )); - } + fd_vote_states_t const * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( bank ); + ulong vote_acc_cnt = fd_vote_states_cnt( vote_states_prev_prev ) ; + fd_vote_stake_weight_t * epoch_weights = fd_spad_alloc_check( runtime_spad, alignof(fd_vote_stake_weight_t), vote_acc_cnt * sizeof(fd_vote_stake_weight_t) ); + ulong stake_weight_cnt = fd_stake_weights_by_node( vote_states_prev_prev, epoch_weights ); + fd_bank_vote_states_prev_prev_end_locking_query( bank ); /* Derive leader schedule */ @@ -163,21 +153,24 @@ fd_runtime_update_leaders( fd_bank_t * bank, ulong vote_keyed_lsched = (ulong)fd_runtime_should_use_vote_keyed_leader_schedule( bank ); void * epoch_leaders_mem = fd_bank_epoch_leaders_locking_modify( bank ); - fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( fd_epoch_leaders_new( epoch_leaders_mem, - epoch, - slot0, - slot_cnt, - stake_weight_cnt, - epoch_weights, - 0UL, - vote_keyed_lsched ) ); - fd_bank_epoch_leaders_end_locking_modify( bank ); + fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( fd_epoch_leaders_new( + epoch_leaders_mem, + epoch, + slot0, + slot_cnt, + stake_weight_cnt, + epoch_weights, + 0UL, + vote_keyed_lsched ) ); if( FD_UNLIKELY( !leaders ) ) { FD_LOG_ERR(( "Unable to init and join fd_epoch_leaders" )); } + fd_bank_epoch_leaders_end_locking_modify( bank ); } - } FD_SPAD_FRAME_END; + + fd_bank_epoch_leaders_locking_query( bank ); + fd_bank_epoch_leaders_end_locking_query( bank ); } /******************************************************************************/ @@ -525,12 +518,7 @@ fd_runtime_block_sysvar_update_pre_execute( fd_exec_slot_ctx_t * slot_ctx, fd_runtime_new_fee_rate_governor_derived( slot_ctx->bank, fd_bank_parent_signature_cnt_get( slot_ctx->bank ) ); - // TODO: move all these out to a fd_sysvar_update() call... - long clock_update_time = -fd_log_wallclock(); fd_sysvar_clock_update( slot_ctx, runtime_spad ); - clock_update_time += fd_log_wallclock(); - double clock_update_time_ms = (double)clock_update_time * 1e-6; - FD_LOG_INFO(( "clock updated - slot: %lu, elapsed: %6.6f ms", fd_bank_slot_get( slot_ctx->bank ), clock_update_time_ms )); // It has to go into the current txn previous info but is not in slot 0 if( fd_bank_slot_get( slot_ctx->bank ) != 0 ) { @@ -1160,7 +1148,6 @@ void fd_runtime_finalize_txn( fd_funk_t * funk, fd_funk_txn_t * funk_txn, fd_exec_txn_ctx_t * txn_ctx, - fd_spad_t * finalize_spad, fd_bank_t * bank, fd_capture_ctx_t * capture_ctx ) { @@ -1198,7 +1185,6 @@ fd_runtime_finalize_txn( fd_funk_t * funk, } else { int dirty_vote_acc = txn_ctx->dirty_vote_acc; - int dirty_stake_acc = txn_ctx->dirty_stake_acc; for( ushort i=0; iaccounts_cnt; i++ ) { /* We are only interested in saving writable accounts and the fee @@ -1212,50 +1198,18 @@ fd_runtime_finalize_txn( fd_funk_t * funk, FD_LOG_CRIT(( "fd_runtime_finalize_txn: failed to join account at idx %u", i )); } - /* Reclaim any accounts that have 0-lamports */ - fd_executor_reclaim_account( txn_ctx, &txn_ctx->accounts[i] ); - if( dirty_vote_acc && 0==memcmp( fd_txn_account_get_owner( acc_rec ), &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) { fd_vote_store_account( acc_rec, bank ); - FD_SPAD_FRAME_BEGIN( finalize_spad ) { - int err; - fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad( - vote_state_versioned, finalize_spad, - fd_txn_account_get_data( acc_rec ), - fd_txn_account_get_data_len( acc_rec ), - &err ); - if( FD_UNLIKELY( err ) ) { - FD_LOG_WARNING(( "failed to decode vote state versioned" )); - continue; - } - - fd_vote_block_timestamp_t const * ts = NULL; - switch( vsv->discriminant ) { - case fd_vote_state_versioned_enum_v0_23_5: - ts = &vsv->inner.v0_23_5.last_timestamp; - break; - case fd_vote_state_versioned_enum_v1_14_11: - ts = &vsv->inner.v1_14_11.last_timestamp; - break; - case fd_vote_state_versioned_enum_current: - ts = &vsv->inner.current.last_timestamp; - break; - default: - __builtin_unreachable(); - } - - fd_vote_record_timestamp_vote_with_slot( acc_rec->pubkey, - ts->timestamp, - ts->slot, - bank ); - } FD_SPAD_FRAME_END; } - if( dirty_stake_acc && 0==memcmp( fd_txn_account_get_owner( acc_rec ), &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) { - // TODO: does this correctly handle stake account close? + if( 0==memcmp( fd_txn_account_get_owner( acc_rec ), &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) { fd_update_stake_delegation( acc_rec, bank ); } + /* Reclaim any accounts that have 0-lamports, now that any related + cache updates have been applied. */ + fd_executor_reclaim_account( txn_ctx, &txn_ctx->accounts[i] ); + fd_runtime_save_account( funk, funk_txn, &txn_ctx->accounts[i], bank, txn_ctx->spad_wksp, capture_ctx ); } @@ -1400,38 +1354,29 @@ fd_runtime_prepare_and_execute_txn( fd_banks_t * banks, /* Epoch Boundary */ /******************************************************************************/ -/* Replace the stakes in T-2 (epoch_stakes) by the stakes at T-1 (next_epoch_stakes) */ -static void -fd_update_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) { - - /* Copy epoch_bank->next_epoch_stakes into fd_bank_slot_get( slot_ctx->bank )_bank.epoch_stakes */ - fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank ); - - fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank ); - fd_memcpy( epoch_stakes, next_epoch_stakes, fd_bank_epoch_stakes_footprint ); - fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank ); - - fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank ); +/* Replace the vote states for T-2 (vote_states_prev_prev) with the vote + states for T-1 (vote_states_prev) */ -} - -/* Copy stakes->vote_accounts into next_epoch_stakes. */ static void -fd_update_next_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) { - - /* FIXME: This is technically not correct, since the vote accounts - could be laid out after the stake delegations from fd_stakes. - The correct solution is to split out the stake delgations from the - vote accounts in fd_stakes. */ +fd_update_vote_states_prev_prev( fd_exec_slot_ctx_t * slot_ctx ) { - /* Copy stakes->vote_accounts into next_epoch_stakes */ - fd_vote_accounts_global_t const * vote_stakes = fd_bank_curr_epoch_stakes_locking_query( slot_ctx->bank ); + fd_vote_states_t * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ); + fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_locking_query( slot_ctx->bank ); + fd_memcpy( vote_states_prev_prev, vote_states_prev, fd_bank_vote_states_footprint ); + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_prev_end_locking_query( slot_ctx->bank ); +} - fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank ); - fd_memcpy( next_epoch_stakes, vote_stakes, fd_bank_next_epoch_stakes_footprint ); - fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank ); +/* Replace the vote states for T-1 (vote_states_prev) with the vote + states for T-1 (vote_states) */ - fd_bank_curr_epoch_stakes_end_locking_query( slot_ctx->bank ); +static void +fd_update_vote_states_prev( fd_exec_slot_ctx_t * slot_ctx ) { + fd_vote_states_t * vote_states_prev = fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ); + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( slot_ctx->bank ); + fd_memcpy( vote_states_prev, vote_states, fd_bank_vote_states_footprint ); + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); } /* Mimics bank.new_target_program_account(). Assumes out_rec is a @@ -2066,7 +2011,7 @@ static void fd_runtime_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx, ulong parent_epoch, fd_spad_t * runtime_spad ) { - FD_LOG_NOTICE(( "fd_process_new_epoch start" )); + FD_LOG_NOTICE(( "fd_process_new_epoch start, epoch: %lu, slot: %lu", fd_bank_epoch_get( slot_ctx->bank ), fd_bank_slot_get( slot_ctx->bank ) )); FD_SPAD_FRAME_BEGIN( runtime_spad ) { @@ -2098,23 +2043,17 @@ fd_runtime_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx, new_rate_activation_epoch = NULL; } - fd_epoch_info_t temp_info = {0}; - fd_epoch_info_new( &temp_info ); - - /* If appropiate, use the stakes at T-1 to generate the leader schedule instead of T-2. - This is due to a subtlety in how Agave's stake caches interact when loading from snapshots. - See the comment in fd_exec_slot_ctx_recover_. */ + /* If appropiate, use the stakes at T-1 to generate the leader + schedule instead of T-2. This is due to a subtlety in how Agave's + stake caches interact when loading from snapshots. + See the comment in fd_exec_slot_ctx_recover_. */ - if( fd_bank_use_prev_epoch_stake_get( slot_ctx->bank ) == epoch ) { - fd_update_epoch_stakes( slot_ctx ); + if( fd_bank_use_prev_epoch_stake_get( slot_ctx->bank )==epoch ) { + fd_update_vote_states_prev_prev( slot_ctx ); } /* Updates stake history sysvar accumulated values. */ - fd_stakes_activate_epoch( - slot_ctx, - new_rate_activation_epoch, - &temp_info, - runtime_spad ); + fd_stakes_activate_epoch( slot_ctx, new_rate_activation_epoch, runtime_spad ); /* Refresh vote accounts in stakes cache using updated stake weights, and merges slot bank vote accounts with the epoch bank vote accounts. https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L363-L370 */ @@ -2123,12 +2062,9 @@ fd_runtime_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx, FD_LOG_ERR(( "StakeHistory sysvar could not be read and decoded" )); } - /* FIXME: There are allocations made in here that are persisted. */ fd_refresh_vote_accounts( slot_ctx, history, - new_rate_activation_epoch, - &temp_info, - runtime_spad ); + new_rate_activation_epoch ); /* Distribute rewards */ @@ -2144,19 +2080,23 @@ fd_runtime_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx, slot_ctx->capture_ctx, &parent_blockhash, parent_epoch, - &temp_info, runtime_spad ); - /* Replace stakes at T-2 (epoch_stakes) by stakes at T-1 (next_epoch_stakes) */ - fd_update_epoch_stakes( slot_ctx ); - /* Replace stakes at T-1 (next_epoch_stakes) by updated stakes at T (stakes->vote_accounts) */ - fd_update_next_epoch_stakes( slot_ctx ); + /* Update vote_states_prev_prev with vote_states_prev */ + + fd_update_vote_states_prev_prev( slot_ctx ); + + /* Update vote_states_prev with vote_states */ + + fd_update_vote_states_prev( slot_ctx ); /* Update current leaders using epoch_stakes (new T-2 stakes) */ + fd_runtime_update_leaders( slot_ctx->bank, fd_bank_slot_get( slot_ctx->bank ), runtime_spad ); /* Increment the epoch. */ + fd_bank_epoch_set( slot_ctx->bank, fd_bank_epoch_get( slot_ctx->bank ) + 1UL ); FD_LOG_NOTICE(( "fd_process_new_epoch end" )); @@ -2305,12 +2245,14 @@ fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t * slot_ctx, /* Derive epoch stakes */ fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( fd_stake_delegations_new( fd_bank_stake_delegations_locking_modify( slot_ctx->bank ), 5000UL ) ); - FD_TEST( stake_delegations ); + if( FD_UNLIKELY( !stake_delegations ) ) { + FD_LOG_CRIT(( "Failed to join and new a stake delegations" )); + } - fd_vote_accounts_global_t * vote_accounts = fd_bank_curr_epoch_stakes_locking_modify( slot_ctx->bank ); - uchar * vacc_pool_mem = (uchar *)fd_ulong_align_up( (ulong)vote_accounts + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * vacc_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( vacc_pool_mem, 5000UL ) ); - fd_vote_accounts_pair_global_t_mapnode_t * vacc_root = NULL; + fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( slot_ctx->bank ), 5000UL, 999UL ) ); + if( FD_UNLIKELY( !vote_states ) ) { + FD_LOG_CRIT(( "Failed to join and new a vote states" )); + } fd_acc_lamports_t capitalization = 0UL; @@ -2319,27 +2261,8 @@ fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t * slot_ctx, capitalization = fd_ulong_sat_add( capitalization, acc->account.lamports ); if( !memcmp(acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t)) ) { - /* Vote Program Account */ - fd_vote_accounts_pair_global_t_mapnode_t * node = fd_vote_accounts_pair_global_t_map_acquire(vacc_pool); - FD_TEST( node ); - - fd_memcpy(node->elem.key.key, acc->key.key, sizeof(fd_pubkey_t)); - node->elem.stake = acc->account.lamports; - node->elem.value = (fd_solana_account_global_t){ - .lamports = acc->account.lamports, - .data_len = acc->account.data_len, - .data_offset = 0UL, /* FIXME: remove this field from the cache altogether. */ - .owner = acc->account.owner, - .executable = acc->account.executable, - .rent_epoch = acc->account.rent_epoch - }; - fd_solana_account_data_update( &node->elem.value, acc->account.data ); - - fd_vote_accounts_pair_global_t_map_insert( vacc_pool, &vacc_root, node ); - - FD_LOG_INFO(( "Adding genesis vote account: key=%s stake=%lu", - FD_BASE58_ENC_32_ALLOCA( node->elem.key.key ), - node->elem.stake )); + + fd_vote_states_update_from_account( vote_states, &acc->key, acc->account.data, acc->account.data_len ); } else if( !memcmp( acc->account.owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) { FD_SPAD_FRAME_BEGIN( runtime_spad ) { @@ -2413,97 +2336,30 @@ fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t * slot_ctx, } } - fd_vote_accounts_global_t * epoch_stakes = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank ); - uchar * pool_mem = (uchar *)fd_ulong_align_up( (ulong)epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, 50000UL ) ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = NULL; - - uchar * epoch_stakes_vote_acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)vote_accounts_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL ); - - fd_vote_accounts_global_t * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank ); - uchar * next_pool_mem = (uchar *)fd_ulong_align_up( (ulong)next_epoch_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * next_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( next_pool_mem, 50000UL ) ); - fd_vote_accounts_pair_global_t_mapnode_t * next_root = NULL; - - uchar * next_epoch_stakes_acc_region_curr = (uchar *)fd_ulong_align_up( (ulong)next_pool + fd_vote_accounts_pair_global_t_map_footprint( 50000UL ), 8UL ); + fd_vote_states_t * vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + fd_vote_states_t * vote_states_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ), FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); for( ulong i=0UL; iaccounts_len; i++ ) { fd_pubkey_account_pair_t const * acc = &genesis_block->accounts[i]; if( !memcmp( acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) { - /* Insert into the epoch_stakes vote accounts map */ - fd_vote_accounts_pair_global_t_mapnode_t * e = fd_vote_accounts_pair_global_t_map_acquire( vote_accounts_pool ); - FD_TEST( e ); - e->elem.key = acc->key; - e->elem.stake = acc->account.lamports; - e->elem.value = (fd_solana_account_global_t){ - .lamports = acc->account.lamports, - .data_len = acc->account.data_len, - .data_offset = 0UL, /* FIXME: remove this field from the cache altogether. */ - .owner = acc->account.owner, - .executable = acc->account.executable, - .rent_epoch = acc->account.rent_epoch - }; - - memcpy( epoch_stakes_vote_acc_region_curr, acc->account.data, acc->account.data_len ); - e->elem.value.data_offset = (ulong)(epoch_stakes_vote_acc_region_curr - (uchar *)&e->elem.value); - epoch_stakes_vote_acc_region_curr += acc->account.data_len; - - fd_vote_accounts_pair_global_t_map_insert( vote_accounts_pool, &vote_accounts_root, e ); - - /* Insert into the next_epoch_stakes vote accounts map */ - /* FIXME: is this correct? */ - fd_vote_accounts_pair_global_t_mapnode_t * next_e = fd_vote_accounts_pair_global_t_map_acquire( next_pool ); - FD_TEST( next_e ); - next_e->elem.key = acc->key; - next_e->elem.stake = acc->account.lamports; - next_e->elem.value = (fd_solana_account_global_t){ - .lamports = acc->account.lamports, - .data_len = acc->account.data_len, - .data_offset = 0UL, /* FIXME: remove this field from the cache altogether. */ - .owner = acc->account.owner, - .executable = acc->account.executable, - .rent_epoch = acc->account.rent_epoch - }; - - memcpy( next_epoch_stakes_acc_region_curr, acc->account.data, acc->account.data_len ); - next_e->elem.value.data_offset = (ulong)(next_epoch_stakes_acc_region_curr - (uchar *)&next_e->elem.value); - next_epoch_stakes_acc_region_curr += acc->account.data_len; - - fd_vote_accounts_pair_global_t_map_insert( next_pool, &next_root, next_e ); + fd_vote_states_update_from_account( vote_states_prev_prev, &acc->key, acc->account.data, acc->account.data_len ); + fd_vote_states_update_from_account( vote_states_prev, &acc->key, acc->account.data, acc->account.data_len ); } - } - fd_vote_accounts_vote_accounts_pool_update( epoch_stakes, vote_accounts_pool ); - fd_vote_accounts_vote_accounts_root_update( epoch_stakes, vote_accounts_root ); - - - fd_vote_accounts_vote_accounts_pool_update( next_epoch_stakes, next_pool ); - fd_vote_accounts_vote_accounts_root_update( next_epoch_stakes, next_root ); - - fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank ); - - fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); fd_bank_epoch_set( slot_ctx->bank, 0UL ); - fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vacc_pool ); - fd_vote_accounts_vote_accounts_root_update( vote_accounts, vacc_root ); - fd_bank_curr_epoch_stakes_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_end_locking_modify( slot_ctx->bank ); fd_bank_stake_delegations_end_locking_modify( slot_ctx->bank ); fd_bank_capitalization_set( slot_ctx->bank, capitalization ); - - fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank ); - uchar * clock_pool_mem = (uchar *)fd_ulong_align_up( (ulong)clock_timestamp_votes + sizeof(fd_clock_timestamp_votes_global_t), fd_clock_timestamp_vote_t_map_align() ); - fd_clock_timestamp_vote_t_mapnode_t * clock_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new(clock_pool_mem, 30000UL ) ); - clock_timestamp_votes->votes_pool_offset = (ulong)fd_clock_timestamp_vote_t_map_leave( clock_pool) - (ulong)clock_timestamp_votes; - clock_timestamp_votes->votes_root_offset = 0UL; - fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank ); } static int diff --git a/src/flamenco/runtime/fd_runtime.h b/src/flamenco/runtime/fd_runtime.h index ca4f9d0667c..d44a55a933e 100644 --- a/src/flamenco/runtime/fd_runtime.h +++ b/src/flamenco/runtime/fd_runtime.h @@ -513,7 +513,6 @@ void fd_runtime_finalize_txn( fd_funk_t * funk, fd_funk_txn_t * funk_txn, fd_exec_txn_ctx_t * txn_ctx, - fd_spad_t * finalize_spad, fd_bank_t * bank, fd_capture_ctx_t * capture_ctx ); diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 472af88fe25..c26d95dde40 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -1913,24 +1913,6 @@ do_process_vote_state_update( fd_vote_state_t * vote_state, ctx ); } -// ?? -ulong -fd_query_pubkey_stake( fd_pubkey_t const * pubkey, fd_vote_accounts_global_t const * vote_accounts ) { - fd_vote_accounts_pair_global_t_mapnode_t key = { 0 }; - key.elem.key = *pubkey; - - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); - - if( !vote_accounts_pool && !vote_accounts_root ) { - return 0; - } - - fd_vote_accounts_pair_global_t_mapnode_t * vote_node = fd_vote_accounts_pair_global_t_map_find( - vote_accounts_pool, vote_accounts_root, &key ); - return vote_node ? vote_node->elem.stake : 0; -} - static int process_vote_state_update( fd_borrowed_account_t * vote_account, fd_slot_hash_t const * slot_hashes, @@ -1944,7 +1926,15 @@ process_vote_state_update( fd_borrowed_account_t * vote_account, // // There is no corresponding code in anza - fd_vote_accounts_global_t const * vote_accounts = fd_bank_curr_epoch_stakes_locking_query( ctx->txn_ctx->bank ); + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( ctx->txn_ctx->bank ); + if( !vote_states ) { + FD_LOG_CRIT(( "vote_states is NULL" )); + } + + fd_vote_state_ele_t * vote_state_ele = fd_vote_states_query( vote_states, vote_account->acct->pubkey ); + if( !vote_state_ele ) { + FD_LOG_CRIT(( "vote_state is NULL" )); + } if( !deq_fd_vote_lockout_t_empty( vote_state_update->lockouts ) ) { fd_vote_lockout_t * lockout = deq_fd_vote_lockout_t_peek_tail( vote_state_update->lockouts ); @@ -1956,13 +1946,12 @@ process_vote_state_update( fd_borrowed_account_t * vote_account, lockout->slot, &vote_state_update->hash, 0, - fd_query_pubkey_stake( vote_account->acct->pubkey, - vote_accounts ) ); + vote_state_ele->stake ); fd_bank_hash_cmp_unlock( bank_hash_cmp ); } } - fd_bank_curr_epoch_stakes_end_locking_query( ctx->txn_ctx->bank ); + fd_bank_vote_states_end_locking_query( ctx->txn_ctx->bank ); fd_vote_state_t vote_state; // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1144 @@ -2031,19 +2020,22 @@ process_tower_sync( fd_borrowed_account_t * vote_account, if( !deq_fd_vote_lockout_t_empty( tower_sync->lockouts ) ) { fd_vote_lockout_t * lockout = deq_fd_vote_lockout_t_peek_tail( tower_sync->lockouts ); fd_bank_hash_cmp_t * bank_hash_cmp = ctx->txn_ctx->bank_hash_cmp; - fd_vote_accounts_global_t const * vote_accounts = fd_bank_curr_epoch_stakes_locking_query( ctx->txn_ctx->bank ); - if( FD_LIKELY( lockout && bank_hash_cmp ) ) { + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( ctx->txn_ctx->bank ); + if( !vote_states ) { + FD_LOG_CRIT(( "vote_states is NULL" )); + } + fd_vote_state_ele_t * vote_state_ele = fd_vote_states_query( vote_states, vote_account->acct->pubkey ); + if( FD_LIKELY( lockout && bank_hash_cmp && vote_state_ele ) ) { fd_bank_hash_cmp_lock( bank_hash_cmp ); fd_bank_hash_cmp_insert( - bank_hash_cmp, + bank_hash_cmp, lockout->slot, &tower_sync->hash, 0, - fd_query_pubkey_stake( vote_account->acct->pubkey, - vote_accounts ) ); + vote_state_ele->stake ); fd_bank_hash_cmp_unlock( bank_hash_cmp ); } - fd_bank_curr_epoch_stakes_end_locking_query( ctx->txn_ctx->bank ); + fd_bank_vote_states_end_locking_query( ctx->txn_ctx->bank ); } // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1194 @@ -2111,54 +2103,15 @@ fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update, return 1; } -void -fd_vote_record_timestamp_vote_with_slot( fd_pubkey_t const * vote_acc, - long timestamp, - ulong slot, - fd_bank_t * bank ) { - - fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( bank ); - - fd_clock_timestamp_vote_t_mapnode_t * pool = fd_clock_timestamp_votes_votes_pool_join( clock_timestamp_votes ); - fd_clock_timestamp_vote_t_mapnode_t * root = fd_clock_timestamp_votes_votes_root_join( clock_timestamp_votes ); - - if( FD_UNLIKELY( !pool ) ) { - FD_LOG_ERR(( "Timestamp vote account pool not allocated" )); - } - - fd_clock_timestamp_vote_t timestamp_vote = { - .pubkey = *vote_acc, - .timestamp = (long)timestamp, - .slot = slot, - }; - fd_clock_timestamp_vote_t_mapnode_t key = { .elem = timestamp_vote }; - fd_clock_timestamp_vote_t_mapnode_t * node = - fd_clock_timestamp_vote_t_map_find( pool, root, &key ); - if( NULL != node ) { - node->elem = timestamp_vote; - } else { - node = fd_clock_timestamp_vote_t_map_acquire( pool ); - FD_TEST( node != NULL ); - node->elem = timestamp_vote; - fd_clock_timestamp_vote_t_map_insert( pool, &root, node ); - } - - fd_clock_timestamp_votes_votes_pool_update( clock_timestamp_votes, pool ); - fd_clock_timestamp_votes_votes_root_update( clock_timestamp_votes, root ); - - fd_bank_clock_timestamp_votes_end_locking_modify( bank ); -} - /// returns commission split as (voter_portion, staker_portion, was_split) tuple /// /// if commission calculation is 100% one way or other, indicate with false for was_split // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L543 void -fd_vote_commission_split( fd_vote_state_versioned_t * vote_state_versioned, - ulong on, - fd_commission_split_t * result ) { - uchar commission = (uchar)fd_vote_account_commission( vote_state_versioned ); +fd_vote_commission_split( uchar commission, + ulong on, + fd_commission_split_t * result ) { uint commission_split = fd_uint_min( (uint)commission, 100 ); result->is_split = ( commission_split != 0 && commission_split != 100 ); // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L545 @@ -2842,62 +2795,27 @@ fd_vote_convert_to_current( fd_vote_state_versioned_t * self, } static void -remove_vote_account( fd_txn_account_t * vote_account, - fd_bank_t * bank ) { - - fd_vote_accounts_global_t * epoch_vote_accounts = fd_bank_curr_epoch_stakes_locking_modify( bank ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( epoch_vote_accounts ); - - if( FD_UNLIKELY( epoch_vote_accounts_pool==NULL ) ) { - fd_bank_curr_epoch_stakes_end_locking_modify( bank ); - return; - } - - fd_vote_accounts_pair_global_t_mapnode_t vote_acc; - fd_memcpy( vote_acc.elem.key.uc, vote_account->pubkey->uc, sizeof(fd_pubkey_t) ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_account_entry = fd_vote_accounts_pair_global_t_map_find( epoch_vote_accounts_pool, epoch_vote_accounts_root, &vote_acc ); - if( FD_LIKELY( vote_account_entry ) ) { - fd_vote_accounts_pair_global_t_map_remove( epoch_vote_accounts_pool, &epoch_vote_accounts_root, vote_account_entry); - } +remove_vote_account( fd_txn_account_t * vote_account, + fd_bank_t * bank ) { - fd_vote_accounts_vote_accounts_pool_update( epoch_vote_accounts, epoch_vote_accounts_pool ); - fd_vote_accounts_vote_accounts_root_update( epoch_vote_accounts, epoch_vote_accounts_root ); - fd_bank_curr_epoch_stakes_end_locking_modify( bank ); + fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank ); + fd_vote_states_remove( vote_states, vote_account->pubkey ); + fd_bank_vote_states_end_locking_modify( bank ); } static void -upsert_vote_account( fd_txn_account_t * vote_account, - fd_bank_t * bank ) { - - fd_vote_accounts_global_t * vote_accounts = fd_bank_curr_epoch_stakes_locking_modify( bank ); - fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); +upsert_vote_account( fd_txn_account_t * vote_account, + fd_bank_t * bank ) { if( fd_vote_state_versions_is_correct_and_initialized( vote_account ) ) { - fd_account_keys_pair_t_mapnode_t key; - fd_memcpy( &key.elem.key, vote_account->pubkey->uc, sizeof(fd_pubkey_t) ); - - fd_vote_accounts_pair_global_t_mapnode_t vote_acc; - fd_memcpy( &vote_acc.elem.key, vote_account->pubkey->uc, sizeof(fd_pubkey_t) ); - - // Skip duplicates - if( FD_LIKELY( fd_vote_accounts_pair_global_t_map_find( stakes_vote_accounts_pool, stakes_vote_accounts_root, &vote_acc ) ) ) { - fd_bank_curr_epoch_stakes_end_locking_modify( bank ); - return; - } - - fd_vote_accounts_pair_global_t_mapnode_t * new_node = fd_vote_accounts_pair_global_t_map_acquire( stakes_vote_accounts_pool ); - if( FD_UNLIKELY( !new_node ) ) { - FD_LOG_CRIT(( "Map full" )); - } - - fd_memcpy( &new_node->elem.key, vote_account->pubkey, sizeof(fd_pubkey_t) ); - - fd_vote_accounts_pair_global_t_map_insert( stakes_vote_accounts_pool, &stakes_vote_accounts_root, new_node ); - fd_bank_curr_epoch_stakes_end_locking_modify( bank ); + fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank ); + fd_vote_states_update_from_account( + vote_states, + vote_account->pubkey, + fd_txn_account_get_data( vote_account ), + fd_txn_account_get_data_len( vote_account ) ); + fd_bank_vote_states_end_locking_modify( bank ); } else { - fd_bank_curr_epoch_stakes_end_locking_modify( bank ); remove_vote_account( vote_account, bank ); } } @@ -2907,8 +2825,8 @@ fd_vote_store_account( fd_txn_account_t * vote_account, fd_bank_t * bank ) { fd_pubkey_t const * owner = fd_txn_account_get_owner( vote_account ); - if (memcmp(owner->uc, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t)) != 0) { - return; + if( FD_UNLIKELY( memcmp( owner->uc, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) ) { + return; } if( fd_txn_account_get_lamports( vote_account ) == 0 ) { diff --git a/src/flamenco/runtime/program/fd_vote_program.h b/src/flamenco/runtime/program/fd_vote_program.h index a1c494a67ca..5ee19ffd849 100644 --- a/src/flamenco/runtime/program/fd_vote_program.h +++ b/src/flamenco/runtime/program/fd_vote_program.h @@ -51,11 +51,6 @@ fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ); uint fd_vote_state_versions_is_correct_and_initialized( fd_txn_account_t * vote_account ); -/* Queries the delegated stake amount for the given vote account pubkey, - given the vote accounts map. Returns 0 if nonexistent. */ -ulong -fd_query_pubkey_stake( fd_pubkey_t const * pubkey, fd_vote_accounts_global_t const * vote_accounts ); - /* An implementation of solana_sdk::transaction_context::BorrowedAccount::get_state for setting the vote state. @@ -69,12 +64,6 @@ void fd_vote_convert_to_current( fd_vote_state_versioned_t * self, fd_spad_t * spad ); -void -fd_vote_record_timestamp_vote_with_slot( fd_pubkey_t const * vote_acc, - long timestamp, - ulong slot, - fd_bank_t * bank ); - struct fd_commission_split { ulong voter_portion; ulong staker_portion; @@ -102,9 +91,9 @@ fd_vote_account_commission( fd_vote_state_versioned_t const * vote_state_version } void -fd_vote_commission_split( fd_vote_state_versioned_t * vote_state_versioned, - ulong on, - fd_commission_split_t * result ); +fd_vote_commission_split( uchar commission, + ulong on, + fd_commission_split_t * result ); void fd_vote_store_account( fd_txn_account_t * vote_account, diff --git a/src/flamenco/runtime/program/test_program_cache.c b/src/flamenco/runtime/program/test_program_cache.c index 8a5889c0d13..2167a383bc1 100644 --- a/src/flamenco/runtime/program/test_program_cache.c +++ b/src/flamenco/runtime/program/test_program_cache.c @@ -635,7 +635,7 @@ main( int argc, FD_LOG_NOTICE(( "Starting BPF program cache tests" )); /* Create workspace */ - test_wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 2UL, fd_log_cpu_id(), "test_wksp", 0UL ); + test_wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 3UL, fd_log_cpu_id(), "test_wksp", 0UL ); FD_TEST( test_wksp ); /* Create funk */ @@ -709,7 +709,7 @@ main( int argc, test_teardown(); - FD_LOG_NOTICE(( "All BPF program cache tests passed" )); + FD_LOG_NOTICE(( "pass" )); fd_halt(); return 0; } diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_clock.c b/src/flamenco/runtime/sysvar/fd_sysvar_clock.c index 10bd1e57dfb..f359e5fc16b 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_clock.c +++ b/src/flamenco/runtime/sysvar/fd_sysvar_clock.c @@ -120,19 +120,27 @@ estimate_timestamp( fd_bank_t * bank ) { /* TODO: bound the estimate to ensure it stays within a certain range of the expected PoH clock: https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/runtime/src/stake_weighted_timestamp.rs#L13 */ - fd_clock_timestamp_votes_global_t const * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_query( bank ); - fd_clock_timestamp_vote_t_mapnode_t * votes = !!clock_timestamp_votes ? fd_clock_timestamp_votes_votes_root_join( clock_timestamp_votes ) : NULL; - if( NULL==votes ) { - fd_bank_clock_timestamp_votes_end_locking_query( bank ); + /* TODO: actually take the stake-weighted median. For now, just use a node. */ + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( bank ); + + if( !fd_vote_states_cnt( vote_states ) ) { + fd_bank_vote_states_end_locking_query( bank ); return timestamp_from_genesis( bank ); } - /* TODO: actually take the stake-weighted median. For now, just use the root node. */ - fd_clock_timestamp_vote_t * head = &votes->elem; - ulong slots = fd_bank_slot_get( bank ) - head->slot; - uint128 ns_correction = fd_bank_ns_per_slot_get( bank ) * slots; - fd_bank_clock_timestamp_votes_end_locking_query( bank ); - return head->timestamp + (long) (ns_correction / NS_IN_S) ; + fd_vote_states_iter_t iter[1]; + fd_vote_states_iter_init( vote_states, iter ); + + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); + + ulong slots = fd_bank_slot_get( bank ) - vote_state->last_vote_slot; + uint128 ns_correction = fd_bank_ns_per_slot_get( bank ) * slots; + + long timestamp = vote_state->last_vote_timestamp + (long)(ns_correction / NS_IN_S); + + fd_bank_vote_states_end_locking_query( bank ); + + return timestamp; } #define CIDX_T ulong @@ -202,105 +210,42 @@ fd_calculate_stake_weighted_timestamp( fd_exec_slot_ctx_t * slot_ctx, ulong total_stake = 0; - fd_clock_timestamp_votes_global_t const * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_query( bank ); - if( FD_UNLIKELY( !clock_timestamp_votes ) ) { - fd_bank_clock_timestamp_votes_end_locking_query( bank ); - *result_timestamp = 0; - return; - } - - fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_pool = fd_clock_timestamp_votes_votes_pool_join( clock_timestamp_votes ); - fd_clock_timestamp_vote_t_mapnode_t * timestamp_votes_root = fd_clock_timestamp_votes_votes_root_join( clock_timestamp_votes ); - - fd_vote_accounts_global_t const * epoch_stakes = fd_bank_epoch_stakes_locking_query( bank ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_root = fd_vote_accounts_vote_accounts_root_join( epoch_stakes ); + /* TODO: this calculation currently uses stake values as of the start + of the current epoch. Make sure that is correct. */ + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( bank ); - for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum(vote_acc_pool, vote_acc_root); - n; - n = fd_vote_accounts_pair_global_t_map_successor( vote_acc_pool, n ) ) { + fd_vote_states_iter_t iter[1]; + for( fd_vote_states_iter_init( vote_states, iter ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); - /* get timestamp */ - fd_pubkey_t const * vote_pubkey = &n->elem.key; + ulong vote_timestamp = (ulong)vote_state->last_vote_timestamp; + ulong vote_slot = vote_state->last_vote_slot; - if( timestamp_votes_pool == NULL ) { + ulong slot_delta = fd_ulong_sat_sub(fd_bank_slot_get( bank ), vote_slot); + fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank ); + if( slot_delta > epoch_schedule->slots_per_epoch ) { continue; - } else { - fd_clock_timestamp_vote_t_mapnode_t query_vote_acc_node; - query_vote_acc_node.elem.pubkey = *vote_pubkey; - fd_clock_timestamp_vote_t_mapnode_t * vote_acc_node = fd_clock_timestamp_vote_t_map_find( timestamp_votes_pool, - timestamp_votes_root, - &query_vote_acc_node ); - ulong vote_timestamp = 0; - ulong vote_slot = 0; - if( vote_acc_node == NULL ) { - int err; - - uchar * data = fd_solana_account_data_join( &n->elem.value ); - ulong data_len = n->elem.value.data_len; - - FD_SPAD_FRAME_BEGIN( spad ) { - fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad( - vote_state_versioned, spad, - data, - data_len, - &err ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) { - FD_LOG_WARNING(( "Vote state versioned decode failed" )); - continue; - } - - switch( vsv->discriminant ) { - case fd_vote_state_versioned_enum_v0_23_5: - vote_timestamp = (ulong)vsv->inner.v0_23_5.last_timestamp.timestamp; - vote_slot = vsv->inner.v0_23_5.last_timestamp.slot; - break; - case fd_vote_state_versioned_enum_v1_14_11: - vote_timestamp = (ulong)vsv->inner.v1_14_11.last_timestamp.timestamp; - vote_slot = vsv->inner.v1_14_11.last_timestamp.slot; - break; - case fd_vote_state_versioned_enum_current: - vote_timestamp = (ulong)vsv->inner.current.last_timestamp.timestamp; - vote_slot = vsv->inner.current.last_timestamp.slot; - break; - default: - __builtin_unreachable(); - } - } - FD_SPAD_FRAME_END; - - } else { - vote_timestamp = (ulong)vote_acc_node->elem.timestamp; - vote_slot = vote_acc_node->elem.slot; - } - - ulong slot_delta = fd_ulong_sat_sub(fd_bank_slot_get( bank ), vote_slot); - fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( bank ); - if( slot_delta > epoch_schedule->slots_per_epoch ) { - continue; - } + } - ulong offset = fd_ulong_sat_mul(slot_duration, slot_delta); - long estimate = (long)vote_timestamp + (long)(offset / NS_IN_S); - /* get stake */ - total_stake += n->elem.stake; - ulong treap_idx = stake_ts_treap_idx_query( treap, estimate, pool ); - if ( FD_LIKELY( treap_idx < ULONG_MAX ) ) { - pool[ treap_idx ].stake += n->elem.stake; - } else { - if( 0 == stake_ts_pool_free( pool ) ) { - FD_LOG_ERR(( "stake_ts_pool is empty" )); - } - ulong idx = stake_ts_pool_idx_acquire( pool ); - pool[ idx ].prio_cidx = fd_rng_ulong( rng ); - pool[ idx ].timestamp = estimate; - pool[ idx ].stake = n->elem.stake; - stake_ts_treap_idx_insert( treap, idx, pool ); + ulong offset = fd_ulong_sat_mul(slot_duration, slot_delta); + long estimate = (long)vote_timestamp + (long)(offset / NS_IN_S); + /* get stake */ + total_stake += vote_state->stake; + ulong treap_idx = stake_ts_treap_idx_query( treap, estimate, pool ); + if ( FD_LIKELY( treap_idx < ULONG_MAX ) ) { + pool[ treap_idx ].stake += vote_state->stake; + } else { + if( 0 == stake_ts_pool_free( pool ) ) { + FD_LOG_ERR(( "stake_ts_pool is empty" )); } + ulong idx = stake_ts_pool_idx_acquire( pool ); + pool[ idx ].prio_cidx = fd_rng_ulong( rng ); + pool[ idx ].timestamp = estimate; + pool[ idx ].stake = vote_state->stake; + stake_ts_treap_idx_insert( treap, idx, pool ); } } - fd_bank_epoch_stakes_end_locking_query( bank ); - fd_bank_clock_timestamp_votes_end_locking_query( bank ); + fd_bank_vote_states_end_locking_query( bank ); *result_timestamp = 0; if( total_stake == 0 ) { diff --git a/src/flamenco/runtime/test_bank.c b/src/flamenco/runtime/test_bank.c index fa18d85fe2b..bca97980b1f 100644 --- a/src/flamenco/runtime/test_bank.c +++ b/src/flamenco/runtime/test_bank.c @@ -7,7 +7,7 @@ main( int argc, char ** argv ) { char * _page_sz = "gigantic"; ulong numa_idx = fd_shmem_numa_idx( 0 ); fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz( _page_sz ), - 20UL, + 25UL, fd_shmem_cpu_idx( numa_idx ), "wksp", 0UL ); @@ -181,14 +181,13 @@ main( int argc, char ** argv ) { keys->magic = 101UL; fd_bank_stake_delegations_end_locking_modify( bank11 ); - fd_clock_timestamp_votes_global_t const * votes_const = fd_bank_clock_timestamp_votes_locking_query( bank11 ); + fd_vote_states_t const * votes_const = fd_bank_vote_states_locking_query( bank11 ); FD_TEST( !votes_const ); - fd_bank_clock_timestamp_votes_end_locking_query( bank11 ); + fd_bank_vote_states_end_locking_query( bank11 ); - fd_clock_timestamp_votes_global_t * votes = fd_bank_clock_timestamp_votes_locking_modify( bank11 ); - votes->votes_pool_offset = 102UL; - votes->votes_root_offset = 102UL; - fd_bank_clock_timestamp_votes_end_locking_modify( bank11 ); + fd_vote_states_t * votes = fd_bank_vote_states_locking_modify( bank11 ); + votes->magic = 102UL; + fd_bank_vote_states_end_locking_modify( bank11 ); /* Now there should be 3 forks: 1. 7 (1234) -> 8 @@ -217,10 +216,9 @@ main( int argc, char ** argv ) { FD_TEST( keys4->magic == 101UL ); fd_bank_stake_delegations_end_locking_query( bank11 ); - votes_const = fd_bank_clock_timestamp_votes_locking_query( bank11 ); - FD_TEST( votes->votes_pool_offset == 102UL ); - FD_TEST( votes->votes_root_offset == 102UL ); - fd_bank_clock_timestamp_votes_end_locking_query( bank11 ); + votes_const = fd_bank_vote_states_locking_query( bank11 ); + FD_TEST( votes->magic == 102UL ); + fd_bank_vote_states_end_locking_query( bank11 ); /* Clear bank11, we need to make sure that the pool indices are cleared and properly released. @@ -243,9 +241,9 @@ main( int argc, char ** argv ) { FD_TEST( keys4->magic == 101UL ); fd_bank_stake_delegations_end_locking_query( bank11 ); - votes_const = fd_bank_clock_timestamp_votes_locking_query( bank11 ); + votes_const = fd_bank_vote_states_locking_query( bank11 ); FD_TEST( !votes_const ); - fd_bank_clock_timestamp_votes_end_locking_query( bank11 ); + fd_bank_vote_states_end_locking_query( bank11 ); FD_TEST( fd_banks_leave( banks ) ); FD_TEST( fd_banks_join( fd_banks_leave( banks ) ) == banks ); diff --git a/src/flamenco/runtime/tests/fd_block_harness.c b/src/flamenco/runtime/tests/fd_block_harness.c index 4c9d179d0cf..7924491797b 100644 --- a/src/flamenco/runtime/tests/fd_block_harness.c +++ b/src/flamenco/runtime/tests/fd_block_harness.c @@ -19,9 +19,8 @@ /* Stripped down version of `fd_refresh_vote_accounts()` that simply refreshes the stake delegation amount for each of the vote accounts using the stake delegations cache. */ static void -fd_runtime_fuzz_block_refresh_vote_accounts( fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool, - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root, - fd_stake_delegations_t * stake_delegations ) { +fd_runtime_fuzz_block_refresh_vote_accounts( fd_vote_states_t * vote_states, + fd_stake_delegations_t * stake_delegations ) { fd_stake_delegation_map_t * map = fd_stake_delegations_get_map( stake_delegations ); fd_stake_delegation_t * pool = fd_stake_delegations_get_pool( stake_delegations ); @@ -34,12 +33,10 @@ fd_runtime_fuzz_block_refresh_vote_accounts( fd_vote_accounts_pair_global_t_mapn ulong stake = node->stake; /* Find the voter in the vote accounts cache and update their delegation amount */ - fd_vote_accounts_pair_global_t_mapnode_t vode_node[1]; - fd_memcpy( vode_node->elem.key.uc, voter_pubkey, sizeof(fd_pubkey_t) ); - fd_vote_accounts_pair_global_t_mapnode_t * found_node = fd_vote_accounts_pair_global_t_map_find( vote_accounts_pool, vote_accounts_root, vode_node ); - if( FD_LIKELY( found_node ) ) { - found_node->elem.stake += stake; - } + fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, voter_pubkey ); + ulong vote_stake = vote_state->stake; + fd_vote_states_update_stake( vote_states, voter_pubkey, vote_stake + stake ); + } } @@ -47,11 +44,10 @@ fd_runtime_fuzz_block_refresh_vote_accounts( fd_vote_accounts_pair_global_t_mapn from the current present account state. This function also registers a vote timestamp for the vote account */ static void -fd_runtime_fuzz_block_register_vote_account( fd_exec_slot_ctx_t * slot_ctx, - fd_vote_accounts_pair_global_t_mapnode_t * pool, - fd_vote_accounts_pair_global_t_mapnode_t ** root, - fd_pubkey_t * pubkey, - fd_spad_t * spad ) { +fd_runtime_fuzz_block_register_vote_account( fd_exec_slot_ctx_t * slot_ctx, + fd_vote_states_t * vote_states, + fd_pubkey_t * pubkey, + fd_spad_t * spad ) { FD_TXN_ACCOUNT_DECL( acc ); if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( acc, pubkey, slot_ctx->funk, slot_ctx->funk_txn ) ) ) { return; @@ -79,47 +75,11 @@ fd_runtime_fuzz_block_register_vote_account( fd_exec_slot_ctx_t * return; } - /* Nothing to do if the account already exists in the cache */ - fd_vote_accounts_pair_global_t_mapnode_t existing_node[1]; - fd_memcpy( existing_node->elem.key.uc, pubkey, sizeof(fd_pubkey_t) ); - if( fd_vote_accounts_pair_global_t_map_find( pool, *root, existing_node ) ) { - return; - } - - /* At this point, the node is new and needs to be inserted into the cache. */ - fd_vote_accounts_pair_global_t_mapnode_t * node_to_insert = fd_vote_accounts_pair_global_t_map_acquire( pool ); - fd_memcpy( node_to_insert->elem.key.uc, pubkey, sizeof(fd_pubkey_t) ); - - ulong account_dlen = fd_txn_account_get_data_len( acc ); - node_to_insert->elem.stake = 0UL; // This will get set later - node_to_insert->elem.value.executable = !!fd_txn_account_is_executable( acc ); - node_to_insert->elem.value.lamports = fd_txn_account_get_lamports( acc ); - node_to_insert->elem.value.rent_epoch = fd_txn_account_get_rent_epoch( acc ); - node_to_insert->elem.value.data_len = account_dlen; - - uchar * data = fd_spad_alloc( spad, alignof(uchar), account_dlen ); - memcpy( data, fd_txn_account_get_data( acc ), account_dlen ); - fd_solana_account_data_update( &node_to_insert->elem.value, data ); - - fd_vote_accounts_pair_global_t_map_insert( pool, root, node_to_insert ); - - /* Record a timestamp for the vote account */ - fd_vote_block_timestamp_t const * ts = NULL; - switch( vsv->discriminant ) { - case fd_vote_state_versioned_enum_v0_23_5: - ts = &vsv->inner.v0_23_5.last_timestamp; - break; - case fd_vote_state_versioned_enum_v1_14_11: - ts = &vsv->inner.v1_14_11.last_timestamp; - break; - case fd_vote_state_versioned_enum_current: - ts = &vsv->inner.current.last_timestamp; - break; - default: - __builtin_unreachable(); - } - - fd_vote_record_timestamp_vote_with_slot( pubkey, ts->timestamp, ts->slot, slot_ctx->bank ); + fd_vote_states_update_from_account( + vote_states, + acc->pubkey, + fd_txn_account_get_data( acc ), + fd_txn_account_get_data_len( acc ) ); } /* Stores an entry in the stake delegations cache for the given vote account. Deserializes and uses the present @@ -170,27 +130,38 @@ fd_runtime_fuzz_block_register_stake_delegation( fd_exec_slot_ctx_t * slot_c static void fd_runtime_fuzz_block_update_prev_epoch_votes_cache( fd_vote_accounts_pair_global_t_mapnode_t * pool, fd_vote_accounts_pair_global_t_mapnode_t ** root, + fd_vote_states_t * vote_states, fd_exec_test_vote_account_t * vote_accounts, pb_size_t vote_accounts_cnt, fd_spad_t * spad ) { for( uint i=0U; ielem.stake = stake; - fd_memcpy( &vote_node->elem.key, vote_account->address, sizeof(fd_pubkey_t) ); - vote_node->elem.value.executable = vote_account->executable; - vote_node->elem.value.lamports = vote_account->lamports; - vote_node->elem.value.rent_epoch = vote_account->rent_epoch; - vote_node->elem.value.data_len = vote_account->data->size; - fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) ); - - uchar * data = fd_spad_alloc( spad, alignof(uchar), vote_account->data->size ); - memcpy( data, vote_account->data->bytes, vote_account->data->size ); - fd_solana_account_data_update( &vote_node->elem.value, data ); - - fd_vote_accounts_pair_global_t_map_insert( pool, root, vote_node ); + fd_exec_test_acct_state_t * vote_account = &vote_accounts[i].vote_account; + ulong stake = vote_accounts[i].stake; + uchar * vote_data = vote_account->data->bytes; + ulong vote_data_len = vote_account->data->size; + fd_pubkey_t vote_address = {0}; + fd_memcpy( &vote_address, vote_account->address, sizeof(fd_pubkey_t) ); + + if( !!pool ) { + + fd_vote_accounts_pair_global_t_mapnode_t * vote_node = fd_vote_accounts_pair_global_t_map_acquire( pool ); + vote_node->elem.stake = stake; + vote_node->elem.key = vote_address; + vote_node->elem.value.executable = vote_account->executable; + vote_node->elem.value.lamports = vote_account->lamports; + vote_node->elem.value.rent_epoch = vote_account->rent_epoch; + vote_node->elem.value.data_len = vote_account->data->size; + fd_memcpy( &vote_node->elem.value.owner, vote_account->owner, sizeof(fd_pubkey_t) ); + + uchar * data = fd_spad_alloc( spad, alignof(uchar), vote_account->data->size ); + memcpy( data, vote_account->data->bytes, vote_account->data->size ); + fd_solana_account_data_update( &vote_node->elem.value, data ); + + fd_vote_accounts_pair_global_t_map_insert( pool, root, vote_node ); + } + + fd_vote_states_update_from_account( vote_states, &vote_address, vote_data, vote_data_len ); + fd_vote_states_update_stake( vote_states, &vote_address, stake ); } } @@ -220,9 +191,6 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, fd_funk_txn_t * funk_txn = fd_funk_txn_prepare( funk, NULL, xid, 1 ); fd_funk_txn_end_write( funk ); - /* Allocate contexts */ - ulong vote_acct_max = fd_ulong_max( 128UL, test_ctx->acct_states_count ); - /* Restore feature flags */ fd_features_t features = {0}; if( !fd_runtime_fuzz_restore_features( &features, &test_ctx->epoch_ctx.features ) ) { @@ -243,16 +211,6 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, /* All bank mgr stuff here. */ - /* Initialize vote timestamps cache */ - fd_clock_timestamp_votes_global_t * clock_timestamp_votes = fd_bank_clock_timestamp_votes_locking_modify( slot_ctx->bank ); - uchar * pool_mem = (uchar *)fd_ulong_align_up( (ulong)clock_timestamp_votes + sizeof(fd_clock_timestamp_votes_global_t), fd_clock_timestamp_vote_t_map_align() ); - fd_clock_timestamp_vote_t_mapnode_t * clock_pool = fd_clock_timestamp_vote_t_map_join( fd_clock_timestamp_vote_t_map_new( pool_mem, 15000UL ) ); - fd_clock_timestamp_vote_t_mapnode_t * clock_root = NULL; - - fd_clock_timestamp_votes_votes_pool_update( clock_timestamp_votes, clock_pool ); - fd_clock_timestamp_votes_votes_root_update( clock_timestamp_votes, clock_root ); - fd_bank_clock_timestamp_votes_end_locking_modify( slot_ctx->bank ); - slot_ctx->bank->slot_ = slot; fd_bank_block_height_set( slot_ctx->bank, test_ctx->slot_ctx.block_height ); @@ -295,15 +253,23 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, /* Initialize the current running epoch stake and vote accounts */ /* SETUP STAKES HERE */ - fd_vote_accounts_global_t * curr_stakes = fd_bank_curr_epoch_stakes_locking_modify( slot_ctx->bank ); - pool_mem = (uchar *)fd_ulong_align_up( (ulong)curr_stakes + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_t_map_align() ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, vote_acct_max ) ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = NULL; + fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( slot_ctx->bank ); + vote_states = fd_vote_states_join( fd_vote_states_new( vote_states, FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + fd_bank_vote_states_end_locking_modify( slot_ctx->bank ); + + fd_vote_states_t * vote_states_prev = fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ); + vote_states_prev = fd_vote_states_join( fd_vote_states_new( vote_states_prev, FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); + + fd_vote_states_t * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ); + vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( vote_states_prev_prev, FD_RUNTIME_MAX_VOTE_ACCOUNTS, 999UL ) ); + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); fd_stake_delegations_t * stake_delegations = fd_bank_stake_delegations_locking_modify( slot_ctx->bank ); stake_delegations = fd_stake_delegations_join( fd_stake_delegations_new( stake_delegations, FD_RUNTIME_MAX_STAKE_ACCOUNTS ) ); /* Load in all accounts with > 0 lamports provided in the context. The input expects unique account pubkeys. */ + vote_states = fd_bank_vote_states_locking_modify( slot_ctx->bank ); for( ushort i=0; iacct_states_count; i++ ) { FD_TXN_ACCOUNT_DECL(acc); fd_runtime_fuzz_load_account( acc, funk, funk_txn, &test_ctx->acct_states[i], 1 ); @@ -311,11 +277,11 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, /* Update vote accounts cache for epoch T */ fd_pubkey_t pubkey; memcpy( &pubkey, test_ctx->acct_states[i].address, sizeof(fd_pubkey_t) ); - fd_runtime_fuzz_block_register_vote_account( slot_ctx, - vote_accounts_pool, - &vote_accounts_root, - &pubkey, - runner->spad ); + fd_runtime_fuzz_block_register_vote_account( + slot_ctx, + vote_states, + &pubkey, + runner->spad ); /* Update the stake delegations cache for epoch T */ fd_runtime_fuzz_block_register_stake_delegation( slot_ctx, @@ -324,17 +290,9 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, } /* Refresh vote accounts to calculate stake delegations */ - fd_runtime_fuzz_block_refresh_vote_accounts( vote_accounts_pool, - vote_accounts_root, - stake_delegations ); - - fd_vote_accounts_vote_accounts_pool_update( curr_stakes, vote_accounts_pool ); - fd_vote_accounts_vote_accounts_root_update( curr_stakes, vote_accounts_root ); - - // fd_vote_accounts_stake_delegations_pool_update( curr_stakes, stake_delegations_pool ); - // fd_vote_accounts_stake_delegations_root_update( curr_stakes, stake_delegations_root ); + fd_runtime_fuzz_block_refresh_vote_accounts( vote_states, stake_delegations ); + fd_bank_vote_states_end_locking_modify( slot_ctx->bank ); - fd_bank_curr_epoch_stakes_end_locking_modify( slot_ctx->bank ); fd_bank_stake_delegations_end_locking_modify( slot_ctx->bank ); /* Finish init epoch bank sysvars */ @@ -351,38 +309,25 @@ fd_runtime_fuzz_block_ctx_create( fd_solfuzz_runner_t * runner, /* Refresh the program cache */ fd_runtime_fuzz_refresh_program_cache( slot_ctx, test_ctx->acct_states, test_ctx->acct_states_count, runner->spad ); - fd_vote_accounts_global_t * vote_accounts = fd_bank_next_epoch_stakes_locking_modify( slot_ctx->bank ); - pool_mem = (uchar *)fd_ulong_align_up( (ulong)vote_accounts + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() ); - vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, vote_acct_max ) ); - vote_accounts_root = NULL; - /* Update vote cache for epoch T-1 */ - fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_accounts_pool, - &vote_accounts_root, + vote_states_prev = fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ); + fd_runtime_fuzz_block_update_prev_epoch_votes_cache( NULL, + NULL, + vote_states_prev, test_ctx->epoch_ctx.vote_accounts_t_1, test_ctx->epoch_ctx.vote_accounts_t_1_count, runner->spad ); - - fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vote_accounts_pool ); - fd_vote_accounts_vote_accounts_root_update( vote_accounts, vote_accounts_root ); - - fd_bank_next_epoch_stakes_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); /* Update vote cache for epoch T-2 */ - vote_accounts = fd_bank_epoch_stakes_locking_modify( slot_ctx->bank ); - pool_mem = (uchar *)fd_ulong_align_up( (ulong)vote_accounts + sizeof(fd_vote_accounts_global_t), fd_vote_accounts_pair_global_t_map_align() ); - vote_accounts_pool = fd_vote_accounts_pair_global_t_map_join( fd_vote_accounts_pair_global_t_map_new( pool_mem, vote_acct_max ) ); - vote_accounts_root = NULL; - - fd_runtime_fuzz_block_update_prev_epoch_votes_cache( vote_accounts_pool, - &vote_accounts_root, + vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ); + fd_runtime_fuzz_block_update_prev_epoch_votes_cache( NULL, + NULL, + vote_states_prev_prev, test_ctx->epoch_ctx.vote_accounts_t_2, test_ctx->epoch_ctx.vote_accounts_t_2_count, runner->spad ); - - fd_vote_accounts_vote_accounts_pool_update( vote_accounts, vote_accounts_pool ); - fd_vote_accounts_vote_accounts_root_update( vote_accounts, vote_accounts_root ); - fd_bank_epoch_stakes_end_locking_modify( slot_ctx->bank ); + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); /* Update leader schedule */ fd_runtime_update_leaders( slot_ctx->bank, fd_bank_slot_get( slot_ctx->bank ), runner->spad ); @@ -565,7 +510,6 @@ fd_runtime_fuzz_block_ctx_exec( fd_solfuzz_runner_t * runner, slot_ctx->funk, slot_ctx->funk_txn, txn_ctx, - runner->spad, slot_ctx->bank, capture_ctx ); diff --git a/src/flamenco/runtime/tests/fd_dump_pb.c b/src/flamenco/runtime/tests/fd_dump_pb.c index fcb6150b28f..e6a5c9ecaf4 100644 --- a/src/flamenco/runtime/tests/fd_dump_pb.c +++ b/src/flamenco/runtime/tests/fd_dump_pb.c @@ -183,52 +183,17 @@ dump_executable_account_if_exists( fd_funk_t const * funk, /** VOTE ACCOUNTS DUMPING **/ static void dump_vote_accounts( fd_exec_slot_ctx_t const * slot_ctx, - fd_vote_accounts_global_t const * vote_accounts, + fd_vote_states_t const * vote_states, fd_spad_t * spad, - fd_exec_test_vote_account_t ** out_vote_accounts, - pb_size_t * out_vote_accounts_count, fd_exec_test_acct_state_t * out_acct_states, pb_size_t * out_acct_states_cnt ) { - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); - - pb_size_t idx = 0UL; - ulong vote_account_t_cnt = fd_vote_accounts_pair_global_t_map_size( vote_accounts_pool, - vote_accounts_root ); - fd_exec_test_vote_account_t * vote_account_out = fd_spad_alloc( spad, - alignof(fd_exec_test_vote_account_t), - vote_account_t_cnt * sizeof(fd_exec_test_vote_account_t) ); - - for( fd_vote_accounts_pair_global_t_mapnode_t const * curr = fd_vote_accounts_pair_global_t_map_minimum_const( - vote_accounts_pool, - vote_accounts_root ); - curr; - curr = fd_vote_accounts_pair_global_t_map_successor_const( vote_accounts_pool, curr ) ) { - fd_exec_test_vote_account_t * vote_out = &vote_account_out[idx++]; - - vote_out->has_vote_account = true; - vote_out->stake = curr->elem.stake; - vote_out->vote_account.lamports = curr->elem.value.lamports; - vote_out->vote_account.rent_epoch = curr->elem.value.rent_epoch; - vote_out->vote_account.executable = curr->elem.value.executable; - vote_out->vote_account.has_seed_addr = false; - - fd_memcpy( &vote_out->vote_account.address, &curr->elem.key, sizeof(fd_pubkey_t) ); - fd_memcpy( &vote_out->vote_account.owner, &curr->elem.value.owner, sizeof(fd_pubkey_t) ); - - vote_out->vote_account.data = fd_spad_alloc( spad, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( curr->elem.value.data_len ) ); - vote_out->vote_account.data->size = (pb_size_t) curr->elem.value.data_len; - - uchar * data = fd_solana_account_data_join( &curr->elem.value ); - fd_memcpy( &vote_out->vote_account.data->bytes, data, curr->elem.value.data_len ); - - // Dump the vote account - dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.key, spad, out_acct_states, out_acct_states_cnt, NULL ); + fd_vote_states_iter_t iter[1]; + for( fd_vote_states_iter_init( vote_states, iter ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); + dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &vote_state->vote_account, spad, out_acct_states, out_acct_states_cnt, NULL ); } - *out_vote_accounts = vote_account_out; - *out_vote_accounts_count = idx; } /** TRANSACTION DUMPING **/ @@ -424,34 +389,22 @@ create_block_context_protobuf_from_block( fd_exec_test_block_context_t * block_c ulong num_sysvar_entries = (sizeof(fd_relevant_sysvar_ids) / sizeof(fd_pubkey_t)); ulong num_loaded_builtins = (sizeof(loaded_builtins) / sizeof(fd_pubkey_t)); - fd_stake_delegation_t * stake_delegations_pool = fd_stake_delegations_get_pool( fd_bank_stake_delegations_locking_query( slot_ctx->bank ) ); - - fd_vote_accounts_global_t const * vote_accounts = fd_bank_curr_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); - - ulong stake_account_cnt = fd_stake_delegation_pool_used( stake_delegations_pool ); - - ulong vote_account_t_cnt = fd_vote_accounts_pair_global_t_map_size( stakes_vote_accounts_pool, - stakes_vote_accounts_root ); - - fd_bank_curr_epoch_stakes_end_locking_query( slot_ctx->bank ); + fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_locking_query( slot_ctx->bank ); + ulong stake_account_cnt = fd_stake_delegations_cnt( stake_delegations ); fd_bank_stake_delegations_end_locking_query( slot_ctx->bank ); + fd_vote_states_t const * vote_states = fd_bank_vote_states_locking_query( slot_ctx->bank ); + ulong vote_account_t_cnt = fd_vote_states_cnt( vote_states ); + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); + - fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( next_epoch_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( next_epoch_stakes ); - ulong vote_account_t_1_cnt = fd_vote_accounts_pair_global_t_map_size( next_epoch_stakes_pool, - next_epoch_stakes_root ); - fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank ); + fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_locking_query( slot_ctx->bank ); + ulong vote_account_t_1_cnt = fd_vote_states_cnt( vote_states_prev ); + fd_bank_vote_states_prev_end_locking_query( slot_ctx->bank ); - fd_vote_accounts_global_t const * epoch_stakes = fd_bank_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( epoch_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( epoch_stakes ); - ulong vote_account_t_2_cnt = fd_vote_accounts_pair_global_t_map_size( epoch_stakes_pool, - epoch_stakes_root ); - fd_bank_epoch_stakes_end_locking_query( slot_ctx->bank ); + fd_vote_states_t const * vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( slot_ctx->bank ); + ulong vote_account_t_2_cnt = fd_vote_states_cnt( vote_states_prev_prev ); + fd_bank_vote_states_prev_prev_end_locking_query( slot_ctx->bank ); ulong total_num_accounts = num_sysvar_entries + num_loaded_builtins + @@ -515,7 +468,7 @@ create_block_context_protobuf_from_block( fd_exec_test_block_context_t * block_c /* Dumping stake accounts for this epoch */ - fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_locking_query( slot_ctx->bank ); + stake_delegations = fd_bank_stake_delegations_locking_query( slot_ctx->bank ); fd_stake_delegation_map_t * map = fd_stake_delegations_get_map( stake_delegations ); fd_stake_delegation_t * pool = fd_stake_delegations_get_pool( stake_delegations ); @@ -530,41 +483,32 @@ create_block_context_protobuf_from_block( fd_exec_test_block_context_t * block_c /* Dumping vote accounts for this epoch */ - fd_vote_accounts_global_t const * curr_vote_accounts = fd_bank_curr_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( curr_vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( curr_vote_accounts ); - - /* Dump all existing vote accounts */ - for( fd_vote_accounts_pair_global_t_mapnode_t const * curr = fd_vote_accounts_pair_global_t_map_minimum_const( - vote_accounts_pool, - vote_accounts_root ); - curr; - curr = fd_vote_accounts_pair_global_t_map_successor_const( vote_accounts_pool, curr ) ) { - dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &curr->elem.key, spad, block_context->acct_states, &block_context->acct_states_count, NULL ); + vote_states = fd_bank_vote_states_locking_query( slot_ctx->bank ); + fd_vote_states_iter_t iter[1]; + for( fd_vote_states_iter_init( vote_states, iter ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter ); + dump_account_if_not_already_dumped( slot_ctx->funk, slot_ctx->funk_txn, &vote_state->vote_account, spad, block_context->acct_states, &block_context->acct_states_count, NULL ); } - fd_bank_curr_epoch_stakes_end_locking_query( slot_ctx->bank ); + fd_bank_vote_states_end_locking_query( slot_ctx->bank ); // BlockContext -> EpochContext -> vote_accounts_t_1 (vote accounts at epoch T-1) - fd_vote_accounts_global_t const * next_epoch_stakes_vaccs = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank ); + vote_states_prev = fd_bank_vote_states_prev_locking_query( slot_ctx->bank ); dump_vote_accounts( slot_ctx, - next_epoch_stakes_vaccs, + vote_states_prev, spad, - &block_context->epoch_ctx.vote_accounts_t_1, - &block_context->epoch_ctx.vote_accounts_t_1_count, block_context->acct_states, &block_context->acct_states_count ); + fd_bank_vote_states_prev_end_locking_query( slot_ctx->bank ); // BlockContext -> EpochContext -> vote_accounts_t_2 (vote accounts at epoch T-2) - fd_vote_accounts_global_t const * epoch_stakes_vaccs = fd_bank_epoch_stakes_locking_query( slot_ctx->bank ); + vote_states_prev_prev = fd_bank_vote_states_prev_prev_locking_query( slot_ctx->bank ); dump_vote_accounts( slot_ctx, - epoch_stakes_vaccs, + vote_states, spad, - &block_context->epoch_ctx.vote_accounts_t_2, - &block_context->epoch_ctx.vote_accounts_t_2_count, block_context->acct_states, &block_context->acct_states_count ); - fd_bank_epoch_stakes_end_locking_query( slot_ctx->bank ); + fd_bank_vote_states_prev_prev_end_locking_query( slot_ctx->bank ); } static void diff --git a/src/flamenco/runtime/tests/fd_instr_harness.c b/src/flamenco/runtime/tests/fd_instr_harness.c index fc49f58a4c1..69a182226f7 100644 --- a/src/flamenco/runtime/tests/fd_instr_harness.c +++ b/src/flamenco/runtime/tests/fd_instr_harness.c @@ -13,6 +13,7 @@ #include "../sysvar/fd_sysvar_last_restart_slot.h" #include "../sysvar/fd_sysvar_rent.h" #include "../fd_system_ids.h" +#include "../fd_cost_tracker.h" #include int @@ -63,6 +64,25 @@ fd_runtime_fuzz_instr_ctx_create( fd_solfuzz_runner_t * runner, return 0; } + /* Setup vote states accounts */ + fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( slot_ctx->bank ), FD_WRITABLE_ACCOUNTS_PER_BLOCK, 999UL ) ); + if( FD_UNLIKELY( !vote_states ) ) { + return 0; + } + fd_bank_vote_states_end_locking_modify( slot_ctx->bank ); + + fd_vote_states_t * vote_states_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ), FD_WRITABLE_ACCOUNTS_PER_BLOCK, 999UL ) ); + if( FD_UNLIKELY( !vote_states_prev ) ) { + return 0; + } + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); + + fd_vote_states_t * vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ), FD_WRITABLE_ACCOUNTS_PER_BLOCK, 999UL ) ); + if( FD_UNLIKELY( !vote_states_prev_prev ) ) { + return 0; + } + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); + /* Set up epoch context. Defaults obtained from GenesisConfig::Default() */ fd_rent_t * rent_bm = fd_bank_rent_modify( slot_ctx->bank ); diff --git a/src/flamenco/runtime/tests/fd_txn_harness.c b/src/flamenco/runtime/tests/fd_txn_harness.c index 5d65b311e29..a84283792b2 100644 --- a/src/flamenco/runtime/tests/fd_txn_harness.c +++ b/src/flamenco/runtime/tests/fd_txn_harness.c @@ -4,6 +4,7 @@ #include "../fd_runtime.h" #include "../fd_executor.h" #include "../fd_txn_account.h" +#include "../fd_cost_tracker.h" #include "../context/fd_exec_slot_ctx.h" #include "../program/fd_builtin_programs.h" #include "../sysvar/fd_sysvar_clock.h" @@ -174,6 +175,27 @@ fd_runtime_fuzz_txn_ctx_create( fd_solfuzz_runner_t * runner, fd_sysvar_last_restart_slot_init( slot_ctx ); } + /* Setup vote states dummy account */ + fd_vote_states_t * vote_states = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( slot_ctx->bank ), FD_WRITABLE_ACCOUNTS_PER_BLOCK, 999UL ) ); + if( FD_UNLIKELY( !vote_states ) ) { + return NULL; + } + fd_bank_vote_states_end_locking_modify( slot_ctx->bank ); + + /* Setup vote states dummy account */ + fd_vote_states_t * vote_states_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_locking_modify( slot_ctx->bank ), FD_WRITABLE_ACCOUNTS_PER_BLOCK, 999UL ) ); + if( FD_UNLIKELY( !vote_states_prev ) ) { + return NULL; + } + fd_bank_vote_states_prev_end_locking_modify( slot_ctx->bank ); + + /* Setup vote states dummy account */ + fd_vote_states_t * vote_states_prev_prev = fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_locking_modify( slot_ctx->bank ), FD_WRITABLE_ACCOUNTS_PER_BLOCK, 999UL ) ); + if( FD_UNLIKELY( !vote_states_prev_prev ) ) { + return NULL; + } + fd_bank_vote_states_prev_prev_end_locking_modify( slot_ctx->bank ); + /* Provide a default clock if not present */ fd_sol_sysvar_clock_t clock_[1]; fd_sol_sysvar_clock_t const * clock = fd_sysvar_clock_read( funk, funk_txn, clock_ ); diff --git a/src/flamenco/stakes/Local.mk b/src/flamenco/stakes/Local.mk index 8edfe178111..45e34773601 100644 --- a/src/flamenco/stakes/Local.mk +++ b/src/flamenco/stakes/Local.mk @@ -7,6 +7,11 @@ $(call add-objs,fd_stake_delegations,fd_flamenco) $(call make-unit-test,test_stake_delegations,test_stake_delegations,fd_flamenco fd_ballet fd_util) $(call run-unit-test,test_stake_delegations) +$(call add-hdrs,fd_vote_states.h) +$(call add-objs,fd_vote_states,fd_flamenco) +$(call make-unit-test,test_vote_states,test_vote_states,fd_flamenco fd_ballet fd_util) +$(call run-unit-test,test_vote_states) + # TODO this should not depend on fd_funk ifdef FD_HAS_HOSTED $(call make-bin,fd_stakes_from_snapshot,fd_stakes_from_snapshot,fd_flamenco fd_funk fd_ballet fd_util) diff --git a/src/flamenco/stakes/fd_stake_delegations.c b/src/flamenco/stakes/fd_stake_delegations.c index fb4320b335c..890f719640b 100644 --- a/src/flamenco/stakes/fd_stake_delegations.c +++ b/src/flamenco/stakes/fd_stake_delegations.c @@ -68,9 +68,6 @@ fd_stake_delegations_new( void * mem, ulong max_stake_accounts ) { return NULL; } - stake_delegations->magic = FD_STAKE_DELEGATIONS_MAGIC; - stake_delegations->max_stake_accounts = max_stake_accounts; - if( FD_UNLIKELY( !fd_stake_delegation_pool_new( pool_mem, max_stake_accounts ) ) ) { FD_LOG_WARNING(( "Failed to create stake delegations pool" )); return NULL; @@ -82,6 +79,12 @@ fd_stake_delegations_new( void * mem, ulong max_stake_accounts ) { return NULL; } + stake_delegations->max_stake_accounts = max_stake_accounts; + + FD_COMPILER_MFENCE(); + FD_VOLATILE( stake_delegations->magic ) = FD_STAKE_DELEGATIONS_MAGIC; + FD_COMPILER_MFENCE(); + return mem; } @@ -104,6 +107,7 @@ fd_stake_delegations_join( void * mem ) { return NULL; } + #if FD_STAKES_USE_HANDHOLDING ulong map_chain_cnt = fd_stake_delegation_map_chain_cnt_est( stake_delegations->max_stake_accounts ); FD_SCRATCH_ALLOC_INIT( l, stake_delegations ); @@ -125,6 +129,7 @@ fd_stake_delegations_join( void * mem ) { FD_LOG_WARNING(( "Failed to join stake delegations map" )); return NULL; } + #endif return stake_delegations; } diff --git a/src/flamenco/stakes/fd_stake_delegations.h b/src/flamenco/stakes/fd_stake_delegations.h index 6e91f63b6fe..728548d5e23 100644 --- a/src/flamenco/stakes/fd_stake_delegations.h +++ b/src/flamenco/stakes/fd_stake_delegations.h @@ -4,7 +4,7 @@ #include "../fd_flamenco_base.h" #include "../types/fd_types.h" -#define FD_STAKE_DELEGATIONS_MAGIC (0x09151995UL) +#define FD_STAKE_DELEGATIONS_MAGIC (0x0915199511111111UL) /* fd_stakes_delegations_t is a cache of stake accounts mapping the pubkey of the stake account to various information including @@ -39,7 +39,7 @@ reward distribution. The stake accounts are read-only during the epoch boundary. */ -/* The static footprint fo the stake delegation struct is roughly equal +/* The static footprint of the stake delegation struct is roughly equal to the footprint of each stake_delegation * the number of total stake accounts that the system will support. If there are 3M stake accounts and each one is 112 bytes, then we can assume that the total diff --git a/src/flamenco/stakes/fd_stakes.c b/src/flamenco/stakes/fd_stakes.c index 4a6a07ed841..79bbf66d541 100644 --- a/src/flamenco/stakes/fd_stakes.c +++ b/src/flamenco/stakes/fd_stakes.c @@ -7,88 +7,67 @@ #include "fd_stake_delegations.h" ulong -fd_stake_weights_by_node( fd_vote_accounts_global_t const * accs, - fd_vote_stake_weight_t * weights ) { - fd_vote_accounts_pair_global_t_mapnode_t * pool = fd_vote_accounts_vote_accounts_pool_join( accs ); - fd_vote_accounts_pair_global_t_mapnode_t * root = fd_vote_accounts_vote_accounts_root_join( accs ); +fd_stake_weights_by_node( fd_vote_states_t const * vote_states, + fd_vote_stake_weight_t * weights ) { + fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states ); + fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states ); - /* For each active vote account, return (vote_key, node_identity, stake), sorted by (stake, vote) */ ulong weights_cnt = 0; - for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( pool, root ); - n; - n = fd_vote_accounts_pair_global_t_map_successor( pool, n ) ) { - - /* ... filter(|(stake, _)| *stake != 0u64) */ - if( n->elem.stake == 0UL ) continue; - - /* Copy output values */ - memcpy( weights[ weights_cnt ].vote_key.uc, n->elem.key.uc, sizeof(fd_pubkey_t) ); - weights[ weights_cnt ].stake = n->elem.stake; - uchar * vote_account_data = fd_solana_account_data_join( &n->elem.value ); - /* node_pubkey is at offset 4, no need to fully deserialize the account(s) */ - memcpy( weights[ weights_cnt ].id_key.uc, vote_account_data+4UL, sizeof(fd_pubkey_t) ); + for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool ); + !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool ); + iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) { + fd_vote_state_ele_t const * vote_state = fd_vote_state_map_iter_ele_const( iter, vote_state_map, vote_state_pool ); + if( FD_UNLIKELY( !vote_state->stake ) ) continue; + + fd_memcpy( weights[ weights_cnt ].vote_key.uc, &vote_state->vote_account, sizeof(fd_pubkey_t) ); + fd_memcpy( weights[ weights_cnt ].id_key.uc, &vote_state->node_account, sizeof(fd_pubkey_t) ); + weights[ weights_cnt ].stake = vote_state->stake; weights_cnt++; } - sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt ); return weights_cnt; } -/* Helper function to deserialize a vote account. If successful, populates vote account info in `elem` - and saves the decoded vote state in `vote_state` */ -static fd_vote_state_versioned_t * -deserialize_and_update_vote_account( fd_exec_slot_ctx_t * slot_ctx, - fd_vote_accounts_pair_global_t_mapnode_t * elem, - fd_stake_weight_t_mapnode_t * stake_delegations_root, - fd_stake_weight_t_mapnode_t * stake_delegations_pool, - fd_pubkey_t const * vote_account_pubkey, - fd_spad_t * runtime_spad ) { - - FD_TXN_ACCOUNT_DECL( vote_account ); - if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( vote_account, - vote_account_pubkey, - slot_ctx->funk, - slot_ctx->funk_txn ) ) ) { - FD_LOG_DEBUG(( "Vote account not found" )); - return NULL; - } +// static void +// dump_vote_and_stake_states( fd_vote_states_t const * vote_states, +// fd_stake_delegations_t const * stake_delegations ) { - // Deserialize the vote account and ensure its in the correct state - int err; - fd_vote_state_versioned_t * res = fd_bincode_decode_spad( - vote_state_versioned, runtime_spad, - fd_txn_account_get_data( vote_account ), - fd_txn_account_get_data_len( vote_account ), - &err ); - if( FD_UNLIKELY( err ) ) { - return NULL; - } +// return; +// fd_stake_delegation_map_t * stake_delegation_map = fd_stake_delegations_get_map( stake_delegations ); +// fd_stake_delegation_t * stake_delegation_pool = fd_stake_delegations_get_pool( stake_delegations ); - // Get the stake amount from the stake delegations map - fd_stake_weight_t_mapnode_t temp; - temp.elem.key = *vote_account_pubkey; - fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_find( stake_delegations_pool, stake_delegations_root, &temp ); - elem->elem.stake = ( entry==NULL ) ? 0UL : entry->elem.stake; +// for( fd_stake_delegation_map_iter_t iter = fd_stake_delegation_map_iter_init( stake_delegation_map, stake_delegation_pool ); +// !fd_stake_delegation_map_iter_done( iter, stake_delegation_map, stake_delegation_pool ); +// iter = fd_stake_delegation_map_iter_next( iter, stake_delegation_map, stake_delegation_pool ) ) { +// fd_stake_delegation_t const * stake_delegation = fd_stake_delegation_map_iter_ele_const( iter, stake_delegation_map, stake_delegation_pool ); +// FD_LOG_NOTICE(( "stake_delegation: %s, vote_account: %s, stake: %lu, activation_epoch: %lu, deactivation_epoch: %lu", FD_BASE58_ENC_32_ALLOCA( &stake_delegation->stake_account ), FD_BASE58_ENC_32_ALLOCA( &stake_delegation->vote_account ), stake_delegation->stake, stake_delegation->activation_epoch, stake_delegation->deactivation_epoch )); +// } - return res; -} +// fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states ); +// fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states ); + +// for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool ); +// !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool ); +// iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) { + +// fd_vote_state_ele_t const * vote_state = fd_vote_state_map_iter_ele_const( iter, vote_state_map, vote_state_pool ); + +// FD_LOG_NOTICE(( "vote_state: %s, stake: %lu, lvs: %lu, lvt: %ld, c: %d", FD_BASE58_ENC_32_ALLOCA( &vote_state->vote_account ), vote_state->stake, vote_state->last_vote_slot, vote_state->last_vote_timestamp, vote_state->commission )); +// } +// } static void -compute_stake_delegations( fd_bank_t * bank, - ulong const epoch, - fd_stake_history_t const * history, - ulong * new_rate_activation_epoch, - fd_stake_weight_t_mapnode_t * delegation_pool, - fd_stake_weight_t_mapnode_t * delegation_root, - ulong vote_states_pool_sz, - fd_spad_t * spad ) { - - FD_SPAD_FRAME_BEGIN( spad ) { - - /* Create a temporary map to hold delegations */ - void * mem = fd_spad_alloc( spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) ); - fd_stake_weight_t_mapnode_t * temp_pool = fd_stake_weight_t_map_join( fd_stake_weight_t_map_new( mem, vote_states_pool_sz ) ); - fd_stake_weight_t_mapnode_t * temp_root = NULL; +compute_stake_delegations( fd_bank_t * bank, + ulong const epoch, + fd_stake_history_t const * history, + ulong * new_rate_activation_epoch ) { + + ulong total_stake = 0UL; + + fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank ); + if( FD_UNLIKELY( !vote_states ) ) { + FD_LOG_CRIT(( "vote_states is NULL" )); + } fd_stake_delegations_t const * stake_delegations = fd_bank_stake_delegations_locking_query( bank ); if( FD_UNLIKELY( !stake_delegations ) ) { @@ -97,22 +76,17 @@ compute_stake_delegations( fd_bank_t * bank, fd_stake_delegation_map_t * stake_delegation_map = fd_stake_delegations_get_map( stake_delegations ); fd_stake_delegation_t * stake_delegation_pool = fd_stake_delegations_get_pool( stake_delegations ); - fd_stake_weight_t_mapnode_t temp; + /* Reset the vote stakes so we can re-compute them based on the most + current stake delegation values. */ + fd_vote_states_reset_stakes( vote_states ); for( fd_stake_delegation_map_iter_t iter = fd_stake_delegation_map_iter_init( stake_delegation_map, stake_delegation_pool ); !fd_stake_delegation_map_iter_done( iter, stake_delegation_map, stake_delegation_pool ); iter = fd_stake_delegation_map_iter_next( iter, stake_delegation_map, stake_delegation_pool ) ) { - fd_stake_delegation_t const * stake_delegation = fd_stake_delegation_map_iter_ele_const( iter, stake_delegation_map, stake_delegation_pool ); - temp.elem.key = stake_delegation->vote_account; - // Skip any delegations that are not in the delegation pool - fd_stake_weight_t_mapnode_t * delegation_entry = fd_stake_weight_t_map_find( delegation_pool, delegation_root, &temp ); - if( FD_UNLIKELY( delegation_entry==NULL ) ) { - continue; - } fd_delegation_t delegation = { .voter_pubkey = stake_delegation->vote_account, @@ -122,30 +96,27 @@ compute_stake_delegations( fd_bank_t * bank, .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate, }; - fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( &delegation, epoch, history, new_rate_activation_epoch ); - delegation_entry = fd_stake_weight_t_map_find( temp_pool, temp_root, &temp ); - if( FD_UNLIKELY( delegation_entry==NULL ) ) { - delegation_entry = fd_stake_weight_t_map_acquire( temp_pool ); - delegation_entry->elem.key = stake_delegation->vote_account; - delegation_entry->elem.stake = new_entry.effective; - fd_stake_weight_t_map_insert( temp_pool, &temp_root, delegation_entry ); - } else { - delegation_entry->elem.stake += new_entry.effective; + fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( + &delegation, + epoch, + history, + new_rate_activation_epoch ); + + fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, &stake_delegation->vote_account ); + if( vote_state ) { + vote_state->stake += new_entry.effective; } - } - // Update the parent delegation pool with the calculated delegation values - for( fd_stake_weight_t_mapnode_t * elem = fd_stake_weight_t_map_minimum( temp_pool, temp_root ); - elem; - elem = fd_stake_weight_t_map_successor( temp_pool, elem ) ) { - fd_stake_weight_t_mapnode_t * output_delegation_node = fd_stake_weight_t_map_find( delegation_pool, delegation_root, elem ); - output_delegation_node->elem.stake += elem->elem.stake; + total_stake += new_entry.effective; } - fd_bank_stake_delegations_end_locking_query( bank ); + fd_bank_total_epoch_stake_set( bank, total_stake ); - } FD_SPAD_FRAME_END; + // dump_vote_and_stake_states( vote_states, stake_delegations ); + fd_bank_stake_delegations_end_locking_query( bank ); + + fd_bank_vote_states_end_locking_modify( bank ); } @@ -153,90 +124,24 @@ compute_stake_delegations( fd_bank_t * bank, void fd_populate_vote_accounts( fd_exec_slot_ctx_t * slot_ctx, fd_stake_history_t const * history, - ulong * new_rate_activation_epoch, - fd_epoch_info_t * temp_info, - fd_spad_t * runtime_spad ) { - - fd_vote_accounts_global_t const * vote_accounts = fd_bank_curr_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); - ulong vote_accounts_stakes_map_sz = vote_accounts_pool ? fd_vote_accounts_pair_global_t_map_size( vote_accounts_pool, vote_accounts_root ) : 0UL; - - ulong vote_states_pool_sz = vote_accounts_stakes_map_sz; - temp_info->vote_states_root = NULL; - uchar * pool_mem = fd_spad_alloc( runtime_spad, fd_vote_info_pair_t_map_align(), fd_vote_info_pair_t_map_footprint( vote_states_pool_sz ) ); - temp_info->vote_states_pool = fd_vote_info_pair_t_map_join( fd_vote_info_pair_t_map_new( pool_mem, vote_states_pool_sz ) ); - - /* Create a map of to store the total stake of each vote account. */ - void * mem = fd_spad_alloc( runtime_spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) ); - fd_stake_weight_t_mapnode_t * pool = fd_stake_weight_t_map_join( fd_stake_weight_t_map_new( mem, vote_states_pool_sz ) ); - fd_stake_weight_t_mapnode_t * root = NULL; - - /* We can optimize this function by only iterating over the vote accounts (since there's much fewer of them) instead of all - of the stake accounts, and pre-inserting them into the delegations pool. This way, the delegation calculations can be tpooled. */ - for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( vote_accounts_pool, vote_accounts_root ); - elem; - elem = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_pool, elem ) ) { - fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_acquire( pool ); - entry->elem.key = elem->elem.key; - entry->elem.stake = 0UL; - fd_stake_weight_t_map_insert( pool, &root, entry ); - } - - fd_bank_curr_epoch_stakes_end_locking_query( slot_ctx->bank ); + ulong * new_rate_activation_epoch ) { + /* We can optimize this function by only iterating over the vote + accounts (since there's much fewer of them) instead of all of the + stake accounts, and pre-inserting them into the delegations pool. + This way, the delegation calculations can be tpooled. */ compute_stake_delegations( slot_ctx->bank, fd_bank_epoch_get( slot_ctx->bank ), history, - new_rate_activation_epoch, - pool, - root, - vote_states_pool_sz, - runtime_spad ); - - // Iterate over each vote account in the epoch stakes cache and populate the new vote accounts pool - /* NOTE: we use epoch_bank->next_epoch_stakes because Agave indexes their epoch stakes cache by leader schedule epoch. - This means that the epoch stakes for epoch E are indexed by epoch E+1. - This is just a workaround for now. - https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L309 */ - ulong total_epoch_stake = 0UL; - - fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( next_epoch_stakes ); - fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( next_epoch_stakes ); - - for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( next_epoch_stakes_pool, next_epoch_stakes_root ); - elem; - elem = fd_vote_accounts_pair_global_t_map_successor( next_epoch_stakes_pool, elem ) ) { - fd_pubkey_t const * vote_account_pubkey = &elem->elem.key; - FD_TXN_ACCOUNT_DECL( acc ); - int rc = fd_txn_account_init_from_funk_readonly( acc, vote_account_pubkey, slot_ctx->funk, slot_ctx->funk_txn ); - FD_TEST( rc == 0 ); - uchar * data = fd_solana_account_data_join( &elem->elem.value ); - ulong data_len = elem->elem.value.data_len; - - int err; - fd_vote_state_versioned_t * vote_state = fd_bincode_decode_spad( vote_state_versioned, - runtime_spad, - data, - data_len, - &err ); - - if( FD_LIKELY( vote_state ) ) { - total_epoch_stake += elem->elem.stake; - // Insert into the temporary vote states cache - fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool ); - new_vote_state_node->elem.account = *vote_account_pubkey; - new_vote_state_node->elem.state = *vote_state; - fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node ); - } else { - FD_LOG_WARNING(( "Failed to deserialize vote account" )); - } - } - fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank ); - - fd_bank_total_epoch_stake_set( slot_ctx->bank, total_epoch_stake ); + new_rate_activation_epoch ); + + /* TODO: because we are calculating the stake delegation partially + into the new epoch, we are interested in calculating the rewards + with stake delegations from epoch E-1. We already have this + information in vote_states_prev in the bank. However, this needs + to be implemented properly throughout the rewards calculation. */ + FD_LOG_ERR(( "snapshots during rewards distribution are currently not supported" )); } /* @@ -251,83 +156,13 @@ new vote account keys from this epoch. void fd_refresh_vote_accounts( fd_exec_slot_ctx_t * slot_ctx, fd_stake_history_t const * history, - ulong * new_rate_activation_epoch, - fd_epoch_info_t * temp_info, - fd_spad_t * runtime_spad ) { - - fd_vote_accounts_global_t * vote_accounts = fd_bank_curr_epoch_stakes_locking_modify( slot_ctx->bank ); - fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts ); - fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts ); - - ulong vote_accounts_stakes_map_sz = !!stakes_vote_accounts_pool ? fd_vote_accounts_pair_global_t_map_size( stakes_vote_accounts_pool, stakes_vote_accounts_root ) : 0UL; - ulong vote_states_pool_sz = vote_accounts_stakes_map_sz; - - /* Initialize a temporary vote states cache */ - temp_info->vote_states_root = NULL; - uchar * pool_mem = fd_spad_alloc( runtime_spad, fd_vote_info_pair_t_map_align(), fd_vote_info_pair_t_map_footprint( vote_states_pool_sz ) ); - temp_info->vote_states_pool = fd_vote_info_pair_t_map_join( fd_vote_info_pair_t_map_new( pool_mem, vote_states_pool_sz ) ); - - /* Create a map of to store the total stake of each vote account. */ - void * mem = fd_spad_alloc( runtime_spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) ); - fd_stake_weight_t_mapnode_t * pool = fd_stake_weight_t_map_join( fd_stake_weight_t_map_new( mem, vote_states_pool_sz ) ); - fd_stake_weight_t_mapnode_t * root = NULL; - - /* We can optimize this function by only iterating over the vote accounts (since there's much fewer of them) instead of all - of the stake accounts, and pre-inserting them into the delegations pool. This way, the delegation calculations can be tpooled. */ - for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( stakes_vote_accounts_pool, stakes_vote_accounts_root ); - elem; - elem = fd_vote_accounts_pair_global_t_map_successor( stakes_vote_accounts_pool, elem ) ) { - fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_acquire( pool ); - entry->elem.key = elem->elem.key; - entry->elem.stake = 0UL; - fd_stake_weight_t_map_insert( pool, &root, entry ); - } + ulong * new_rate_activation_epoch ) { compute_stake_delegations( slot_ctx->bank, fd_bank_epoch_get( slot_ctx->bank ), history, - new_rate_activation_epoch, - pool, - root, - vote_states_pool_sz, - runtime_spad ); - - // Iterate over each vote account in the epoch stakes cache and populate the new vote accounts pool - ulong total_epoch_stake = 0UL; - for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( stakes_vote_accounts_pool, stakes_vote_accounts_root ); - elem; - elem = fd_vote_accounts_pair_global_t_map_successor( stakes_vote_accounts_pool, elem ) ) { - - fd_pubkey_t const * vote_account_pubkey = &elem->elem.key; - fd_vote_state_versioned_t * vote_state = deserialize_and_update_vote_account( slot_ctx, - elem, - root, - pool, - vote_account_pubkey, - runtime_spad ); - if( FD_LIKELY( vote_state ) ) { - total_epoch_stake += elem->elem.stake; - // Insert into the temporary vote states cache - /* FIXME: This copy copies over some local pointers, which means - that the allocation done when deserializing the vote account - is not freed until the end of the epoch boundary processing. */ - fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool ); - new_vote_state_node->elem.account = *vote_account_pubkey; - new_vote_state_node->elem.state = *vote_state; - fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node ); - } else { - FD_LOG_WARNING(( "Failed to deserialize vote account" )); - } - } - - // Update the epoch stakes cache with new vote accounts from the epoch - fd_vote_accounts_vote_accounts_pool_update( vote_accounts, stakes_vote_accounts_pool ); - fd_vote_accounts_vote_accounts_root_update( vote_accounts, stakes_vote_accounts_root ); - - fd_bank_curr_epoch_stakes_end_locking_modify( slot_ctx->bank ); - - fd_bank_total_epoch_stake_set( slot_ctx->bank, total_epoch_stake ); + new_rate_activation_epoch ); } static void @@ -400,12 +235,10 @@ fd_accumulate_stake_infos( fd_exec_slot_ctx_t const * slot_ctx, /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L169 */ void -fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx, - ulong * new_rate_activation_epoch, - fd_epoch_info_t * temp_info, - fd_spad_t * runtime_spad ) { +fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx, + ulong * new_rate_activation_epoch, + fd_spad_t * runtime_spad ) { - (void)temp_info; fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( (void*)fd_bank_stake_delegations_locking_modify( slot_ctx->bank ) ); /* Current stake delegations: list of all current delegations in stake_delegations @@ -523,13 +356,8 @@ fd_stakes_upsert_stake_delegation( fd_txn_account_t * stake_account, } void -fd_update_stake_delegation( fd_txn_account_t * stake_account, - fd_bank_t * bank ) { - fd_pubkey_t const * owner = fd_txn_account_get_owner( stake_account ); - - if( memcmp( owner->uc, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) { - return; - } +fd_update_stake_delegation( fd_txn_account_t * stake_account, + fd_bank_t * bank ) { int is_empty = fd_txn_account_get_lamports( stake_account )==0UL; int is_uninit = 1; diff --git a/src/flamenco/stakes/fd_stakes.h b/src/flamenco/stakes/fd_stakes.h index cc2392b9f6f..741a780642d 100644 --- a/src/flamenco/stakes/fd_stakes.h +++ b/src/flamenco/stakes/fd_stakes.h @@ -4,6 +4,7 @@ #include "../fd_flamenco_base.h" #include "../types/fd_types.h" #include "fd_stake_delegations.h" +#include "fd_vote_states.h" FD_PROTOTYPES_BEGIN @@ -22,15 +23,13 @@ FD_PROTOTYPES_BEGIN #define STAKE_ACCOUNT_SIZE ( 200 ) ulong -fd_stake_weights_by_node( fd_vote_accounts_global_t const * accs, - fd_vote_stake_weight_t * weights ); - +fd_stake_weights_by_node( fd_vote_states_t const * vote_states, + fd_vote_stake_weight_t * weights ); void -fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx, - ulong * new_rate_activation_epoch, - fd_epoch_info_t * temp_info, - fd_spad_t * runtime_spad ); +fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx, + ulong * new_rate_activation_epoch, + fd_spad_t * runtime_spad ); fd_stake_history_entry_t stake_and_activating( fd_delegation_t const * delegation, @@ -51,18 +50,14 @@ write_stake_state( fd_txn_account_t * stake_acc_rec, void fd_refresh_vote_accounts( fd_exec_slot_ctx_t * slot_ctx, fd_stake_history_t const * history, - ulong * new_rate_activation_epoch, - fd_epoch_info_t * temp_info, - fd_spad_t * runtime_spad ); + ulong * new_rate_activation_epoch ); /* A workaround to mimic Agave function get_epoch_reward_calculate_param_info https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L299 */ void fd_populate_vote_accounts( fd_exec_slot_ctx_t * slot_ctx, fd_stake_history_t const * history, - ulong * new_rate_activation_epoch, - fd_epoch_info_t * temp_info, - fd_spad_t * runtime_spad ); + ulong * new_rate_activation_epoch ); void fd_accumulate_stake_infos( fd_exec_slot_ctx_t const * slot_ctx, diff --git a/src/flamenco/stakes/fd_vote_states.c b/src/flamenco/stakes/fd_vote_states.c new file mode 100644 index 00000000000..f447e354e9c --- /dev/null +++ b/src/flamenco/stakes/fd_vote_states.c @@ -0,0 +1,489 @@ +#include "fd_vote_states.h" + +fd_vote_state_ele_t * +fd_vote_states_get_pool( fd_vote_states_t const * vote_states ) { + FD_SCRATCH_ALLOC_INIT( l, vote_states ); + FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) ); + uchar * pool = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), fd_vote_states_footprint( vote_states->max_vote_accounts ) ); + return fd_vote_state_pool_join( pool ); +} + +fd_vote_state_map_t * +fd_vote_states_get_map( fd_vote_states_t const * vote_states ) { + FD_SCRATCH_ALLOC_INIT( l, vote_states ); + FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) ); + FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( vote_states->max_vote_accounts ) ); + ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( vote_states->max_vote_accounts ); + uchar * map = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) ); + return fd_vote_state_map_join( map ); +} + +ulong +fd_vote_states_align( void ) { + /* The align of the struct should be the max of the align of the data + structures that it contains. In this case, this is the map, the + pool, and the struct itself. */ + return fd_ulong_max( fd_ulong_max( fd_vote_state_map_align(), + fd_vote_state_pool_align() ), alignof(fd_vote_states_t) ); +} + +ulong +fd_vote_states_footprint( ulong max_vote_accounts ) { + + ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts ); + + ulong l = FD_LAYOUT_INIT; + l = FD_LAYOUT_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) ); + l = FD_LAYOUT_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( max_vote_accounts ) ); + l = FD_LAYOUT_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) ); + return FD_LAYOUT_FINI( l, fd_vote_states_align() ); +} + +void * +fd_vote_states_new( void * mem, + ulong max_vote_accounts, + ulong seed ) { + if( FD_UNLIKELY( !mem ) ) { + FD_LOG_WARNING(( "NULL mem" )); + return NULL; + } + + if( FD_UNLIKELY( !max_vote_accounts ) ) { + FD_LOG_WARNING(( "max_vote_accounts is 0" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_states_align() ) ) ) { + FD_LOG_WARNING(( "misaligned mem" )); + return NULL; + } + + ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts ); + + FD_SCRATCH_ALLOC_INIT( l, mem ); + fd_vote_states_t * vote_states = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) ); + void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( max_vote_accounts ) ); + void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) ); + + if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_states_align() )!=(ulong)mem+fd_vote_states_footprint( max_vote_accounts ) ) ) { + FD_LOG_WARNING(( "fd_vote_states_new: bad layout" )); + return NULL; + } + + vote_states->max_vote_accounts = max_vote_accounts; + + if( FD_UNLIKELY( !fd_vote_state_pool_join( fd_vote_state_pool_new( pool_mem, max_vote_accounts ) ) ) ) { + FD_LOG_WARNING(( "Failed to create vote states pool" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_vote_state_map_join( fd_vote_state_map_new( map_mem, map_chain_cnt, seed ) ) ) ) { + FD_LOG_WARNING(( "Failed to create vote states map" )); + return NULL; + } + + FD_COMPILER_MFENCE(); + FD_VOLATILE( vote_states->magic ) = FD_VOTE_STATES_MAGIC; + FD_COMPILER_MFENCE(); + + return mem; +} + +fd_vote_states_t * +fd_vote_states_join( void * mem ) { + if( FD_UNLIKELY( !mem ) ) { + FD_LOG_WARNING(( "NULL mem" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_states_align() ) ) ) { + FD_LOG_WARNING(( "misaligned mem" )); + return NULL; + } + + fd_vote_states_t * vote_states = (fd_vote_states_t *)mem; + + if( FD_UNLIKELY( vote_states->magic != FD_VOTE_STATES_MAGIC ) ) { + FD_LOG_WARNING(( "Invalid vote states magic" )); + return NULL; + } + + #if FD_VOTE_STATES_USE_HANDHOLDING + + ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( vote_states->max_vote_accounts ); + FD_SCRATCH_ALLOC_INIT( l, vote_states ); + vote_states = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) ); + void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( vote_states->max_vote_accounts ) ); + void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) ); + + if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_states_align() )!=(ulong)mem+fd_vote_states_footprint( vote_states->max_vote_accounts ) ) ) { + FD_LOG_WARNING(( "fd_vote_states_join: bad layout" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_vote_state_pool_join( pool_mem ) ) ) { + FD_LOG_WARNING(( "Failed to join vote states pool" )); + return NULL; + } + + if( FD_UNLIKELY( !fd_vote_state_map_join( map_mem ) ) ) { + FD_LOG_WARNING(( "Failed to join vote states map" )); + return NULL; + } + + #endif + + return vote_states; +} + +void +fd_vote_states_update( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account, + fd_pubkey_t const * node_account, + uchar commission, + long last_vote_timestamp, + ulong last_vote_slot, + ulong credits_cnt, + ushort * epoch, + ulong * credits, + ulong * prev_credits ) { + fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states ); + fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states ); + + #if FD_VOTE_STATES_USE_HANDHOLDING + if( FD_UNLIKELY( !vote_state_pool ) ) { + FD_LOG_CRIT(( "unable to retrieve join to vote state pool" )); + } + if( FD_UNLIKELY( !vote_state_map ) ) { + FD_LOG_CRIT(( "unable to retrieve join to vote state map" )); + } + #endif + + /* First, handle the case where the vote state already exists + and we just need to update the entry. The reason we do a const idx + query is to allow fd_vote_states_update to be called while + iterating over the map. It is unsafe to call + fd_vote_state_map_ele_query() during iteration, but we only + need to change fields which are not used for pool/map management. */ + + ulong idx = fd_vote_state_map_idx_query_const( + vote_state_map, + vote_account, + ULONG_MAX, + vote_state_pool ); + + if( idx!=ULONG_MAX ) { + + fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx ); + if( FD_UNLIKELY( !vote_state ) ) { + FD_LOG_CRIT(( "unable to retrieve vote state" )); + } + + /* TODO: can do something smarter where we only update the + comission and the credits coresponding to the new epoch. */ + vote_state->commission = commission; + vote_state->credits_cnt = credits_cnt; + vote_state->last_vote_timestamp = last_vote_timestamp; + vote_state->last_vote_slot = last_vote_slot; + vote_state->node_account = *node_account; + for( ulong i=0UL; iepoch[i] = epoch[i]; + vote_state->credits[i] = credits[i]; + vote_state->prev_credits[i] = prev_credits[i]; + } + return; + } + + /* If the vote state does not exist, we need to create a new entry. */ + /* Otherwise, try to acquire a new node and populate it. */ + if( FD_UNLIKELY( !fd_vote_state_pool_free( vote_state_pool ) ) ) { + FD_LOG_CRIT(( "no free vote states in pool" )); + } + + fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele_acquire( vote_state_pool ); + if( FD_UNLIKELY( !vote_state ) ) { + FD_LOG_CRIT(( "unable to acquire vote state" )); + } + + vote_state->vote_account = *vote_account; + vote_state->node_account = *node_account; + vote_state->commission = commission; + vote_state->last_vote_timestamp = last_vote_timestamp; + vote_state->last_vote_slot = last_vote_slot; + vote_state->credits_cnt = credits_cnt; + for( ulong i=0UL; iepoch[i] = epoch[i]; + vote_state->credits[i] = credits[i]; + vote_state->prev_credits[i] = prev_credits[i]; + } + + if( FD_UNLIKELY( !fd_vote_state_map_ele_insert( + vote_state_map, + vote_state, + vote_state_pool ) ) ) { + FD_LOG_CRIT(( "unable to insert stake delegation into map" )); + } +} + +void +fd_vote_states_remove( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account ) { + fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states ); + fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states ); + #if FD_VOTE_STATES_USE_HANDHOLDING + if( FD_UNLIKELY( !vote_state_pool ) ) { + FD_LOG_CRIT(( "unable to retrieve join to stake delegation pool" )); + } + if( FD_UNLIKELY( !vote_state_map ) ) { + FD_LOG_CRIT(( "unable to retrieve join to stake delegation map" )); + } + #endif + + ulong vote_state_idx = fd_vote_state_map_idx_query( + vote_state_map, + vote_account, + ULONG_MAX, + vote_state_pool ); + if( FD_UNLIKELY( vote_state_idx == ULONG_MAX ) ) { + /* The vote state was not found, nothing to do. */ + return; + } + + ulong idx = fd_vote_state_map_idx_remove( vote_state_map, vote_account, ULONG_MAX, vote_state_pool ); + if( FD_UNLIKELY( idx==ULONG_MAX ) ) { + return; + } + + fd_vote_state_pool_idx_release( vote_state_pool, vote_state_idx ); +} + +void +fd_vote_states_update_from_account( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account, + uchar const * account_data, + ulong account_data_len ) { + + /* TODO: Instead of doing this messy + unbounded decode, it should be + replaced with a more efficient decode that just reads the fields + we need directly. */ + + fd_bincode_decode_ctx_t ctx = { + .data = account_data, + .dataend = account_data + account_data_len, + }; + + ulong total_sz = 0UL; + int err = fd_vote_state_versioned_decode_footprint( &ctx, &total_sz ); + if( FD_UNLIKELY( err ) ) { + FD_LOG_CRIT(( "unable to decode vote state versioned" )); + } + + uchar vote_state_versioned[total_sz]; + + fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( vote_state_versioned, &ctx ); + if( FD_UNLIKELY( err ) ) { + FD_LOG_CRIT(( "unable to decode vote state versioned" )); + } + + fd_pubkey_t node_account; + uchar commission; + long last_vote_timestamp; + ulong last_vote_slot; + ulong credits_cnt = 0UL; + ushort epoch[EPOCH_CREDITS_MAX]; + ulong credits[EPOCH_CREDITS_MAX]; + ulong prev_credits[EPOCH_CREDITS_MAX]; + + fd_vote_epoch_credits_t * epoch_credits = NULL; + + switch( vsv->discriminant ) { + case fd_vote_state_versioned_enum_v0_23_5: + node_account = vsv->inner.v0_23_5.node_pubkey; + commission = vsv->inner.v0_23_5.commission; + last_vote_timestamp = vsv->inner.v0_23_5.last_timestamp.timestamp; + last_vote_slot = vsv->inner.v0_23_5.last_timestamp.slot; + epoch_credits = vsv->inner.v0_23_5.epoch_credits; + + for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits ); + !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter ); + iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) { + + fd_vote_epoch_credits_t * ele = deq_fd_vote_epoch_credits_t_iter_ele( epoch_credits, iter ); + + epoch[credits_cnt] = (ushort)ele->epoch; + credits[credits_cnt] = ele->credits; + prev_credits[credits_cnt] = ele->prev_credits; + credits_cnt++; + } + + break; + case fd_vote_state_versioned_enum_v1_14_11: + node_account = vsv->inner.v1_14_11.node_pubkey; + commission = vsv->inner.v1_14_11.commission; + last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp; + last_vote_slot = vsv->inner.v1_14_11.last_timestamp.slot; + epoch_credits = vsv->inner.v1_14_11.epoch_credits; + + for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits ); + !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter ); + iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) { + + fd_vote_epoch_credits_t * ele = deq_fd_vote_epoch_credits_t_iter_ele( epoch_credits, iter ); + + epoch[credits_cnt] = (ushort)ele->epoch; + credits[credits_cnt] = ele->credits; + prev_credits[credits_cnt] = ele->prev_credits; + credits_cnt++; + } + break; + case fd_vote_state_versioned_enum_current: + node_account = vsv->inner.current.node_pubkey; + commission = vsv->inner.current.commission; + last_vote_timestamp = vsv->inner.current.last_timestamp.timestamp; + last_vote_slot = vsv->inner.current.last_timestamp.slot; + epoch_credits = vsv->inner.current.epoch_credits; + + for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits ); + !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter ); + iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) { + + fd_vote_epoch_credits_t * ele = deq_fd_vote_epoch_credits_t_iter_ele( epoch_credits, iter ); + + epoch[credits_cnt] = (ushort)ele->epoch; + credits[credits_cnt] = ele->credits; + prev_credits[credits_cnt] = ele->prev_credits; + credits_cnt++; + } + break; + default: + __builtin_unreachable(); + } + + fd_vote_states_update( + vote_states, + vote_account, + &node_account, + commission, + last_vote_timestamp, + last_vote_slot, + credits_cnt, + epoch, + credits, + prev_credits ); +} + +void +fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ) { + fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states ); + fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states ); + #if FD_VOTE_STATES_USE_HANDHOLDING + if( FD_UNLIKELY( !vote_state_pool ) ) { + FD_LOG_CRIT(( "unable to retrieve join to vote state pool" )); + } + if( FD_UNLIKELY( !vote_state_map ) ) { + FD_LOG_CRIT(( "unable to retrieve join to vote state map" )); + } + #endif + + for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool ); + !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool ); + iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) { + ulong idx = fd_vote_state_map_iter_idx( iter, vote_state_map, vote_state_pool ); + + fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx ); + if( FD_UNLIKELY( !vote_state ) ) { + FD_LOG_CRIT(( "unable to retrieve vote state" )); + } + + vote_state->stake = 0UL; + } +} + +void +fd_vote_states_update_stake( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account, + ulong stake ) { + fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states ); + fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states ); + + fd_vote_state_ele_t * vote_state = fd_vote_state_map_ele_query( + vote_state_map, + vote_account, + NULL, + vote_state_pool ); + if( FD_UNLIKELY( !vote_state ) ) { + FD_LOG_WARNING(( "unable to retrieve vote state" )); + return; + } + + vote_state->stake = stake; +} + +fd_vote_state_ele_t * +fd_vote_states_query( fd_vote_states_t const * vote_states, + fd_pubkey_t const * vote_account ) { + + return fd_vote_state_map_ele_query( + fd_vote_states_get_map( vote_states ), + vote_account, + NULL, + fd_vote_states_get_pool( vote_states ) ); +} + +/* fd_vote_states_query_const is the same as fd_vote_states but instead + returns a const pointer. */ + +fd_vote_state_ele_t const * +fd_vote_states_query_const( fd_vote_states_t const * vote_states, + fd_pubkey_t const * vote_account ) { + return fd_vote_state_map_ele_query_const( + fd_vote_states_get_map( vote_states ), + vote_account, + NULL, + fd_vote_states_get_pool( vote_states ) ); +} + +ulong +fd_vote_states_max( fd_vote_states_t const * vote_states ) { + return vote_states->max_vote_accounts; +} + +ulong +fd_vote_states_cnt( fd_vote_states_t const * vote_states ) { + return fd_vote_state_pool_used( fd_vote_states_get_pool( vote_states ) ); +} + +fd_vote_state_ele_t * +fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ) { + ulong idx = fd_vote_state_map_iter_idx( iter->iter, iter->map, iter->pool ); + return fd_vote_state_pool_ele( iter->pool, idx ); +} + +int +fd_vote_states_iter_init( fd_vote_states_t const * vote_states, + fd_vote_states_iter_t * iter ) { + #if FD_VOTE_STATES_USE_HANDHOLDING + if( FD_UNLIKELY( !vote_states ) ) { + FD_LOG_CRIT(( "unable to retrieve join to vote state pool" )); + } + if( FD_UNLIKELY( !iter ) ) { + FD_LOG_CRIT(( "unable to retrieve join to vote state map" )); + } + #endif + + iter->map = fd_vote_states_get_map( vote_states ); + iter->pool = fd_vote_states_get_pool( vote_states ); + iter->iter = fd_vote_state_map_iter_init( iter->map, iter->pool ); + + return 0; +} + +int +fd_vote_states_iter_done( fd_vote_states_iter_t * iter ) { + return fd_vote_state_map_iter_done( iter->iter, iter->map, iter->pool ); +} + +void +fd_vote_states_iter_next( fd_vote_states_iter_t * iter ) { + iter->iter = fd_vote_state_map_iter_next( iter->iter, iter->map, iter->pool ); +} diff --git a/src/flamenco/stakes/fd_vote_states.h b/src/flamenco/stakes/fd_vote_states.h new file mode 100644 index 00000000000..36052df5db9 --- /dev/null +++ b/src/flamenco/stakes/fd_vote_states.h @@ -0,0 +1,267 @@ +#ifndef HEADER_fd_src_flamenco_stakes_fd_vote_states_h +#define HEADER_fd_src_flamenco_stakes_fd_vote_states_h + +#include "../fd_flamenco_base.h" +#include "../types/fd_types.h" +#include "../../util/fd_util_base.h" + +#define FD_VOTE_STATES_MAGIC (0xF17EDA2CE7601E70) /* FIREDANCER VOTER V0 */ + +/* fd_vote_states_t is a cache of vote accounts mapping the pubkey of + a vote account to various infromation about the vote account + including, stake, last vote slot/timestamp, commission, and the + epoch credits for the vote account. + + In the runtime, there are 3 instances of fd_vote_states_t that are + maintained and used at different points, notably around epoch reward + and leader schedule calculations. The 3 instances are: + 1. vote_states: This is the vote states for the current epoch. This + is updated through the course of an epoch as vote accounts are + updated. + 2. vote_states_prev: This is the vote states as of the end of + previous epoch E-1 if we are currently executing epoch E. + This gets updated at the end of an epoch when vote_states are + copied into vote_states_prev. + 3. vote_states_prev_prev: This is the vote states as of the end of + epoch E-2 if we are currently executing epoch E. This only gets + updated at the end of an epoch when vote_states_prev is copied + into vote_states_prev_prev. + + The implementation of fd_vote_states_t is a hash map which is backed + by a memory pool. Callers are allowed to insert, replace, and remove + entries from the map. + + In practice, fd_vote_states_t are updated in 3 cases: + 1. They are initially populated from the versioned vote account + stake accounts in the snapshot manifest. These are populated from + the raw vote account data. This is done in a single pass over the + vote account data. + 2. The vote states for the current epoch can be updated after + transaction execution. This is done for vote accounts that are + referenced during a transaction. + 3. Vote states are updated at the epoch boundary. The stake + information for the vote states is refreshed at the boundary. + TODO: The total stake delegated to a vote account should be + calculated during execution as the stake delegations are updated. +*/ + +#define FD_VOTE_STATES_ALIGN (128UL) + +/* Agave defines the max number of epoch credits to store to be 64. + https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v2.2.6/vote-interface/src/state/mod.rs#L37 */ +#define EPOCH_CREDITS_MAX (64UL) + +/* FD_STAKES_USE_HANDHOLDING: Define this to non-zero at compile time + to turn on additional runtime checks and logging. */ + +#ifndef FD_VOTE_STATES_USE_HANDHOLDING +#define FD_VOTE_STATES_USE_HANDHOLDING 1 +#endif + +struct fd_vote_state_ele { + fd_pubkey_t vote_account; + fd_pubkey_t node_account; + ulong next_; /* Internal pool/map use */ + ulong stake; + ulong last_vote_slot; + long last_vote_timestamp; + uchar commission; + + ulong credits_cnt; + ushort epoch [ EPOCH_CREDITS_MAX ]; + ulong credits [ EPOCH_CREDITS_MAX ]; + ulong prev_credits[ EPOCH_CREDITS_MAX ]; +}; +typedef struct fd_vote_state_ele fd_vote_state_ele_t; + +#define POOL_NAME fd_vote_state_pool +#define POOL_T fd_vote_state_ele_t +#define POOL_NEXT next_ +#include "../../util/tmpl/fd_pool.c" + +#define MAP_NAME fd_vote_state_map +#define MAP_KEY_T fd_pubkey_t +#define MAP_ELE_T fd_vote_state_ele_t +#define MAP_KEY vote_account +#define MAP_KEY_EQ(k0,k1) (!(memcmp( &(k0)->key,&(k1)->key,sizeof(fd_pubkey_t) ))) +#define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) )) +#define MAP_NEXT next_ +#include "../../util/tmpl/fd_map_chain.c" + +struct fd_vote_states_iter { + fd_vote_state_map_t * map; + fd_vote_state_ele_t * pool; + fd_vote_state_map_iter_t iter; +}; +typedef struct fd_vote_states_iter fd_vote_states_iter_t; + + +struct __attribute__((aligned(FD_VOTE_STATES_ALIGN))) fd_vote_states { + ulong magic; + ulong max_vote_accounts; +}; +typedef struct fd_vote_states fd_vote_states_t; + +/* This guarantees that the pool alignment is at most 128UL. */ +FD_STATIC_ASSERT(alignof(fd_vote_state_ele_t)<=FD_VOTE_STATES_ALIGN, unexpected pool element alignment); + +/* The static footprint of the vote states assumes that there are + FD_RUNTIME_MAX_VOTE_ACCOUNTS. It also assumes worst case alignment + for each struct. fd_vote_states_t is laid out as first the + fd_vote_states_t struct, followed by a pool of fd_vote_state_ele_t + structs, followed by a map of fd_vote_state_map_ele_t structs. + The pool has FD_RUNTIME_MAX_VOTE_ACCOUNTS elements, and the map + has a chain count deteremined by a call to + fd_vote_states_chain_cnt_est. + NOTE: the footprint is validated to be at least as large as the + actual runtime-determined footprint (see test_vote_states.c) */ + +#define FD_VOTE_STATES_CHAIN_CNT_EST (65536UL) +#define FD_VOTE_STATES_FOOTPRINT \ + /* First, layout the struct with alignment */ \ + sizeof(fd_vote_states_t) + alignof(fd_vote_states_t) + \ + /* Now layout the pool's data footprint */ \ + FD_VOTE_STATES_ALIGN + sizeof(fd_vote_state_ele_t) * FD_RUNTIME_MAX_VOTE_ACCOUNTS + \ + /* Now layout the pool's meta footprint */ \ + FD_VOTE_STATES_ALIGN + sizeof(fd_vote_state_pool_private_t) + \ + /* Now layout the map. We must make assumptions about the chain */ \ + /* count to be equivalent to chain_cnt_est. */ \ + FD_VOTE_STATES_ALIGN + sizeof(fd_vote_state_map_private_t) + (FD_VOTE_STATES_CHAIN_CNT_EST * sizeof(ulong)) + +FD_PROTOTYPES_BEGIN + +/* fd_vote_states_get_pool returns the underlying pool that the + vote states uses to manage the vote states. */ + +fd_vote_state_ele_t * +fd_vote_states_get_pool( fd_vote_states_t const * vote_states ); + +/* fd_vote_states_get_map returns the underlying map that the + vote states uses to manage the vote states. */ + +fd_vote_state_map_t * +fd_vote_states_get_map( fd_vote_states_t const * vote_states ); + +/* fd_vote_states_align returns the minimum alignment required for a + vote states struct. */ + +FD_FN_CONST ulong +fd_vote_states_align( void ); + +/* fd_vote_states_footprint returns the footprint of the vote states + struct for a given amount of max vote accounts. */ + +FD_FN_CONST ulong +fd_vote_states_footprint( ulong max_vote_accounts ); + +/* fd_vote_states_new creates a new vote states struct with a given + number of max vote accounts and a seed. It formats a memory region + which is sized based off of the number of vote accounts. */ + +void * +fd_vote_states_new( void * mem, + ulong max_vote_accounts, + ulong seed ); + +/* fd_vote_states_join joins a vote states struct from a + memory region. There can be multiple valid joins for a given memory + region but the caller is responsible for accessing memory in a + thread-safe manner. */ + +fd_vote_states_t * +fd_vote_states_join( void * mem ); + +/* fd_vote_states_update inserts or updates the vote state corresponding + to a given account. The caller is expected to pass in valid arrays of + epoch, credits, and prev_credits that corresponds to a length of + credits_cnt. */ + +void +fd_vote_states_update( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account, + fd_pubkey_t const * node_account, + uchar commission, + long last_vote_timestamp, + ulong last_vote_slot, + ulong credits_cnt, + ushort * epoch, + ulong * credits, + ulong * prev_credits ); + +/* fd_vote_states_update_from_account inserts or updates the vote state + corresponding to a valid vote account. This is the same as + fd_vote_states_update but is also responsible for decoding the vote + account data into a versioned vote state object and extracing the + commission and credits. */ + +void +fd_vote_states_update_from_account( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account, + uchar const * account_data, + ulong account_data_len ); + +/* fd_vote_states_reset_stakes_t resets the stakes to 0 for each of the + vote accounts in fd_vote_states_t. */ + +void +fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ); + +/* fd_vote_states_update_stake updates the stake for a given vote + account. */ + +void +fd_vote_states_update_stake( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account, + ulong stake ); + +/* fd_vote_states_remove removes the vote state corresponding to a given + account. Does nothing if the account does not exist. */ + +void +fd_vote_states_remove( fd_vote_states_t * vote_states, + fd_pubkey_t const * vote_account ); + +/* fd_vote_states_query returns the vote state corresponding to a given + account. Returns NULL if the account does not exist. */ + +fd_vote_state_ele_t * +fd_vote_states_query( fd_vote_states_t const * vote_states, + fd_pubkey_t const * vote_account ); + +/* fd_vote_states_query_const is the same as fd_vote_states but instead + returns a const pointer. */ + +fd_vote_state_ele_t const * +fd_vote_states_query_const( fd_vote_states_t const * vote_states, + fd_pubkey_t const * vote_account ); + +/* fd_vote_states_max returns the maximum number of vote accounts that + the vote states struct can support. */ + +ulong +fd_vote_states_max( fd_vote_states_t const * vote_states ); + +/* fd_vote_states_cnt returns the number of vote states in the vote + states struct. */ + +ulong +fd_vote_states_cnt( fd_vote_states_t const * vote_states ); + +/* Iterator API for vote states. */ + +fd_vote_state_ele_t * +fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ); + +int +fd_vote_states_iter_init( fd_vote_states_t const * vote_states, + fd_vote_states_iter_t * iter ); + +int +fd_vote_states_iter_done( fd_vote_states_iter_t * iter ); + +void +fd_vote_states_iter_next( fd_vote_states_iter_t * iter ); + +FD_PROTOTYPES_END + +#endif /* HEADER_fd_src_flamenco_stakes_fd_vote_states_h */ diff --git a/src/flamenco/stakes/test_vote_states.c b/src/flamenco/stakes/test_vote_states.c new file mode 100644 index 00000000000..f149155678d --- /dev/null +++ b/src/flamenco/stakes/test_vote_states.c @@ -0,0 +1,138 @@ +#include "fd_vote_states.h" +#include "../runtime/fd_runtime_const.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 ); + } + + /* We need to make sure that the hard constant is at least large + enough to actually hold the footprint of vote states for the max + number of vote accounts that the runtime can support. */ + FD_TEST( fd_vote_states_footprint( FD_RUNTIME_MAX_VOTE_ACCOUNTS ) <= FD_VOTE_STATES_FOOTPRINT ); + FD_TEST( fd_vote_state_map_chain_cnt_est( FD_RUNTIME_MAX_VOTE_ACCOUNTS ) == FD_VOTE_STATES_CHAIN_CNT_EST ); + + ulong const max_vote_accounts = 10UL; + + void * vote_states_mem = fd_wksp_alloc_laddr( wksp, fd_vote_states_align(), fd_vote_states_footprint( max_vote_accounts ), wksp_tag ); + FD_TEST( vote_states_mem ); + + FD_TEST( fd_vote_states_align()>=alignof(fd_vote_states_t) ); + FD_TEST( fd_vote_states_align()>=fd_vote_state_pool_align() ); + FD_TEST( fd_vote_states_align()>=fd_vote_state_map_align() ); + FD_TEST( fd_vote_states_align()==FD_VOTE_STATES_ALIGN ); + + ulong chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts ); + FD_TEST( fd_vote_states_footprint( max_vote_accounts ) >= fd_vote_states_align() + fd_vote_state_pool_footprint( max_vote_accounts ) + fd_vote_state_map_footprint( chain_cnt ) ); + + FD_TEST( !fd_vote_states_new( NULL, max_vote_accounts, 999UL ) ); + FD_TEST( !fd_vote_states_new( vote_states_mem, 0UL, 999UL ) ); + void * new_vote_states_mem = fd_vote_states_new( vote_states_mem, max_vote_accounts, 999UL ); + FD_TEST( new_vote_states_mem ); + + FD_TEST( !fd_vote_states_join( NULL ) ); + void * junk_mem = fd_wksp_alloc_laddr( wksp, 1UL, 1UL, 999UL ); + FD_TEST( junk_mem ); + FD_TEST( !fd_vote_states_join( junk_mem ) ); + + fd_vote_states_t * vote_states = fd_vote_states_join( new_vote_states_mem ); + FD_TEST( vote_states ); + + fd_pubkey_t vote_account_0 = { .ul = { 0, 1, 2, 3 } }; + fd_pubkey_t vote_account_1 = { .ul = { 1, 2, 3, 6 } }; + fd_pubkey_t node_account_0 = { .ul = { 4, 5, 6, 7 } }; + + ushort epoch[100] = { 1 }; + ulong credits[100] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + ulong prev_credits[100] = { 102, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + + fd_vote_states_update( + vote_states, + &vote_account_0, + &node_account_0, + 50, + 100L, + 1000UL, + 0UL, + epoch, + credits, + prev_credits ); + + FD_TEST( fd_vote_states_cnt( vote_states ) == 1UL ); + + fd_vote_states_update( + vote_states, + &vote_account_1, + &node_account_0, + 51, + 100L, + 10000UL, + 1UL, + epoch, + credits, + prev_credits ); + + FD_TEST( fd_vote_states_cnt( vote_states ) == 2UL ); + + fd_vote_state_ele_t * vote_state_ele = fd_vote_states_query( vote_states, &vote_account_0 ); + FD_TEST( vote_state_ele ); + FD_TEST( memcmp( &vote_state_ele->vote_account, &vote_account_0, sizeof(fd_pubkey_t) ) == 0 ); + FD_TEST( memcmp( &vote_state_ele->node_account, &node_account_0, sizeof(fd_pubkey_t) ) == 0 ); + FD_TEST( vote_state_ele->last_vote_slot == 1000UL ); + FD_TEST( vote_state_ele->last_vote_timestamp == 100L ); + FD_TEST( vote_state_ele->commission == 50 ); + FD_TEST( vote_state_ele->credits_cnt == 0UL ); + + fd_vote_state_ele_t * vote_state_ele_1 = fd_vote_states_query( vote_states, &vote_account_1 ); + FD_TEST( vote_state_ele_1 ); + FD_TEST( memcmp( &vote_state_ele_1->vote_account, &vote_account_1, sizeof(fd_pubkey_t) ) == 0 ); + FD_TEST( memcmp( &vote_state_ele_1->node_account, &node_account_0, sizeof(fd_pubkey_t) ) == 0 ); + FD_TEST( vote_state_ele_1->last_vote_slot == 10000UL ); + FD_TEST( vote_state_ele_1->last_vote_timestamp == 100L ); + FD_TEST( vote_state_ele_1->commission == 51 ); + FD_TEST( vote_state_ele_1->credits_cnt == 1UL ); + FD_TEST( vote_state_ele_1->credits[0] == 100 ); + FD_TEST( vote_state_ele_1->epoch[0] == 1 ); + FD_TEST( vote_state_ele_1->epoch[0] == 1 ); + FD_TEST( vote_state_ele_1->prev_credits[0] == 102 ); + + fd_vote_states_update_stake( vote_states, &vote_account_0, 10UL ); + + vote_state_ele = fd_vote_states_query( vote_states, &vote_account_0 ); + FD_TEST( vote_state_ele->stake == 10UL ); + + fd_vote_states_reset_stakes( vote_states ); + + vote_state_ele = fd_vote_states_query( vote_states, &vote_account_0 ); + FD_TEST( vote_state_ele->stake == 0UL ); + + fd_vote_states_update_stake( vote_states, &vote_account_0, 100UL ); + + vote_state_ele = fd_vote_states_query( vote_states, &vote_account_0 ); + FD_TEST( vote_state_ele->stake == 100UL ); + + fd_vote_states_remove( vote_states, &vote_account_0 ); + + FD_TEST( fd_vote_states_cnt( vote_states ) == 1UL ); + + vote_state_ele = fd_vote_states_query( vote_states, &vote_account_0 ); + FD_TEST( !vote_state_ele ); + + FD_LOG_NOTICE(( "pass" )); + fd_halt(); + return 0; +} diff --git a/src/flamenco/types/fd_fuzz_types.h b/src/flamenco/types/fd_fuzz_types.h index 4ad8288f9d5..0aad489ef20 100644 --- a/src/flamenco/types/fd_fuzz_types.h +++ b/src/flamenco/types/fd_fuzz_types.h @@ -3888,31 +3888,6 @@ void *fd_epoch_info_pair_generate( void *mem, void **alloc_mem, fd_rng_t * rng ) return mem; } -void *fd_vote_info_pair_generate( void *mem, void **alloc_mem, fd_rng_t * rng ) { - fd_vote_info_pair_t *self = (fd_vote_info_pair_t *) mem; - *alloc_mem = (uchar *) *alloc_mem + sizeof(fd_vote_info_pair_t); - fd_vote_info_pair_new(mem); - fd_pubkey_generate( &self->account, alloc_mem, rng ); - fd_vote_state_versioned_generate( &self->state, alloc_mem, rng ); - return mem; -} - -void *fd_epoch_info_generate( void *mem, void **alloc_mem, fd_rng_t * rng ) { - fd_epoch_info_t *self = (fd_epoch_info_t *) mem; - *alloc_mem = (uchar *) *alloc_mem + sizeof(fd_epoch_info_t); - fd_epoch_info_new(mem); - ulong vote_states_len = fd_rng_ulong( rng ) % 8; - self->vote_states_pool = fd_vote_info_pair_t_map_join_new( alloc_mem, vote_states_len ); - self->vote_states_root = NULL; - for( ulong i=0; i < vote_states_len; i++ ) { - fd_vote_info_pair_t_mapnode_t * node = fd_vote_info_pair_t_map_acquire( self->vote_states_pool ); - fd_vote_info_pair_generate( &node->elem, alloc_mem, rng ); - fd_vote_info_pair_t_map_insert( self->vote_states_pool, &self->vote_states_root, node ); - } - self->stake_infos_new_keys_start_idx = fd_rng_ulong( rng ); - return mem; -} - void *fd_usage_cost_details_generate( void *mem, void **alloc_mem, fd_rng_t * rng ) { fd_usage_cost_details_t *self = (fd_usage_cost_details_t *) mem; *alloc_mem = (uchar *) *alloc_mem + sizeof(fd_usage_cost_details_t); diff --git a/src/flamenco/types/fd_types.c b/src/flamenco/types/fd_types.c index 1625e51d980..b8eeed738d7 100644 --- a/src/flamenco/types/fd_types.c +++ b/src/flamenco/types/fd_types.c @@ -24996,162 +24996,6 @@ void fd_epoch_info_pair_walk( void * w, fd_epoch_info_pair_t const * self, fd_ty fd_stake_walk( w, &self->stake, fun, "stake", level, 0 ); fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_epoch_info_pair", level--, 0 ); } -int fd_vote_info_pair_encode( fd_vote_info_pair_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - err = fd_pubkey_encode( &self->account, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_vote_state_versioned_encode( &self->state, ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -static int fd_vote_info_pair_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { - if( ctx->data>=ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; - int err = 0; - err = fd_pubkey_decode_footprint_inner( ctx, total_sz ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_vote_state_versioned_decode_footprint_inner( ctx, total_sz ); - if( FD_UNLIKELY( err ) ) return err; - return 0; -} -int fd_vote_info_pair_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { - *total_sz += sizeof(fd_vote_info_pair_t); - void const * start_data = ctx->data; - int err = fd_vote_info_pair_decode_footprint_inner( ctx, total_sz ); - if( ctx->data>ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; - ctx->data = start_data; - return err; -} -static void fd_vote_info_pair_decode_inner( void * struct_mem, void * * alloc_mem, fd_bincode_decode_ctx_t * ctx ) { - fd_vote_info_pair_t * self = (fd_vote_info_pair_t *)struct_mem; - fd_pubkey_decode_inner( &self->account, alloc_mem, ctx ); - fd_vote_state_versioned_decode_inner( &self->state, alloc_mem, ctx ); -} -void * fd_vote_info_pair_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { - fd_vote_info_pair_t * self = (fd_vote_info_pair_t *)mem; - fd_vote_info_pair_new( self ); - void * alloc_region = (uchar *)mem + sizeof(fd_vote_info_pair_t); - void * * alloc_mem = &alloc_region; - fd_vote_info_pair_decode_inner( mem, alloc_mem, ctx ); - return self; -} -void fd_vote_info_pair_new(fd_vote_info_pair_t * self) { - fd_memset( self, 0, sizeof(fd_vote_info_pair_t) ); - fd_pubkey_new( &self->account ); - fd_vote_state_versioned_new( &self->state ); -} -void fd_vote_info_pair_walk( void * w, fd_vote_info_pair_t const * self, fd_types_walk_fn_t fun, const char *name, uint level, uint varint ) { - (void) varint; - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_vote_info_pair", level++, 0 ); - fd_pubkey_walk( w, &self->account, fun, "account", level, 0 ); - fd_vote_state_versioned_walk( w, &self->state, fun, "state", level, 0 ); - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_vote_info_pair", level--, 0 ); -} -ulong fd_vote_info_pair_size( fd_vote_info_pair_t const * self ) { - ulong size = 0; - size += fd_pubkey_size( &self->account ); - size += fd_vote_state_versioned_size( &self->state ); - return size; -} - -int fd_epoch_info_encode( fd_epoch_info_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - if( self->vote_states_root ) { - ulong vote_states_len = fd_vote_info_pair_t_map_size( self->vote_states_pool, self->vote_states_root ); - err = fd_bincode_uint64_encode( vote_states_len, ctx ); - if( FD_UNLIKELY( err ) ) return err; - for( fd_vote_info_pair_t_mapnode_t * n = fd_vote_info_pair_t_map_minimum( self->vote_states_pool, self->vote_states_root ); n; n = fd_vote_info_pair_t_map_successor( self->vote_states_pool, n ) ) { - err = fd_vote_info_pair_encode( &n->elem, ctx ); - if( FD_UNLIKELY( err ) ) return err; - } - } else { - ulong vote_states_len = 0; - err = fd_bincode_uint64_encode( vote_states_len, ctx ); - if( FD_UNLIKELY( err ) ) return err; - } - err = fd_bincode_uint64_encode( self->stake_infos_new_keys_start_idx, ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -static int fd_epoch_info_decode_footprint_inner( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { - if( ctx->data>=ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; - int err = 0; - ulong vote_states_len = 0UL; - err = fd_bincode_uint64_decode( &vote_states_len, ctx ); - ulong vote_states_cnt = !!vote_states_len ? vote_states_len : 1; - *total_sz += fd_vote_info_pair_t_map_align() + fd_vote_info_pair_t_map_footprint( vote_states_cnt ); - if( FD_UNLIKELY( err ) ) return err; - for( ulong i=0; i < vote_states_len; i++ ) { - err = fd_vote_info_pair_decode_footprint_inner( ctx, total_sz ); - if( FD_UNLIKELY( err ) ) return err; - } - err = fd_bincode_uint64_decode_footprint( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - return 0; -} -int fd_epoch_info_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ) { - *total_sz += sizeof(fd_epoch_info_t); - void const * start_data = ctx->data; - int err = fd_epoch_info_decode_footprint_inner( ctx, total_sz ); - if( ctx->data>ctx->dataend ) { return FD_BINCODE_ERR_OVERFLOW; }; - ctx->data = start_data; - return err; -} -static void fd_epoch_info_decode_inner( void * struct_mem, void * * alloc_mem, fd_bincode_decode_ctx_t * ctx ) { - fd_epoch_info_t * self = (fd_epoch_info_t *)struct_mem; - ulong vote_states_len; - fd_bincode_uint64_decode_unsafe( &vote_states_len, ctx ); - self->vote_states_pool = fd_vote_info_pair_t_map_join_new( alloc_mem, vote_states_len ); - self->vote_states_root = NULL; - for( ulong i=0; i < vote_states_len; i++ ) { - fd_vote_info_pair_t_mapnode_t * node = fd_vote_info_pair_t_map_acquire( self->vote_states_pool ); - fd_vote_info_pair_new( &node->elem ); - fd_vote_info_pair_decode_inner( &node->elem, alloc_mem, ctx ); - fd_vote_info_pair_t_mapnode_t * out = NULL;; - fd_vote_info_pair_t_map_insert_or_replace( self->vote_states_pool, &self->vote_states_root, node, &out ); - if( out != NULL ) { - fd_vote_info_pair_t_map_release( self->vote_states_pool, out ); - } - } - fd_bincode_uint64_decode_unsafe( &self->stake_infos_new_keys_start_idx, ctx ); -} -void * fd_epoch_info_decode( void * mem, fd_bincode_decode_ctx_t * ctx ) { - fd_epoch_info_t * self = (fd_epoch_info_t *)mem; - fd_epoch_info_new( self ); - void * alloc_region = (uchar *)mem + sizeof(fd_epoch_info_t); - void * * alloc_mem = &alloc_region; - fd_epoch_info_decode_inner( mem, alloc_mem, ctx ); - return self; -} -void fd_epoch_info_new(fd_epoch_info_t * self) { - fd_memset( self, 0, sizeof(fd_epoch_info_t) ); -} -void fd_epoch_info_walk( void * w, fd_epoch_info_t const * self, fd_types_walk_fn_t fun, const char *name, uint level, uint varint ) { - (void) varint; - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_epoch_info", level++, 0 ); - if( self->vote_states_root ) { - for( fd_vote_info_pair_t_mapnode_t * n = fd_vote_info_pair_t_map_minimum(self->vote_states_pool, self->vote_states_root ); n; n = fd_vote_info_pair_t_map_successor( self->vote_states_pool, n ) ) { - fd_vote_info_pair_walk(w, &n->elem, fun, "vote_states", level, 0 ); - } - } - fun( w, &self->stake_infos_new_keys_start_idx, "stake_infos_new_keys_start_idx", FD_FLAMENCO_TYPE_ULONG, "ulong", level, 0 ); - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_epoch_info", level--, 0 ); -} -ulong fd_epoch_info_size( fd_epoch_info_t const * self ) { - ulong size = 0; - if( self->vote_states_root ) { - size += sizeof(ulong); - ulong max = fd_vote_info_pair_t_map_max( self->vote_states_pool ); - size += fd_vote_info_pair_t_map_footprint( max ); - for( fd_vote_info_pair_t_mapnode_t * n = fd_vote_info_pair_t_map_minimum( self->vote_states_pool, self->vote_states_root ); n; n = fd_vote_info_pair_t_map_successor( self->vote_states_pool, n ) ) { - size += fd_vote_info_pair_size( &n->elem ) - sizeof(fd_vote_info_pair_t); - } - } else { - size += sizeof(ulong); - } - size += sizeof(ulong); - return size; -} - int fd_usage_cost_details_encode( fd_usage_cost_details_t const * self, fd_bincode_encode_ctx_t * ctx ) { int err; err = fd_bincode_uint64_encode( self->signature_cost, ctx ); @@ -25823,13 +25667,6 @@ long fd_clock_timestamp_vote_t_map_compare( fd_clock_timestamp_vote_t_mapnode_t long fd_vote_reward_t_map_compare( fd_vote_reward_t_mapnode_t * left, fd_vote_reward_t_mapnode_t * right ) { return memcmp( left->elem.pubkey.uc, right->elem.pubkey.uc, sizeof(right->elem.pubkey) ); } -#define REDBLK_T fd_vote_info_pair_t_mapnode_t -#define REDBLK_NAME fd_vote_info_pair_t_map -#define REDBLK_IMPL_STYLE 2 -#include "../../util/tmpl/fd_redblack.c" -long fd_vote_info_pair_t_map_compare( fd_vote_info_pair_t_mapnode_t * left, fd_vote_info_pair_t_mapnode_t * right ) { - return memcmp( left->elem.account.uc, right->elem.account.uc, sizeof(right->elem.account) ); -} #define REDBLK_T fd_account_costs_pair_t_mapnode_t #define REDBLK_NAME fd_account_costs_pair_t_map #define REDBLK_IMPL_STYLE 2 diff --git a/src/flamenco/types/fd_types.h b/src/flamenco/types/fd_types.h index 03557304b81..573fb74fc26 100644 --- a/src/flamenco/types/fd_types.h +++ b/src/flamenco/types/fd_types.h @@ -3417,43 +3417,6 @@ struct fd_epoch_info_pair { typedef struct fd_epoch_info_pair fd_epoch_info_pair_t; #define FD_EPOCH_INFO_PAIR_ALIGN alignof(fd_epoch_info_pair_t) -/* Encoded Size: Dynamic */ -struct fd_vote_info_pair { - fd_pubkey_t account; - fd_vote_state_versioned_t state; -}; -typedef struct fd_vote_info_pair fd_vote_info_pair_t; -#define FD_VOTE_INFO_PAIR_ALIGN alignof(fd_vote_info_pair_t) - -typedef struct fd_vote_info_pair_t_mapnode fd_vote_info_pair_t_mapnode_t; -#define REDBLK_T fd_vote_info_pair_t_mapnode_t -#define REDBLK_NAME fd_vote_info_pair_t_map -#define REDBLK_IMPL_STYLE 1 -#include "../../util/tmpl/fd_redblack.c" -struct fd_vote_info_pair_t_mapnode { - fd_vote_info_pair_t elem; - ulong redblack_parent; - ulong redblack_left; - ulong redblack_right; - int redblack_color; -}; -static inline fd_vote_info_pair_t_mapnode_t * -fd_vote_info_pair_t_map_join_new( void * * alloc_mem, ulong len ) { - if( FD_UNLIKELY( 0 == len ) ) len = 1; // prevent underflow - *alloc_mem = (void*)fd_ulong_align_up( (ulong)*alloc_mem, fd_vote_info_pair_t_map_align() ); - void * map_mem = *alloc_mem; - *alloc_mem = (uchar *)*alloc_mem + fd_vote_info_pair_t_map_footprint( len ); - return fd_vote_info_pair_t_map_join( fd_vote_info_pair_t_map_new( map_mem, len ) ); -} -/* Encoded Size: Dynamic */ -struct fd_epoch_info { - fd_vote_info_pair_t_mapnode_t * vote_states_pool; - fd_vote_info_pair_t_mapnode_t * vote_states_root; - ulong stake_infos_new_keys_start_idx; -}; -typedef struct fd_epoch_info fd_epoch_info_t; -#define FD_EPOCH_INFO_ALIGN alignof(fd_epoch_info_t) - /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L153-L161 */ /* Encoded Size: Fixed (48 bytes) */ struct fd_usage_cost_details { @@ -6440,22 +6403,6 @@ static inline int fd_epoch_info_pair_decode_footprint( fd_bincode_decode_ctx_t * } void * fd_epoch_info_pair_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); -void fd_vote_info_pair_new( fd_vote_info_pair_t * self ); -int fd_vote_info_pair_encode( fd_vote_info_pair_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_vote_info_pair_walk( void * w, fd_vote_info_pair_t const * self, fd_types_walk_fn_t fun, const char *name, uint level, uint varint ); -ulong fd_vote_info_pair_size( fd_vote_info_pair_t const * self ); -static inline ulong fd_vote_info_pair_align( void ) { return FD_VOTE_INFO_PAIR_ALIGN; } -int fd_vote_info_pair_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ); -void * fd_vote_info_pair_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); - -void fd_epoch_info_new( fd_epoch_info_t * self ); -int fd_epoch_info_encode( fd_epoch_info_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_epoch_info_walk( void * w, fd_epoch_info_t const * self, fd_types_walk_fn_t fun, const char *name, uint level, uint varint ); -ulong fd_epoch_info_size( fd_epoch_info_t const * self ); -static inline ulong fd_epoch_info_align( void ) { return FD_EPOCH_INFO_ALIGN; } -int fd_epoch_info_decode_footprint( fd_bincode_decode_ctx_t * ctx, ulong * total_sz ); -void * fd_epoch_info_decode( void * mem, fd_bincode_decode_ctx_t * ctx ); - static inline void fd_usage_cost_details_new( fd_usage_cost_details_t * self ) { fd_memset( self, 0, sizeof(fd_usage_cost_details_t) ); } int fd_usage_cost_details_encode( fd_usage_cost_details_t const * self, fd_bincode_encode_ctx_t * ctx ); void fd_usage_cost_details_walk( void * w, fd_usage_cost_details_t const * self, fd_types_walk_fn_t fun, const char *name, uint level, uint varint ); diff --git a/src/flamenco/types/fd_types.json b/src/flamenco/types/fd_types.json index db6c6e7a210..7a0a284ddce 100644 --- a/src/flamenco/types/fd_types.json +++ b/src/flamenco/types/fd_types.json @@ -2707,22 +2707,6 @@ { "name": "stake", "type": "stake" } ] }, - { - "name": "vote_info_pair", - "type": "struct", - "fields": [ - { "name": "account", "type": "pubkey" }, - { "name": "state", "type": "vote_state_versioned" } - ] - }, - { - "name": "epoch_info", - "type": "struct", - "fields": [ - { "name": "vote_states", "type": "map", "element": "vote_info_pair", "key": "account" }, - { "name": "stake_infos_new_keys_start_idx", "type": "ulong" } - ] - }, { "name": "usage_cost_details", "type": "struct", diff --git a/src/flamenco/types/fd_types_reflect_generated.c b/src/flamenco/types/fd_types_reflect_generated.c index f0a23a7bdcd..e5d14587195 100644 --- a/src/flamenco/types/fd_types_reflect_generated.c +++ b/src/flamenco/types/fd_types_reflect_generated.c @@ -3,7 +3,7 @@ #include "fd_types_custom.h" #include "fd_types_reflect_private.h" #pragma GCC diagnostic ignored "-Wpedantic" -ulong fd_types_vt_list_cnt = 247; +ulong fd_types_vt_list_cnt = 245; fd_types_vt_t const fd_types_vt_list[] = { { .name="fd_hash", .name_len=7, .align=FD_HASH_ALIGN, .new_=(void *)fd_hash_new, .decode=(void *)fd_hash_decode, .size=(void *)fd_hash_size, .walk=(void *)fd_hash_walk, .decode_footprint=(void *)fd_hash_decode_footprint, .encode=(void *)fd_hash_encode }, { .name="fd_pubkey", .name_len=9, .align=FD_PUBKEY_ALIGN, .new_=(void *)fd_pubkey_new, .decode=(void *)fd_pubkey_decode, .size=(void *)fd_pubkey_size, .walk=(void *)fd_pubkey_walk, .decode_footprint=(void *)fd_pubkey_decode_footprint, .encode=(void *)fd_pubkey_encode }, @@ -243,8 +243,6 @@ fd_types_vt_t const fd_types_vt_list[] = { { .name="fd_calculated_stake_rewards", .name_len=27, .align=FD_CALCULATED_STAKE_REWARDS_ALIGN, .new_=(void *)fd_calculated_stake_rewards_new, .decode=(void *)fd_calculated_stake_rewards_decode, .size=(void *)fd_calculated_stake_rewards_size, .walk=(void *)fd_calculated_stake_rewards_walk, .decode_footprint=(void *)fd_calculated_stake_rewards_decode_footprint, .encode=(void *)fd_calculated_stake_rewards_encode }, { .name="fd_duplicate_slot_proof", .name_len=23, .align=FD_DUPLICATE_SLOT_PROOF_ALIGN, .new_=(void *)fd_duplicate_slot_proof_new, .decode=(void *)fd_duplicate_slot_proof_decode, .size=(void *)fd_duplicate_slot_proof_size, .walk=(void *)fd_duplicate_slot_proof_walk, .decode_footprint=(void *)fd_duplicate_slot_proof_decode_footprint, .encode=(void *)fd_duplicate_slot_proof_encode }, { .name="fd_epoch_info_pair", .name_len=18, .align=FD_EPOCH_INFO_PAIR_ALIGN, .new_=(void *)fd_epoch_info_pair_new, .decode=(void *)fd_epoch_info_pair_decode, .size=(void *)fd_epoch_info_pair_size, .walk=(void *)fd_epoch_info_pair_walk, .decode_footprint=(void *)fd_epoch_info_pair_decode_footprint, .encode=(void *)fd_epoch_info_pair_encode }, - { .name="fd_vote_info_pair", .name_len=17, .align=FD_VOTE_INFO_PAIR_ALIGN, .new_=(void *)fd_vote_info_pair_new, .decode=(void *)fd_vote_info_pair_decode, .size=(void *)fd_vote_info_pair_size, .walk=(void *)fd_vote_info_pair_walk, .decode_footprint=(void *)fd_vote_info_pair_decode_footprint, .encode=(void *)fd_vote_info_pair_encode }, - { .name="fd_epoch_info", .name_len=13, .align=FD_EPOCH_INFO_ALIGN, .new_=(void *)fd_epoch_info_new, .decode=(void *)fd_epoch_info_decode, .size=(void *)fd_epoch_info_size, .walk=(void *)fd_epoch_info_walk, .decode_footprint=(void *)fd_epoch_info_decode_footprint, .encode=(void *)fd_epoch_info_encode }, { .name="fd_usage_cost_details", .name_len=21, .align=FD_USAGE_COST_DETAILS_ALIGN, .new_=(void *)fd_usage_cost_details_new, .decode=(void *)fd_usage_cost_details_decode, .size=(void *)fd_usage_cost_details_size, .walk=(void *)fd_usage_cost_details_walk, .decode_footprint=(void *)fd_usage_cost_details_decode_footprint, .encode=(void *)fd_usage_cost_details_encode }, { .name="fd_transaction_cost", .name_len=19, .align=FD_TRANSACTION_COST_ALIGN, .new_=(void *)fd_transaction_cost_new, .decode=(void *)fd_transaction_cost_decode, .size=(void *)fd_transaction_cost_size, .walk=(void *)fd_transaction_cost_walk, .decode_footprint=(void *)fd_transaction_cost_decode_footprint, .encode=(void *)fd_transaction_cost_encode }, { .name="fd_account_costs_pair", .name_len=21, .align=FD_ACCOUNT_COSTS_PAIR_ALIGN, .new_=(void *)fd_account_costs_pair_new, .decode=(void *)fd_account_costs_pair_decode, .size=(void *)fd_account_costs_pair_size, .walk=(void *)fd_account_costs_pair_walk, .decode_footprint=(void *)fd_account_costs_pair_decode_footprint, .encode=(void *)fd_account_costs_pair_encode }, diff --git a/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c b/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c index 9e24e63990c..b1e7e7816a0 100644 --- a/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c +++ b/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c @@ -263,9 +263,10 @@ fd_vm_syscall_sol_get_epoch_stake( /**/ void * _vm, const fd_pubkey_t * vote_address = FD_VM_MEM_HADDR_LD( vm, var_addr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT ); /* https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6954 */ - fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( vm->instr_ctx->txn_ctx->bank ); - *_ret = fd_query_pubkey_stake( vote_address, next_epoch_stakes ); - fd_bank_next_epoch_stakes_end_locking_query( vm->instr_ctx->txn_ctx->bank ); + fd_vote_states_t const * vote_states = fd_bank_vote_states_prev_locking_query( vm->instr_ctx->txn_ctx->bank ); + fd_vote_state_ele_t * vote_state_ele = fd_vote_states_query( vote_states, vote_address ); + *_ret = vote_state_ele ? vote_state_ele->stake : 0UL; + fd_bank_vote_states_prev_end_locking_query( vm->instr_ctx->txn_ctx->bank ); return FD_VM_SUCCESS; }