From 1b4ff18d49c6041f751b26ff64f025414e19b013 Mon Sep 17 00:00:00 2001 From: Tom Pointon Date: Wed, 24 Jul 2024 16:33:07 +0000 Subject: [PATCH] flamenco, runtime: re-work partitioned epoch rewards --- contrib/test/instr-fixtures.list | 1 - src/flamenco/capture/fd_solcap_proto.h | 4 +- src/flamenco/capture/fd_solcap_writer.c | 8 +- src/flamenco/rewards/Local.mk | 2 +- src/flamenco/rewards/fd_rewards.c | 1664 +++++++++-------- src/flamenco/rewards/fd_rewards.h | 24 +- src/flamenco/rewards/fd_rewards_types.h | 121 -- .../runtime/context/fd_exec_slot_ctx.c | 5 +- .../runtime/context/fd_exec_slot_ctx.h | 1 - src/flamenco/runtime/fd_runtime.c | 94 +- src/flamenco/runtime/fd_runtime_init.c | 9 - .../runtime/program/fd_stake_program.c | 3 +- .../runtime/sysvar/fd_sysvar_epoch_rewards.c | 108 +- .../runtime/sysvar/fd_sysvar_epoch_rewards.h | 35 +- .../runtime/sysvar/fd_sysvar_epoch_schedule.c | 11 +- .../runtime/tests/run_ledger_tests_all.txt | 3 +- src/flamenco/snapshot/fd_snapshot.c | 6 + src/flamenco/stakes/fd_stakes.c | 70 +- src/flamenco/stakes/fd_stakes.h | 12 +- src/flamenco/types/fd_type_names.c | 12 +- src/flamenco/types/fd_types.c | 1368 +++++++------- src/flamenco/types/fd_types.h | 400 ++-- src/flamenco/types/fd_types.json | 125 +- src/flamenco/types/fd_types_custom.c | 10 + src/flamenco/types/fd_types_custom.h | 173 ++ 25 files changed, 2131 insertions(+), 2138 deletions(-) delete mode 100644 src/flamenco/rewards/fd_rewards_types.h diff --git a/contrib/test/instr-fixtures.list b/contrib/test/instr-fixtures.list index a204c26f35..f37b8f3782 100644 --- a/contrib/test/instr-fixtures.list +++ b/contrib/test/instr-fixtures.list @@ -2843,7 +2843,6 @@ dump/test-vectors/instr/fixtures/stake/instr-11JMaFqtRHm1AmGZheH12hBtd6PXPeMHup5 dump/test-vectors/instr/fixtures/stake/instr-11JMezVj9uGqwHR1fsJFLP7nX5XNRvH2GyaVBFzn7Dw2rXAPc7Mt5xhQwdQLH1rAhQQgasf8EAQnw7jEu9fe2b-0837.fix dump/test-vectors/instr/fixtures/stake/crash-07f2f688f82c8c33ee50302b71fb3c057c41a434.fix dump/test-vectors/instr/fixtures/stake/crash-20ce38c3517c8466df5a5cbc5adf6ed5d1fd61a9.fix -dump/test-vectors/instr/fixtures/stake/crash-69a7d999158549209b2a9aecc8535c4e465cab7a.fix dump/test-vectors/instr/fixtures/stake/crash-7c91a7f6fd2fae3516bea36d344110d9e2bda7ff.fix dump/test-vectors/instr/fixtures/stake/crash-f706da836efde5073bea149eb5a7d7f4d711fbd2.fix dump/test-vectors/instr/fixtures/stake/30572e1204a480dadbfb5881d24274d68c41de5b_1512750.fix diff --git a/src/flamenco/capture/fd_solcap_proto.h b/src/flamenco/capture/fd_solcap_proto.h index 15d91e4846..c64b06896c 100644 --- a/src/flamenco/capture/fd_solcap_proto.h +++ b/src/flamenco/capture/fd_solcap_proto.h @@ -142,7 +142,9 @@ typedef struct fd_solcap_account_tbl fd_solcap_account_tbl_t; #define FD_SOLCAP_FHDR_SZ (256UL) /* FD_SOLCAP_ACC_TBL_CNT is the number of entries that fit in the in- - memory buffer for the account table. */ + memory buffer for the account table. + + N.b: to support epoch boundaries increase this number to 2097152 */ #define FD_SOLCAP_ACC_TBL_CNT (8192U) diff --git a/src/flamenco/capture/fd_solcap_writer.c b/src/flamenco/capture/fd_solcap_writer.c index d29c1a2d9a..fbe4668231 100644 --- a/src/flamenco/capture/fd_solcap_writer.c +++ b/src/flamenco/capture/fd_solcap_writer.c @@ -196,11 +196,8 @@ fd_solcap_writer_init( fd_solcap_writer_t * writer, } /* Init writer */ - - *writer = (fd_solcap_writer_t) { - .file = (FILE *)file, - .stream_goff = (ulong)stream_goff - }; + writer->file = file; + writer->stream_goff = stream_goff; return writer; } @@ -285,6 +282,7 @@ fd_solcap_flush_account_table( fd_solcap_writer_t * writer ) { /* FIXME: This breaks account recording for epoch boundaries and needs to be fixed */ if( writer->account_idx >= FD_SOLCAP_ACC_TBL_CNT ) { + FD_LOG_WARNING(( "too many records in solcap accounts table - try increasing FD_SOLCAP_ACC_TBL_CNT" )); writer->account_idx = 0UL; return 0; } diff --git a/src/flamenco/rewards/Local.mk b/src/flamenco/rewards/Local.mk index c3256c102e..c72668769e 100644 --- a/src/flamenco/rewards/Local.mk +++ b/src/flamenco/rewards/Local.mk @@ -1,4 +1,4 @@ ifdef FD_HAS_INT128 -$(call add-hdrs,fd_rewards.h fd_rewards_types.h) +$(call add-hdrs,fd_rewards.h) $(call add-objs,fd_rewards,fd_flamenco) endif diff --git a/src/flamenco/rewards/fd_rewards.c b/src/flamenco/rewards/fd_rewards.c index d983db51b3..e545e7f65a 100644 --- a/src/flamenco/rewards/fd_rewards.c +++ b/src/flamenco/rewards/fd_rewards.c @@ -1,5 +1,4 @@ #include "fd_rewards.h" -#include "fd_rewards_types.h" #include #include "../runtime/fd_executor_err.h" @@ -7,24 +6,43 @@ #include "../runtime/context/fd_exec_epoch_ctx.h" #include "../runtime/context/fd_exec_slot_ctx.h" #include "../../ballet/siphash13/fd_siphash13.h" +#include "../runtime/program/fd_program_util.h" #pragma GCC diagnostic ignored "-Wformat" #pragma GCC diagnostic ignored "-Wformat-extra-args" +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/sdk/program/src/native_token.rs#L6 */ +#define LAMPORTS_PER_SOL ( 1000000000UL ) + +/* Number of blocks for reward calculation and storing vote accounts. + Distributing rewards to stake accounts begins AFTER this many blocks. + + https://github.com/anza-xyz/agave/blob/9a7bf72940f4b3cd7fc94f54e005868ce707d53d/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L27 */ +#define REWARD_CALCULATION_NUM_BLOCKS ( 1UL ) + +/* stake accounts to store in one block during partitioned reward interval. Target to store 64 rewards per entry/tick in a block. A block has a minimum of 64 entries/tick. This gives 4096 total rewards to store in one block. */ +#define STAKE_ACCOUNT_STORES_PER_BLOCK ( 4096UL ) + +/* https://github.com/anza-xyz/agave/blob/2316fea4c0852e59c071f72d72db020017ffd7d0/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L219 */ +#define MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH ( 10UL ) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/inflation.rs#L85 */ static double total( fd_inflation_t const * inflation, double year ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/src/inflation.rs#L84-L93 */ - FD_TEST( year >= 0.0 ); + if ( FD_UNLIKELY( year == 0.0 ) ) { + FD_LOG_ERR(( "inflation year 0" )); + } double tapered = inflation->initial * pow((1.0 - inflation->taper), year); return (tapered > inflation->terminal) ? tapered : inflation->terminal; } +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/inflation.rs#L102 */ static double foundation( fd_inflation_t const * inflation, double year ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/src/inflation.rs#L100-L108 */ return (year < inflation->foundation_term) ? inflation->foundation * total(inflation, year) : 0.0; } +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/inflation.rs#L97 */ static double validator( fd_inflation_t const * inflation, double year) { /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/sdk/src/inflation.rs#L96-L99 */ @@ -32,11 +50,18 @@ validator( fd_inflation_t const * inflation, double year) { return total( inflation, year ) - foundation( inflation, year ); } +/* Calculates the starting slot for inflation from the activation slot. The activation slot is the earliest + activation slot of the following features: + - devnet_and_testnet + - full_inflation_enable, if full_inflation_vote has been activated + + https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank.rs#L2095 */ static FD_FN_CONST ulong get_inflation_start_slot( fd_exec_slot_ctx_t * slot_ctx ) { ulong devnet_and_testnet = FD_FEATURE_ACTIVE(slot_ctx, devnet_and_testnet) ? slot_ctx->epoch_ctx->features.devnet_and_testnet : ULONG_MAX; + ulong enable = ULONG_MAX; - if (FD_FEATURE_ACTIVE( slot_ctx, full_inflation_vote ) && FD_FEATURE_ACTIVE(slot_ctx, full_inflation_enable)) { + if ( FD_FEATURE_ACTIVE( slot_ctx, full_inflation_vote ) && FD_FEATURE_ACTIVE(slot_ctx, full_inflation_enable ) ) { enable = slot_ctx->epoch_ctx->features.full_inflation_enable; } @@ -51,11 +76,11 @@ get_inflation_start_slot( fd_exec_slot_ctx_t * slot_ctx ) { return min_slot; } +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank.rs#L2110 */ static ulong get_inflation_num_slots( fd_exec_slot_ctx_t * slot_ctx, fd_epoch_schedule_t const * epoch_schedule, ulong slot ) { - /* https://github.com/firedancer-io/solana/blob/de02601d73d626edf98ef63efd772824746f2f33/runtime/src/bank.rs#L2333-L2342 */ ulong inflation_activation_slot = get_inflation_start_slot( slot_ctx ); ulong inflation_start_slot = fd_epoch_slot0( epoch_schedule, @@ -69,18 +94,28 @@ get_inflation_num_slots( fd_exec_slot_ctx_t * slot_ctx, return fd_epoch_slot0(epoch_schedule, epoch) - inflation_start_slot; } +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank.rs#L2121 */ +static double +slot_in_year_for_inflation( fd_exec_slot_ctx_t * slot_ctx ) { + fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); + ulong num_slots = get_inflation_num_slots( slot_ctx, &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot ); + return (double)num_slots / (double)epoch_bank->slots_per_year; +} -// for a given stake and vote_state, calculate how many -// points were earned (credits * stake) and new value -// for credits_observed were the points paid +/* For a given stake and vote_state, calculate how many points were earned (credits * stake) and new value + for credits_observed were the points paid + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L109 */ static void calculate_stake_points_and_credits ( - fd_stake_history_t const * stake_history, - fd_stake_state_v2_t * stake_state, - fd_vote_state_versioned_t * vote_state_versioned, - fd_calculate_stake_points_t * result + fd_stake_history_t const * stake_history, + fd_stake_t * stake, + fd_vote_state_versioned_t * vote_state_versioned, + fd_calculated_stake_points_t * result ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/programs/stake/src/stake_state.rs#L249-L351 */ + + 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: @@ -93,52 +128,65 @@ calculate_stake_points_and_credits ( epoch_credits = vote_state_versioned->inner.v1_14_11.epoch_credits; break; default: - __builtin_unreachable(); + FD_LOG_ERR(( "invalid vote account, should never happen" )); } - ulong credits_in_stake = stake_state->inner.stake.stake.credits_observed; - ulong credits_in_vote = deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ? 0 : deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->credits; -// FD_LOG_WARNING(("Vote credits: %lu %32J %lu %lu %lu", credits_in_stake, stake_state->inner.stake.stake.delegation.voter_pubkey.key, credits_in_vote, deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->epoch, deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->prev_credits )); - - result->points = 0; - result->force_credits_update_with_skipped_reward = credits_in_vote < credits_in_stake; - if (credits_in_vote < credits_in_stake) { - // FD_LOG_WARNING(("Vote credits 2: %lu %32J %lu %lu %lu", credits_in_stake, stake_state->inner.stake.stake.delegation.voter_pubkey.key, credits_in_vote, deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->epoch, deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->prev_credits )); - - result->new_credits_observed = credits_in_vote; - return; - } - if (credits_in_vote == credits_in_stake) { - // don't hint caller and return current value if credits remain unchanged (= delinquent) - result->new_credits_observed = credits_in_stake; - return; - } - - 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 epoch = ele->epoch; - ulong final_epoch_credits = ele->credits; - ulong initial_epoch_credits = ele->prev_credits; - uint128 earned_credits = 0; - if (credits_in_stake < initial_epoch_credits) { - earned_credits = (uint128)(final_epoch_credits - initial_epoch_credits); - } else if (credits_in_stake < final_epoch_credits) { - earned_credits = (uint128)(final_epoch_credits - new_credits_observed); + 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; } - new_credits_observed = fd_ulong_max(new_credits_observed, final_epoch_credits); - uint128 stake_amount = (uint128)(fd_stake_activating_and_deactivating(&stake_state->inner.stake.stake.delegation, epoch, stake_history, NULL).effective); - points += stake_amount * earned_credits; - } - result->points = points; - result->new_credits_observed = new_credits_observed; -// FD_LOG_WARNING(("Vote credits 3: %lu", new_credits_observed )); + /* If the Vote account has less credits observed than the Stake account, + something is wrong and we need to force an update. + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L142 */ + if ( FD_UNLIKELY( credits_in_vote < credits_in_stake ) ) { + result->points = 0; + result->new_credits_observed = credits_in_vote; + result->force_credits_update_with_skipped_reward = 1; + return; + } -} + /* If the Vote account has the same amount of credits observed as the Stake account, + then the Vote account hasn't earnt any credits and so there is nothing to update. + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L148 */ + if ( FD_UNLIKELY( credits_in_vote == credits_in_stake ) ) { + result->points = 0; + result->new_credits_observed = credits_in_vote; + result->force_credits_update_with_skipped_reward = 0; + return; + } + + /* 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 earned_credits = 0; + if ( FD_LIKELY( credits_in_stake < initial_epoch_credits ) ) { + earned_credits = (uint128)(final_epoch_credits - initial_epoch_credits); + } else if ( FD_UNLIKELY( credits_in_stake < final_epoch_credits ) ) { + earned_credits = (uint128)(final_epoch_credits - new_credits_observed); + } + + new_credits_observed = fd_ulong_max( new_credits_observed, final_epoch_credits ); + + ulong stake_amount = fd_stake_activating_and_deactivating( &stake->delegation, ele->epoch, stake_history, NULL ).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; +} +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/rewards.rs#L127 */ static int calculate_stake_rewards( fd_stake_history_t const * stake_history, @@ -148,24 +196,15 @@ calculate_stake_rewards( fd_point_value_t * point_value, fd_calculated_stake_rewards_t * result ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/programs/stake/src/stake_state.rs#L360-L464 */ - /* - implements the `calculate_stake_rewards` solana function - for a given stake and vote_state, calculate what distributions and what updates should be made - returns a tuple in the case of a payout of: - * staker_rewards to be distributed - * voter_rewards to be distributed - * new value for credits_observed in the stake - returns None if there's no payout or if any deserved payout is < 1 lamport */ - fd_calculate_stake_points_t stake_points_result = {0}; - // TODO - calculate_stake_points_and_credits( stake_history, stake_state, vote_state_versioned, &stake_points_result); - // FD_LOG_WARNING(("CSR: %lu", stake_points_result.new_credits_observed )); + fd_calculated_stake_points_t stake_points_result = {0}; + calculate_stake_points_and_credits( stake_history, &stake_state->inner.stake.stake, vote_state_versioned, &stake_points_result); // Drive credits_observed forward unconditionally when rewards are disabled // or when this is the stake's activation epoch - stake_points_result.force_credits_update_with_skipped_reward |= (point_value->rewards == 0); - stake_points_result.force_credits_update_with_skipped_reward |= (stake_state->inner.stake.stake.delegation.activation_epoch == rewarded_epoch); + if ( ( point_value->rewards == 0 ) || + ( stake_state->inner.stake.stake.delegation.activation_epoch == rewarded_epoch ) ) { + stake_points_result.force_credits_update_with_skipped_reward |= 1; + } if (stake_points_result.force_credits_update_with_skipped_reward) { result->staker_rewards = 0; @@ -177,8 +216,8 @@ calculate_stake_rewards( return 1; } - - ulong rewards = (ulong)(stake_points_result.points * (uint128)point_value->rewards / (uint128) point_value->points); + /* FIXME: need to error out if the conversion from uint128 to u64 fails, also use 128 checked mul and div */ + ulong rewards = (ulong)(stake_points_result.points * (uint128)(point_value->rewards) / (uint128) point_value->points); if (rewards == 0) { return 1; } @@ -192,50 +231,27 @@ calculate_stake_rewards( result->staker_rewards = split_result.staker_portion; result->voter_rewards = split_result.voter_portion; result->new_credits_observed = stake_points_result.new_credits_observed; - - /* implements the `redeem_stake_rewards` solana function */ - // stake_state->inner.stake.stake.credits_observed += result->new_credits_observed; - // stake_state->inner.stake.stake.delegation.stake += result->staker_rewards; return 0; } +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/rewards.rs#L33 */ static int -stake_state_redeem_rewards( fd_exec_slot_ctx_t * slot_ctx, - fd_stake_history_t const * stake_history, - fd_pubkey_t const * stake_acc, - fd_vote_state_versioned_t * vote_state, - ulong rewarded_epoch, - fd_point_value_t * point_value, - fd_calculated_stake_rewards_t * result ) { - - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/programs/stake/src/stake_state.rs#L1525-L1571 */ - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec ); - if( FD_UNLIKELY( err ) ) { - return err; - } - - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - } - // fd_stake_state_t stake_state; - // read_stake_state( global, stake_acc_rec->const_meta, &stake_state ); - // if (!fd_stake_state_is_stake( &stake_state)) { - // return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - // } - - rc = calculate_stake_rewards(stake_history, &stake_state, vote_state, rewarded_epoch, point_value, result); - // FD_LOG_WARNING(("SSRR: %32J RES->NCO: %lu, SS->NCO: %lu %lu", stake_acc->key, result->new_credits_observed, stake_state.discriminant, stake_state.inner.stake.stake.credits_observed)); - if (rc != 0) { - // ctx->txn_ctx->custom_err = 0; /* Err(StakeError::NoCreditsToRedeem.into()) */ +redeem_rewards( fd_stake_history_t const * stake_history, + fd_stake_state_v2_t * stake_state, + fd_vote_state_versioned_t * vote_state_versioned, + ulong rewarded_epoch, + fd_point_value_t * point_value, + fd_calculated_stake_rewards_t * calculated_stake_rewards) { + + int rc = calculate_stake_rewards( stake_history, stake_state, vote_state_versioned, rewarded_epoch, point_value, calculated_stake_rewards ); + if ( FD_UNLIKELY( rc != 0 ) ) { return rc; } return FD_EXECUTOR_INSTR_SUCCESS; } +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L70 */ int calculate_points( fd_stake_state_v2_t * stake_state, @@ -243,196 +259,75 @@ calculate_points( fd_stake_history_t const * stake_history, uint128 * result ) { - // TODO - // if (!fd_stake_state_is_stake( stake_state)) { - // return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; - // } - fd_calculate_stake_points_t stake_point_result; - calculate_stake_points_and_credits(stake_history, stake_state, vote_state_versioned, &stake_point_result); + if ( FD_UNLIKELY( !fd_stake_state_v2_is_stake( stake_state ) ) ) { + return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + } + + fd_calculated_stake_points_t stake_point_result; + calculate_stake_points_and_credits( stake_history, &stake_state->inner.stake.stake, vote_state_versioned, &stake_point_result ); *result = stake_point_result.points; return FD_EXECUTOR_INSTR_SUCCESS; } +/* Returns the length of the given epoch in slots + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/sdk/program/src/epoch_schedule.rs#L103 */ +static ulong +get_slots_in_epoch( + ulong epoch, + fd_epoch_bank_t const * epoch_bank +) { + return (epoch < epoch_bank->epoch_schedule.first_normal_epoch) ? + 1UL << fd_ulong_sat_add(epoch, FD_EPOCH_LEN_MIN_TRAILING_ZERO) : + epoch_bank->epoch_schedule.slots_per_epoch; +} + +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank.rs#L2082 */ static double epoch_duration_in_years( fd_epoch_bank_t const * epoch_bank, ulong prev_epoch ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2283-L2288 */ - /* get_slots_in_epoch */ - ulong slots_in_epoch = (prev_epoch < epoch_bank->epoch_schedule.first_normal_epoch) ? - 1UL << fd_ulong_sat_add(prev_epoch, FD_EPOCH_LEN_MIN_TRAILING_ZERO) : - epoch_bank->epoch_schedule.slots_per_epoch; + ulong slots_in_epoch = get_slots_in_epoch( prev_epoch, epoch_bank ); return (double)slots_in_epoch / (double) epoch_bank->slots_per_year; } +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank.rs#L2128 */ static void calculate_previous_epoch_inflation_rewards( fd_exec_slot_ctx_t * slot_ctx, - fd_epoch_bank_t const * epoch_bank, - ulong slot, ulong prev_epoch_capitalization, ulong prev_epoch, fd_prev_epoch_inflation_rewards_t * rewards ) { - /* https://github.com/firedancer-io/solana/blob/de02601d73d626edf98ef63efd772824746f2f33/runtime/src/bank.rs#L2351-L2376 */ + double slot_in_year = slot_in_year_for_inflation( slot_ctx ); - - /* slot_in_year_for_inflation - https://github.com/firedancer-io/solana/blob/de02601d73d626edf98ef63efd772824746f2f33/runtime/src/bank.rs#L2344-L2349 - */ - ulong num_slots = get_inflation_num_slots( slot_ctx, &epoch_bank->epoch_schedule, slot ); - double slot_in_year = (double)num_slots / (double)epoch_bank->slots_per_year; + fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); rewards->validator_rate = validator( &epoch_bank->inflation, slot_in_year ); - rewards->foundation_rate = foundation(&epoch_bank->inflation, slot_in_year); + rewards->foundation_rate = foundation( &epoch_bank->inflation, slot_in_year ); rewards->prev_epoch_duration_in_years = epoch_duration_in_years(epoch_bank, prev_epoch); rewards->validator_rewards = (ulong)(rewards->validator_rate * (double)prev_epoch_capitalization * rewards->prev_epoch_duration_in_years); FD_LOG_DEBUG(("Rewards %lu, Rate %.16f, Duration %.18f Capitalization %lu Slot in year %.16f", rewards->validator_rewards, rewards->validator_rate, rewards->prev_epoch_duration_in_years, prev_epoch_capitalization, slot_in_year)); } - -// Sum the lamports of the vote accounts and the delegated stake +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/lib.rs#L29 */ static ulong -vote_balance_and_staked( fd_exec_slot_ctx_t * slot_ctx, fd_stakes_t const * stakes) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/stakes.rs#L346-L356 */ - ulong result = 0; - for( fd_vote_accounts_pair_t_mapnode_t const * n = fd_vote_accounts_pair_t_map_minimum_const( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root ); - n; - n = fd_vote_accounts_pair_t_map_successor_const( stakes->vote_accounts.vote_accounts_pool, n ) ) { - result += n->elem.value.lamports; - } - - for( fd_vote_accounts_pair_t_mapnode_t const * n = fd_vote_accounts_pair_t_map_minimum_const( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, slot_ctx->slot_bank.vote_account_keys.vote_accounts_root ); - n; - n = fd_vote_accounts_pair_t_map_successor_const( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, n ) ) { - result += n->elem.value.lamports; +get_minimum_stake_delegation( fd_exec_slot_ctx_t * slot_ctx ) { + if ( !FD_FEATURE_ACTIVE( slot_ctx, stake_minimum_delegation_for_rewards ) ) { + return 0UL; } - for( fd_delegation_pair_t_mapnode_t const * n = fd_delegation_pair_t_map_minimum_const( stakes->stake_delegations_pool, stakes->stake_delegations_root ); - n; - n = fd_delegation_pair_t_map_successor_const( stakes->stake_delegations_pool, n ) ) { - fd_pubkey_t const * stake_acc = &n->elem.account; - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - if (fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec ) != FD_ACC_MGR_SUCCESS ) { - continue; - } - - fd_stake_state_v2_t stake_state; - if (fd_stake_get_state( stake_acc_rec, &slot_ctx->valloc, &stake_state) != 0) { - continue; - } - - result += stake_state.inner.stake.stake.delegation.stake; + if ( !FD_FEATURE_ACTIVE( slot_ctx, stake_raise_minimum_delegation_to_1_sol ) ) { + return LAMPORTS_PER_SOL; } - for( fd_stake_accounts_pair_t_mapnode_t const * n = fd_stake_accounts_pair_t_map_minimum_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root ); - n; - n = fd_stake_accounts_pair_t_map_successor_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, n ) ) { - fd_pubkey_t const * stake_acc = &n->elem.key; - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - if (fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec ) != FD_ACC_MGR_SUCCESS ) { - continue; - } - // result += stake_acc_rec->const_meta->info.lamports; - fd_stake_state_v2_t stake_state; - if (fd_stake_get_state( stake_acc_rec, &slot_ctx->valloc, &stake_state) != 0) { - continue; - } - - result += stake_state.inner.stake.stake.delegation.stake; - } - - return result; + return 1; } -static void -calculate_reward_points_account( - fd_exec_slot_ctx_t * slot_ctx, - fd_stake_history_t const * stake_history, - fd_pubkey_t const * voter_acc, - fd_pubkey_t const * stake_acc, - uint128 * points, - ulong * actual_len - ) { - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - ulong min_stake_delegation = 1000000000; - - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - if( 0!=fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec) ) { - FD_LOG_DEBUG(("Stake acc not found %32J", stake_acc->uc)); - return; - } - - if (stake_acc_rec->const_meta->info.lamports == 0) { - FD_LOG_DEBUG(("Stake acc not found %32J", stake_acc->uc)); - return; - } - - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - // FD_LOG_ERR(("failed to read")); - return; - } - - if (FD_FEATURE_ACTIVE(slot_ctx, stake_minimum_delegation_for_rewards)) { - if (stake_state.inner.stake.stake.delegation.stake < min_stake_delegation) { - return; - } - } - *actual_len += 1; - - fd_vote_accounts_pair_t_mapnode_t key; - fd_memcpy(&key.elem.key, voter_acc, sizeof(fd_pubkey_t)); - - if (fd_vote_accounts_pair_t_map_find(epoch_bank->stakes.vote_accounts.vote_accounts_pool, epoch_bank->stakes.vote_accounts.vote_accounts_root, &key) == NULL) { - return; - } - - FD_BORROWED_ACCOUNT_DECL(voter_acc_rec); - int read_err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, voter_acc, voter_acc_rec ); - if( read_err!=0 || 0!=memcmp( &voter_acc_rec->const_meta->info.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) { - return; - } - - /* Deserialize vote account */ - fd_bincode_decode_ctx_t decode = { - .data = voter_acc_rec->const_data, - .dataend = voter_acc_rec->const_data + voter_acc_rec->const_meta->dlen, - /* TODO: Make this a instruction-scoped allocator */ - .valloc = slot_ctx->valloc, - }; - fd_vote_state_versioned_t vote_state[1] = {0}; - if( FD_UNLIKELY( 0!=fd_vote_state_versioned_decode( vote_state, &decode ) ) ) - FD_LOG_ERR(( "vote_state_versioned_decode failed" )); - - // fd_vote_epoch_credits_t * epoch_credits; - // switch (vote_state->discriminant) { - // case fd_vote_state_versioned_enum_current: - // epoch_credits = vote_state->inner.current.epoch_credits; - // break; - // case fd_vote_state_versioned_enum_v0_23_5: - // epoch_credits = vote_state->inner.v0_23_5.epoch_credits; - // break; - // case fd_vote_state_versioned_enum_v1_14_11: - // epoch_credits = vote_state->inner.v1_14_11.epoch_credits; - // break; - // default: - // __builtin_unreachable(); - // } - - // FD_LOG_WARNING(("VOTE ACCOUNT: %32J, %lu", voter_acc->key, deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->credits)); - - uint128 result; - *points += (calculate_points(&stake_state, vote_state, stake_history, &result) == FD_EXECUTOR_INSTR_SUCCESS ? result : 0); - // FD_LOG_WARNING(("PER_ACC_POINTS: Acc: %32J, Points: %K", stake_acc->key, &result )); - fd_bincode_destroy_ctx_t destroy = {.valloc = slot_ctx->valloc}; - fd_stake_state_v2_destroy( &stake_state, &destroy ); - fd_vote_state_versioned_destroy( vote_state, &destroy ); -} +/* Calculates epoch reward points from stake/vote accounts. + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L472 */ static void calculate_reward_points_partitioned( fd_exec_slot_ctx_t * slot_ctx, @@ -440,709 +335,880 @@ calculate_reward_points_partitioned( ulong rewards, fd_point_value_t * result ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2961-L3018 */ + /* There is a cache of vote account keys stored in the slot context */ + /* TODO: check this cache is correct */ + uint128 points = 0; - ulong actual_len = 0; fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - FD_LOG_DEBUG(("Delegations len %lu, slot del len %lu", fd_delegation_pair_t_map_size( epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root ), fd_stake_accounts_pair_t_map_size( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root ))); + + ulong minimum_stake_delegation = get_minimum_stake_delegation( slot_ctx ); + + /* Calculate the points for each stake delegation */ for( fd_delegation_pair_t_mapnode_t const * n = fd_delegation_pair_t_map_minimum_const( epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root ); n; n = fd_delegation_pair_t_map_successor_const( epoch_bank->stakes.stake_delegations_pool, n ) ) { - fd_pubkey_t const * voter_acc = &n->elem.delegation.voter_pubkey; - fd_pubkey_t const * stake_acc = &n->elem.account; - // FD_LOG_WARNING(("STAKE ACC1: %32J, %32J", stake_acc->key, voter_acc->key)); - calculate_reward_points_account( slot_ctx, stake_history, voter_acc, stake_acc, &points, &actual_len ); + FD_SCRATCH_SCOPE_BEGIN { + fd_valloc_t valloc = fd_scratch_virtual(); + + /* Fetch the stake account */ + FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); + fd_pubkey_t const * stake_acc = &n->elem.account; + int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec); + if ( err != FD_ACC_MGR_SUCCESS && err != FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) { + FD_LOG_ERR(( "failed to read stake account from funk" )); + continue; + } + if ( err == FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) { + FD_LOG_DEBUG(( "stake account not found %32J", stake_acc->uc )); + continue; + } + if ( stake_acc_rec->const_meta->info.lamports == 0 ) { + FD_LOG_DEBUG(( "stake acc with zero lamports %32J", stake_acc->uc)); + continue; + } + + /* Check the minimum stake delegation */ + fd_stake_state_v2_t stake_state[1] = {0}; + err = fd_stake_get_state( stake_acc_rec, &valloc, stake_state ); + if ( err != 0 ) { + FD_LOG_DEBUG(( "get stake state failed" )); + continue; + } + if ( FD_UNLIKELY( stake_state->inner.stake.stake.delegation.stake < minimum_stake_delegation ) ) { + continue; + } + + /* Check that the vote account is present in our cache */ + fd_vote_accounts_pair_t_mapnode_t key; + fd_pubkey_t const * voter_acc = &n->elem.delegation.voter_pubkey; + fd_memcpy( &key.elem.key, voter_acc, sizeof(fd_pubkey_t) ); + fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( + slot_ctx->epoch_ctx ); + if ( FD_UNLIKELY( fd_vote_accounts_pair_t_map_find( + epoch_bank->stakes.vote_accounts.vote_accounts_pool, + epoch_bank->stakes.vote_accounts.vote_accounts_root, + &key ) == NULL ) ) { + FD_LOG_DEBUG(( "vote account missing from cache" )); + continue; + } + + /* Check that the vote account is valid and has the correct owner */ + FD_BORROWED_ACCOUNT_DECL(voter_acc_rec); + err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, voter_acc, voter_acc_rec ); + if ( FD_UNLIKELY( err ) ) { + FD_LOG_DEBUG(( "failed to read vote account from funk" )); + continue; + } + if( FD_UNLIKELY( memcmp( &voter_acc_rec->const_meta->info.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) != 0 ) ) { + FD_LOG_DEBUG(( "vote account has wrong owner" )); + continue; + } + fd_bincode_decode_ctx_t decode = { + .data = voter_acc_rec->const_data, + .dataend = voter_acc_rec->const_data + voter_acc_rec->const_meta->dlen, + .valloc = valloc, + }; + fd_vote_state_versioned_t vote_state[1] = {0}; + if( FD_UNLIKELY( 0!=fd_vote_state_versioned_decode( vote_state, &decode ) ) ) { + FD_LOG_DEBUG(( "vote_state_versioned_decode failed" )); + continue; + } + + uint128 account_points; + err = calculate_points( stake_state, vote_state, stake_history, &account_points ); + if ( FD_UNLIKELY( err ) ) { + FD_LOG_DEBUG(( "failed to calculate points" )); + continue; + } + + points += account_points; + } FD_SCRATCH_SCOPE_END; } - // FD_LOG_HEXDUMP_WARNING(( "POINTS 1", &points, 16 )); + /* TODO: factor this out */ + /* Calculate points for each stake account in slot_bank.stake_account_keys.stake_accounts_pool */ for ( fd_stake_accounts_pair_t_mapnode_t const * n = fd_stake_accounts_pair_t_map_minimum_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root ); n; n = fd_stake_accounts_pair_t_map_successor_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, n ) ) { - (void) n; - fd_pubkey_t const * stake_acc = &n->elem.key; - // FD_LOG_WARNING(("STAKE ACC2: %32J", stake_acc->key)); - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - if( 0!=fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec) ) { - FD_LOG_DEBUG(("Stake acc not found %32J", stake_acc->uc)); - continue; - } + FD_SCRATCH_SCOPE_BEGIN { + fd_valloc_t valloc = fd_scratch_virtual(); + + /* Fetch the stake account */ + FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); + fd_pubkey_t const * stake_acc = &n->elem.key; + int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec); + if ( err != FD_ACC_MGR_SUCCESS && err != FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) { + FD_LOG_ERR(( "failed to read stake account from funk" )); + continue; + } + if ( err == FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) { + FD_LOG_DEBUG(( "stake account not found %32J", stake_acc->uc )); + continue; + } + if ( stake_acc_rec->const_meta->info.lamports == 0 ) { + FD_LOG_DEBUG(( "stake acc with zero lamports %32J", stake_acc->uc)); + continue; + } - if (stake_acc_rec->const_meta->info.lamports == 0) continue; + /* Check the minimum stake delegation */ + fd_stake_state_v2_t stake_state[1] = {0}; + err = fd_stake_get_state( stake_acc_rec, &valloc, stake_state ); + if ( err != 0 ) { + FD_LOG_DEBUG(( "get stake state failed" )); + continue; + } + if ( FD_UNLIKELY( stake_state->inner.stake.stake.delegation.stake < minimum_stake_delegation ) ) { + continue; + } - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - FD_LOG_WARNING(("Failed to read stake state from stake account %32J", stake_acc)); - continue; - } + /* Check that the vote account is present in our cache */ + fd_vote_accounts_pair_t_mapnode_t key; + fd_pubkey_t const * voter_acc = &stake_state->inner.stake.stake.delegation.voter_pubkey; + fd_memcpy( &key.elem.key, voter_acc, sizeof(fd_pubkey_t) ); + fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( + slot_ctx->epoch_ctx ); + if ( FD_UNLIKELY( fd_vote_accounts_pair_t_map_find( + epoch_bank->stakes.vote_accounts.vote_accounts_pool, + epoch_bank->stakes.vote_accounts.vote_accounts_root, + &key ) == NULL ) ) { + FD_LOG_DEBUG(( "vote account missing from cache" )); + continue; + } + + /* Check that the vote account is valid and has the correct owner */ + FD_BORROWED_ACCOUNT_DECL(voter_acc_rec); + err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, voter_acc, voter_acc_rec ); + if ( FD_UNLIKELY( err ) ) { + FD_LOG_DEBUG(( "failed to read vote account from funk" )); + continue; + } + if( FD_UNLIKELY( memcmp( &voter_acc_rec->const_meta->info.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) != 0 ) ) { + FD_LOG_DEBUG(( "vote account has wrong owner" )); + continue; + } + fd_bincode_decode_ctx_t decode = { + .data = voter_acc_rec->const_data, + .dataend = voter_acc_rec->const_data + voter_acc_rec->const_meta->dlen, + .valloc = valloc, + }; + fd_vote_state_versioned_t vote_state[1] = {0}; + if( FD_UNLIKELY( 0!=fd_vote_state_versioned_decode( vote_state, &decode ) ) ) { + FD_LOG_DEBUG(( "vote_state_versioned_decode failed" )); + continue; + } + + uint128 account_points; + err = calculate_points( stake_state, vote_state, stake_history, &account_points ); + if ( FD_UNLIKELY( err ) ) { + FD_LOG_DEBUG(( "failed to calculate points" )); + continue; + } - fd_pubkey_t const * voter_acc = &stake_state.inner.stake.stake.delegation.voter_pubkey; - calculate_reward_points_account( slot_ctx, stake_history, voter_acc, stake_acc, &points, &actual_len ); - fd_bincode_destroy_ctx_t destroy = {.valloc = slot_ctx->valloc}; - fd_stake_state_v2_destroy( &stake_state, &destroy ); + points += account_points; + } FD_SCRATCH_SCOPE_END; } - // FD_LOG_HEXDUMP_WARNING(( "POINTS", &points, 16 )); - // FD_LOG_WARNING(("REWARDS 2: %lu TOT POINTS: %llu",rewards, points )); if (points > 0) { result->points = points; result->rewards = rewards; - } else { - result = NULL; } } +/* Calculate the partitioned stake rewards for a single stake/vote account pair, updates result with these. */ static void calculate_stake_vote_rewards_account( - fd_exec_slot_ctx_t * slot_ctx, - fd_stake_history_t const * stake_history, - ulong rewarded_epoch, - fd_point_value_t * point_value, - fd_pubkey_t const * voter_acc, - fd_pubkey_t const * stake_acc, - fd_stake_reward_t * stake_reward_deq, - fd_vote_reward_t_mapnode_t * vote_reward_map, - fd_acc_lamports_t * total_stake_rewards + fd_exec_slot_ctx_t * slot_ctx, + fd_stake_history_t const * stake_history, + ulong rewarded_epoch, + fd_point_value_t * point_value, + fd_pubkey_t const * stake_acc, + fd_calculate_stake_vote_rewards_result_t * result ) { - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - ulong min_stake_delegation = 1000000000; - - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - int err = fd_acc_mgr_view(slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec); - if (FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS)) { - FD_LOG_DEBUG(("stake_state::stake_state_redeem_rewards() %32J not found", stake_acc )); - return; - } + FD_SCRATCH_SCOPE_BEGIN { - if (stake_acc_rec->const_meta->info.lamports == 0) return; + fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); + ulong minimum_stake_delegation = get_minimum_stake_delegation( slot_ctx ); - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - // FD_LOG_ERR(("failed to read")); - return; - } - - if (FD_FEATURE_ACTIVE(slot_ctx, stake_minimum_delegation_for_rewards)) { - if (stake_state.inner.stake.stake.delegation.stake < min_stake_delegation) { - return; - } - } - - fd_vote_accounts_pair_t_mapnode_t key; - fd_memcpy(&key.elem.key, voter_acc, sizeof(fd_pubkey_t)); - - if (fd_vote_accounts_pair_t_map_find(epoch_bank->stakes.vote_accounts.vote_accounts_pool, epoch_bank->stakes.vote_accounts.vote_accounts_root, &key) == NULL - && fd_vote_accounts_pair_t_map_find(slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, slot_ctx->slot_bank.vote_account_keys.vote_accounts_root, &key) == NULL) { - return; - } - - FD_BORROWED_ACCOUNT_DECL(voter_acc_rec); - int read_err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, voter_acc, voter_acc_rec ); - if( read_err!=0 || 0!=memcmp( &voter_acc_rec->const_meta->info.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) { - return; - } + FD_BORROWED_ACCOUNT_DECL( stake_acc_rec ); + if( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec) != 0 ) { + FD_LOG_DEBUG(( "Stake acc not found %32J", stake_acc->uc )); + return; + } - /* Read vote account */ - fd_bincode_decode_ctx_t decode = { - .data = voter_acc_rec->const_data, - .dataend = voter_acc_rec->const_data + voter_acc_rec->const_meta->dlen, - /* TODO: Make this a instruction-scoped allocator */ - .valloc = slot_ctx->valloc, - }; - fd_bincode_destroy_ctx_t destroy = {.valloc = slot_ctx->valloc}; - fd_vote_state_versioned_t vote_state_versioned[1] = {0}; - if( fd_vote_state_versioned_decode( vote_state_versioned, &decode ) != 0 ) { - return; - } + fd_stake_state_v2_t stake_state[1] = {0}; + if ( fd_stake_get_state( stake_acc_rec, &slot_ctx->valloc, stake_state ) != 0 ) { + FD_LOG_DEBUG(( "Failed to read stake state from stake account %32J", stake_acc )); + return; + } + if ( !fd_stake_state_v2_is_stake( stake_state ) ) { + FD_LOG_DEBUG(( "stake account does not have active delegation" )); + return; + } + fd_pubkey_t const * voter_acc = &stake_state->inner.stake.stake.delegation.voter_pubkey; - uchar commission = 0U; - switch (vote_state_versioned->discriminant) { - case fd_vote_state_versioned_enum_current: - commission = (uchar)vote_state_versioned->inner.current.commission; - break; - case fd_vote_state_versioned_enum_v0_23_5: - commission = (uchar)vote_state_versioned->inner.v0_23_5.commission; - break; - case fd_vote_state_versioned_enum_v1_14_11: - commission = (uchar)vote_state_versioned->inner.v1_14_11.commission; - break; - default: - __builtin_unreachable(); - } + if ( FD_FEATURE_ACTIVE(slot_ctx, stake_minimum_delegation_for_rewards )) { + if ( stake_state->inner.stake.stake.delegation.stake < minimum_stake_delegation ) { + return; + } + } - fd_vote_reward_t_mapnode_t * node = fd_vote_reward_t_map_query(vote_reward_map, *voter_acc, NULL); - if (node == NULL) { - node = fd_vote_reward_t_map_insert(vote_reward_map, *voter_acc); - node->vote_rewards = 0; - fd_memcpy(&node->vote_pubkey, voter_acc, sizeof(fd_pubkey_t)); - node->commission = (uchar)commission; - node->needs_store = 0; - } + fd_vote_accounts_pair_t_mapnode_t key; + fd_memcpy( &key.elem.key, voter_acc, sizeof(fd_pubkey_t) ); + if ( fd_vote_accounts_pair_t_map_find( epoch_bank->stakes.vote_accounts.vote_accounts_pool, epoch_bank->stakes.vote_accounts.vote_accounts_root, &key ) == NULL + && fd_vote_accounts_pair_t_map_find( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, slot_ctx->slot_bank.vote_account_keys.vote_accounts_root, &key ) == NULL) { + return; + } - fd_calculated_stake_rewards_t redeemed[1] = {0}; - rc = stake_state_redeem_rewards(slot_ctx, stake_history, stake_acc, vote_state_versioned, rewarded_epoch, point_value, redeemed); - if ( rc != 0) { - fd_vote_state_versioned_destroy( vote_state_versioned, &destroy ); - FD_LOG_DEBUG(("stake_state::stake_state_redeem_rewards() failed for %32J with error %d", stake_acc->key, rc )); + FD_BORROWED_ACCOUNT_DECL( voter_acc_rec ); + int read_err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, voter_acc, voter_acc_rec ); + if( read_err!=0 || memcmp( &voter_acc_rec->const_meta->info.owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) != 0 ) { return; - } + } - fd_acc_lamports_t post_lamports = stake_acc_rec->const_meta->info.lamports; + fd_valloc_t valloc = fd_scratch_virtual(); + fd_bincode_decode_ctx_t decode = { + .data = voter_acc_rec->const_data, + .dataend = voter_acc_rec->const_data + voter_acc_rec->const_meta->dlen, + .valloc = valloc, + }; + fd_vote_state_versioned_t vote_state_versioned[1] = {0}; + if( fd_vote_state_versioned_decode( vote_state_versioned, &decode ) != 0 ) { + FD_LOG_ERR(( "failed to decode vote state" )); + return; + } - // track total_stake_rewards - *total_stake_rewards += redeemed->staker_rewards; + /* 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_state, vote_state_versioned, rewarded_epoch, point_value, calculated_stake_rewards ); + if ( err != 0) { + FD_LOG_DEBUG(( "redeem_rewards failed for %32J with error %d", stake_acc->key, err )); + return; + } - // add stake_reward to the collection - fd_stake_reward_t stake_reward; - fd_memcpy(&stake_reward.stake_pubkey, stake_acc, sizeof(fd_pubkey_t)); + /* Fetch the comission for the vote account */ + uchar commission = 0; + switch (vote_state_versioned->discriminant) { + case fd_vote_state_versioned_enum_current: + commission = vote_state_versioned->inner.current.commission; + break; + case fd_vote_state_versioned_enum_v0_23_5: + commission = vote_state_versioned->inner.v0_23_5.commission; + break; + case fd_vote_state_versioned_enum_v1_14_11: + commission = vote_state_versioned->inner.v1_14_11.commission; + break; + default: + FD_LOG_DEBUG(( "unsupported vote account" )); + return; + } - stake_reward.reward_info = (fd_reward_info_t) { - .reward_type = { .discriminant = fd_reward_type_enum_staking }, - .commission = (uchar)commission, - .lamports = redeemed->staker_rewards, - .new_credits_observed = redeemed->new_credits_observed, - .staker_rewards = redeemed->staker_rewards, - .post_balance = post_lamports - }; + /* Update the vote reward in the map */ + fd_vote_reward_t_mapnode_t vote_map_key[1]; + fd_memcpy( &vote_map_key->elem.pubkey, voter_acc, sizeof(fd_pubkey_t) ); + fd_vote_reward_t_mapnode_t * vote_reward_node = fd_vote_reward_t_map_find( result->vote_reward_map_pool, result->vote_reward_map_root, vote_map_key ); + if ( vote_reward_node == NULL ) { + vote_reward_node = fd_vote_reward_t_map_acquire( result->vote_reward_map_pool ); + fd_memcpy( &vote_reward_node->elem.pubkey, voter_acc, sizeof(fd_pubkey_t) ); + vote_reward_node->elem.commission = 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( result->vote_reward_map_pool, &result->vote_reward_map_root, vote_reward_node ); + } else { + vote_reward_node->elem.needs_store = 1; + vote_reward_node->elem.vote_rewards = fd_ulong_sat_add( + vote_reward_node->elem.vote_rewards, calculated_stake_rewards->voter_rewards + ); + } - // FD_LOG_WARNING(("STAKE REWARD: %32J %lu", stake_acc->key, redeemed->staker_rewards)); - deq_fd_stake_reward_t_push_tail( stake_reward_deq, stake_reward ); + /* Add the stake reward to list of all stake rewards */ + fd_stake_reward_t * stake_reward = fd_stake_reward_pool_ele_acquire( result->stake_reward_calculation.pool ); + fd_memcpy( &stake_reward->stake_pubkey, stake_acc, FD_PUBKEY_FOOTPRINT ); + stake_reward->lamports = calculated_stake_rewards->staker_rewards; + stake_reward->credits_observed = calculated_stake_rewards->new_credits_observed; + + fd_stake_reward_dlist_ele_push_tail( + &result->stake_reward_calculation.stake_rewards, + stake_reward, + result->stake_reward_calculation.pool ); + result->stake_reward_calculation.stake_rewards_len += 1; + + /* Update the total stake rewards */ + result->stake_reward_calculation.total_stake_rewards_lamports += calculated_stake_rewards->staker_rewards; + } FD_SCRATCH_SCOPE_END; +} - // track voter rewards - node->vote_rewards = fd_ulong_sat_add(node->vote_rewards, redeemed->voter_rewards); - node->needs_store = 1; +/* Calculates epoch rewards for stake/vote accounts. + Returns vote rewards, stake rewards, and the sum of all stake rewards in lamports. - fd_stake_state_v2_destroy( &stake_state, &destroy ); - fd_vote_state_versioned_destroy( vote_state_versioned, &destroy ); -} + This uses a pool to allocate the stake rewards, which means that we can use dlists to + distribute these into partitions of variable size without copying them or over-allocating + the partitions. + - We use a single dlist to put all the stake rewards during the calculation phase. + - We then distribute these into partitions (whose size cannot be known in advance), where each + partition is a seperate dlist. + - The dlist elements are all backed by the same pool, and allocated once. + This approach optimizes memory usage and reduces copying. -// return reward info for each vote account -// return account data for each vote account that needs to be stored -// This return value is a little awkward at the moment so that downstream existing code in the non-partitioned rewards code path can be re-used without duplication or modification. -// This function is copied from the existing code path's `store_vote_accounts`. -// The primary differences: -// - we want this fn to have no side effects (such as actually storing vote accounts) so that we -// can compare the expected results with the current code path -// - we want to be able to batch store the vote accounts later for improved performance/cache updating - -/* -Calculates epoch rewards for stake/vote accounts -Returns vote rewards, stake rewards, and the sum of all stake rewards in lamports -*/ + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L334 */ static void calculate_stake_vote_rewards( - fd_exec_slot_ctx_t * slot_ctx, - fd_stake_history_t const * stake_history, - ulong rewarded_epoch, - fd_point_value_t * point_value, - fd_validator_reward_calculation_t * result + fd_exec_slot_ctx_t * slot_ctx, + fd_stake_history_t const * stake_history, + ulong rewarded_epoch, + fd_point_value_t * point_value, + fd_calculate_stake_vote_rewards_result_t * result ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L3062-L3192 */ fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - fd_acc_lamports_t total_stake_rewards = 0; - fd_stake_reward_t * stake_reward_deq = deq_fd_stake_reward_t_alloc( slot_ctx->valloc ); - fd_vote_reward_t_mapnode_t * vote_reward_map = fd_vote_reward_t_map_alloc( slot_ctx->valloc, 24 ); /* 2^24 slots */ - - for( fd_delegation_pair_t_mapnode_t const * n = fd_delegation_pair_t_map_minimum_const( epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root ); + ulong rewards_max_count = fd_ulong_sat_add( + fd_delegation_pair_t_map_size( epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root ), + fd_stake_accounts_pair_t_map_size( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root ) ); + + /* Create the stake rewards pool and dlist. The pool will be destoyed after the stake rewards have been distributed. */ + result->stake_reward_calculation.pool = fd_stake_reward_pool_join( + fd_stake_reward_pool_new( + fd_valloc_malloc( + slot_ctx->valloc, + fd_stake_reward_pool_align(), + fd_stake_reward_pool_footprint( rewards_max_count ) ), rewards_max_count ) ); + fd_stake_reward_dlist_new( &result->stake_reward_calculation.stake_rewards ); + result->stake_reward_calculation.stake_rewards_len = 0UL; + + /* Create the vote rewards map. This will be destroyed after the vote rewards have been distributed. */ + result->vote_reward_map_pool = fd_vote_reward_t_map_join( fd_vote_reward_t_map_new( fd_valloc_malloc( + slot_ctx->valloc, + fd_vote_reward_t_map_align(), + fd_vote_reward_t_map_footprint( rewards_max_count )), rewards_max_count ) ); + result->vote_reward_map_root = NULL; + + /* Loop over all the delegations + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L367 */ + for( fd_delegation_pair_t_mapnode_t const * n = fd_delegation_pair_t_map_minimum_const( + epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root ); n; n = fd_delegation_pair_t_map_successor_const( epoch_bank->stakes.stake_delegations_pool, n ) - ) { - // fd_pubkey_t const * voter_acc = &n->elem.delegation.voter_pubkey; + ) { fd_pubkey_t const * stake_acc = &n->elem.account; - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - if( 0!=fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec) ) { - FD_LOG_DEBUG(("Stake acc not found %32J", stake_acc->uc)); - continue; - } - - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - FD_LOG_WARNING(("Failed to read stake state from stake account %32J", stake_acc)); - continue; - } - fd_pubkey_t const * voter_acc = &stake_state.inner.stake.stake.delegation.voter_pubkey; - - calculate_stake_vote_rewards_account( slot_ctx, stake_history, rewarded_epoch, point_value, voter_acc, stake_acc, stake_reward_deq, vote_reward_map, &total_stake_rewards ); + calculate_stake_vote_rewards_account( + slot_ctx, + stake_history, + rewarded_epoch, + point_value, + stake_acc, + result ); } - for ( fd_stake_accounts_pair_t_mapnode_t const * n = fd_stake_accounts_pair_t_map_minimum_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root ); + /* Loop over all the stake accounts in the slot bank pool */ + for ( fd_stake_accounts_pair_t_mapnode_t const * n = + fd_stake_accounts_pair_t_map_minimum_const( + slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root ); n; n = fd_stake_accounts_pair_t_map_successor_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, n) ) { - fd_pubkey_t const * stake_acc = &n->elem.key; - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - if( 0!=fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec) ) { - FD_LOG_DEBUG(("Stake acc not found %32J", stake_acc->uc)); - continue; - } - if (stake_acc_rec->const_meta->info.lamports == 0) continue; - - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - FD_LOG_WARNING(("Failed to read stake state from stake account %32J", stake_acc)); - continue; - } - fd_pubkey_t const * voter_acc = &stake_state.inner.stake.stake.delegation.voter_pubkey; - calculate_stake_vote_rewards_account( slot_ctx, stake_history, rewarded_epoch, point_value, voter_acc, stake_acc, stake_reward_deq, vote_reward_map, &total_stake_rewards ); - - fd_bincode_destroy_ctx_t destroy = {.valloc = slot_ctx->valloc}; - fd_stake_state_v2_destroy( &stake_state, &destroy ); + fd_pubkey_t const * stake_acc = &n->elem.key; + calculate_stake_vote_rewards_account( + slot_ctx, + stake_history, + rewarded_epoch, + point_value, + stake_acc, + result ); } - - // FD_LOG_WARNING(( "TSRL: %lu", total_stake_rewards )); - - *result = (fd_validator_reward_calculation_t) { - .total_stake_rewards_lamports = total_stake_rewards, - .stake_reward_deq = stake_reward_deq, - .vote_reward_map = vote_reward_map - }; } -/* Calculate epoch reward and return vote and stake rewards. */ +/* Calculate epoch reward and return vote and stake rewards. + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L273 */ static void calculate_validator_rewards( fd_exec_slot_ctx_t * slot_ctx, ulong rewarded_epoch, ulong rewards, - fd_validator_reward_calculation_t * result + fd_calculate_validator_rewards_result_t * result ) { /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2759-L2786 */ fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache ); - if( FD_UNLIKELY( !stake_history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" )); + if( FD_UNLIKELY( !stake_history ) ) { + FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" )); + } + /* Calculate the epoch reward points from stake/vote accounts */ fd_point_value_t point_value_result[1] = {0}; - calculate_reward_points_partitioned(slot_ctx, stake_history, rewards, point_value_result); - calculate_stake_vote_rewards(slot_ctx, stake_history, rewarded_epoch, point_value_result, result); + calculate_reward_points_partitioned( slot_ctx, stake_history, rewards, point_value_result ); + result->total_points = point_value_result->points; + + /* Calculate the stake and vote rewards for each account */ + calculate_stake_vote_rewards( + slot_ctx, + stake_history, + rewarded_epoch, + point_value_result, + &result->calculate_stake_vote_rewards_result ); } +/* Calculate the number of blocks required to distribute rewards to all stake accounts. -// Calculate the number of blocks required to distribute rewards to all stake accounts. -// fn get_reward_distribution_num_blocks(&self, rewards: &StakeRewards) -> u64 { + https://github.com/anza-xyz/agave/blob/9a7bf72940f4b3cd7fc94f54e005868ce707d53d/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L214 + */ static ulong get_reward_distribution_num_blocks( fd_epoch_schedule_t const * epoch_schedule, ulong slot, - fd_stake_reward_t * stake_reward_deq + ulong total_stake_accounts ) { /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1250-L1267 */ - if (epoch_schedule->warmup && fd_slot_to_epoch(epoch_schedule, slot, NULL) < epoch_schedule->first_normal_epoch) { - return 1; + if ( epoch_schedule->warmup && + fd_slot_to_epoch( epoch_schedule, slot, NULL ) < epoch_schedule->first_normal_epoch ) { + return 1UL; } - ulong total_stake_accounts = deq_fd_stake_reward_t_cnt(stake_reward_deq); + ulong num_chunks = total_stake_accounts / (ulong)STAKE_ACCOUNT_STORES_PER_BLOCK + (total_stake_accounts % STAKE_ACCOUNT_STORES_PER_BLOCK != 0); - num_chunks = fd_ulong_max(num_chunks, 1); + num_chunks = fd_ulong_max( num_chunks, 1 ); num_chunks = fd_ulong_min( + num_chunks, fd_ulong_max( epoch_schedule->slots_per_epoch / (ulong)MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH, - 1), - 1); + 1) ); return num_chunks; } static void hash_rewards_into_partitions( - fd_slot_bank_t const * bank, - fd_stake_reward_t * stake_reward_deq, - ulong num_partitions, - fd_stake_rewards_vector_t * result + fd_exec_slot_ctx_t * slot_ctx, + fd_stake_reward_calculation_t * stake_reward_calculation, + const fd_hash_t * parent_blockhash, + fd_stake_reward_calculation_partitioned_t * result ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/epoch_rewards_hasher.rs#L43C31-L61 */ - fd_siphash13_t _sip[1] = {0}; - fd_siphash13_t * hasher = fd_siphash13_init( _sip, 0UL, 0UL ); - hasher = fd_siphash13_append( hasher, bank->banks_hash.hash, sizeof(fd_hash_t)); - - fd_stake_rewards_vector_new( result ); - for (ulong i = 0; i < num_partitions; ++i) { - fd_stake_rewards_t new_partition; - fd_stake_rewards_new(&new_partition); - fd_stake_rewards_vector_push( result, new_partition); + /* Initialize a dlist for every partition. + These will all use the same pool - we do not re-allocate the stake rewards, only move them into partitions. */ + result->partitioned_stake_rewards.pool = stake_reward_calculation->pool; + ulong num_partitions = get_reward_distribution_num_blocks( + &fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx )->epoch_schedule, + slot_ctx->slot_bank.slot, + stake_reward_calculation->stake_rewards_len); + result->partitioned_stake_rewards.partitions_len = num_partitions; + result->partitioned_stake_rewards.partitions = fd_valloc_malloc( + slot_ctx->valloc, + fd_stake_reward_dlist_align(), + fd_stake_reward_dlist_footprint() * num_partitions + ); + + /* Ownership of these dlist's and the pool gets transferred to stake_rewards_by_partition, which then gets transferred to epoch_reward_status. + These are eventually cleaned up when epoch_reward_status_inactive is called. */ + for ( ulong i = 0; i < num_partitions; ++i ) { + fd_stake_reward_dlist_new( &result->partitioned_stake_rewards.partitions[ i ] ); } - for ( - deq_fd_stake_reward_t_iter_t iter = deq_fd_stake_reward_t_iter_init(stake_reward_deq ); - !deq_fd_stake_reward_t_iter_done( stake_reward_deq, iter ); - iter = deq_fd_stake_reward_t_iter_next( stake_reward_deq, iter) + + /* Iterate over all the stake rewards, moving references to them into the appropiate partitions. + IMPORTANT: after this, we cannot use the original stake rewards dlist anymore. */ + fd_stake_reward_dlist_iter_t next_iter; + for ( fd_stake_reward_dlist_iter_t iter = fd_stake_reward_dlist_iter_fwd_init( + &stake_reward_calculation->stake_rewards, stake_reward_calculation->pool ); + !fd_stake_reward_dlist_iter_done( iter, &stake_reward_calculation->stake_rewards, stake_reward_calculation->pool ); + iter = next_iter ) { - fd_stake_reward_t * ele = deq_fd_stake_reward_t_iter_ele( stake_reward_deq, iter ); - /* hash_address_to_partition: find partition index (0..partitions) by hashing `address` with the `hasher` */ - fd_siphash13_append( hasher, (const uchar *) ele->stake_pubkey.key, sizeof(fd_pubkey_t)); - ulong hash64 = fd_siphash13_fini(hasher); + fd_stake_reward_t * stake_reward = fd_stake_reward_dlist_iter_ele( iter, &stake_reward_calculation->stake_rewards, stake_reward_calculation->pool ); + /* Cache the next iter here, as we will overwrite the DLIST_NEXT value further down in the loop iteration. */ + next_iter = fd_stake_reward_dlist_iter_fwd_next( iter, &stake_reward_calculation->stake_rewards, stake_reward_calculation->pool ); + + /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/epoch_rewards_hasher.rs#L43C31-L61 */ + fd_siphash13_t _sip[1] = {0}; + fd_siphash13_t * hasher = fd_siphash13_init( _sip, 0UL, 0UL ); + + hasher = fd_siphash13_append( hasher, parent_blockhash->hash, sizeof(fd_hash_t) ); + fd_siphash13_append( hasher, (const uchar *) stake_reward->stake_pubkey.key, sizeof(fd_pubkey_t) ); + + ulong hash64 = fd_siphash13_fini( hasher ); /* hash_to_partition */ + /* FIXME: should be saturating add */ ulong partition_index = (ulong)( (uint128) num_partitions * (uint128) hash64 / ((uint128)ULONG_MAX + 1) ); - fd_stake_rewards_push(&result->elems[partition_index], ele); + + /* Move the stake reward to the partition's dlist */ + fd_stake_reward_dlist_t * partition = &result->partitioned_stake_rewards.partitions[ partition_index ]; + fd_stake_reward_dlist_ele_push_tail( partition, stake_reward, stake_reward_calculation->pool ); } } -// Calculate rewards from previous epoch to prepare for partitioned distribution. +/* Calculate rewards from previous epoch to prepare for partitioned distribution. + + https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L214 */ static void calculate_rewards_for_partitioning( - fd_exec_slot_ctx_t * slot_ctx, - ulong prev_epoch, + fd_exec_slot_ctx_t * slot_ctx, + ulong prev_epoch, + const fd_hash_t * parent_blockhash, fd_partitioned_rewards_calculation_t * result ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2356-L2403 */ + /* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L227 */ fd_prev_epoch_inflation_rewards_t rewards; - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - fd_slot_bank_t const * slot_bank = &slot_ctx->slot_bank; - calculate_previous_epoch_inflation_rewards( slot_ctx, epoch_bank, slot_bank->slot, slot_bank->capitalization, prev_epoch, &rewards ); - - ulong old_vote_balance_and_staked = vote_balance_and_staked(slot_ctx, &epoch_bank->stakes); - - fd_validator_reward_calculation_t validator_result[1] = {0}; - calculate_validator_rewards(slot_ctx, prev_epoch, rewards.validator_rewards, validator_result); + calculate_previous_epoch_inflation_rewards( slot_ctx, slot_ctx->slot_bank.capitalization, prev_epoch, &rewards ); - ulong num_partitions = get_reward_distribution_num_blocks(&epoch_bank->epoch_schedule, slot_bank->slot, validator_result->stake_reward_deq); - - fd_stake_rewards_vector_t hash_rewards_result = {0}; - hash_rewards_into_partitions(slot_bank, validator_result->stake_reward_deq, num_partitions, &hash_rewards_result); + fd_slot_bank_t const * slot_bank = &slot_ctx->slot_bank; - /* free stake_reward_deq */ - deq_fd_stake_reward_t_delete( validator_result->stake_reward_deq ); + fd_calculate_validator_rewards_result_t validator_result[1] = {0}; + calculate_validator_rewards( slot_ctx, prev_epoch, rewards.validator_rewards, validator_result ); - *result = (fd_partitioned_rewards_calculation_t) { - .vote_account_rewards = validator_result->vote_reward_map, - .stake_rewards_by_partition = { hash_rewards_result }, - .total_stake_rewards_lamports = validator_result->total_stake_rewards_lamports, - .old_vote_balance_and_staked = old_vote_balance_and_staked, - .validator_rewards = rewards.validator_rewards, - .validator_rate = rewards.validator_rate, - .foundation_rate = rewards.foundation_rate, - .prev_epoch_duration_in_years = rewards.prev_epoch_duration_in_years, - .capitalization = slot_bank->capitalization - }; + hash_rewards_into_partitions( + slot_ctx, + &validator_result->calculate_stake_vote_rewards_result.stake_reward_calculation, + parent_blockhash, + &result->stake_rewards_by_partition ); + + result->vote_reward_map_pool = validator_result->calculate_stake_vote_rewards_result.vote_reward_map_pool; + result->vote_reward_map_root = validator_result->calculate_stake_vote_rewards_result.vote_reward_map_root; + result->validator_rewards = rewards.validator_rewards; + result->validator_rate = rewards.validator_rate; + result->foundation_rate = rewards.foundation_rate; + result->prev_epoch_duration_in_years = rewards.prev_epoch_duration_in_years; + result->capitalization = slot_bank->capitalization; + result->total_points = validator_result->total_points; } -/* (TODO) unclear if we need to implement update_reward_history function on solana side. So far it doesn't do much other than logging / record keeping */ -/* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L3471-L3484 */ -// static void -// update_reward_history( -// ) { -// return; -// } - -// Calculate rewards from previous epoch and distribute vote rewards +/* Calculate rewards from previous epoch and distribute vote rewards + + https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L97 */ static void calculate_rewards_and_distribute_vote_rewards( - fd_exec_slot_ctx_t * slot_ctx, - ulong prev_epoch, + fd_exec_slot_ctx_t * slot_ctx, + ulong prev_epoch, + const fd_hash_t * parent_blockhash, fd_calculate_rewards_and_distribute_vote_rewards_result_t * result ) { /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2406-L2492 */ fd_partitioned_rewards_calculation_t rewards_calc_result[1] = {0}; - calculate_rewards_for_partitioning(slot_ctx, prev_epoch, rewards_calc_result); - fd_vote_reward_t_mapnode_t * ref = rewards_calc_result->vote_account_rewards; - for (ulong i = 0; i < fd_vote_reward_t_map_slot_cnt( rewards_calc_result->vote_account_rewards); ++i) { - if (fd_vote_reward_t_map_key_equal( ref[i].vote_pubkey, fd_vote_reward_t_map_key_null() ) ) { - continue; - } - fd_pubkey_t const * vote_pubkey = &ref[i].vote_pubkey; - ulong min_data_sz = 0UL; - FD_BORROWED_ACCOUNT_DECL(vote_rec); - int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, vote_pubkey, 1, min_data_sz, vote_rec); - FD_TEST( err == 0 ); - vote_rec->meta->info.lamports = fd_ulong_sat_add(vote_rec->meta->info.lamports, ref[i].vote_rewards); + calculate_rewards_for_partitioning( slot_ctx, prev_epoch, parent_blockhash, rewards_calc_result ); + + /* Iterate over all the vote reward nodes */ + for ( fd_vote_reward_t_mapnode_t* vote_reward_node = fd_vote_reward_t_map_minimum( + rewards_calc_result->vote_reward_map_pool, + rewards_calc_result->vote_reward_map_root); + vote_reward_node; + vote_reward_node = fd_vote_reward_t_map_successor( rewards_calc_result->vote_reward_map_pool, vote_reward_node ) ) { + + fd_pubkey_t const * vote_pubkey = &vote_reward_node->elem.pubkey; + FD_BORROWED_ACCOUNT_DECL( vote_rec ); + FD_TEST( fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, vote_pubkey, 1, 0UL, vote_rec ) == FD_ACC_MGR_SUCCESS ); + vote_rec->meta->slot = slot_ctx->slot_bank.slot; + + FD_TEST( fd_borrowed_account_checked_add_lamports( vote_rec, vote_reward_node->elem.vote_rewards ) == 0 ); + result->distributed_rewards = fd_ulong_sat_add( result->distributed_rewards, vote_reward_node->elem.vote_rewards ); } - /* TODO: update_reward_history (not sure if reward history is ever needed?) */ - // update_reward_history(); - - // This is for vote rewards only. - fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - ulong new_vote_balance_and_staked = vote_balance_and_staked( slot_ctx, &epoch_bank->stakes ); - ulong validator_rewards_paid = fd_ulong_sat_sub(new_vote_balance_and_staked, rewards_calc_result->old_vote_balance_and_staked); - - // verify that we didn't pay any more than we expected to - FD_TEST( rewards_calc_result->validator_rewards >= fd_ulong_sat_add(validator_rewards_paid, rewards_calc_result->total_stake_rewards_lamports)); - - FD_LOG_NOTICE(( - "distributed vote rewards: %lu out of %lu, remaining %lu", - validator_rewards_paid, - rewards_calc_result->validator_rewards, - rewards_calc_result->total_stake_rewards_lamports - )); - - slot_ctx->slot_bank.capitalization += validator_rewards_paid; - - /* - // only useful for logging - ulong active_stake = 0; - for ( fd_stake_history_epochentry_pair_t_mapnode_t * n = fd_stake_history_epochentry_pair_t_map_minimum( bank->stakes.stake_history.entries_pool, bank->stakes.stake_history.entries_root ); n; n = fd_stake_history_epochentry_pair_t_map_successor( bank->stakes.stake_history.entries_pool, n ) ) { - if (bank->stakes.stake_history.entries_pool->elem.epoch == prev_epoch) { - active_stake = bank->stakes.stake_history.entries_pool->elem.entry.effective; - break; - } + + /* Free the vote reward map */ + fd_valloc_free( slot_ctx->valloc, + fd_vote_reward_t_map_delete( + fd_vote_reward_t_map_leave( rewards_calc_result->vote_reward_map_pool ) ) ); + + /* Verify that we didn't pay any more than we expected to */ + result->total_rewards = fd_ulong_sat_add( result->distributed_rewards, rewards_calc_result->stake_rewards_by_partition.total_stake_rewards_lamports ); + FD_TEST( rewards_calc_result->validator_rewards >= result->total_rewards ); + + slot_ctx->slot_bank.capitalization += result->distributed_rewards; + + /* Cheap because this doesn't copy all the rewards, just pointers to the dlist */ + fd_memcpy( &result->stake_rewards_by_partition, &rewards_calc_result->stake_rewards_by_partition, FD_STAKE_REWARD_CALCULATION_PARTITIONED_FOOTPRINT ); + result->total_points = rewards_calc_result->total_points; +} + +/* Distributes a single partitioned reward to a single stake account */ +static int +distribute_epoch_reward_to_stake_acc( + fd_exec_slot_ctx_t * slot_ctx, + fd_pubkey_t * stake_pubkey, + ulong reward_lamports, + ulong new_credits_observed + ) { + + FD_BORROWED_ACCOUNT_DECL( stake_acc_rec ); + FD_TEST( fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_pubkey, 0, 0UL, stake_acc_rec ) == FD_ACC_MGR_SUCCESS ); + stake_acc_rec->meta->slot = slot_ctx->slot_bank.slot; + + fd_stake_state_v2_t stake_state[1] = {0}; + if ( fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, stake_state) != 0 ) { + FD_LOG_DEBUG(( "failed to read stake state for %32J", stake_pubkey )); + return 1; + } + + if ( !fd_stake_state_v2_is_stake( stake_state ) ) { + FD_LOG_DEBUG(( "non-stake stake account, this should never happen" )); + return 1; + } + + if( fd_borrowed_account_checked_add_lamports( stake_acc_rec, reward_lamports ) ) { + FD_LOG_DEBUG(( "failed to add lamports to stake account" )); + return 1; + } + + stake_state->inner.stake.stake.credits_observed = new_credits_observed; + stake_state->inner.stake.stake.delegation.stake = fd_ulong_sat_add( + stake_state->inner.stake.stake.delegation.stake, + reward_lamports + ); + + if ( FD_UNLIKELY( write_stake_state( stake_acc_rec, stake_state ) != 0 ) ) { + FD_LOG_ERR(( "write_stake_state failed" )); } - */ - /* free vote reward map */ - fd_vote_reward_t_map_delete( rewards_calc_result->vote_account_rewards ); - result->total_rewards = fd_ulong_sat_add(validator_rewards_paid, rewards_calc_result->total_stake_rewards_lamports); - result->distributed_rewards = validator_rewards_paid; - *result->stake_rewards_by_partition = *rewards_calc_result->stake_rewards_by_partition; + return 0; } -static void -bank_redeem_rewards( - fd_exec_slot_ctx_t * slot_ctx, - ulong rewarded_epoch, - fd_point_value_t * point_value, - fd_stake_history_t const * stake_history, - fd_validator_reward_calculation_t * result +/* Sets the epoch reward status to inactive, and destroys any allocated state associated with the active state. */ +void +set_epoch_reward_status_inactive( + fd_exec_slot_ctx_t * slot_ctx ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L3194-L3288 */ - /* the current implement relies on partitioned version with no thread pool*/ - calculate_stake_vote_rewards( slot_ctx, stake_history, rewarded_epoch, point_value, result ); + if ( slot_ctx->epoch_reward_status.discriminant == fd_epoch_reward_status_enum_Active ) { + fd_partitioned_stake_rewards_t * partitioned_rewards = &slot_ctx->epoch_reward_status.inner.Active.partitioned_stake_rewards; + /* Destroy the partitions */ + fd_valloc_free( slot_ctx->valloc, + fd_stake_reward_dlist_delete( + fd_stake_reward_dlist_leave( partitioned_rewards->partitions ) ) ); + + /* Destroy the underlying pool */ + fd_valloc_free( + slot_ctx->valloc, + fd_stake_reward_pool_delete( + fd_stake_reward_pool_leave( partitioned_rewards->pool ) ) ); + } + slot_ctx->epoch_reward_status.discriminant = fd_epoch_reward_status_enum_Inactive; } -static void -calculate_reward_points( - fd_exec_slot_ctx_t * slot_ctx, - fd_stake_history_t const * stake_history, - ulong rewards, - fd_point_value_t * result -) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L3020-L3058 */ - /* the current implement relies on partitioned version with no thread pool*/ - calculate_reward_points_partitioned( slot_ctx, stake_history, rewards, result ); +/* Sets the epoch reward status to active. + + Takes ownership of the given stake_rewards_by_partition data structure, + which will be destroyed when set_epoch_reward_status_inactive is called. */ +void +set_epoch_reward_status_active( + fd_exec_slot_ctx_t * slot_ctx, + ulong distribution_starting_block_height, + fd_partitioned_stake_rewards_t * partitioned_rewards ) { + + slot_ctx->epoch_reward_status.discriminant = fd_epoch_reward_status_enum_Active; + slot_ctx->epoch_reward_status.inner.Active.distribution_starting_block_height = distribution_starting_block_height; + + fd_memcpy( &slot_ctx->epoch_reward_status.inner.Active.partitioned_stake_rewards, partitioned_rewards, FD_PARTITIONED_STAKE_REWARDS_FOOTPRINT ); } -// pay_validator_rewards_with_thread_pool -/* Load, calculate and payout epoch rewards for stake and vote accounts */ +/* Process reward credits for a partition of rewards. + Store the rewards to AccountsDB, update reward history record and total capitalization + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/distribution.rs#L88 */ static void -pay_validator_rewards( - fd_exec_slot_ctx_t * slot_ctx, - ulong rewarded_epoch, - ulong rewards +distribute_epoch_rewards_in_partition( + fd_stake_reward_dlist_t * partition, + fd_stake_reward_t *pool, + fd_exec_slot_ctx_t * slot_ctx ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2789-L2839 */ - fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache ); - if( FD_UNLIKELY( !stake_history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" )); - fd_point_value_t point_value_result[1] = {{0}}; - calculate_reward_points(slot_ctx, stake_history, rewards, point_value_result); - fd_validator_reward_calculation_t rewards_calc_result[1] = {0}; - bank_redeem_rewards( slot_ctx, rewarded_epoch, point_value_result, stake_history, rewards_calc_result ); - - ulong validator_rewards_paid = 0; - - /* store vote accounts */ - fd_vote_reward_t_mapnode_t * ref = rewards_calc_result->vote_reward_map; - FD_LOG_DEBUG(("Num vote rewards %lu", fd_vote_reward_t_map_key_cnt( rewards_calc_result->vote_reward_map))); - for (ulong i = 0; i < fd_vote_reward_t_map_slot_cnt( rewards_calc_result->vote_reward_map); ++i) { - if (fd_vote_reward_t_map_key_equal( ref[i].vote_pubkey, fd_vote_reward_t_map_key_null() ) ) { - continue; - } - fd_pubkey_t const * vote_pubkey = &ref[i].vote_pubkey; - if (ref[i].vote_rewards == 0 && !ref[i].needs_store) { - continue; - } - FD_LOG_DEBUG(("Vote reward for %32J %lu", vote_pubkey->uc, ref[i].vote_rewards)); - ulong min_data_sz = 0UL; - FD_BORROWED_ACCOUNT_DECL(vote_rec); - int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, vote_pubkey, 1, min_data_sz, vote_rec); - FD_TEST( err == 0 ); - vote_rec->meta->info.lamports = fd_ulong_sat_add(vote_rec->meta->info.lamports, ref[i].vote_rewards); - vote_rec->meta->slot = slot_ctx->slot_bank.slot; - validator_rewards_paid = fd_ulong_sat_add(validator_rewards_paid, ref[i].vote_rewards); - } - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - FD_LOG_DEBUG(("Num stake rewards %lu", deq_fd_stake_reward_t_cnt( rewards_calc_result->stake_reward_deq ))); - /* store stake accounts */ - for ( - deq_fd_stake_reward_t_iter_t iter = deq_fd_stake_reward_t_iter_init(rewards_calc_result->stake_reward_deq ); - !deq_fd_stake_reward_t_iter_done( rewards_calc_result->stake_reward_deq, iter ); - iter = deq_fd_stake_reward_t_iter_next( rewards_calc_result->stake_reward_deq, iter) + ulong lamports_distributed = 0UL; + ulong lamports_burned = 0UL; + + for ( fd_stake_reward_dlist_iter_t iter = fd_stake_reward_dlist_iter_fwd_init( partition, pool ); + !fd_stake_reward_dlist_iter_done( iter, partition, pool ); + iter = fd_stake_reward_dlist_iter_fwd_next( iter, partition, pool ) ) { - fd_stake_reward_t * ele = deq_fd_stake_reward_t_iter_ele( rewards_calc_result->stake_reward_deq, iter ); - fd_pubkey_t const * stake_pubkey = &ele->stake_pubkey; - - ulong min_data_sz = 0UL; - FD_BORROWED_ACCOUNT_DECL(stake_rec); - int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_pubkey, 1, min_data_sz, stake_rec); - FD_TEST( err == 0 ); - FD_LOG_DEBUG(("Stake reward for %32J Existing %lu reward lamps %lu staker reward %lu credits observed %lu", stake_pubkey->uc, stake_rec->meta->info.lamports, ele->reward_info.lamports, ele->reward_info.staker_rewards, ele->reward_info.new_credits_observed)); - stake_rec->meta->info.lamports = fd_ulong_sat_add(stake_rec->meta->info.lamports, ele->reward_info.lamports); - stake_rec->meta->slot = slot_ctx->slot_bank.slot; - validator_rewards_paid = fd_ulong_sat_add(validator_rewards_paid, ele->reward_info.lamports); - - fd_stake_state_v2_t stake_state; - int rc = fd_stake_get_state(stake_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - FD_LOG_ERR(("failed to read stake state for %32J", stake_pubkey )); + fd_stake_reward_t * stake_reward = fd_stake_reward_dlist_iter_ele( iter, partition, pool ); + + if ( distribute_epoch_reward_to_stake_acc( + slot_ctx, + &stake_reward->stake_pubkey, + stake_reward->lamports, + stake_reward->credits_observed ) == 0 ) { + lamports_distributed += stake_reward->lamports; + } else { + lamports_burned += stake_reward->lamports; } - /* implements the `redeem_stake_rewards` solana function */ - stake_state.inner.stake.stake.credits_observed = ele->reward_info.new_credits_observed; - stake_state.inner.stake.stake.delegation.stake += ele->reward_info.staker_rewards; - fd_delegation_pair_t_mapnode_t query_node; - fd_memcpy(&query_node.elem.account, stake_pubkey, sizeof(fd_pubkey_t)); - fd_delegation_pair_t_mapnode_t * node = fd_delegation_pair_t_map_find(epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root, &query_node); - if (node != NULL) { - node->elem.delegation.stake += ele->reward_info.staker_rewards; - } + } - /* write_stake_state */ - err = write_stake_state( slot_ctx, stake_pubkey, &stake_state, 0); - FD_TEST( err == 0 ); + /* Update the epoch rewards sysvar with the amount distributed and burnt */ + if ( FD_LIKELY( FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ) ) { + fd_sysvar_epoch_rewards_distribute( slot_ctx, lamports_distributed + lamports_burned ); } - // FD_LOG_WARNING(("REWARDS PAID: %lu POST_CAP: %lu", validator_rewards_paid, slot_ctx->slot_bank.capitalization )); - slot_ctx->slot_bank.capitalization = fd_ulong_sat_add(slot_ctx->slot_bank.capitalization, validator_rewards_paid); + FD_LOG_DEBUG(( "lamports burned: %lu, lamports distributed: %lu", lamports_burned, lamports_distributed )); + + slot_ctx->slot_bank.capitalization += lamports_distributed; +} + +/* Process reward distribution for the block if it is inside reward interval. + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/distribution.rs#L42 */ +void +fd_distribute_partitioned_epoch_rewards( + fd_exec_slot_ctx_t * slot_ctx +) { + if ( slot_ctx->epoch_reward_status.discriminant == fd_epoch_reward_status_enum_Inactive ) { + return; + } + fd_start_block_height_and_rewards_t * status = &slot_ctx->epoch_reward_status.inner.Active; - /* free stake_reward_deq and vote_reward_map */ - fd_valloc_free(slot_ctx->valloc, deq_fd_stake_reward_t_delete( deq_fd_stake_reward_t_leave( rewards_calc_result->stake_reward_deq ) ) ); - fd_valloc_free(slot_ctx->valloc, fd_vote_reward_t_map_delete( fd_vote_reward_t_map_leave( rewards_calc_result->vote_reward_map ) ) ); + fd_slot_bank_t * slot_bank = &slot_ctx->slot_bank; + ulong height = slot_bank->block_height; + fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank_const( slot_ctx->epoch_ctx ); + + ulong distribution_starting_block_height = status->distribution_starting_block_height; + ulong distribution_end_exclusive = distribution_starting_block_height + status->partitioned_stake_rewards.partitions_len; + + /* TODO: track current epoch in epoch ctx? */ + ulong epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_bank->slot, NULL ); + FD_TEST( get_slots_in_epoch( epoch, epoch_bank ) > status->partitioned_stake_rewards.partitions_len ); + + if ( ( height >= distribution_starting_block_height ) && ( height < distribution_end_exclusive ) ) { + ulong partition_index = height - distribution_starting_block_height; + distribute_epoch_rewards_in_partition( + &status->partitioned_stake_rewards.partitions[ partition_index ], + status->partitioned_stake_rewards.pool, + slot_ctx + ); + } - // self.update_reward_history(stake_rewards, vote_rewards); + /* If we have finished distributing rewards, set the status to inactive */ + if ( fd_ulong_sat_add( height, 1UL ) >= distribution_end_exclusive ) { + set_epoch_reward_status_inactive( slot_ctx ); + fd_sysvar_epoch_rewards_set_inactive( slot_ctx ); + } } -// update rewards based on the previous epoch -// non thread pool version below +/* Non-partitioned epoch rewards entry-point. This uses the same logic as the partitioned epoch rewards code, + but distributes the rewards in one go. */ void fd_update_rewards( fd_exec_slot_ctx_t * slot_ctx, - ulong prev_epoch + ulong parent_epoch ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L2515-L2599 */ - /* calculate_previous_epoch_inflation_rewards */ - fd_prev_epoch_inflation_rewards_t rewards; - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - fd_slot_bank_t * slot_bank = &slot_ctx->slot_bank; - calculate_previous_epoch_inflation_rewards( slot_ctx, epoch_bank, slot_bank->slot, slot_bank->capitalization, prev_epoch, &rewards); - /* pay_validator_rewards_with_thread_pool */ - pay_validator_rewards(slot_ctx, prev_epoch, rewards.validator_rewards); + + /* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L55 */ + fd_calculate_rewards_and_distribute_vote_rewards_result_t rewards_result[1] = {0}; + calculate_rewards_and_distribute_vote_rewards( + slot_ctx, + parent_epoch, + &slot_ctx->slot_bank.banks_hash, /* We know this is not correct, but for the non-partitioned case this doesn't matter */ + rewards_result + ); + + /* Distribute all of the partitioned epoch rewards in one go */ + for ( ulong i = 0UL; i < rewards_result->stake_rewards_by_partition.partitioned_stake_rewards.partitions_len; i++ ) { + distribute_epoch_rewards_in_partition( + &rewards_result->stake_rewards_by_partition.partitioned_stake_rewards.partitions[ i ], + rewards_result->stake_rewards_by_partition.partitioned_stake_rewards.pool, + slot_ctx + ); + } } -/* fd_begin_partitioned_rewards: Begin the process of calculating and - distributing rewards. This process can take multiple slots. */ +/* Partitioned epoch rewards entry-point. -// https://github.com/anza-xyz/agave/blob/2d722719a2c74ec4e180b255124c7204ef98ee6c/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L35 + https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L41 +*/ void fd_begin_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, - ulong parent_epoch + const fd_hash_t * parent_blockhash, + ulong parent_epoch ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1613-L1651 */ + /* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L55 */ fd_calculate_rewards_and_distribute_vote_rewards_result_t rewards_result[1] = {0}; calculate_rewards_and_distribute_vote_rewards( slot_ctx, parent_epoch, + parent_blockhash, rewards_result ); - ulong credit_end_exclusive = slot_ctx->slot_bank.block_height + REWARD_CALCULATION_NUM_BLOCK + rewards_result->stake_rewards_by_partition->cnt; - FD_LOG_DEBUG(("self->block_height=%lu, rewards_result->stake_rewards_by_parrition->cnt=%lu", slot_ctx->slot_bank.block_height, rewards_result->stake_rewards_by_partition->cnt)); - - // self.set_epoch_reward_status_active(stake_rewards_by_partition); - slot_ctx->epoch_reward_status = (fd_epoch_reward_status_t){ - .is_active = 1, - .stake_rewards_by_partition = { *rewards_result->stake_rewards_by_partition }, - .start_block_height = slot_ctx->slot_bank.block_height - }; - // create EpochRewards sysvar that holds the balance of undistributed rewards with - // (total_rewards, distributed_rewards, credit_end_exclusive), total capital will increase by (total_rewards - distributed_rewards) - fd_sysvar_epoch_rewards_init( slot_ctx, rewards_result->total_rewards, rewards_result->distributed_rewards, credit_end_exclusive); + + /* https://github.com/anza-xyz/agave/blob/9a7bf72940f4b3cd7fc94f54e005868ce707d53d/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L62 */ + ulong distribution_starting_block_height = slot_ctx->slot_bank.block_height + REWARD_CALCULATION_NUM_BLOCKS; + + /* Set the epoch reward status to be active */ + set_epoch_reward_status_active( slot_ctx, distribution_starting_block_height, &rewards_result->stake_rewards_by_partition.partitioned_stake_rewards ); + + /* Initialise the epoch rewards sysvar + + https://github.com/anza-xyz/agave/blob/9a7bf72940f4b3cd7fc94f54e005868ce707d53d/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L78 */ + fd_sysvar_epoch_rewards_init( + slot_ctx, + rewards_result->total_rewards, + rewards_result->distributed_rewards, + distribution_starting_block_height, + rewards_result->stake_rewards_by_partition.partitioned_stake_rewards.partitions_len, + rewards_result->total_points, + parent_blockhash + ); } -/* Process reward distribution for the block if it is inside reward interval. */ +/* + Re-calculates partitioned stake rewards. + This updates the slot context's epoch reward status with the recalculated partitioned rewards. + + https://github.com/anza-xyz/agave/blob/2316fea4c0852e59c071f72d72db020017ffd7d0/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L536 */ void -fd_distribute_partitioned_epoch_rewards( - fd_exec_slot_ctx_t * slot_ctx +fd_rewards_recalculate_partitioned_rewards( + fd_exec_slot_ctx_t * slot_ctx, + const fd_hash_t * parent_blockhash ) { - /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1654-L1687 */ - /* make sure we are inside the reward interval */ - if ( !slot_ctx->epoch_reward_status.is_active ) { - return; + fd_sysvar_epoch_rewards_t epoch_rewards[1]; + if ( FD_UNLIKELY( fd_sysvar_epoch_rewards_read( epoch_rewards, slot_ctx ) == NULL ) ) { + FD_LOG_NOTICE(( "failed to read sysvar epoch rewards - the sysvar may not have been created yet" )); + set_epoch_reward_status_inactive( slot_ctx ); + return; } - ulong validator_rewards_paid = 0; - - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - fd_slot_bank_t * slot_bank = &slot_ctx->slot_bank; - - ulong credit_start = slot_ctx->epoch_reward_status.start_block_height + REWARD_CALCULATION_NUM_BLOCK; - ulong credit_end_exclusive = credit_start + slot_ctx->epoch_reward_status.stake_rewards_by_partition->cnt; - if (slot_bank->block_height >= credit_start && slot_bank->block_height < credit_end_exclusive) { - ulong partition_index = slot_bank->block_height - credit_start; - ulong total_rewards_in_lamports = 0UL; - fd_stake_rewards_t this_partition_stake_rewards = slot_ctx->epoch_reward_status.stake_rewards_by_partition->elems[partition_index]; - for (uint i = 0; i < this_partition_stake_rewards.cnt; ++i) { - total_rewards_in_lamports = fd_ulong_sat_add(total_rewards_in_lamports, this_partition_stake_rewards.elems[i]->reward_info.lamports); - // store rewards into accounts - fd_pubkey_t const * stake_acc = &this_partition_stake_rewards.elems[i]->stake_pubkey; - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); - FD_TEST( 0==fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, 0, 0UL, stake_acc_rec ) ); - stake_acc_rec->meta->info.lamports += this_partition_stake_rewards.elems[i]->reward_info.lamports; - validator_rewards_paid = fd_ulong_sat_add(validator_rewards_paid, this_partition_stake_rewards.elems[i]->reward_info.lamports); - - fd_stake_state_v2_t stake_state = {0}; - int rc = fd_stake_get_state(stake_acc_rec, &slot_ctx->valloc, &stake_state); - if ( rc != 0 ) { - FD_LOG_ERR(("failed to read stake state for %32J", &this_partition_stake_rewards.elems[i]->stake_pubkey )); - } - // fd_stake_state_t stake_state; - // read_stake_state( global, stake_acc_rec->meta, &stake_state ); - // if (!fd_stake_state_is_stake( &stake_state)) { - // FD_LOG_ERR(("failed to read stake state for %32J", &this_partition_stake_rewards.elems[i]->stake_pubkey )); - // } - - /* implements the `redeem_stake_rewards` solana function */ - stake_state.inner.stake.stake.credits_observed = this_partition_stake_rewards.elems[i]->reward_info.new_credits_observed; - stake_state.inner.stake.stake.delegation.stake += this_partition_stake_rewards.elems[i]->reward_info.staker_rewards; - fd_delegation_pair_t_mapnode_t query_node; - fd_memcpy(&query_node.elem.account, stake_acc, sizeof(fd_pubkey_t)); - fd_delegation_pair_t_mapnode_t * node = fd_delegation_pair_t_map_find(epoch_bank->stakes.stake_delegations_pool, epoch_bank->stakes.stake_delegations_root, &query_node); - if (node != NULL) { - node->elem.delegation.stake += this_partition_stake_rewards.elems[i]->reward_info.staker_rewards; - } - - /* write_stake_state */ - int err = write_stake_state( slot_ctx, stake_acc, &stake_state, 0); - FD_TEST( err == 0 ); - + if ( FD_UNLIKELY( epoch_rewards->active ) ) { + /* If partitioned rewards are active, the rewarded epoch is always the immediately + preceeding epoch. + + https://github.com/anza-xyz/agave/blob/2316fea4c0852e59c071f72d72db020017ffd7d0/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L566 */ + fd_epoch_schedule_t * epoch_schedule = &fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx )->epoch_schedule; + ulong epoch = fd_slot_to_epoch( epoch_schedule, slot_ctx->slot_bank.slot, NULL ); + ulong rewarded_epoch = fd_ulong_sat_sub( epoch, 1UL ); + + fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache ); + if( FD_UNLIKELY( !stake_history ) ) { + FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" )); } - // increase total capitalization by the distributed rewards - slot_bank->capitalization = fd_ulong_sat_add(slot_bank->capitalization, total_rewards_in_lamports); - - // decrease distributed capital from epoch rewards sysvar - fd_sysvar_epoch_rewards_update( slot_ctx, total_rewards_in_lamports ); - - // update reward history for this partitioned distribution - // self.update_reward_history_in_partition(this_partition_stake_rewards); - } + fd_point_value_t point_value = { + .points = epoch_rewards->total_points, + .rewards = epoch_rewards->total_rewards + }; + + /* 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]; + calculate_stake_vote_rewards( + slot_ctx, + stake_history, + rewarded_epoch, + &point_value, + calculate_stake_vote_rewards_result + ); - if ( fd_ulong_sat_add(slot_bank->block_height, 1) >= credit_end_exclusive ) { - // deactivate epoch reward status - slot_ctx->epoch_reward_status.is_active = 0; - // burn and purge EpochRewards sysvar account - fd_sysvar_epoch_rewards_burn_and_purge( slot_ctx ); - // fixing leaks - for ( ulong i = 0; i < slot_ctx->epoch_reward_status.stake_rewards_by_partition->cnt; ++i ) { - fd_stake_rewards_destroy( &slot_ctx->epoch_reward_status.stake_rewards_by_partition->elems[i] ); - } - fd_stake_rewards_vector_destroy( slot_ctx->epoch_reward_status.stake_rewards_by_partition ); + /* Free the vote reward map, as this isn't actually used in this code path. */ + fd_valloc_free( slot_ctx->valloc, + fd_vote_reward_t_map_delete( + fd_vote_reward_t_map_leave( calculate_stake_vote_rewards_result->vote_reward_map_pool ) ) ); + + fd_stake_reward_calculation_partitioned_t stake_rewards_by_partition[1]; + hash_rewards_into_partitions( + slot_ctx, + &calculate_stake_vote_rewards_result->stake_reward_calculation, + parent_blockhash, + stake_rewards_by_partition ); + + /* Update the epoch reward status with the newly re-calculated partitions. */ + set_epoch_reward_status_active( + slot_ctx, + epoch_rewards->distribution_starting_block_height, + &stake_rewards_by_partition->partitioned_stake_rewards ); + } else { + set_epoch_reward_status_inactive( slot_ctx ); } - slot_ctx->slot_bank.capitalization = fd_ulong_sat_add(slot_ctx->slot_bank.capitalization, validator_rewards_paid); -} - -void -fd_calculate_inflation_rates( fd_exec_slot_ctx_t * slot_ctx, - fd_inflation_rates_t * rates ) { - fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); - ulong slot_idx = 0; - rates->epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot_idx ); - ulong num_slots = get_inflation_num_slots( slot_ctx, &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot ); - double slot_in_year = (double)num_slots / epoch_bank->slots_per_year; - rates->validator = validator( &epoch_bank->inflation, slot_in_year ); - rates->foundation = foundation(&epoch_bank->inflation, slot_in_year); - rates->total = total(&epoch_bank->inflation, slot_in_year); } diff --git a/src/flamenco/rewards/fd_rewards.h b/src/flamenco/rewards/fd_rewards.h index 1a83827c10..87347efb58 100644 --- a/src/flamenco/rewards/fd_rewards.h +++ b/src/flamenco/rewards/fd_rewards.h @@ -14,26 +14,22 @@ FD_PROTOTYPES_BEGIN void fd_update_rewards( fd_exec_slot_ctx_t * slot_ctx, - ulong prev_epoch ); + ulong parent_epoch ); void -fd_begin_partitioned_rewards( fd_exec_slot_ctx_t * slot_ctx, - ulong parent_epoch ); +fd_begin_partitioned_rewards( + fd_exec_slot_ctx_t * slot_ctx, + const fd_hash_t * parent_blockhash, + ulong parent_epoch ); void -fd_distribute_partitioned_epoch_rewards( fd_exec_slot_ctx_t * slot_ctx ); - -struct fd_inflation_rates { - ulong epoch; - double foundation; - double total; - double validator; -}; -typedef struct fd_inflation_rates fd_inflation_rates_t; +fd_rewards_recalculate_partitioned_rewards( + fd_exec_slot_ctx_t * slot_ctx, + const fd_hash_t * parent_blockhash +); void -fd_calculate_inflation_rates( fd_exec_slot_ctx_t * slot_ctx, - fd_inflation_rates_t * rates ); +fd_distribute_partitioned_epoch_rewards( fd_exec_slot_ctx_t * slot_ctx ); FD_PROTOTYPES_END diff --git a/src/flamenco/rewards/fd_rewards_types.h b/src/flamenco/rewards/fd_rewards_types.h deleted file mode 100644 index 882c8ca87d..0000000000 --- a/src/flamenco/rewards/fd_rewards_types.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef HEADER_fd_src_flamenco_runtime_program_fd_rewards_types_h -#define HEADER_fd_src_flamenco_runtime_program_fd_rewards_types_h - -#include "../fd_flamenco_base.h" -#include "../types/fd_types.h" - -#define VECT_NAME fd_stake_rewards -#define VECT_ELEMENT fd_stake_reward_t* -#include "../runtime/fd_vector.h" -#undef VECT_NAME -#undef VECT_ELEMENT - -#define VECT_NAME fd_stake_rewards_vector -#define VECT_ELEMENT fd_stake_rewards_t -#include "../runtime/fd_vector.h" -#undef VECT_NAME -#undef VECT_ELEMENT - -/* reward calculation happens synchronously during the first block of the epoch boundary. - So, # blocks for reward calculation is 1. */ -#define REWARD_CALCULATION_NUM_BLOCK 1 -/* stake accounts to store in one block during partitioned reward interval Target to store 64 rewards per entry/tick in a block. A block has a minimum of 64 entries/tick. This gives 4096 total rewards to store in one block. This constant affects consensus. */ -#define STAKE_ACCOUNT_STORES_PER_BLOCK 4096 -#define TEST_ENABLE_PARTITIONED_REWARDS 0 -#define TEST_COMPARE_PARTITIONED_EPOCH_REWARDS 0 -#define MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH 10 - -struct fd_vote_reward_t_mapnode { - fd_pubkey_t vote_pubkey; - ulong vote_rewards; - uchar commission; - uchar needs_store; -}; - -typedef struct fd_vote_reward_t_mapnode fd_vote_reward_t_mapnode_t; - -#define MAP_NAME fd_vote_reward_t_map -#define MAP_T fd_vote_reward_t_mapnode_t -#define MAP_MEMOIZE 0 -#define MAP_KEY vote_pubkey -#define MAP_KEY_T fd_pubkey_t -#define MAP_KEY_NULL (fd_pubkey_t){0} -#define MAP_KEY_INVAL(k) MAP_KEY_EQUAL((k),MAP_KEY_NULL) -#define MAP_KEY_EQUAL(k0,k1) (!memcmp((k0).key, (k1).key, sizeof( fd_pubkey_t ) )) -#define MAP_KEY_EQUAL_IS_SLOW 1 -#define MAP_KEY_HASH(key) ((uint)fd_ulong_hash( fd_ulong_load_8( (key).key ) )) -#define MAP_KEY_MOVE(kd,ks) memcpy( &(kd), &(ks),sizeof(fd_pubkey_t)) -#include "../../util/tmpl/fd_map_dynamic.c" -static inline fd_vote_reward_t_mapnode_t * -fd_vote_reward_t_map_alloc( fd_valloc_t valloc, int lg_slot_cnt ) { - void * mem = fd_valloc_malloc( valloc, fd_vote_reward_t_map_align(), fd_vote_reward_t_map_footprint( lg_slot_cnt )); - return fd_vote_reward_t_map_join(fd_vote_reward_t_map_new(mem, lg_slot_cnt)); -} - - -#define DEQUE_NAME deq_fd_stake_reward_t -#define DEQUE_T fd_stake_reward_t -#define DEQUE_MAX 2000000UL -#include "../../util/tmpl/fd_deque.c" -static inline fd_stake_reward_t * -deq_fd_stake_reward_t_alloc( fd_valloc_t valloc ) { - void * mem = fd_valloc_malloc( valloc, deq_fd_stake_reward_t_align(), deq_fd_stake_reward_t_footprint()); - return deq_fd_stake_reward_t_join( deq_fd_stake_reward_t_new( mem ) ); -} - -struct fd_validator_reward_calculation { - fd_acc_lamports_t total_stake_rewards_lamports; - fd_stake_reward_t * stake_reward_deq; - fd_vote_reward_t_mapnode_t * vote_reward_map; -}; -typedef struct fd_validator_reward_calculation fd_validator_reward_calculation_t; - -struct fd_partitioned_rewards_calculation { - /* VoteRewardsAccount */ - fd_vote_reward_t_mapnode_t * vote_account_rewards; - fd_stake_rewards_vector_t stake_rewards_by_partition[1]; - ulong total_stake_rewards_lamports; - ulong old_vote_balance_and_staked; - ulong validator_rewards; - double validator_rate; - double foundation_rate; - double prev_epoch_duration_in_years; - ulong capitalization; -}; -typedef struct fd_partitioned_rewards_calculation fd_partitioned_rewards_calculation_t; - -struct fd_point_value { - ulong rewards; - uint128 points; -}; -typedef struct fd_point_value fd_point_value_t; - -struct fd_calculated_stake_rewards{ - ulong staker_rewards; - ulong voter_rewards; - ulong new_credits_observed; -}; -typedef struct fd_calculated_stake_rewards fd_calculated_stake_rewards_t; - -struct fd_calculate_stake_points { - uint128 points; - ulong new_credits_observed; - uint force_credits_update_with_skipped_reward; -}; -typedef struct fd_calculate_stake_points fd_calculate_stake_points_t; - -struct fd_calculate_rewards_and_distribute_vote_rewards_result { - ulong total_rewards; - ulong distributed_rewards; - fd_stake_rewards_vector_t stake_rewards_by_partition[1]; -}; -typedef struct fd_calculate_rewards_and_distribute_vote_rewards_result fd_calculate_rewards_and_distribute_vote_rewards_result_t; - -struct fd_epoch_reward_status { - uint is_active; - ulong start_block_height; - fd_stake_rewards_vector_t stake_rewards_by_partition[1]; -}; -typedef struct fd_epoch_reward_status fd_epoch_reward_status_t; - -#endif /* HEADER_fd_src_flamenco_runtime_program_fd_rewards_types_h */ diff --git a/src/flamenco/runtime/context/fd_exec_slot_ctx.c b/src/flamenco/runtime/context/fd_exec_slot_ctx.c index 0eac608df0..95f89a9efd 100644 --- a/src/flamenco/runtime/context/fd_exec_slot_ctx.c +++ b/src/flamenco/runtime/context/fd_exec_slot_ctx.c @@ -28,6 +28,9 @@ fd_exec_slot_ctx_new( void * mem, self->sysvar_cache = fd_sysvar_cache_new( fd_valloc_malloc( valloc, fd_sysvar_cache_align(), fd_sysvar_cache_footprint() ), valloc ); self->account_compute_table = fd_account_compute_table_join( fd_account_compute_table_new( fd_valloc_malloc( valloc, fd_account_compute_table_align(), fd_account_compute_table_footprint( 10000 ) ), 10000, 0 ) ); + /* This is inactive by default */ + self->epoch_reward_status.discriminant = fd_epoch_reward_status_enum_Inactive; + FD_COMPILER_MFENCE(); self->magic = FD_EXEC_SLOT_CTX_MAGIC; FD_COMPILER_MFENCE(); @@ -393,7 +396,5 @@ fd_exec_slot_ctx_free( fd_exec_slot_ctx_t * slot_ctx ) { fd_slot_hashes_destroy( slot_ctx->sysvar_cache_old.slot_hashes, &ctx ); /* leader points to a caller-allocated leader schedule */ - - fd_stake_rewards_vector_destroy( slot_ctx->epoch_reward_status.stake_rewards_by_partition ); fd_exec_slot_ctx_delete( fd_exec_slot_ctx_leave( slot_ctx ) ); } diff --git a/src/flamenco/runtime/context/fd_exec_slot_ctx.h b/src/flamenco/runtime/context/fd_exec_slot_ctx.h index 2cd2726616..3ce0b5b4ef 100644 --- a/src/flamenco/runtime/context/fd_exec_slot_ctx.h +++ b/src/flamenco/runtime/context/fd_exec_slot_ctx.h @@ -6,7 +6,6 @@ #include "../../../util/rng/fd_rng.h" #include "../../../util/wksp/fd_wksp.h" -#include "../../rewards/fd_rewards_types.h" #include "../sysvar/fd_sysvar_cache.h" #include "../sysvar/fd_sysvar_cache_old.h" #include "../../types/fd_types.h" diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index 63d58b451a..5123f3b474 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -1657,6 +1657,13 @@ fd_runtime_block_update_current_leader( fd_exec_slot_ctx_t * slot_ctx ) { int fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx ) { + /* Update block height */ + slot_ctx->slot_bank.block_height += 1UL; + fd_blockstore_block_height_update( + slot_ctx->blockstore, + slot_ctx->slot_bank.slot, + slot_ctx->slot_bank.block_height ); + // TODO: this is not part of block execution, move it. if( slot_ctx->slot_bank.slot != 0 ) { ulong slot_idx; @@ -1683,7 +1690,9 @@ fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx ) { slot_ctx->signature_cnt = 0; if( slot_ctx->slot_bank.slot != 0 && FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ) { + fd_funk_start_write( slot_ctx->acc_mgr->funk ); fd_distribute_partitioned_epoch_rewards( slot_ctx ); + fd_funk_end_write( slot_ctx->acc_mgr->funk ); } int result = fd_runtime_block_update_current_leader( slot_ctx ); @@ -3407,11 +3416,16 @@ FD_SCRATCH_SCOPE_BEGIN { // Update the epoch bank vote_accounts with the latest values from the slot bank // FIXME: resize the vote_accounts_pool if necessary for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum( - slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, slot_ctx->slot_bank.vote_account_keys.vote_accounts_root ); + slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, + slot_ctx->slot_bank.vote_account_keys.vote_accounts_root ); n; n = fd_vote_accounts_pair_t_map_successor( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, n ) ) { - // If the vote account is not in the epoch cache, insert it - if( fd_vote_accounts_pair_t_map_find( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root, n ) == NULL ) { + + // If the vote account is not in the epoch stakes cache, insert it + fd_vote_accounts_pair_t_mapnode_t key; + fd_memcpy( &key.elem.key, &n->elem.key, FD_PUBKEY_FOOTPRINT ); + fd_vote_accounts_pair_t_mapnode_t * epoch_cache_node = fd_vote_accounts_pair_t_map_find( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root, &key ); + if( epoch_cache_node == NULL ) { fd_vote_accounts_pair_t_mapnode_t * new_entry = fd_vote_accounts_pair_t_map_acquire( stakes->vote_accounts.vote_accounts_pool ); fd_memcpy(&new_entry->elem.key, &n->elem.key, sizeof(fd_pubkey_t)); @@ -3419,6 +3433,8 @@ FD_SCRATCH_SCOPE_BEGIN { fd_memcpy(&new_entry->elem.value, &n->elem.value, sizeof(fd_solana_account_t)); fd_vote_accounts_pair_t_map_insert( stakes->vote_accounts.vote_accounts_pool, &stakes->vote_accounts.vote_accounts_root, new_entry ); + } else { + epoch_cache_node->elem.stake = n->elem.stake; } } @@ -3450,39 +3466,43 @@ FD_SCRATCH_SCOPE_BEGIN { } FD_SCRATCH_SCOPE_END; } +/* Save a copy of epoch_ctx->epoch_bank->stakes.vote_accounts into epoch_ctx->epoch->bank->next_epoch_stakes, + to be sent out to other components for the leader schedule calculation. + + Also copy it into the slot_ctx->slot_bank.epoch_stakes. + + TODO: This means the three fields hold the same value, which doesn't change for the duration of the epoch. + Can we remove two? */ void fd_update_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) { FD_SCRATCH_SCOPE_BEGIN { fd_epoch_bank_t * epoch_bank = &slot_ctx->epoch_ctx->epoch_bank; - fd_vote_accounts_t const * vaccs = &epoch_bank->next_epoch_stakes; + fd_vote_accounts_t const * vaccs = &epoch_bank->stakes.vote_accounts; ulong bufsz = fd_vote_accounts_size(vaccs); uchar *buf = fd_scratch_alloc(1UL, bufsz); fd_bincode_encode_ctx_t encode_ctx = { .data = buf, .dataend = (void *)((ulong)buf + bufsz)}; FD_TEST(fd_vote_accounts_encode(vaccs, &encode_ctx) == FD_BINCODE_SUCCESS); + + /* Copy epoch_ctx->epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes */ fd_bincode_decode_ctx_t decode_ctx = { .data = buf, .dataend = (void const *)((ulong)buf + bufsz), .valloc = slot_ctx->valloc, }; - fd_bincode_destroy_ctx_t slot_destroy = {.valloc = slot_ctx->valloc}; - fd_vote_accounts_destroy(&slot_ctx->slot_bank.epoch_stakes, &slot_destroy); - FD_TEST(fd_vote_accounts_decode(&slot_ctx->slot_bank.epoch_stakes, &decode_ctx) == FD_BINCODE_SUCCESS); - - fd_vote_accounts_pair_t_mapnode_t * pool = epoch_bank->stakes.vote_accounts.vote_accounts_pool; - fd_vote_accounts_pair_t_mapnode_t * root = epoch_bank->stakes.vote_accounts.vote_accounts_root; - - // Reset vote accounts in next epoch stakes - /* FIXME consider pool sweep instead of recursive descent */ - fd_vote_accounts_pair_t_map_release_tree( epoch_bank->next_epoch_stakes.vote_accounts_pool, epoch_bank->next_epoch_stakes.vote_accounts_root ); - epoch_bank->next_epoch_stakes.vote_accounts_root = NULL; + fd_bincode_destroy_ctx_t destroy = {.valloc = slot_ctx->valloc}; + fd_vote_accounts_destroy(&epoch_bank->next_epoch_stakes, &destroy); + FD_TEST(fd_vote_accounts_decode(&epoch_bank->next_epoch_stakes, &decode_ctx) == FD_BINCODE_SUCCESS); - for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(pool, root); n; n = fd_vote_accounts_pair_t_map_successor(pool, n) ) { - fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_acquire( epoch_bank->next_epoch_stakes.vote_accounts_pool ); - fd_memcpy( &elem->elem, &n->elem, sizeof(fd_vote_accounts_pair_t)); - fd_vote_accounts_pair_t_map_insert( epoch_bank->next_epoch_stakes.vote_accounts_pool, &epoch_bank->next_epoch_stakes.vote_accounts_root, elem ); - } + /* Copy epoch_ctx->epoch_bank->stakes.vote_accounts into slot_ctx->slot_bank.epoch_stakes */ + fd_bincode_decode_ctx_t second_decode_ctx = { + .data = buf, + .dataend = (void const *)((ulong)buf + bufsz), + .valloc = slot_ctx->valloc, + }; + fd_vote_accounts_destroy(&slot_ctx->slot_bank.epoch_stakes, &destroy); + FD_TEST(fd_vote_accounts_decode(&slot_ctx->slot_bank.epoch_stakes, &second_decode_ctx) == FD_BINCODE_SUCCESS); } FD_SCRATCH_SCOPE_END; } @@ -3513,41 +3533,35 @@ void fd_process_new_epoch( else if (FD_FEATURE_ACTIVE(slot_ctx, update_hashes_per_tick2)) epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK2; - // Add new entry to stakes.stake_history, set appropriate epoch and - // update vote accounts with warmed up stakes before saving a - // snapshot of stakes in epoch stakes - fd_stakes_activate_epoch(slot_ctx, epoch); - - // (We might not implement this part) - /* Save a snapshot of stakes for use in consensus and stake weighted networking - let leader_schedule_epoch = self.epoch_schedule.get_leader_schedule_epoch(slot); - - */ + fd_stake_history_t const * history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache ); + if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" )); + /* Updates `epoch_bank->stakes->epoch` with new epoch number, + and updates stake history sysvar accumulated values. */ + fd_stakes_activate_epoch( slot_ctx ); - /* - let (_, update_epoch_stakes_time) = measure!( - self.update_epoch_stakes(leader_schedule_epoch), - "update_epoch_stakes", - ); */ + /* Distribute rewards */ + fd_hash_t const * parent_blockhash = fd_blockstore_block_hash_query( + slot_ctx->blockstore, fd_ulong_sat_sub( slot_ctx->slot_bank.slot, 1UL ) ); if ( FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ) { - fd_begin_partitioned_rewards( slot_ctx, parent_epoch ); + fd_begin_partitioned_rewards( slot_ctx, parent_blockhash, parent_epoch ); } else { fd_update_rewards( slot_ctx, parent_epoch ); } - fd_update_stake_delegations( slot_ctx ); - - fd_stake_history_t const * history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache ); - if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" )); refresh_vote_accounts( slot_ctx, history ); + fd_update_stake_delegations( slot_ctx ); + fd_calculate_epoch_accounts_hash_values( slot_ctx ); FD_LOG_WARNING(("Leader schedule epoch %lu", fd_slot_to_leader_schedule_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot))); fd_update_epoch_stakes( slot_ctx ); fd_runtime_update_leaders(slot_ctx, slot_ctx->slot_bank.slot); FD_LOG_WARNING(("Updated leader %32J", slot_ctx->leader->uc)); + + /* Update the current epoch value */ + epoch_bank->stakes.epoch = epoch; } /* Loads the sysvar cache. Expects acc_mgr, funk_txn, valloc to be non-NULL and valid. */ diff --git a/src/flamenco/runtime/fd_runtime_init.c b/src/flamenco/runtime/fd_runtime_init.c index d52ef01ad9..96f7238b03 100644 --- a/src/flamenco/runtime/fd_runtime_init.c +++ b/src/flamenco/runtime/fd_runtime_init.c @@ -117,15 +117,6 @@ int fd_runtime_save_slot_bank(fd_exec_slot_ctx_t *slot_ctx) FD_TEST(ctx.data == ctx.dataend); // FD_LOG_DEBUG(("slot frozen, slot=%d bank_hash=%32J poh_hash=%32J", slot_ctx->slot_bank.slot, slot_ctx->slot_bank.banks_hash.hash, slot_ctx->slot_bank.poh.hash)); - slot_ctx->slot_bank.block_height += 1UL; - - // Update blockstore - if ( slot_ctx->blockstore != NULL ) { - fd_blockstore_block_height_update( - slot_ctx->blockstore, slot_ctx->slot_bank.slot, slot_ctx->slot_bank.block_height ); - } else { - FD_LOG_WARNING(( "NULL blockstore in slot_ctx" )); - } return FD_RUNTIME_EXECUTE_SUCCESS; } diff --git a/src/flamenco/runtime/program/fd_stake_program.c b/src/flamenco/runtime/program/fd_stake_program.c index c274e2c716..f100dc321a 100644 --- a/src/flamenco/runtime/program/fd_stake_program.c +++ b/src/flamenco/runtime/program/fd_stake_program.c @@ -2477,8 +2477,7 @@ fd_stake_program_execute( fd_exec_instr_ctx_t ctx ) { enable_partitioned_epoch_reward feature is activated. If it exists, check the `active` field */ fd_sysvar_epoch_rewards_t const * rewards = fd_sysvar_cache_epoch_rewards( ctx.slot_ctx->sysvar_cache ); - // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/programs/stake/src/stake_instruction.rs#L80 - int epoch_rewards_active = (NULL!=rewards) ? rewards->epoch_rewards.active : false; + int epoch_rewards_active = (NULL != rewards) ? rewards->active : false; if (epoch_rewards_active && instruction->discriminant!=fd_stake_instruction_enum_get_minimum_delegation) { ctx.txn_ctx->custom_err = FD_STAKE_ERR_EPOCH_REWARDS_ACTIVE; diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.c b/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.c index db8364a7d0..7cd14672c4 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.c +++ b/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.c @@ -4,31 +4,10 @@ #include "../fd_borrowed_account.h" #include "../fd_system_ids.h" #include "../context/fd_exec_slot_ctx.h" - -// THIS IS ALL WRONG... the partitioned epoch rewards code paths have not been finalized with agave -// so these changes are here to support getting the fuzz tests to pass and not actual ledger correctness. -// -// Also, since this feature has not been activated in mainnet or testnet, the account itself does not -// exist which is why they felt free to change the layout outside of a feature flag. -// -// Once the Agave code has stabilized, we will make a proper implementation pass - -void -fd_sysvar_epoch_rewards_burn_and_purge( - fd_exec_slot_ctx_t * slot_ctx -) { - FD_BORROWED_ACCOUNT_DECL(rewards); - - int rc = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, &fd_sysvar_epoch_rewards_id, 0, 0, rewards); - if ( FD_UNLIKELY( rc ) ) return; // not good... - - fd_memcpy(rewards->meta->info.owner, fd_solana_system_program_id.key, sizeof(fd_pubkey_t)); - rewards->meta->dlen = 0; - rewards->meta->info.lamports = 0; -} +#include "../context/fd_exec_epoch_ctx.h" static void -write_epoch_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_sysvar_epoch_rewards_t * epoch_rewards, fd_acc_lamports_t acc_lamports) { +write_epoch_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_sysvar_epoch_rewards_t * epoch_rewards ) { ulong sz = fd_sysvar_epoch_rewards_size( epoch_rewards ); uchar enc[sz]; fd_memset( enc, 0, sz ); @@ -39,20 +18,19 @@ write_epoch_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_sysvar_epoch_rewards_t * FD_LOG_ERR(("fd_sysvar_epoch_rewards_encode failed")); } - fd_sysvar_set( slot_ctx, fd_sysvar_owner_id.key, &fd_sysvar_epoch_rewards_id, enc, sz, slot_ctx->slot_bank.slot, acc_lamports ); + fd_sysvar_set( slot_ctx, fd_sysvar_owner_id.key, &fd_sysvar_epoch_rewards_id, enc, sz, slot_ctx->slot_bank.slot, 0UL ); } - fd_sysvar_epoch_rewards_t * fd_sysvar_epoch_rewards_read( fd_sysvar_epoch_rewards_t * result, - fd_exec_slot_ctx_t * slot_ctx, - fd_acc_lamports_t * acc_lamports + fd_exec_slot_ctx_t * slot_ctx ) { FD_BORROWED_ACCOUNT_DECL(acc); int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &fd_sysvar_epoch_rewards_id, acc ); - if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) + if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) { return NULL; + } fd_bincode_decode_ctx_t decode = { .data = acc->const_data, @@ -62,56 +40,70 @@ fd_sysvar_epoch_rewards_read( if( FD_UNLIKELY( fd_sysvar_epoch_rewards_decode( result, &decode )!=FD_BINCODE_SUCCESS ) ) return NULL; - if( acc_lamports ) - *acc_lamports = acc->const_meta->info.lamports; - return result; } -/* Update EpochRewards sysvar with distributed rewards */ void -fd_sysvar_epoch_rewards_update( +fd_sysvar_epoch_rewards_distribute( fd_exec_slot_ctx_t * slot_ctx, ulong distributed ) { - fd_sysvar_epoch_rewards_t result; - fd_acc_lamports_t acc_lamports = 0UL; - fd_sysvar_epoch_rewards_read( &result, slot_ctx, &acc_lamports ); - FD_TEST( acc_lamports != 0 ); + FD_TEST( FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ); + + fd_sysvar_epoch_rewards_t epoch_rewards[1]; + if ( FD_UNLIKELY( fd_sysvar_epoch_rewards_read( epoch_rewards, slot_ctx ) == NULL ) ) { + FD_LOG_ERR(( "failed to read sysvar epoch rewards" )); + } + FD_TEST( epoch_rewards->active ); - FD_TEST( result.epoch_rewards.distributed_rewards + distributed <= result.epoch_rewards.total_rewards ); - result.epoch_rewards.distributed_rewards += distributed; + FD_TEST( fd_ulong_sat_add( epoch_rewards->distributed_rewards, distributed ) <= epoch_rewards->total_rewards ); - acc_lamports -= distributed; + epoch_rewards->distributed_rewards += distributed; - write_epoch_rewards( slot_ctx, &result, acc_lamports); + write_epoch_rewards( slot_ctx, epoch_rewards ); } -/* Create EpochRewards syavar with calculated rewards */ +void +fd_sysvar_epoch_rewards_set_inactive( + fd_exec_slot_ctx_t * slot_ctx +) { + fd_sysvar_epoch_rewards_t epoch_rewards[1]; + if ( FD_UNLIKELY( fd_sysvar_epoch_rewards_read( epoch_rewards, slot_ctx ) == NULL ) ) { + FD_LOG_ERR(( "failed to read sysvar epoch rewards" )); + } + FD_TEST( epoch_rewards->distributed_rewards == epoch_rewards->total_rewards ); + + epoch_rewards->active = 0; + + write_epoch_rewards( slot_ctx, epoch_rewards ); +} + +/* Create EpochRewards syavar with calculated rewards + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L25 */ void fd_sysvar_epoch_rewards_init( fd_exec_slot_ctx_t * slot_ctx, ulong total_rewards, ulong distributed_rewards, - ulong distribution_complete_block_height + ulong distribution_starting_block_height, + ulong num_partitions, + uint128 total_points, + const fd_hash_t * last_blockhash ) { + FD_TEST( FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ); FD_TEST( total_rewards >= distributed_rewards ); fd_sysvar_epoch_rewards_t epoch_rewards = { - .epoch_rewards= { -// .distribution_starting_block_height = distribution_starting_block_height, - .distribution_starting_block_height = distribution_complete_block_height, -// .num_partitions = num_partitions, - .num_partitions = 0, -// .parent_blockhash = parent_blockhash, -// .total_points = total_points, - .total_points = 0, - .total_rewards = total_rewards, - .distributed_rewards = distributed_rewards, - .active = true - } + .distribution_starting_block_height = distribution_starting_block_height, + .num_partitions = num_partitions, + .total_points = total_points, + .total_rewards = total_rewards, + .distributed_rewards = distributed_rewards, + .active = 1 }; - // set the account lamports to the undistributed rewards - fd_acc_lamports_t undistributed_rewards = total_rewards - distributed_rewards; - write_epoch_rewards( slot_ctx, &epoch_rewards, undistributed_rewards); + + fd_memcpy( &epoch_rewards.parent_blockhash, last_blockhash, FD_HASH_FOOTPRINT ); + + write_epoch_rewards( slot_ctx, &epoch_rewards ); } diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.h b/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.h index ab049a389e..b75e1e455c 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.h +++ b/src/flamenco/runtime/sysvar/fd_sysvar_epoch_rewards.h @@ -6,31 +6,42 @@ FD_PROTOTYPES_BEGIN -void -fd_sysvar_epoch_rewards_burn_and_purge( - fd_exec_slot_ctx_t * slot_ctx -); - +/* Read the current value of the EpochRewards sysvar from Funk. */ fd_sysvar_epoch_rewards_t * fd_sysvar_epoch_rewards_read( fd_sysvar_epoch_rewards_t * result, - fd_exec_slot_ctx_t * slot_ctx, - fd_acc_lamports_t * acc_lamports + fd_exec_slot_ctx_t * slot_ctx ); -/* Update EpochRewards sysvar with distributed rewards */ +/* Update EpochRewards sysvar with distributed rewards + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/sdk/program/src/epoch_rewards.rs#L44 */ void -fd_sysvar_epoch_rewards_update( +fd_sysvar_epoch_rewards_distribute( fd_exec_slot_ctx_t * slot_ctx, ulong distributed ); -/* Initialize the epoch rewards sysvar account. */ -void fd_sysvar_epoch_rewards_init( +/* Set the EpochRewards sysvar to inactive + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L82 */ +void +fd_sysvar_epoch_rewards_set_inactive( + fd_exec_slot_ctx_t * slot_ctx +); + +/* Initialize the EpochRewards sysvar account + + https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L25 */ +void +fd_sysvar_epoch_rewards_init( fd_exec_slot_ctx_t * slot_ctx, ulong total_rewards, ulong distributed_rewards, - ulong distribution_complete_block_height + ulong distribution_starting_block_height, + ulong num_partitions, + uint128 total_points, + const fd_hash_t * last_blockhash ); FD_PROTOTYPES_END diff --git a/src/flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.c b/src/flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.c index ac4942b08f..0838b0a86e 100644 --- a/src/flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.c +++ b/src/flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.c @@ -98,10 +98,13 @@ fd_epoch_slot0( fd_epoch_schedule_t const * schedule, return fd_ulong_sat_mul( power-1UL, FD_EPOCH_LEN_MIN ); } - ulong n_epoch = epoch - schedule->first_normal_epoch; - ulong n_slot = n_epoch * schedule->slots_per_epoch; - - return schedule->first_normal_slot + n_slot; + return fd_ulong_sat_add( + fd_ulong_sat_mul( + fd_ulong_sat_sub( + epoch, + schedule->first_normal_epoch), + schedule->slots_per_epoch), + schedule->first_normal_slot); } /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/sdk/program/src/epoch_schedule.rs#L140 */ diff --git a/src/flamenco/runtime/tests/run_ledger_tests_all.txt b/src/flamenco/runtime/tests/run_ledger_tests_all.txt index 80b8fd0ae1..c4985ffcb7 100644 --- a/src/flamenco/runtime/tests/run_ledger_tests_all.txt +++ b/src/flamenco/runtime/tests/run_ledger_tests_all.txt @@ -51,4 +51,5 @@ src/flamenco/runtime/tests/run_ledger_test.sh -l v203-move-stake -s snapshot-446 src/flamenco/runtime/tests/run_ledger_test.sh -l v203-move-lamports -s snapshot-650-Q8pVZ4jfmzpAGwAytPs4PJXmb4iaBXwVhvVjc8AaAXh.tar.zst -p 30 -y 16 -m 5000000 -e 655 -o 7bTK6Jis8Xpfrs8ZoUfiMDPazTcdPcTWheZFJTA5Z6X4 src/flamenco/runtime/tests/run_ledger_test.sh -l testnet-283927487 -s snapshot-283927486-7gCbg5g4BnD9SkQUjpHvhepWsQTpo2WaZaA5bhcNBMhG.tar.zst -p 30 -y 16 -m 5000000 -e 283927497 -c 2000 src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-254462437-dm -s snapshot-254462437-9HqBi19BJJRZfHeBS3ZpkeP9B5SAxBxz6Kwug29yLHac.tar.zst -p 45 -y 32 -m 20000000 -e 254462836 -c 1190 -o EenyoWx9UMXYKpR8mW5Jmfmy2fRjzUtM7NduYMY8bx33 -src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-281375356 -s snapshot-281375353-Bg81vkkW8K3UtrQoEn3wLsEpYwoHRQSihW4t5PiF1bBD.tar.zst -p 30 -y 16 -m 5000000 -e 281375359 -c 1190 \ No newline at end of file +src/flamenco/runtime/tests/run_ledger_test.sh -l mainnet-281375356 -s snapshot-281375353-Bg81vkkW8K3UtrQoEn3wLsEpYwoHRQSihW4t5PiF1bBD.tar.zst -p 30 -y 16 -m 5000000 -e 281375359 -c 1190 +src/flamenco/runtime/tests/run_ledger_test.sh -l v203-partitioned-epoch-rewards -s snapshot-70-5sJRKMVrAay6AeeCmYdiRqFqo1tKYABJANF3HHcBb9C7.tar.zst -p 30 -y 16 -m 5000000 -e 350 -c 2003 -o 9bn2vTJUsUcnpiZWbu2woSKtTGW3ErZC9ERv88SDqQjK \ No newline at end of file diff --git a/src/flamenco/snapshot/fd_snapshot.c b/src/flamenco/snapshot/fd_snapshot.c index af1e84fe77..931450c197 100644 --- a/src/flamenco/snapshot/fd_snapshot.c +++ b/src/flamenco/snapshot/fd_snapshot.c @@ -7,6 +7,7 @@ #include "../runtime/fd_system_ids.h" #include "../runtime/context/fd_exec_epoch_ctx.h" #include "../runtime/context/fd_exec_slot_ctx.h" +#include "../rewards/fd_rewards.h" #include #include @@ -211,6 +212,11 @@ fd_snapshot_load( const char * snapshotfile, fd_hashes_load(slot_ctx); + fd_rewards_recalculate_partitioned_rewards( + slot_ctx, + &deq_fd_block_block_hash_entry_t_peek_head_const( + slot_ctx->slot_bank.recent_block_hashes.hashes )->blockhash ); + fd_funk_speed_load_mode( slot_ctx->acc_mgr->funk, 0 ); fd_funk_end_write( slot_ctx->acc_mgr->funk ); } diff --git a/src/flamenco/stakes/fd_stakes.c b/src/flamenco/stakes/fd_stakes.c index b571a95487..56e845ac59 100644 --- a/src/flamenco/stakes/fd_stakes.c +++ b/src/flamenco/stakes/fd_stakes.c @@ -279,8 +279,7 @@ refresh_vote_accounts( fd_exec_slot_ctx_t * 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 next_epoch ) { +fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx) { fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx ); fd_stakes_t * stakes = &epoch_bank->stakes; @@ -380,71 +379,22 @@ fd_stakes_activate_epoch( fd_exec_slot_ctx_t * slot_ctx, fd_sysvar_stake_history_update( slot_ctx, &new_elem); - /* Update the current epoch value */ - stakes->epoch = next_epoch; - fd_valloc_free( slot_ctx->valloc, fd_stake_weight_t_map_delete( fd_stake_weight_t_map_leave ( pool ) ) ); - - // Update the list of vote accounts in the epoch stake cache - // https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L314 - // refresh_vote_accounts( slot_ctx, &history ); - - // TODO: Update epoch stakes? - // refresh_vote_accounts( slot_ctx, &history ); - - // ulong sz = fd_vote_accounts_pair_t_map_size( slot_ctx->epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_pool, slot_ctx->epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_root ); - // fd_vote_accounts_pair_t_mapnode_t * new_vote_root = NULL; - // fd_vote_accounts_pair_t_mapnode_t * new_vote_pool = fd_vote_accounts_pair_t_map_alloc( slot_ctx->valloc, sz ); - // fd_bincode_destroy_ctx_t destroy = {.valloc = slot_ctx->valloc}; - - // for ( fd_vote_accounts_pair_t_mapnode_t const * n = fd_vote_accounts_pair_t_map_minimum_const( slot_ctx->epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_pool, slot_ctx->epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_root ); - // n; - // n = fd_vote_accounts_pair_t_map_successor_const( slot_ctx->epoch_ctx->epoch_bank.stakes.vote_accounts.vote_accounts_pool, n )) { - // fd_vote_accounts_pair_t_mapnode_t * entry = fd_vote_accounts_pair_t_map_acquire( new_vote_pool ); - // fd_memcpy( &entry->elem, &n->elem, sizeof(fd_vote_accounts_pair_t)); - // fd_vote_accounts_pair_t_map_insert( new_vote_pool, &new_vote_root, entry ); - // } - // fd_vote_accounts_destroy( &slot_ctx->slot_bank.epoch_stakes, &destroy ); - - // slot_ctx->slot_bank.epoch_stakes.vote_accounts_root = new_vote_root; - // slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool = new_vote_pool; - } int -write_stake_state( fd_exec_slot_ctx_t * global, - fd_pubkey_t const * stake_acc, - fd_stake_state_v2_t * stake_state, - ushort is_new_account ) { - // TODO - (void)stake_state; - - ulong encoded_stake_state_size = (is_new_account) ? STAKE_ACCOUNT_SIZE : fd_stake_state_v2_size(stake_state); +write_stake_state( fd_borrowed_account_t * stake_acc_rec, + fd_stake_state_v2_t * stake_state ) { - FD_BORROWED_ACCOUNT_DECL(stake_acc_rec); + ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state); - int err = fd_acc_mgr_modify( global->acc_mgr, global->funk_txn, stake_acc, !!is_new_account, encoded_stake_state_size, stake_acc_rec ); - if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) { - FD_LOG_WARNING(( "write_stake_state failed" )); - return err; - } - - if (is_new_account) - fd_memset( stake_acc_rec->data, 0, encoded_stake_state_size ); - - fd_bincode_encode_ctx_t ctx3; - ctx3.data = stake_acc_rec->data; - ctx3.dataend = stake_acc_rec->data + encoded_stake_state_size; - if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx3 )!=FD_BINCODE_SUCCESS ) ) - FD_LOG_ERR(("fd_stake_state_encode failed")); - - if( is_new_account ) { - stake_acc_rec->meta->dlen = STAKE_ACCOUNT_SIZE; - /* TODO Lamports? */ - stake_acc_rec->meta->info.executable = 0; - stake_acc_rec->meta->info.rent_epoch = 0UL; - memcpy( &stake_acc_rec->meta->info.owner, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ); + fd_bincode_encode_ctx_t ctx = { + .data = stake_acc_rec->data, + .dataend = stake_acc_rec->data + encoded_stake_state_size, + }; + if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) { + FD_LOG_ERR(( "fd_stake_state_encode failed" )); } return 0; diff --git a/src/flamenco/stakes/fd_stakes.h b/src/flamenco/stakes/fd_stakes.h index 0f12b18224..0113db2f4f 100644 --- a/src/flamenco/stakes/fd_stakes.h +++ b/src/flamenco/stakes/fd_stakes.h @@ -29,19 +29,15 @@ fd_stake_weights_by_node( fd_vote_accounts_t const * accs, void -fd_stakes_activate_epoch( fd_exec_slot_ctx_t * global, - ulong next_epoch ); +fd_stakes_activate_epoch( fd_exec_slot_ctx_t * global ); fd_stake_history_entry_t stake_and_activating( fd_delegation_t const * delegation, ulong target_epoch, fd_stake_history_t * stake_history, ulong * new_rate_activation_epoch ); fd_stake_history_entry_t stake_activating_and_deactivating( fd_delegation_t const * delegation, ulong target_epoch, fd_stake_history_t * stake_history, ulong * new_rate_activation_epoch ); -int write_stake_state( - fd_exec_slot_ctx_t* global, - fd_pubkey_t const * stake_acc, - fd_stake_state_v2_t* stake_state, - ushort is_new_account -); +int +write_stake_state( fd_borrowed_account_t * stake_acc_rec, + fd_stake_state_v2_t * stake_state ); void fd_stakes_remove_stake_delegation( fd_exec_slot_ctx_t * slot_ctx, fd_borrowed_account_t * stake_account, ulong * new_rate_activation_epoch ); diff --git a/src/flamenco/types/fd_type_names.c b/src/flamenco/types/fd_type_names.c index c624c57381..bd915e3d58 100644 --- a/src/flamenco/types/fd_type_names.c +++ b/src/flamenco/types/fd_type_names.c @@ -8,7 +8,6 @@ static char const * fd_type_names[FD_TYPE_NAME_COUNT] = { "fd_gossip_ip6_addr", "fd_feature", "fd_fee_calculator", - "fd_epoch_rewards", "fd_hash_age", "fd_hash_hash_age_pair", "fd_block_hash_vec", @@ -50,15 +49,11 @@ static char const * fd_type_names[FD_TYPE_NAME_COUNT] = { "fd_snapshot_acc_vec", "fd_snapshot_slot_acc_vecs", "fd_reward_type", - "fd_reward_info", - "fd_stake_reward", - "fd_serializable_stake_rewards", - "fd_start_block_height_and_rewards", - "fd_serializable_epoch_reward_status", "fd_solana_accounts_db_fields", "fd_epoch_stakes_current", "fd_versioned_epoch_stakes", "fd_versioned_epoch_stakes_pair", + "fd_reward_info", "fd_solana_manifest", "fd_rust_duration", "fd_poh_config", @@ -216,4 +211,9 @@ static char const * fd_type_names[FD_TYPE_NAME_COUNT] = { "fd_status_pair", "fd_slot_delta", "fd_bank_slot_deltas", + "fd_pubkey_rewardinfo_pair", + "fd_optional_account", + "fd_calculated_stake_points", + "fd_point_value", + "fd_calculated_stake_rewards", }; diff --git a/src/flamenco/types/fd_types.c b/src/flamenco/types/fd_types.c index 556a23d31f..dc4b73e59e 100644 --- a/src/flamenco/types/fd_types.c +++ b/src/flamenco/types/fd_types.c @@ -313,122 +313,6 @@ ulong fd_fee_calculator_size( fd_fee_calculator_t const * self ) { return size; } -int fd_epoch_rewards_decode( fd_epoch_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - void const * data = ctx->data; - int err = fd_epoch_rewards_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ctx->data = data; - if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_epoch_rewards_new( self ); - } - fd_epoch_rewards_decode_unsafe( self, ctx ); - return FD_BINCODE_SUCCESS; -} -int fd_epoch_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { - int err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_hash_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint128_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bincode_bool_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -void fd_epoch_rewards_decode_unsafe( fd_epoch_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_bincode_uint64_decode_unsafe( &self->distribution_starting_block_height, ctx ); - fd_bincode_uint64_decode_unsafe( &self->num_partitions, ctx ); - fd_hash_decode_unsafe( &self->parent_blockhash, ctx ); - fd_bincode_uint128_decode_unsafe( &self->total_points, ctx ); - fd_bincode_uint64_decode_unsafe( &self->total_rewards, ctx ); - fd_bincode_uint64_decode_unsafe( &self->distributed_rewards, ctx ); - fd_bincode_bool_decode_unsafe( &self->active, ctx ); -} -int fd_epoch_rewards_encode( fd_epoch_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - err = fd_bincode_uint64_encode( self->distribution_starting_block_height, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->num_partitions, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_hash_encode( &self->parent_blockhash, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint128_encode( self->total_points, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->total_rewards, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->distributed_rewards, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_bool_encode( (uchar)(self->active), ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -int fd_epoch_rewards_decode_offsets( fd_epoch_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ) { - uchar const * data = ctx->data; - int err; - self->distribution_starting_block_height_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->num_partitions_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->parent_blockhash_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_hash_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - self->total_points_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint128_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - self->total_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->distributed_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->active_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_bool_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -void fd_epoch_rewards_new(fd_epoch_rewards_t * self) { - fd_memset( self, 0, sizeof(fd_epoch_rewards_t) ); - fd_hash_new( &self->parent_blockhash ); -} -void fd_epoch_rewards_destroy( fd_epoch_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ) { - fd_hash_destroy( &self->parent_blockhash, ctx ); -} - -ulong fd_epoch_rewards_footprint( void ){ return FD_EPOCH_REWARDS_FOOTPRINT; } -ulong fd_epoch_rewards_align( void ){ return FD_EPOCH_REWARDS_ALIGN; } - -void fd_epoch_rewards_walk( void * w, fd_epoch_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_epoch_rewards", level++ ); - fun( w, &self->distribution_starting_block_height, "distribution_starting_block_height", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->num_partitions, "num_partitions", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fd_hash_walk( w, &self->parent_blockhash, fun, "parent_blockhash", level ); - fun( w, &self->total_points, "total_points", FD_FLAMENCO_TYPE_UINT128, "uint128", level ); - fun( w, &self->total_rewards, "total_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->distributed_rewards, "distributed_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->active, "active", FD_FLAMENCO_TYPE_BOOL, "bool", level ); - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_epoch_rewards", level-- ); -} -ulong fd_epoch_rewards_size( fd_epoch_rewards_t const * self ) { - ulong size = 0; - size += sizeof(ulong); - size += sizeof(ulong); - size += fd_hash_size( &self->parent_blockhash ); - size += sizeof(uint128); - size += sizeof(ulong); - size += sizeof(ulong); - size += sizeof(char); - return size; -} - int fd_hash_age_decode( fd_hash_age_t * self, fd_bincode_decode_ctx_t * ctx ) { void const * data = ctx->data; int err = fd_hash_age_decode_preflight( ctx ); @@ -6717,645 +6601,139 @@ int fd_reward_type_encode( fd_reward_type_t const * self, fd_bincode_encode_ctx_ return fd_reward_type_inner_encode( &self->inner, self->discriminant, ctx ); } -int fd_reward_info_decode( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ) { +int fd_solana_accounts_db_fields_decode( fd_solana_accounts_db_fields_t * self, fd_bincode_decode_ctx_t * ctx ) { void const * data = ctx->data; - int err = fd_reward_info_decode_preflight( ctx ); + int err = fd_solana_accounts_db_fields_decode_preflight( ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; ctx->data = data; if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_reward_info_new( self ); + fd_solana_accounts_db_fields_new( self ); } - fd_reward_info_decode_unsafe( self, ctx ); + fd_solana_accounts_db_fields_decode_unsafe( self, ctx ); return FD_BINCODE_SUCCESS; } -int fd_reward_info_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { +int fd_solana_accounts_db_fields_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { int err; - err = fd_reward_type_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); + ulong storages_len; + err = fd_bincode_uint64_decode( &storages_len, ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( storages_len ) { + for( ulong i=0; i < storages_len; i++ ) { + err = fd_snapshot_slot_acc_vecs_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } err = fd_bincode_uint64_decode_preflight( ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; err = fd_bincode_uint64_decode_preflight( ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); + err = fd_bank_hash_info_decode_preflight( ctx ); if( FD_UNLIKELY( err ) ) return err; + ulong historical_roots_len; + err = fd_bincode_uint64_decode( &historical_roots_len, ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( historical_roots_len ) { + for( ulong i=0; i < historical_roots_len; i++ ) { + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } + ulong historical_roots_with_hash_len; + err = fd_bincode_uint64_decode( &historical_roots_with_hash_len, ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( historical_roots_with_hash_len ) { + for( ulong i=0; i < historical_roots_with_hash_len; i++ ) { + err = fd_slot_map_pair_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } return FD_BINCODE_SUCCESS; } -void fd_reward_info_decode_unsafe( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_reward_type_decode_unsafe( &self->reward_type, ctx ); - fd_bincode_uint64_decode_unsafe( &self->lamports, ctx ); - fd_bincode_uint64_decode_unsafe( &self->staker_rewards, ctx ); - fd_bincode_uint64_decode_unsafe( &self->new_credits_observed, ctx ); - fd_bincode_uint64_decode_unsafe( &self->post_balance, ctx ); - fd_bincode_uint64_decode_unsafe( (ulong *) &self->commission, ctx ); +void fd_solana_accounts_db_fields_decode_unsafe( fd_solana_accounts_db_fields_t * self, fd_bincode_decode_ctx_t * ctx ) { + fd_bincode_uint64_decode_unsafe( &self->storages_len, ctx ); + if( self->storages_len ) { + self->storages = (fd_snapshot_slot_acc_vecs_t *)fd_valloc_malloc( ctx->valloc, FD_SNAPSHOT_SLOT_ACC_VECS_ALIGN, FD_SNAPSHOT_SLOT_ACC_VECS_FOOTPRINT*self->storages_len ); + for( ulong i=0; i < self->storages_len; i++ ) { + fd_snapshot_slot_acc_vecs_new( self->storages + i ); + fd_snapshot_slot_acc_vecs_decode_unsafe( self->storages + i, ctx ); + } + } else + self->storages = NULL; + fd_bincode_uint64_decode_unsafe( &self->version, ctx ); + fd_bincode_uint64_decode_unsafe( &self->slot, ctx ); + fd_bank_hash_info_decode_unsafe( &self->bank_hash_info, ctx ); + fd_bincode_uint64_decode_unsafe( &self->historical_roots_len, ctx ); + if( self->historical_roots_len ) { + self->historical_roots = fd_valloc_malloc( ctx->valloc, 8UL, sizeof(ulong)*self->historical_roots_len ); + for( ulong i=0; i < self->historical_roots_len; i++ ) { + fd_bincode_uint64_decode_unsafe( self->historical_roots + i, ctx ); + } + } else + self->historical_roots = NULL; + fd_bincode_uint64_decode_unsafe( &self->historical_roots_with_hash_len, ctx ); + if( self->historical_roots_with_hash_len ) { + self->historical_roots_with_hash = (fd_slot_map_pair_t *)fd_valloc_malloc( ctx->valloc, FD_SLOT_MAP_PAIR_ALIGN, FD_SLOT_MAP_PAIR_FOOTPRINT*self->historical_roots_with_hash_len ); + for( ulong i=0; i < self->historical_roots_with_hash_len; i++ ) { + fd_slot_map_pair_new( self->historical_roots_with_hash + i ); + fd_slot_map_pair_decode_unsafe( self->historical_roots_with_hash + i, ctx ); + } + } else + self->historical_roots_with_hash = NULL; } -int fd_reward_info_encode( fd_reward_info_t const * self, fd_bincode_encode_ctx_t * ctx ) { +int fd_solana_accounts_db_fields_encode( fd_solana_accounts_db_fields_t const * self, fd_bincode_encode_ctx_t * ctx ) { int err; - err = fd_reward_type_encode( &self->reward_type, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->lamports, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->staker_rewards, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->new_credits_observed, ctx ); + err = fd_bincode_uint64_encode( self->storages_len, ctx ); + if( FD_UNLIKELY(err) ) return err; + if( self->storages_len ) { + for( ulong i=0; i < self->storages_len; i++ ) { + err = fd_snapshot_slot_acc_vecs_encode( self->storages + i, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + } + err = fd_bincode_uint64_encode( self->version, ctx ); if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->post_balance, ctx ); + err = fd_bincode_uint64_encode( self->slot, ctx ); if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( (ulong)self->commission, ctx ); + err = fd_bank_hash_info_encode( &self->bank_hash_info, ctx ); if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->historical_roots_len, ctx ); + if( FD_UNLIKELY(err) ) return err; + if( self->historical_roots_len ) { + for( ulong i=0; i < self->historical_roots_len; i++ ) { + err = fd_bincode_uint64_encode( self->historical_roots[i], ctx ); + } + } + err = fd_bincode_uint64_encode( self->historical_roots_with_hash_len, ctx ); + if( FD_UNLIKELY(err) ) return err; + if( self->historical_roots_with_hash_len ) { + for( ulong i=0; i < self->historical_roots_with_hash_len; i++ ) { + err = fd_slot_map_pair_encode( self->historical_roots_with_hash + i, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + } return FD_BINCODE_SUCCESS; } -int fd_reward_info_decode_offsets( fd_reward_info_off_t * self, fd_bincode_decode_ctx_t * ctx ) { +int fd_solana_accounts_db_fields_decode_offsets( fd_solana_accounts_db_fields_off_t * self, fd_bincode_decode_ctx_t * ctx ) { uchar const * data = ctx->data; int err; - self->reward_type_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_reward_type_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - self->lamports_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->staker_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); + self->storages_off = (uint)( (ulong)ctx->data - (ulong)data ); + ulong storages_len; + err = fd_bincode_uint64_decode( &storages_len, ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->new_credits_observed_off = (uint)( (ulong)ctx->data - (ulong)data ); + if( storages_len ) { + for( ulong i=0; i < storages_len; i++ ) { + err = fd_snapshot_slot_acc_vecs_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } + self->version_off = (uint)( (ulong)ctx->data - (ulong)data ); err = fd_bincode_uint64_decode_preflight( ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->post_balance_off = (uint)( (ulong)ctx->data - (ulong)data ); + self->slot_off = (uint)( (ulong)ctx->data - (ulong)data ); err = fd_bincode_uint64_decode_preflight( ctx ); if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->commission_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -void fd_reward_info_new(fd_reward_info_t * self) { - fd_memset( self, 0, sizeof(fd_reward_info_t) ); - fd_reward_type_new( &self->reward_type ); -} -void fd_reward_info_destroy( fd_reward_info_t * self, fd_bincode_destroy_ctx_t * ctx ) { - fd_reward_type_destroy( &self->reward_type, ctx ); -} - -ulong fd_reward_info_footprint( void ){ return FD_REWARD_INFO_FOOTPRINT; } -ulong fd_reward_info_align( void ){ return FD_REWARD_INFO_ALIGN; } - -void fd_reward_info_walk( void * w, fd_reward_info_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_reward_info", level++ ); - fd_reward_type_walk( w, &self->reward_type, fun, "reward_type", level ); - fun( w, &self->lamports, "lamports", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->staker_rewards, "staker_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->new_credits_observed, "new_credits_observed", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->post_balance, "post_balance", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - fun( w, &self->commission, "commission", FD_FLAMENCO_TYPE_SLONG, "long", level ); - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_reward_info", level-- ); -} -ulong fd_reward_info_size( fd_reward_info_t const * self ) { - ulong size = 0; - size += fd_reward_type_size( &self->reward_type ); - size += sizeof(ulong); - size += sizeof(ulong); - size += sizeof(ulong); - size += sizeof(ulong); - size += sizeof(long); - return size; -} - -int fd_stake_reward_decode( fd_stake_reward_t * self, fd_bincode_decode_ctx_t * ctx ) { - void const * data = ctx->data; - int err = fd_stake_reward_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ctx->data = data; - if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_stake_reward_new( self ); - } - fd_stake_reward_decode_unsafe( self, ctx ); - return FD_BINCODE_SUCCESS; -} -int fd_stake_reward_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { - int err; - err = fd_bincode_bytes_decode_preflight( 32, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_reward_info_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -void fd_stake_reward_decode_unsafe( fd_stake_reward_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_pubkey_decode_unsafe( &self->stake_pubkey, ctx ); - fd_reward_info_decode_unsafe( &self->reward_info, ctx ); -} -int fd_stake_reward_encode( fd_stake_reward_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - err = fd_pubkey_encode( &self->stake_pubkey, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_reward_info_encode( &self->reward_info, ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -int fd_stake_reward_decode_offsets( fd_stake_reward_off_t * self, fd_bincode_decode_ctx_t * ctx ) { - uchar const * data = ctx->data; - int err; - self->stake_pubkey_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_bytes_decode_preflight( 32, ctx ); - if( FD_UNLIKELY( err ) ) return err; - self->reward_info_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_reward_info_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; -} -void fd_stake_reward_new(fd_stake_reward_t * self) { - fd_memset( self, 0, sizeof(fd_stake_reward_t) ); - fd_pubkey_new( &self->stake_pubkey ); - fd_reward_info_new( &self->reward_info ); -} -void fd_stake_reward_destroy( fd_stake_reward_t * self, fd_bincode_destroy_ctx_t * ctx ) { - fd_pubkey_destroy( &self->stake_pubkey, ctx ); - fd_reward_info_destroy( &self->reward_info, ctx ); -} - -ulong fd_stake_reward_footprint( void ){ return FD_STAKE_REWARD_FOOTPRINT; } -ulong fd_stake_reward_align( void ){ return FD_STAKE_REWARD_ALIGN; } - -void fd_stake_reward_walk( void * w, fd_stake_reward_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_stake_reward", level++ ); - fd_pubkey_walk( w, &self->stake_pubkey, fun, "stake_pubkey", level ); - fd_reward_info_walk( w, &self->reward_info, fun, "reward_info", level ); - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_stake_reward", level-- ); -} -ulong fd_stake_reward_size( fd_stake_reward_t const * self ) { - ulong size = 0; - size += fd_pubkey_size( &self->stake_pubkey ); - size += fd_reward_info_size( &self->reward_info ); - return size; -} - -int fd_serializable_stake_rewards_decode( fd_serializable_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - void const * data = ctx->data; - int err = fd_serializable_stake_rewards_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ctx->data = data; - if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_serializable_stake_rewards_new( self ); - } - fd_serializable_stake_rewards_decode_unsafe( self, ctx ); - return FD_BINCODE_SUCCESS; -} -int fd_serializable_stake_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { - int err; - ulong body_len; - err = fd_bincode_uint64_decode( &body_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( body_len ) { - for( ulong i=0; i < body_len; i++ ) { - err = fd_stake_reward_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -void fd_serializable_stake_rewards_decode_unsafe( fd_serializable_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_bincode_uint64_decode_unsafe( &self->body_len, ctx ); - if( self->body_len ) { - self->body = (fd_stake_reward_t *)fd_valloc_malloc( ctx->valloc, FD_STAKE_REWARD_ALIGN, FD_STAKE_REWARD_FOOTPRINT*self->body_len ); - for( ulong i=0; i < self->body_len; i++ ) { - fd_stake_reward_new( self->body + i ); - fd_stake_reward_decode_unsafe( self->body + i, ctx ); - } - } else - self->body = NULL; -} -int fd_serializable_stake_rewards_encode( fd_serializable_stake_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - err = fd_bincode_uint64_encode( self->body_len, ctx ); - if( FD_UNLIKELY(err) ) return err; - if( self->body_len ) { - for( ulong i=0; i < self->body_len; i++ ) { - err = fd_stake_reward_encode( self->body + i, ctx ); - if( FD_UNLIKELY( err ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -int fd_serializable_stake_rewards_decode_offsets( fd_serializable_stake_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ) { - uchar const * data = ctx->data; - int err; - self->body_off = (uint)( (ulong)ctx->data - (ulong)data ); - ulong body_len; - err = fd_bincode_uint64_decode( &body_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( body_len ) { - for( ulong i=0; i < body_len; i++ ) { - err = fd_stake_reward_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -void fd_serializable_stake_rewards_new(fd_serializable_stake_rewards_t * self) { - fd_memset( self, 0, sizeof(fd_serializable_stake_rewards_t) ); -} -void fd_serializable_stake_rewards_destroy( fd_serializable_stake_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ) { - if( self->body ) { - for( ulong i=0; i < self->body_len; i++ ) - fd_stake_reward_destroy( self->body + i, ctx ); - fd_valloc_free( ctx->valloc, self->body ); - self->body = NULL; - } -} - -ulong fd_serializable_stake_rewards_footprint( void ){ return FD_SERIALIZABLE_STAKE_REWARDS_FOOTPRINT; } -ulong fd_serializable_stake_rewards_align( void ){ return FD_SERIALIZABLE_STAKE_REWARDS_ALIGN; } - -void fd_serializable_stake_rewards_walk( void * w, fd_serializable_stake_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_serializable_stake_rewards", level++ ); - if( self->body_len ) { - fun( w, NULL, NULL, FD_FLAMENCO_TYPE_ARR, "body", level++ ); - for( ulong i=0; i < self->body_len; i++ ) - fd_stake_reward_walk(w, self->body + i, fun, "stake_reward", level ); - fun( w, NULL, NULL, FD_FLAMENCO_TYPE_ARR_END, "body", level-- ); - } - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_serializable_stake_rewards", level-- ); -} -ulong fd_serializable_stake_rewards_size( fd_serializable_stake_rewards_t const * self ) { - ulong size = 0; - do { - size += sizeof(ulong); - for( ulong i=0; i < self->body_len; i++ ) - size += fd_stake_reward_size( self->body + i ); - } while(0); - return size; -} - -int fd_start_block_height_and_rewards_decode( fd_start_block_height_and_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - void const * data = ctx->data; - int err = fd_start_block_height_and_rewards_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ctx->data = data; - if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_start_block_height_and_rewards_new( self ); - } - fd_start_block_height_and_rewards_decode_unsafe( self, ctx ); - return FD_BINCODE_SUCCESS; -} -int fd_start_block_height_and_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { - int err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ulong stake_rewards_by_partition_len; - err = fd_bincode_uint64_decode( &stake_rewards_by_partition_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( stake_rewards_by_partition_len ) { - for( ulong i=0; i < stake_rewards_by_partition_len; i++ ) { - err = fd_serializable_stake_rewards_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -void fd_start_block_height_and_rewards_decode_unsafe( fd_start_block_height_and_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_bincode_uint64_decode_unsafe( &self->start_block_height, ctx ); - fd_bincode_uint64_decode_unsafe( &self->stake_rewards_by_partition_len, ctx ); - if( self->stake_rewards_by_partition_len ) { - self->stake_rewards_by_partition = (fd_serializable_stake_rewards_t *)fd_valloc_malloc( ctx->valloc, FD_SERIALIZABLE_STAKE_REWARDS_ALIGN, FD_SERIALIZABLE_STAKE_REWARDS_FOOTPRINT*self->stake_rewards_by_partition_len ); - for( ulong i=0; i < self->stake_rewards_by_partition_len; i++ ) { - fd_serializable_stake_rewards_new( self->stake_rewards_by_partition + i ); - fd_serializable_stake_rewards_decode_unsafe( self->stake_rewards_by_partition + i, ctx ); - } - } else - self->stake_rewards_by_partition = NULL; -} -int fd_start_block_height_and_rewards_encode( fd_start_block_height_and_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - err = fd_bincode_uint64_encode( self->start_block_height, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->stake_rewards_by_partition_len, ctx ); - if( FD_UNLIKELY(err) ) return err; - if( self->stake_rewards_by_partition_len ) { - for( ulong i=0; i < self->stake_rewards_by_partition_len; i++ ) { - err = fd_serializable_stake_rewards_encode( self->stake_rewards_by_partition + i, ctx ); - if( FD_UNLIKELY( err ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -int fd_start_block_height_and_rewards_decode_offsets( fd_start_block_height_and_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ) { - uchar const * data = ctx->data; - int err; - self->start_block_height_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->stake_rewards_by_partition_off = (uint)( (ulong)ctx->data - (ulong)data ); - ulong stake_rewards_by_partition_len; - err = fd_bincode_uint64_decode( &stake_rewards_by_partition_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( stake_rewards_by_partition_len ) { - for( ulong i=0; i < stake_rewards_by_partition_len; i++ ) { - err = fd_serializable_stake_rewards_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -void fd_start_block_height_and_rewards_new(fd_start_block_height_and_rewards_t * self) { - fd_memset( self, 0, sizeof(fd_start_block_height_and_rewards_t) ); -} -void fd_start_block_height_and_rewards_destroy( fd_start_block_height_and_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ) { - if( self->stake_rewards_by_partition ) { - for( ulong i=0; i < self->stake_rewards_by_partition_len; i++ ) - fd_serializable_stake_rewards_destroy( self->stake_rewards_by_partition + i, ctx ); - fd_valloc_free( ctx->valloc, self->stake_rewards_by_partition ); - self->stake_rewards_by_partition = NULL; - } -} - -ulong fd_start_block_height_and_rewards_footprint( void ){ return FD_START_BLOCK_HEIGHT_AND_REWARDS_FOOTPRINT; } -ulong fd_start_block_height_and_rewards_align( void ){ return FD_START_BLOCK_HEIGHT_AND_REWARDS_ALIGN; } - -void fd_start_block_height_and_rewards_walk( void * w, fd_start_block_height_and_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { - fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_start_block_height_and_rewards", level++ ); - fun( w, &self->start_block_height, "start_block_height", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); - if( self->stake_rewards_by_partition_len ) { - fun( w, NULL, NULL, FD_FLAMENCO_TYPE_ARR, "stake_rewards_by_partition", level++ ); - for( ulong i=0; i < self->stake_rewards_by_partition_len; i++ ) - fd_serializable_stake_rewards_walk(w, self->stake_rewards_by_partition + i, fun, "serializable_stake_rewards", level ); - fun( w, NULL, NULL, FD_FLAMENCO_TYPE_ARR_END, "stake_rewards_by_partition", level-- ); - } - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_start_block_height_and_rewards", level-- ); -} -ulong fd_start_block_height_and_rewards_size( fd_start_block_height_and_rewards_t const * self ) { - ulong size = 0; - size += sizeof(ulong); - do { - size += sizeof(ulong); - for( ulong i=0; i < self->stake_rewards_by_partition_len; i++ ) - size += fd_serializable_stake_rewards_size( self->stake_rewards_by_partition + i ); - } while(0); - return size; -} - -FD_FN_PURE uchar fd_serializable_epoch_reward_status_is_Active(fd_serializable_epoch_reward_status_t const * self) { - return self->discriminant == 0; -} -FD_FN_PURE uchar fd_serializable_epoch_reward_status_is_Inactive(fd_serializable_epoch_reward_status_t const * self) { - return self->discriminant == 1; -} -void fd_serializable_epoch_reward_status_inner_new( fd_serializable_epoch_reward_status_inner_t * self, uint discriminant ); -int fd_serializable_epoch_reward_status_inner_decode_preflight( uint discriminant, fd_bincode_decode_ctx_t * ctx ) { - int err; - switch (discriminant) { - case 0: { - err = fd_start_block_height_and_rewards_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - return FD_BINCODE_SUCCESS; - } - case 1: { - return FD_BINCODE_SUCCESS; - } - default: return FD_BINCODE_ERR_ENCODING; - } -} -void fd_serializable_epoch_reward_status_inner_decode_unsafe( fd_serializable_epoch_reward_status_inner_t * self, uint discriminant, fd_bincode_decode_ctx_t * ctx ) { - switch (discriminant) { - case 0: { - fd_start_block_height_and_rewards_decode_unsafe( &self->Active, ctx ); - break; - } - case 1: { - break; - } - } -} -int fd_serializable_epoch_reward_status_decode( fd_serializable_epoch_reward_status_t * self, fd_bincode_decode_ctx_t * ctx ) { - void const * data = ctx->data; - int err = fd_serializable_epoch_reward_status_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ctx->data = data; - if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_serializable_epoch_reward_status_new( self ); - } - fd_serializable_epoch_reward_status_decode_unsafe( self, ctx ); - return FD_BINCODE_SUCCESS; -} -int fd_serializable_epoch_reward_status_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { - uint discriminant = 0; - int err = fd_bincode_uint32_decode( &discriminant, ctx ); - if( FD_UNLIKELY( err ) ) return err; - return fd_serializable_epoch_reward_status_inner_decode_preflight( discriminant, ctx ); -} -void fd_serializable_epoch_reward_status_decode_unsafe( fd_serializable_epoch_reward_status_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_bincode_uint32_decode_unsafe( &self->discriminant, ctx ); - fd_serializable_epoch_reward_status_inner_decode_unsafe( &self->inner, self->discriminant, ctx ); -} -void fd_serializable_epoch_reward_status_inner_new( fd_serializable_epoch_reward_status_inner_t * self, uint discriminant ) { - switch( discriminant ) { - case 0: { - fd_start_block_height_and_rewards_new( &self->Active ); - break; - } - case 1: { - break; - } - default: break; // FD_LOG_ERR(( "unhandled type")); - } -} -void fd_serializable_epoch_reward_status_new_disc( fd_serializable_epoch_reward_status_t * self, uint discriminant ) { - self->discriminant = discriminant; - fd_serializable_epoch_reward_status_inner_new( &self->inner, self->discriminant ); -} -void fd_serializable_epoch_reward_status_new( fd_serializable_epoch_reward_status_t * self ) { - fd_memset( self, 0, sizeof(fd_serializable_epoch_reward_status_t) ); - fd_serializable_epoch_reward_status_new_disc( self, UINT_MAX ); -} -void fd_serializable_epoch_reward_status_inner_destroy( fd_serializable_epoch_reward_status_inner_t * self, uint discriminant, fd_bincode_destroy_ctx_t * ctx ) { - switch( discriminant ) { - case 0: { - fd_start_block_height_and_rewards_destroy( &self->Active, ctx ); - break; - } - default: break; // FD_LOG_ERR(( "unhandled type" )); - } -} -void fd_serializable_epoch_reward_status_destroy( fd_serializable_epoch_reward_status_t * self, fd_bincode_destroy_ctx_t * ctx ) { - fd_serializable_epoch_reward_status_inner_destroy( &self->inner, self->discriminant, ctx ); -} - -ulong fd_serializable_epoch_reward_status_footprint( void ){ return FD_SERIALIZABLE_EPOCH_REWARD_STATUS_FOOTPRINT; } -ulong fd_serializable_epoch_reward_status_align( void ){ return FD_SERIALIZABLE_EPOCH_REWARD_STATUS_ALIGN; } - -void fd_serializable_epoch_reward_status_walk( void * w, fd_serializable_epoch_reward_status_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { - fun(w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_serializable_epoch_reward_status", level++); - switch( self->discriminant ) { - case 0: { - fd_start_block_height_and_rewards_walk( w, &self->inner.Active, fun, "Active", level ); - break; - } - } - fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_serializable_epoch_reward_status", level-- ); -} -ulong fd_serializable_epoch_reward_status_size( fd_serializable_epoch_reward_status_t const * self ) { - ulong size = 0; - size += sizeof(uint); - switch (self->discriminant) { - case 0: { - size += fd_start_block_height_and_rewards_size( &self->inner.Active ); - break; - } - } - return size; -} - -int fd_serializable_epoch_reward_status_inner_encode( fd_serializable_epoch_reward_status_inner_t const * self, uint discriminant, fd_bincode_encode_ctx_t * ctx ) { - int err; - switch (discriminant) { - case 0: { - err = fd_start_block_height_and_rewards_encode( &self->Active, ctx ); - if( FD_UNLIKELY( err ) ) return err; - break; - } - } - return FD_BINCODE_SUCCESS; -} -int fd_serializable_epoch_reward_status_encode( fd_serializable_epoch_reward_status_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err = fd_bincode_uint32_encode( self->discriminant, ctx ); - if( FD_UNLIKELY( err ) ) return err; - return fd_serializable_epoch_reward_status_inner_encode( &self->inner, self->discriminant, ctx ); -} - -int fd_solana_accounts_db_fields_decode( fd_solana_accounts_db_fields_t * self, fd_bincode_decode_ctx_t * ctx ) { - void const * data = ctx->data; - int err = fd_solana_accounts_db_fields_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - ctx->data = data; - if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { - fd_solana_accounts_db_fields_new( self ); - } - fd_solana_accounts_db_fields_decode_unsafe( self, ctx ); - return FD_BINCODE_SUCCESS; -} -int fd_solana_accounts_db_fields_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { - int err; - ulong storages_len; - err = fd_bincode_uint64_decode( &storages_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( storages_len ) { - for( ulong i=0; i < storages_len; i++ ) { - err = fd_snapshot_slot_acc_vecs_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - err = fd_bank_hash_info_decode_preflight( ctx ); - if( FD_UNLIKELY( err ) ) return err; - ulong historical_roots_len; - err = fd_bincode_uint64_decode( &historical_roots_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( historical_roots_len ) { - for( ulong i=0; i < historical_roots_len; i++ ) { - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - ulong historical_roots_with_hash_len; - err = fd_bincode_uint64_decode( &historical_roots_with_hash_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( historical_roots_with_hash_len ) { - for( ulong i=0; i < historical_roots_with_hash_len; i++ ) { - err = fd_slot_map_pair_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -void fd_solana_accounts_db_fields_decode_unsafe( fd_solana_accounts_db_fields_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_bincode_uint64_decode_unsafe( &self->storages_len, ctx ); - if( self->storages_len ) { - self->storages = (fd_snapshot_slot_acc_vecs_t *)fd_valloc_malloc( ctx->valloc, FD_SNAPSHOT_SLOT_ACC_VECS_ALIGN, FD_SNAPSHOT_SLOT_ACC_VECS_FOOTPRINT*self->storages_len ); - for( ulong i=0; i < self->storages_len; i++ ) { - fd_snapshot_slot_acc_vecs_new( self->storages + i ); - fd_snapshot_slot_acc_vecs_decode_unsafe( self->storages + i, ctx ); - } - } else - self->storages = NULL; - fd_bincode_uint64_decode_unsafe( &self->version, ctx ); - fd_bincode_uint64_decode_unsafe( &self->slot, ctx ); - fd_bank_hash_info_decode_unsafe( &self->bank_hash_info, ctx ); - fd_bincode_uint64_decode_unsafe( &self->historical_roots_len, ctx ); - if( self->historical_roots_len ) { - self->historical_roots = fd_valloc_malloc( ctx->valloc, 8UL, sizeof(ulong)*self->historical_roots_len ); - for( ulong i=0; i < self->historical_roots_len; i++ ) { - fd_bincode_uint64_decode_unsafe( self->historical_roots + i, ctx ); - } - } else - self->historical_roots = NULL; - fd_bincode_uint64_decode_unsafe( &self->historical_roots_with_hash_len, ctx ); - if( self->historical_roots_with_hash_len ) { - self->historical_roots_with_hash = (fd_slot_map_pair_t *)fd_valloc_malloc( ctx->valloc, FD_SLOT_MAP_PAIR_ALIGN, FD_SLOT_MAP_PAIR_FOOTPRINT*self->historical_roots_with_hash_len ); - for( ulong i=0; i < self->historical_roots_with_hash_len; i++ ) { - fd_slot_map_pair_new( self->historical_roots_with_hash + i ); - fd_slot_map_pair_decode_unsafe( self->historical_roots_with_hash + i, ctx ); - } - } else - self->historical_roots_with_hash = NULL; -} -int fd_solana_accounts_db_fields_encode( fd_solana_accounts_db_fields_t const * self, fd_bincode_encode_ctx_t * ctx ) { - int err; - err = fd_bincode_uint64_encode( self->storages_len, ctx ); - if( FD_UNLIKELY(err) ) return err; - if( self->storages_len ) { - for( ulong i=0; i < self->storages_len; i++ ) { - err = fd_snapshot_slot_acc_vecs_encode( self->storages + i, ctx ); - if( FD_UNLIKELY( err ) ) return err; - } - } - err = fd_bincode_uint64_encode( self->version, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->slot, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bank_hash_info_encode( &self->bank_hash_info, ctx ); - if( FD_UNLIKELY( err ) ) return err; - err = fd_bincode_uint64_encode( self->historical_roots_len, ctx ); - if( FD_UNLIKELY(err) ) return err; - if( self->historical_roots_len ) { - for( ulong i=0; i < self->historical_roots_len; i++ ) { - err = fd_bincode_uint64_encode( self->historical_roots[i], ctx ); - } - } - err = fd_bincode_uint64_encode( self->historical_roots_with_hash_len, ctx ); - if( FD_UNLIKELY(err) ) return err; - if( self->historical_roots_with_hash_len ) { - for( ulong i=0; i < self->historical_roots_with_hash_len; i++ ) { - err = fd_slot_map_pair_encode( self->historical_roots_with_hash + i, ctx ); - if( FD_UNLIKELY( err ) ) return err; - } - } - return FD_BINCODE_SUCCESS; -} -int fd_solana_accounts_db_fields_decode_offsets( fd_solana_accounts_db_fields_off_t * self, fd_bincode_decode_ctx_t * ctx ) { - uchar const * data = ctx->data; - int err; - self->storages_off = (uint)( (ulong)ctx->data - (ulong)data ); - ulong storages_len; - err = fd_bincode_uint64_decode( &storages_len, ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - if( storages_len ) { - for( ulong i=0; i < storages_len; i++ ) { - err = fd_snapshot_slot_acc_vecs_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - } - } - self->version_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->slot_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bincode_uint64_decode_preflight( ctx ); - if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; - self->bank_hash_info_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_bank_hash_info_decode_preflight( ctx ); + self->bank_hash_info_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bank_hash_info_decode_preflight( ctx ); if( FD_UNLIKELY( err ) ) return err; self->historical_roots_off = (uint)( (ulong)ctx->data - (ulong)data ); ulong historical_roots_len; @@ -7807,6 +7185,92 @@ ulong fd_versioned_epoch_stakes_pair_size( fd_versioned_epoch_stakes_pair_t cons return size; } +int fd_reward_info_decode( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ) { + void const * data = ctx->data; + int err = fd_reward_info_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + ctx->data = data; + if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { + fd_reward_info_new( self ); + } + fd_reward_info_decode_unsafe( self, ctx ); + return FD_BINCODE_SUCCESS; +} +int fd_reward_info_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { + int err; + err = fd_reward_type_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_reward_info_decode_unsafe( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ) { + fd_reward_type_decode_unsafe( &self->reward_type, ctx ); + fd_bincode_uint64_decode_unsafe( &self->lamports, ctx ); + fd_bincode_uint64_decode_unsafe( &self->post_balance, ctx ); + fd_bincode_uint64_decode_unsafe( &self->commission, ctx ); +} +int fd_reward_info_encode( fd_reward_info_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_reward_type_encode( &self->reward_type, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->lamports, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->post_balance, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->commission, ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +int fd_reward_info_decode_offsets( fd_reward_info_off_t * self, fd_bincode_decode_ctx_t * ctx ) { + uchar const * data = ctx->data; + int err; + self->reward_type_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_reward_type_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + self->lamports_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->post_balance_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->commission_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_reward_info_new(fd_reward_info_t * self) { + fd_memset( self, 0, sizeof(fd_reward_info_t) ); + fd_reward_type_new( &self->reward_type ); +} +void fd_reward_info_destroy( fd_reward_info_t * self, fd_bincode_destroy_ctx_t * ctx ) { + fd_reward_type_destroy( &self->reward_type, ctx ); +} + +ulong fd_reward_info_footprint( void ){ return FD_REWARD_INFO_FOOTPRINT; } +ulong fd_reward_info_align( void ){ return FD_REWARD_INFO_ALIGN; } + +void fd_reward_info_walk( void * w, fd_reward_info_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { + fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_reward_info", level++ ); + fd_reward_type_walk( w, &self->reward_type, fun, "reward_type", level ); + fun( w, &self->lamports, "lamports", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->post_balance, "post_balance", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->commission, "commission", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_reward_info", level-- ); +} +ulong fd_reward_info_size( fd_reward_info_t const * self ) { + ulong size = 0; + size += fd_reward_type_size( &self->reward_type ); + size += sizeof(ulong); + size += sizeof(ulong); + size += sizeof(ulong); + return size; +} + int fd_solana_manifest_decode( fd_solana_manifest_t * self, fd_bincode_decode_ctx_t * ctx ) { void const * data = ctx->data; int err = fd_solana_manifest_decode_preflight( ctx ); @@ -13161,33 +12625,81 @@ int fd_sysvar_epoch_rewards_decode( fd_sysvar_epoch_rewards_t * self, fd_bincode } int fd_sysvar_epoch_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { int err; - err = fd_epoch_rewards_decode_preflight( ctx ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_hash_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint128_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint8_decode_preflight( ctx ); if( FD_UNLIKELY( err ) ) return err; return FD_BINCODE_SUCCESS; } void fd_sysvar_epoch_rewards_decode_unsafe( fd_sysvar_epoch_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { - fd_epoch_rewards_decode_unsafe( &self->epoch_rewards, ctx ); + fd_bincode_uint64_decode_unsafe( &self->distribution_starting_block_height, ctx ); + fd_bincode_uint64_decode_unsafe( &self->num_partitions, ctx ); + fd_hash_decode_unsafe( &self->parent_blockhash, ctx ); + fd_bincode_uint128_decode_unsafe( &self->total_points, ctx ); + fd_bincode_uint64_decode_unsafe( &self->total_rewards, ctx ); + fd_bincode_uint64_decode_unsafe( &self->distributed_rewards, ctx ); + fd_bincode_uint8_decode_unsafe( &self->active, ctx ); } int fd_sysvar_epoch_rewards_encode( fd_sysvar_epoch_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ) { int err; - err = fd_epoch_rewards_encode( &self->epoch_rewards, ctx ); + err = fd_bincode_uint64_encode( self->distribution_starting_block_height, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->num_partitions, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_hash_encode( &self->parent_blockhash, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint128_encode( self->total_points, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->total_rewards, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->distributed_rewards, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint8_encode( (uchar)(self->active), ctx ); if( FD_UNLIKELY( err ) ) return err; return FD_BINCODE_SUCCESS; } int fd_sysvar_epoch_rewards_decode_offsets( fd_sysvar_epoch_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ) { uchar const * data = ctx->data; int err; - self->epoch_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); - err = fd_epoch_rewards_decode_preflight( ctx ); + self->distribution_starting_block_height_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->num_partitions_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->parent_blockhash_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_hash_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + self->total_points_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint128_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + self->total_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->distributed_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->active_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint8_decode_preflight( ctx ); if( FD_UNLIKELY( err ) ) return err; return FD_BINCODE_SUCCESS; } void fd_sysvar_epoch_rewards_new(fd_sysvar_epoch_rewards_t * self) { fd_memset( self, 0, sizeof(fd_sysvar_epoch_rewards_t) ); - fd_epoch_rewards_new( &self->epoch_rewards ); + fd_hash_new( &self->parent_blockhash ); } void fd_sysvar_epoch_rewards_destroy( fd_sysvar_epoch_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ) { - fd_epoch_rewards_destroy( &self->epoch_rewards, ctx ); + fd_hash_destroy( &self->parent_blockhash, ctx ); } ulong fd_sysvar_epoch_rewards_footprint( void ){ return FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT; } @@ -13195,12 +12707,24 @@ ulong fd_sysvar_epoch_rewards_align( void ){ return FD_SYSVAR_EPOCH_REWARDS_ALIG void fd_sysvar_epoch_rewards_walk( void * w, fd_sysvar_epoch_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_sysvar_epoch_rewards", level++ ); - fd_epoch_rewards_walk( w, &self->epoch_rewards, fun, "epoch_rewards", level ); + fun( w, &self->distribution_starting_block_height, "distribution_starting_block_height", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->num_partitions, "num_partitions", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fd_hash_walk( w, &self->parent_blockhash, fun, "parent_blockhash", level ); + fun( w, &self->total_points, "total_points", FD_FLAMENCO_TYPE_UINT128, "uint128", level ); + fun( w, &self->total_rewards, "total_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->distributed_rewards, "distributed_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->active, "active", FD_FLAMENCO_TYPE_UCHAR, "uchar", level ); fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_sysvar_epoch_rewards", level-- ); } ulong fd_sysvar_epoch_rewards_size( fd_sysvar_epoch_rewards_t const * self ) { ulong size = 0; - size += fd_epoch_rewards_size( &self->epoch_rewards ); + size += sizeof(ulong); + size += sizeof(ulong); + size += fd_hash_size( &self->parent_blockhash ); + size += sizeof(uint128); + size += sizeof(ulong); + size += sizeof(ulong); + size += sizeof(char); return size; } @@ -30165,6 +29689,382 @@ ulong fd_bank_slot_deltas_size( fd_bank_slot_deltas_t const * self ) { return size; } +int fd_pubkey_rewardinfo_pair_decode( fd_pubkey_rewardinfo_pair_t * self, fd_bincode_decode_ctx_t * ctx ) { + void const * data = ctx->data; + int err = fd_pubkey_rewardinfo_pair_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + ctx->data = data; + if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { + fd_pubkey_rewardinfo_pair_new( self ); + } + fd_pubkey_rewardinfo_pair_decode_unsafe( self, ctx ); + return FD_BINCODE_SUCCESS; +} +int fd_pubkey_rewardinfo_pair_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { + int err; + err = fd_bincode_bytes_decode_preflight( 32, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_reward_info_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_pubkey_rewardinfo_pair_decode_unsafe( fd_pubkey_rewardinfo_pair_t * self, fd_bincode_decode_ctx_t * ctx ) { + fd_pubkey_decode_unsafe( &self->pubkey, ctx ); + fd_reward_info_decode_unsafe( &self->reward_info, ctx ); +} +int fd_pubkey_rewardinfo_pair_encode( fd_pubkey_rewardinfo_pair_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_pubkey_encode( &self->pubkey, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_reward_info_encode( &self->reward_info, ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +int fd_pubkey_rewardinfo_pair_decode_offsets( fd_pubkey_rewardinfo_pair_off_t * self, fd_bincode_decode_ctx_t * ctx ) { + uchar const * data = ctx->data; + int err; + self->pubkey_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_bytes_decode_preflight( 32, ctx ); + if( FD_UNLIKELY( err ) ) return err; + self->reward_info_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_reward_info_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_pubkey_rewardinfo_pair_new(fd_pubkey_rewardinfo_pair_t * self) { + fd_memset( self, 0, sizeof(fd_pubkey_rewardinfo_pair_t) ); + fd_pubkey_new( &self->pubkey ); + fd_reward_info_new( &self->reward_info ); +} +void fd_pubkey_rewardinfo_pair_destroy( fd_pubkey_rewardinfo_pair_t * self, fd_bincode_destroy_ctx_t * ctx ) { + fd_pubkey_destroy( &self->pubkey, ctx ); + fd_reward_info_destroy( &self->reward_info, ctx ); +} + +ulong fd_pubkey_rewardinfo_pair_footprint( void ){ return FD_PUBKEY_REWARDINFO_PAIR_FOOTPRINT; } +ulong fd_pubkey_rewardinfo_pair_align( void ){ return FD_PUBKEY_REWARDINFO_PAIR_ALIGN; } + +void fd_pubkey_rewardinfo_pair_walk( void * w, fd_pubkey_rewardinfo_pair_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { + fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_pubkey_rewardinfo_pair", level++ ); + fd_pubkey_walk( w, &self->pubkey, fun, "pubkey", level ); + fd_reward_info_walk( w, &self->reward_info, fun, "reward_info", level ); + fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_pubkey_rewardinfo_pair", level-- ); +} +ulong fd_pubkey_rewardinfo_pair_size( fd_pubkey_rewardinfo_pair_t const * self ) { + ulong size = 0; + size += fd_pubkey_size( &self->pubkey ); + size += fd_reward_info_size( &self->reward_info ); + return size; +} + +int fd_optional_account_decode( fd_optional_account_t * self, fd_bincode_decode_ctx_t * ctx ) { + void const * data = ctx->data; + int err = fd_optional_account_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + ctx->data = data; + if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { + fd_optional_account_new( self ); + } + fd_optional_account_decode_unsafe( self, ctx ); + return FD_BINCODE_SUCCESS; +} +int fd_optional_account_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { + int err; + { + uchar o; + err = fd_bincode_bool_decode( &o, ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( o ) { + err = fd_solana_account_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } + return FD_BINCODE_SUCCESS; +} +void fd_optional_account_decode_unsafe( fd_optional_account_t * self, fd_bincode_decode_ctx_t * ctx ) { + { + uchar o; + fd_bincode_bool_decode_unsafe( &o, ctx ); + if( o ) { + self->account = (fd_solana_account_t *)fd_valloc_malloc( ctx->valloc, FD_SOLANA_ACCOUNT_ALIGN, FD_SOLANA_ACCOUNT_FOOTPRINT ); + fd_solana_account_new( self->account ); + fd_solana_account_decode_unsafe( self->account, ctx ); + } else + self->account = NULL; + } +} +int fd_optional_account_encode( fd_optional_account_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + if( self->account != NULL ) { + err = fd_bincode_bool_encode( 1, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_solana_account_encode( self->account, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } else { + err = fd_bincode_bool_encode( 0, ctx ); + if( FD_UNLIKELY( err ) ) return err; + } + return FD_BINCODE_SUCCESS; +} +int fd_optional_account_decode_offsets( fd_optional_account_off_t * self, fd_bincode_decode_ctx_t * ctx ) { + uchar const * data = ctx->data; + int err; + self->account_off = (uint)( (ulong)ctx->data - (ulong)data ); + { + uchar o; + err = fd_bincode_bool_decode( &o, ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + if( o ) { + err = fd_solana_account_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + } + } + return FD_BINCODE_SUCCESS; +} +void fd_optional_account_new(fd_optional_account_t * self) { + fd_memset( self, 0, sizeof(fd_optional_account_t) ); +} +void fd_optional_account_destroy( fd_optional_account_t * self, fd_bincode_destroy_ctx_t * ctx ) { + if( self->account ) { + fd_solana_account_destroy( self->account, ctx ); + fd_valloc_free( ctx->valloc, self->account ); + self->account = NULL; + } +} + +ulong fd_optional_account_footprint( void ){ return FD_OPTIONAL_ACCOUNT_FOOTPRINT; } +ulong fd_optional_account_align( void ){ return FD_OPTIONAL_ACCOUNT_ALIGN; } + +void fd_optional_account_walk( void * w, fd_optional_account_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { + fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_optional_account", level++ ); + if( !self->account ) { + fun( w, NULL, "account", FD_FLAMENCO_TYPE_NULL, "solana_account", level ); + } else { + fd_solana_account_walk( w, self->account, fun, "account", level ); + } + fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_optional_account", level-- ); +} +ulong fd_optional_account_size( fd_optional_account_t const * self ) { + ulong size = 0; + size += sizeof(char); + if( NULL != self->account ) { + size += fd_solana_account_size( self->account ); + } + return size; +} + +int fd_calculated_stake_points_decode( fd_calculated_stake_points_t * self, fd_bincode_decode_ctx_t * ctx ) { + void const * data = ctx->data; + int err = fd_calculated_stake_points_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + ctx->data = data; + if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { + fd_calculated_stake_points_new( self ); + } + fd_calculated_stake_points_decode_unsafe( self, ctx ); + return FD_BINCODE_SUCCESS; +} +int fd_calculated_stake_points_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { + int err; + err = fd_bincode_uint128_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint8_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_calculated_stake_points_decode_unsafe( fd_calculated_stake_points_t * self, fd_bincode_decode_ctx_t * ctx ) { + fd_bincode_uint128_decode_unsafe( &self->points, ctx ); + fd_bincode_uint64_decode_unsafe( &self->new_credits_observed, ctx ); + fd_bincode_uint8_decode_unsafe( &self->force_credits_update_with_skipped_reward, ctx ); +} +int fd_calculated_stake_points_encode( fd_calculated_stake_points_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_bincode_uint128_encode( self->points, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->new_credits_observed, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint8_encode( (uchar)(self->force_credits_update_with_skipped_reward), ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +int fd_calculated_stake_points_decode_offsets( fd_calculated_stake_points_off_t * self, fd_bincode_decode_ctx_t * ctx ) { + uchar const * data = ctx->data; + int err; + self->points_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint128_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + self->new_credits_observed_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->force_credits_update_with_skipped_reward_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint8_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_calculated_stake_points_new(fd_calculated_stake_points_t * self) { + fd_memset( self, 0, sizeof(fd_calculated_stake_points_t) ); +} +void fd_calculated_stake_points_destroy( fd_calculated_stake_points_t * self, fd_bincode_destroy_ctx_t * ctx ) { +} + +ulong fd_calculated_stake_points_footprint( void ){ return FD_CALCULATED_STAKE_POINTS_FOOTPRINT; } +ulong fd_calculated_stake_points_align( void ){ return FD_CALCULATED_STAKE_POINTS_ALIGN; } + +void fd_calculated_stake_points_walk( void * w, fd_calculated_stake_points_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { + fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_calculated_stake_points", level++ ); + fun( w, &self->points, "points", FD_FLAMENCO_TYPE_UINT128, "uint128", level ); + fun( w, &self->new_credits_observed, "new_credits_observed", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->force_credits_update_with_skipped_reward, "force_credits_update_with_skipped_reward", FD_FLAMENCO_TYPE_UCHAR, "uchar", level ); + fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_calculated_stake_points", level-- ); +} +ulong fd_calculated_stake_points_size( fd_calculated_stake_points_t const * self ) { + ulong size = 0; + size += sizeof(uint128); + size += sizeof(ulong); + size += sizeof(char); + return size; +} + +int fd_point_value_decode( fd_point_value_t * self, fd_bincode_decode_ctx_t * ctx ) { + void const * data = ctx->data; + int err = fd_point_value_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + ctx->data = data; + if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { + fd_point_value_new( self ); + } + fd_point_value_decode_unsafe( self, ctx ); + return FD_BINCODE_SUCCESS; +} +int fd_point_value_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { + int err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint128_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_point_value_decode_unsafe( fd_point_value_t * self, fd_bincode_decode_ctx_t * ctx ) { + fd_bincode_uint64_decode_unsafe( &self->rewards, ctx ); + fd_bincode_uint128_decode_unsafe( &self->points, ctx ); +} +int fd_point_value_encode( fd_point_value_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_bincode_uint64_encode( self->rewards, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint128_encode( self->points, ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +int fd_point_value_decode_offsets( fd_point_value_off_t * self, fd_bincode_decode_ctx_t * ctx ) { + uchar const * data = ctx->data; + int err; + self->rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->points_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint128_decode_preflight( ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_point_value_new(fd_point_value_t * self) { + fd_memset( self, 0, sizeof(fd_point_value_t) ); +} +void fd_point_value_destroy( fd_point_value_t * self, fd_bincode_destroy_ctx_t * ctx ) { +} + +ulong fd_point_value_footprint( void ){ return FD_POINT_VALUE_FOOTPRINT; } +ulong fd_point_value_align( void ){ return FD_POINT_VALUE_ALIGN; } + +void fd_point_value_walk( void * w, fd_point_value_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { + fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_point_value", level++ ); + fun( w, &self->rewards, "rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->points, "points", FD_FLAMENCO_TYPE_UINT128, "uint128", level ); + fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_point_value", level-- ); +} +ulong fd_point_value_size( fd_point_value_t const * self ) { + ulong size = 0; + size += sizeof(ulong); + size += sizeof(uint128); + return size; +} + +int fd_calculated_stake_rewards_decode( fd_calculated_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { + void const * data = ctx->data; + int err = fd_calculated_stake_rewards_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + ctx->data = data; + if( !fd_is_null_alloc_virtual( ctx->valloc ) ) { + fd_calculated_stake_rewards_new( self ); + } + fd_calculated_stake_rewards_decode_unsafe( self, ctx ); + return FD_BINCODE_SUCCESS; +} +int fd_calculated_stake_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ) { + int err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_calculated_stake_rewards_decode_unsafe( fd_calculated_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ) { + fd_bincode_uint64_decode_unsafe( &self->staker_rewards, ctx ); + fd_bincode_uint64_decode_unsafe( &self->voter_rewards, ctx ); + fd_bincode_uint64_decode_unsafe( &self->new_credits_observed, ctx ); +} +int fd_calculated_stake_rewards_encode( fd_calculated_stake_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ) { + int err; + err = fd_bincode_uint64_encode( self->staker_rewards, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->voter_rewards, ctx ); + if( FD_UNLIKELY( err ) ) return err; + err = fd_bincode_uint64_encode( self->new_credits_observed, ctx ); + if( FD_UNLIKELY( err ) ) return err; + return FD_BINCODE_SUCCESS; +} +int fd_calculated_stake_rewards_decode_offsets( fd_calculated_stake_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ) { + uchar const * data = ctx->data; + int err; + self->staker_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->voter_rewards_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + self->new_credits_observed_off = (uint)( (ulong)ctx->data - (ulong)data ); + err = fd_bincode_uint64_decode_preflight( ctx ); + if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) return err; + return FD_BINCODE_SUCCESS; +} +void fd_calculated_stake_rewards_new(fd_calculated_stake_rewards_t * self) { + fd_memset( self, 0, sizeof(fd_calculated_stake_rewards_t) ); +} +void fd_calculated_stake_rewards_destroy( fd_calculated_stake_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ) { +} + +ulong fd_calculated_stake_rewards_footprint( void ){ return FD_CALCULATED_STAKE_REWARDS_FOOTPRINT; } +ulong fd_calculated_stake_rewards_align( void ){ return FD_CALCULATED_STAKE_REWARDS_ALIGN; } + +void fd_calculated_stake_rewards_walk( void * w, fd_calculated_stake_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ) { + fun( w, self, name, FD_FLAMENCO_TYPE_MAP, "fd_calculated_stake_rewards", level++ ); + fun( w, &self->staker_rewards, "staker_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->voter_rewards, "voter_rewards", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, &self->new_credits_observed, "new_credits_observed", FD_FLAMENCO_TYPE_ULONG, "ulong", level ); + fun( w, self, name, FD_FLAMENCO_TYPE_MAP_END, "fd_calculated_stake_rewards", level-- ); +} +ulong fd_calculated_stake_rewards_size( fd_calculated_stake_rewards_t const * self ) { + ulong size = 0; + size += sizeof(ulong); + size += sizeof(ulong); + size += sizeof(ulong); + return size; +} + #define REDBLK_T fd_hash_hash_age_pair_t_mapnode_t #define REDBLK_NAME fd_hash_hash_age_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 790112747b..105555aba0 100644 --- a/src/flamenco/types/fd_types.h +++ b/src/flamenco/types/fd_types.h @@ -39,34 +39,6 @@ typedef struct fd_fee_calculator_off fd_fee_calculator_off_t; #define FD_FEE_CALCULATOR_OFF_FOOTPRINT sizeof(fd_fee_calculator_off_t) #define FD_FEE_CALCULATOR_OFF_ALIGN (8UL) -/* https://github.com/anza-xyz/agave/blob/7fcd8c03fd71418fe49459c8460ab3986a8b608a/sdk/program/src/epoch_rewards.rs#L12 */ -/* Encoded Size: Dynamic */ -struct __attribute__((aligned(8UL))) fd_epoch_rewards { - ulong distribution_starting_block_height; - ulong num_partitions; - fd_hash_t parent_blockhash; - uint128 total_points; - ulong total_rewards; - ulong distributed_rewards; - uchar active; -}; -typedef struct fd_epoch_rewards fd_epoch_rewards_t; -#define FD_EPOCH_REWARDS_FOOTPRINT sizeof(fd_epoch_rewards_t) -#define FD_EPOCH_REWARDS_ALIGN (8UL) - -struct __attribute__((aligned(8UL))) fd_epoch_rewards_off { - uint distribution_starting_block_height_off; - uint num_partitions_off; - uint parent_blockhash_off; - uint total_points_off; - uint total_rewards_off; - uint distributed_rewards_off; - uint active_off; -}; -typedef struct fd_epoch_rewards_off fd_epoch_rewards_off_t; -#define FD_EPOCH_REWARDS_OFF_FOOTPRINT sizeof(fd_epoch_rewards_off_t) -#define FD_EPOCH_REWARDS_OFF_ALIGN (8UL) - /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_hash_age { fd_fee_calculator_t fee_calculator; @@ -1159,7 +1131,7 @@ union fd_reward_type_inner { }; typedef union fd_reward_type_inner fd_reward_type_inner_t; -/* https://github.com/firedancer-io/solana/blob/de02601d73d626edf98ef63efd772824746f2f33/sdk/src/reward_type.rs#L5-L11 */ +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/reward_type.rs#L7 */ struct fd_reward_type { uint discriminant; fd_reward_type_inner_t inner; @@ -1168,95 +1140,6 @@ typedef struct fd_reward_type fd_reward_type_t; #define FD_REWARD_TYPE_FOOTPRINT sizeof(fd_reward_type_t) #define FD_REWARD_TYPE_ALIGN (8UL) -/* Encoded Size: Dynamic */ -struct __attribute__((aligned(8UL))) fd_reward_info { - fd_reward_type_t reward_type; - ulong lamports; - ulong staker_rewards; - ulong new_credits_observed; - ulong post_balance; - long commission; -}; -typedef struct fd_reward_info fd_reward_info_t; -#define FD_REWARD_INFO_FOOTPRINT sizeof(fd_reward_info_t) -#define FD_REWARD_INFO_ALIGN (8UL) - -struct __attribute__((aligned(8UL))) fd_reward_info_off { - uint reward_type_off; - uint lamports_off; - uint staker_rewards_off; - uint new_credits_observed_off; - uint post_balance_off; - uint commission_off; -}; -typedef struct fd_reward_info_off fd_reward_info_off_t; -#define FD_REWARD_INFO_OFF_FOOTPRINT sizeof(fd_reward_info_off_t) -#define FD_REWARD_INFO_OFF_ALIGN (8UL) - -/* Encoded Size: Dynamic */ -struct __attribute__((aligned(8UL))) fd_stake_reward { - fd_pubkey_t stake_pubkey; - fd_reward_info_t reward_info; -}; -typedef struct fd_stake_reward fd_stake_reward_t; -#define FD_STAKE_REWARD_FOOTPRINT sizeof(fd_stake_reward_t) -#define FD_STAKE_REWARD_ALIGN (8UL) - -struct __attribute__((aligned(8UL))) fd_stake_reward_off { - uint stake_pubkey_off; - uint reward_info_off; -}; -typedef struct fd_stake_reward_off fd_stake_reward_off_t; -#define FD_STAKE_REWARD_OFF_FOOTPRINT sizeof(fd_stake_reward_off_t) -#define FD_STAKE_REWARD_OFF_ALIGN (8UL) - -/* Encoded Size: Dynamic */ -struct __attribute__((aligned(8UL))) fd_serializable_stake_rewards { - ulong body_len; - fd_stake_reward_t * body; -}; -typedef struct fd_serializable_stake_rewards fd_serializable_stake_rewards_t; -#define FD_SERIALIZABLE_STAKE_REWARDS_FOOTPRINT sizeof(fd_serializable_stake_rewards_t) -#define FD_SERIALIZABLE_STAKE_REWARDS_ALIGN (8UL) - -struct __attribute__((aligned(8UL))) fd_serializable_stake_rewards_off { - uint body_off; -}; -typedef struct fd_serializable_stake_rewards_off fd_serializable_stake_rewards_off_t; -#define FD_SERIALIZABLE_STAKE_REWARDS_OFF_FOOTPRINT sizeof(fd_serializable_stake_rewards_off_t) -#define FD_SERIALIZABLE_STAKE_REWARDS_OFF_ALIGN (8UL) - -/* Encoded Size: Dynamic */ -struct __attribute__((aligned(8UL))) fd_start_block_height_and_rewards { - ulong start_block_height; - ulong stake_rewards_by_partition_len; - fd_serializable_stake_rewards_t * stake_rewards_by_partition; -}; -typedef struct fd_start_block_height_and_rewards fd_start_block_height_and_rewards_t; -#define FD_START_BLOCK_HEIGHT_AND_REWARDS_FOOTPRINT sizeof(fd_start_block_height_and_rewards_t) -#define FD_START_BLOCK_HEIGHT_AND_REWARDS_ALIGN (8UL) - -struct __attribute__((aligned(8UL))) fd_start_block_height_and_rewards_off { - uint start_block_height_off; - uint stake_rewards_by_partition_off; -}; -typedef struct fd_start_block_height_and_rewards_off fd_start_block_height_and_rewards_off_t; -#define FD_START_BLOCK_HEIGHT_AND_REWARDS_OFF_FOOTPRINT sizeof(fd_start_block_height_and_rewards_off_t) -#define FD_START_BLOCK_HEIGHT_AND_REWARDS_OFF_ALIGN (8UL) - -union fd_serializable_epoch_reward_status_inner { - fd_start_block_height_and_rewards_t Active; -}; -typedef union fd_serializable_epoch_reward_status_inner fd_serializable_epoch_reward_status_inner_t; - -struct fd_serializable_epoch_reward_status { - uint discriminant; - fd_serializable_epoch_reward_status_inner_t inner; -}; -typedef struct fd_serializable_epoch_reward_status fd_serializable_epoch_reward_status_t; -#define FD_SERIALIZABLE_EPOCH_REWARD_STATUS_FOOTPRINT sizeof(fd_serializable_epoch_reward_status_t) -#define FD_SERIALIZABLE_EPOCH_REWARD_STATUS_ALIGN (8UL) - /* Accounts DB related fields in a snapshot */ /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_solana_accounts_db_fields { @@ -1339,6 +1222,28 @@ typedef struct fd_versioned_epoch_stakes_pair_off fd_versioned_epoch_stakes_pair #define FD_VERSIONED_EPOCH_STAKES_PAIR_OFF_FOOTPRINT sizeof(fd_versioned_epoch_stakes_pair_off_t) #define FD_VERSIONED_EPOCH_STAKES_PAIR_OFF_ALIGN (8UL) +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/reward_info.rs#L5 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_reward_info { + fd_reward_type_t reward_type; + ulong lamports; + ulong post_balance; + ulong commission; +}; +typedef struct fd_reward_info fd_reward_info_t; +#define FD_REWARD_INFO_FOOTPRINT sizeof(fd_reward_info_t) +#define FD_REWARD_INFO_ALIGN (8UL) + +struct __attribute__((aligned(8UL))) fd_reward_info_off { + uint reward_type_off; + uint lamports_off; + uint post_balance_off; + uint commission_off; +}; +typedef struct fd_reward_info_off fd_reward_info_off_t; +#define FD_REWARD_INFO_OFF_FOOTPRINT sizeof(fd_reward_info_off_t) +#define FD_REWARD_INFO_OFF_ALIGN (8UL) + /* Encoded Size: Dynamic */ struct __attribute__((aligned(16UL))) fd_solana_manifest { fd_deserializable_versioned_bank_t bank; @@ -2318,17 +2223,29 @@ typedef struct fd_sysvar_fees_off fd_sysvar_fees_off_t; #define FD_SYSVAR_FEES_OFF_FOOTPRINT sizeof(fd_sysvar_fees_off_t) #define FD_SYSVAR_FEES_OFF_ALIGN (8UL) -/* https://github.com/solana-labs/solana/blob/a02aebaa4b3aa0b24e13644cf0ffa5ae8bd47e7b/sdk/program/src/sysvar/epoch_rewards.rs */ +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/sdk/program/src/epoch_rewards.rs#L14 */ /* Encoded Size: Dynamic */ struct __attribute__((aligned(8UL))) fd_sysvar_epoch_rewards { - fd_epoch_rewards_t epoch_rewards; + ulong distribution_starting_block_height; + ulong num_partitions; + fd_hash_t parent_blockhash; + uint128 total_points; + ulong total_rewards; + ulong distributed_rewards; + uchar active; }; typedef struct fd_sysvar_epoch_rewards fd_sysvar_epoch_rewards_t; #define FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT sizeof(fd_sysvar_epoch_rewards_t) #define FD_SYSVAR_EPOCH_REWARDS_ALIGN (8UL) struct __attribute__((aligned(8UL))) fd_sysvar_epoch_rewards_off { - uint epoch_rewards_off; + uint distribution_starting_block_height_off; + uint num_partitions_off; + uint parent_blockhash_off; + uint total_points_off; + uint total_rewards_off; + uint distributed_rewards_off; + uint active_off; }; typedef struct fd_sysvar_epoch_rewards_off fd_sysvar_epoch_rewards_off_t; #define FD_SYSVAR_EPOCH_REWARDS_OFF_FOOTPRINT sizeof(fd_sysvar_epoch_rewards_off_t) @@ -4666,6 +4583,97 @@ typedef struct fd_bank_slot_deltas_off fd_bank_slot_deltas_off_t; #define FD_BANK_SLOT_DELTAS_OFF_FOOTPRINT sizeof(fd_bank_slot_deltas_off_t) #define FD_BANK_SLOT_DELTAS_OFF_ALIGN (8UL) +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L85 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_pubkey_rewardinfo_pair { + fd_pubkey_t pubkey; + fd_reward_info_t reward_info; +}; +typedef struct fd_pubkey_rewardinfo_pair fd_pubkey_rewardinfo_pair_t; +#define FD_PUBKEY_REWARDINFO_PAIR_FOOTPRINT sizeof(fd_pubkey_rewardinfo_pair_t) +#define FD_PUBKEY_REWARDINFO_PAIR_ALIGN (8UL) + +struct __attribute__((aligned(8UL))) fd_pubkey_rewardinfo_pair_off { + uint pubkey_off; + uint reward_info_off; +}; +typedef struct fd_pubkey_rewardinfo_pair_off fd_pubkey_rewardinfo_pair_off_t; +#define FD_PUBKEY_REWARDINFO_PAIR_OFF_FOOTPRINT sizeof(fd_pubkey_rewardinfo_pair_off_t) +#define FD_PUBKEY_REWARDINFO_PAIR_OFF_ALIGN (8UL) + +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_optional_account { + fd_solana_account_t * account; +}; +typedef struct fd_optional_account fd_optional_account_t; +#define FD_OPTIONAL_ACCOUNT_FOOTPRINT sizeof(fd_optional_account_t) +#define FD_OPTIONAL_ACCOUNT_ALIGN (8UL) + +struct __attribute__((aligned(8UL))) fd_optional_account_off { + uint account_off; +}; +typedef struct fd_optional_account_off fd_optional_account_off_t; +#define FD_OPTIONAL_ACCOUNT_OFF_FOOTPRINT sizeof(fd_optional_account_off_t) +#define FD_OPTIONAL_ACCOUNT_OFF_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L27 */ +/* Encoded Size: Fixed (25 bytes) */ +struct __attribute__((aligned(8UL))) fd_calculated_stake_points { + uint128 points; + ulong new_credits_observed; + uchar force_credits_update_with_skipped_reward; +}; +typedef struct fd_calculated_stake_points fd_calculated_stake_points_t; +#define FD_CALCULATED_STAKE_POINTS_FOOTPRINT sizeof(fd_calculated_stake_points_t) +#define FD_CALCULATED_STAKE_POINTS_ALIGN (8UL) + +struct __attribute__((aligned(8UL))) fd_calculated_stake_points_off { + uint points_off; + uint new_credits_observed_off; + uint force_credits_update_with_skipped_reward_off; +}; +typedef struct fd_calculated_stake_points_off fd_calculated_stake_points_off_t; +#define FD_CALCULATED_STAKE_POINTS_OFF_FOOTPRINT sizeof(fd_calculated_stake_points_off_t) +#define FD_CALCULATED_STAKE_POINTS_OFF_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L21 */ +/* Encoded Size: Fixed (24 bytes) */ +struct __attribute__((aligned(8UL))) fd_point_value { + ulong rewards; + uint128 points; +}; +typedef struct fd_point_value fd_point_value_t; +#define FD_POINT_VALUE_FOOTPRINT sizeof(fd_point_value_t) +#define FD_POINT_VALUE_ALIGN (8UL) + +struct __attribute__((aligned(8UL))) fd_point_value_off { + uint rewards_off; + uint points_off; +}; +typedef struct fd_point_value_off fd_point_value_off_t; +#define FD_POINT_VALUE_OFF_FOOTPRINT sizeof(fd_point_value_off_t) +#define FD_POINT_VALUE_OFF_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/rewards.rs#L24 */ +/* Encoded Size: Fixed (24 bytes) */ +struct __attribute__((aligned(8UL))) fd_calculated_stake_rewards { + ulong staker_rewards; + ulong voter_rewards; + ulong new_credits_observed; +}; +typedef struct fd_calculated_stake_rewards fd_calculated_stake_rewards_t; +#define FD_CALCULATED_STAKE_REWARDS_FOOTPRINT sizeof(fd_calculated_stake_rewards_t) +#define FD_CALCULATED_STAKE_REWARDS_ALIGN (8UL) + +struct __attribute__((aligned(8UL))) fd_calculated_stake_rewards_off { + uint staker_rewards_off; + uint voter_rewards_off; + uint new_credits_observed_off; +}; +typedef struct fd_calculated_stake_rewards_off fd_calculated_stake_rewards_off_t; +#define FD_CALCULATED_STAKE_REWARDS_OFF_FOOTPRINT sizeof(fd_calculated_stake_rewards_off_t) +#define FD_CALCULATED_STAKE_REWARDS_OFF_ALIGN (8UL) + FD_PROTOTYPES_BEGIN @@ -4741,18 +4749,6 @@ int fd_fee_calculator_decode_archival_preflight( fd_bincode_decode_ctx_t * ctx ) void fd_fee_calculator_decode_archival_unsafe( fd_fee_calculator_t * self, fd_bincode_decode_ctx_t * ctx ); int fd_fee_calculator_encode_archival( fd_fee_calculator_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_epoch_rewards_new( fd_epoch_rewards_t * self ); -int fd_epoch_rewards_decode( fd_epoch_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_epoch_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ); -void fd_epoch_rewards_decode_unsafe( fd_epoch_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_epoch_rewards_decode_offsets( fd_epoch_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_epoch_rewards_encode( fd_epoch_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_epoch_rewards_destroy( fd_epoch_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ); -void fd_epoch_rewards_walk( void * w, fd_epoch_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); -ulong fd_epoch_rewards_size( fd_epoch_rewards_t const * self ); -ulong fd_epoch_rewards_footprint( void ); -ulong fd_epoch_rewards_align( void ); - void fd_hash_age_new( fd_hash_age_t * self ); int fd_hash_age_decode( fd_hash_age_t * self, fd_bincode_decode_ctx_t * ctx ); int fd_hash_age_decode_preflight( fd_bincode_decode_ctx_t * ctx ); @@ -5319,72 +5315,6 @@ fd_reward_type_enum_rent = 1, fd_reward_type_enum_staking = 2, fd_reward_type_enum_voting = 3, }; -void fd_reward_info_new( fd_reward_info_t * self ); -int fd_reward_info_decode( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_reward_info_decode_preflight( fd_bincode_decode_ctx_t * ctx ); -void fd_reward_info_decode_unsafe( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_reward_info_decode_offsets( fd_reward_info_off_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_reward_info_encode( fd_reward_info_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_reward_info_destroy( fd_reward_info_t * self, fd_bincode_destroy_ctx_t * ctx ); -void fd_reward_info_walk( void * w, fd_reward_info_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); -ulong fd_reward_info_size( fd_reward_info_t const * self ); -ulong fd_reward_info_footprint( void ); -ulong fd_reward_info_align( void ); - -void fd_stake_reward_new( fd_stake_reward_t * self ); -int fd_stake_reward_decode( fd_stake_reward_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_stake_reward_decode_preflight( fd_bincode_decode_ctx_t * ctx ); -void fd_stake_reward_decode_unsafe( fd_stake_reward_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_stake_reward_decode_offsets( fd_stake_reward_off_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_stake_reward_encode( fd_stake_reward_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_stake_reward_destroy( fd_stake_reward_t * self, fd_bincode_destroy_ctx_t * ctx ); -void fd_stake_reward_walk( void * w, fd_stake_reward_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); -ulong fd_stake_reward_size( fd_stake_reward_t const * self ); -ulong fd_stake_reward_footprint( void ); -ulong fd_stake_reward_align( void ); - -void fd_serializable_stake_rewards_new( fd_serializable_stake_rewards_t * self ); -int fd_serializable_stake_rewards_decode( fd_serializable_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_serializable_stake_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ); -void fd_serializable_stake_rewards_decode_unsafe( fd_serializable_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_serializable_stake_rewards_decode_offsets( fd_serializable_stake_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_serializable_stake_rewards_encode( fd_serializable_stake_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_serializable_stake_rewards_destroy( fd_serializable_stake_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ); -void fd_serializable_stake_rewards_walk( void * w, fd_serializable_stake_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); -ulong fd_serializable_stake_rewards_size( fd_serializable_stake_rewards_t const * self ); -ulong fd_serializable_stake_rewards_footprint( void ); -ulong fd_serializable_stake_rewards_align( void ); - -void fd_start_block_height_and_rewards_new( fd_start_block_height_and_rewards_t * self ); -int fd_start_block_height_and_rewards_decode( fd_start_block_height_and_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_start_block_height_and_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ); -void fd_start_block_height_and_rewards_decode_unsafe( fd_start_block_height_and_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_start_block_height_and_rewards_decode_offsets( fd_start_block_height_and_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_start_block_height_and_rewards_encode( fd_start_block_height_and_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_start_block_height_and_rewards_destroy( fd_start_block_height_and_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ); -void fd_start_block_height_and_rewards_walk( void * w, fd_start_block_height_and_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); -ulong fd_start_block_height_and_rewards_size( fd_start_block_height_and_rewards_t const * self ); -ulong fd_start_block_height_and_rewards_footprint( void ); -ulong fd_start_block_height_and_rewards_align( void ); - -void fd_serializable_epoch_reward_status_new_disc( fd_serializable_epoch_reward_status_t * self, uint discriminant ); -void fd_serializable_epoch_reward_status_new( fd_serializable_epoch_reward_status_t * self ); -int fd_serializable_epoch_reward_status_decode( fd_serializable_epoch_reward_status_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_serializable_epoch_reward_status_decode_preflight( fd_bincode_decode_ctx_t * ctx ); -void fd_serializable_epoch_reward_status_decode_unsafe( fd_serializable_epoch_reward_status_t * self, fd_bincode_decode_ctx_t * ctx ); -int fd_serializable_epoch_reward_status_encode( fd_serializable_epoch_reward_status_t const * self, fd_bincode_encode_ctx_t * ctx ); -void fd_serializable_epoch_reward_status_destroy( fd_serializable_epoch_reward_status_t * self, fd_bincode_destroy_ctx_t * ctx ); -void fd_serializable_epoch_reward_status_walk( void * w, fd_serializable_epoch_reward_status_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); -ulong fd_serializable_epoch_reward_status_size( fd_serializable_epoch_reward_status_t const * self ); -ulong fd_serializable_epoch_reward_status_footprint( void ); -ulong fd_serializable_epoch_reward_status_align( void ); - -FD_FN_PURE uchar fd_serializable_epoch_reward_status_is_Active( fd_serializable_epoch_reward_status_t const * self ); -FD_FN_PURE uchar fd_serializable_epoch_reward_status_is_Inactive( fd_serializable_epoch_reward_status_t const * self ); -enum { -fd_serializable_epoch_reward_status_enum_Active = 0, -fd_serializable_epoch_reward_status_enum_Inactive = 1, -}; void fd_solana_accounts_db_fields_new( fd_solana_accounts_db_fields_t * self ); int fd_solana_accounts_db_fields_decode( fd_solana_accounts_db_fields_t * self, fd_bincode_decode_ctx_t * ctx ); int fd_solana_accounts_db_fields_decode_preflight( fd_bincode_decode_ctx_t * ctx ); @@ -5437,6 +5367,18 @@ ulong fd_versioned_epoch_stakes_pair_size( fd_versioned_epoch_stakes_pair_t cons ulong fd_versioned_epoch_stakes_pair_footprint( void ); ulong fd_versioned_epoch_stakes_pair_align( void ); +void fd_reward_info_new( fd_reward_info_t * self ); +int fd_reward_info_decode( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_reward_info_decode_preflight( fd_bincode_decode_ctx_t * ctx ); +void fd_reward_info_decode_unsafe( fd_reward_info_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_reward_info_decode_offsets( fd_reward_info_off_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_reward_info_encode( fd_reward_info_t const * self, fd_bincode_encode_ctx_t * ctx ); +void fd_reward_info_destroy( fd_reward_info_t * self, fd_bincode_destroy_ctx_t * ctx ); +void fd_reward_info_walk( void * w, fd_reward_info_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); +ulong fd_reward_info_size( fd_reward_info_t const * self ); +ulong fd_reward_info_footprint( void ); +ulong fd_reward_info_align( void ); + void fd_solana_manifest_new( fd_solana_manifest_t * self ); int fd_solana_manifest_decode( fd_solana_manifest_t * self, fd_bincode_decode_ctx_t * ctx ); int fd_solana_manifest_decode_preflight( fd_bincode_decode_ctx_t * ctx ); @@ -7876,6 +7818,66 @@ ulong fd_bank_slot_deltas_size( fd_bank_slot_deltas_t const * self ); ulong fd_bank_slot_deltas_footprint( void ); ulong fd_bank_slot_deltas_align( void ); +void fd_pubkey_rewardinfo_pair_new( fd_pubkey_rewardinfo_pair_t * self ); +int fd_pubkey_rewardinfo_pair_decode( fd_pubkey_rewardinfo_pair_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_pubkey_rewardinfo_pair_decode_preflight( fd_bincode_decode_ctx_t * ctx ); +void fd_pubkey_rewardinfo_pair_decode_unsafe( fd_pubkey_rewardinfo_pair_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_pubkey_rewardinfo_pair_decode_offsets( fd_pubkey_rewardinfo_pair_off_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_pubkey_rewardinfo_pair_encode( fd_pubkey_rewardinfo_pair_t const * self, fd_bincode_encode_ctx_t * ctx ); +void fd_pubkey_rewardinfo_pair_destroy( fd_pubkey_rewardinfo_pair_t * self, fd_bincode_destroy_ctx_t * ctx ); +void fd_pubkey_rewardinfo_pair_walk( void * w, fd_pubkey_rewardinfo_pair_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); +ulong fd_pubkey_rewardinfo_pair_size( fd_pubkey_rewardinfo_pair_t const * self ); +ulong fd_pubkey_rewardinfo_pair_footprint( void ); +ulong fd_pubkey_rewardinfo_pair_align( void ); + +void fd_optional_account_new( fd_optional_account_t * self ); +int fd_optional_account_decode( fd_optional_account_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_optional_account_decode_preflight( fd_bincode_decode_ctx_t * ctx ); +void fd_optional_account_decode_unsafe( fd_optional_account_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_optional_account_decode_offsets( fd_optional_account_off_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_optional_account_encode( fd_optional_account_t const * self, fd_bincode_encode_ctx_t * ctx ); +void fd_optional_account_destroy( fd_optional_account_t * self, fd_bincode_destroy_ctx_t * ctx ); +void fd_optional_account_walk( void * w, fd_optional_account_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); +ulong fd_optional_account_size( fd_optional_account_t const * self ); +ulong fd_optional_account_footprint( void ); +ulong fd_optional_account_align( void ); + +void fd_calculated_stake_points_new( fd_calculated_stake_points_t * self ); +int fd_calculated_stake_points_decode( fd_calculated_stake_points_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_calculated_stake_points_decode_preflight( fd_bincode_decode_ctx_t * ctx ); +void fd_calculated_stake_points_decode_unsafe( fd_calculated_stake_points_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_calculated_stake_points_decode_offsets( fd_calculated_stake_points_off_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_calculated_stake_points_encode( fd_calculated_stake_points_t const * self, fd_bincode_encode_ctx_t * ctx ); +void fd_calculated_stake_points_destroy( fd_calculated_stake_points_t * self, fd_bincode_destroy_ctx_t * ctx ); +void fd_calculated_stake_points_walk( void * w, fd_calculated_stake_points_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); +ulong fd_calculated_stake_points_size( fd_calculated_stake_points_t const * self ); +ulong fd_calculated_stake_points_footprint( void ); +ulong fd_calculated_stake_points_align( void ); + +void fd_point_value_new( fd_point_value_t * self ); +int fd_point_value_decode( fd_point_value_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_point_value_decode_preflight( fd_bincode_decode_ctx_t * ctx ); +void fd_point_value_decode_unsafe( fd_point_value_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_point_value_decode_offsets( fd_point_value_off_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_point_value_encode( fd_point_value_t const * self, fd_bincode_encode_ctx_t * ctx ); +void fd_point_value_destroy( fd_point_value_t * self, fd_bincode_destroy_ctx_t * ctx ); +void fd_point_value_walk( void * w, fd_point_value_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); +ulong fd_point_value_size( fd_point_value_t const * self ); +ulong fd_point_value_footprint( void ); +ulong fd_point_value_align( void ); + +void fd_calculated_stake_rewards_new( fd_calculated_stake_rewards_t * self ); +int fd_calculated_stake_rewards_decode( fd_calculated_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_calculated_stake_rewards_decode_preflight( fd_bincode_decode_ctx_t * ctx ); +void fd_calculated_stake_rewards_decode_unsafe( fd_calculated_stake_rewards_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_calculated_stake_rewards_decode_offsets( fd_calculated_stake_rewards_off_t * self, fd_bincode_decode_ctx_t * ctx ); +int fd_calculated_stake_rewards_encode( fd_calculated_stake_rewards_t const * self, fd_bincode_encode_ctx_t * ctx ); +void fd_calculated_stake_rewards_destroy( fd_calculated_stake_rewards_t * self, fd_bincode_destroy_ctx_t * ctx ); +void fd_calculated_stake_rewards_walk( void * w, fd_calculated_stake_rewards_t const * self, fd_types_walk_fn_t fun, const char *name, uint level ); +ulong fd_calculated_stake_rewards_size( fd_calculated_stake_rewards_t const * self ); +ulong fd_calculated_stake_rewards_footprint( void ); +ulong fd_calculated_stake_rewards_align( void ); + FD_PROTOTYPES_END #endif // HEADER_FD_RUNTIME_TYPES diff --git a/src/flamenco/types/fd_types.json b/src/flamenco/types/fd_types.json index 7ded8aeb22..911ccff432 100644 --- a/src/flamenco/types/fd_types.json +++ b/src/flamenco/types/fd_types.json @@ -50,20 +50,6 @@ ], "comment": "https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/fee_calculator.rs#L9" }, - { - "name": "epoch_rewards", - "type": "struct", - "fields": [ - { "name": "distribution_starting_block_height", "type": "ulong"}, - { "name": "num_partitions", "type": "ulong" }, - { "name": "parent_blockhash", "type": "hash" }, - { "name": "total_points", "type": "uint128" }, - { "name": "total_rewards", "type": "ulong" }, - { "name": "distributed_rewards", "type": "ulong" }, - { "name": "active", "type": "bool" } - ], - "comment": "https://github.com/anza-xyz/agave/blob/7fcd8c03fd71418fe49459c8460ab3986a8b608a/sdk/program/src/epoch_rewards.rs#L12" - }, { "name": "hash_age", "type": "struct", @@ -521,50 +507,7 @@ { "name": "staking" }, { "name": "voting" } ], - "comment": "https://github.com/firedancer-io/solana/blob/de02601d73d626edf98ef63efd772824746f2f33/sdk/src/reward_type.rs#L5-L11" - }, - { - "name": "reward_info", - "type": "struct", - "fields": [ - { "name": "reward_type", "type": "reward_type" }, - { "name": "lamports", "type": "ulong" }, - { "name": "staker_rewards", "type": "ulong" }, - { "name": "new_credits_observed", "type": "ulong" }, - { "name": "post_balance", "type": "ulong" }, - { "name": "commission", "type": "long" } - ] - }, - { - "name": "stake_reward", - "type": "struct", - "fields": [ - { "name": "stake_pubkey", "type": "pubkey" }, - { "name": "reward_info", "type": "reward_info" } - ] - }, - { - "name": "serializable_stake_rewards", - "type": "struct", - "fields": [ - { "name": "body", "type": "vector", "element": "stake_reward" } - ] - }, - { - "name": "start_block_height_and_rewards", - "type": "struct", - "fields": [ - { "name": "start_block_height", "type": "ulong" }, - { "name": "stake_rewards_by_partition", "type": "vector", "element": "serializable_stake_rewards" } - ] - }, - { - "name": "serializable_epoch_reward_status", - "type": "enum", - "variants": [ - { "name": "Active", "type": "start_block_height_and_rewards"}, - { "name": "Inactive"} - ] + "comment": "https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/reward_type.rs#L7" }, { "name": "solana_accounts_db_fields", @@ -604,6 +547,17 @@ { "name": "val", "type": "versioned_epoch_stakes" } ] }, + { + "name": "reward_info", + "type": "struct", + "fields": [ + { "name": "reward_type", "type": "reward_type" }, + { "name": "lamports", "type": "ulong" }, + { "name": "post_balance", "type": "ulong" }, + { "name": "commission", "type": "ulong" } + ], + "comment": "https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/sdk/src/reward_info.rs#L5" + }, { "name": "solana_manifest", "type": "struct", @@ -1016,9 +970,15 @@ "name": "sysvar_epoch_rewards", "type": "struct", "fields": [ - { "name": "epoch_rewards", "type": "epoch_rewards" } + { "name": "distribution_starting_block_height", "type": "ulong" }, + { "name": "num_partitions", "type": "ulong" }, + { "name": "parent_blockhash", "type": "hash" }, + { "name": "total_points", "type": "uint128" }, + { "name": "total_rewards", "type": "ulong" }, + { "name": "distributed_rewards", "type": "ulong" }, + { "name": "active", "type": "uchar" } ], - "comment": "https://github.com/solana-labs/solana/blob/a02aebaa4b3aa0b24e13644cf0ffa5ae8bd47e7b/sdk/program/src/sysvar/epoch_rewards.rs" + "comment": "https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/sdk/program/src/epoch_rewards.rs#L14" }, { "name": "config_keys_pair", @@ -2532,6 +2492,51 @@ "fields": [ { "name": "slot_deltas", "type": "vector", "element": "slot_delta" } ] + }, + { + "name": "pubkey_rewardinfo_pair", + "type": "struct", + "fields": [ + { "name": "pubkey", "type": "pubkey" }, + { "name": "reward_info", "type": "reward_info" } + ], + "comment": "https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L85" + }, + { + "name": "optional_account", + "type": "struct", + "fields": [ + { "name": "account", "type": "option", "element": "solana_account" } + ] + }, + { + "name": "calculated_stake_points", + "type": "struct", + "fields": [ + { "name": "points", "type": "uint128" }, + { "name": "new_credits_observed", "type": "ulong" }, + { "name": "force_credits_update_with_skipped_reward", "type": "uchar" } + ], + "comment": "https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L27" + }, + { + "name": "point_value", + "type": "struct", + "fields": [ + { "name": "rewards", "type": "ulong" }, + { "name": "points", "type": "uint128" } + ], + "comment": "https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L21" + }, + { + "name": "calculated_stake_rewards", + "type": "struct", + "fields": [ + { "name": "staker_rewards", "type": "ulong" }, + { "name": "voter_rewards", "type": "ulong" }, + { "name": "new_credits_observed", "type": "ulong" } + ], + "comment": "https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/rewards.rs#L24" } ] } diff --git a/src/flamenco/types/fd_types_custom.c b/src/flamenco/types/fd_types_custom.c index f8d703f2cd..b31c3ae30b 100644 --- a/src/flamenco/types/fd_types_custom.c +++ b/src/flamenco/types/fd_types_custom.c @@ -347,3 +347,13 @@ int fd_archive_decode_skip_field( fd_bincode_decode_ctx_t * ctx, ushort tag ) { ctx->data = ptr + len; return FD_BINCODE_SUCCESS; } + +#define REDBLK_T fd_vote_reward_t_mapnode_t +#define REDBLK_NAME fd_vote_reward_t_map +#define REDBLK_IMPL_STYLE 2 +#include "../../util/tmpl/fd_redblack.c" +#undef REDBLK_T +#undef REDBLK_NAME +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) ); +} diff --git a/src/flamenco/types/fd_types_custom.h b/src/flamenco/types/fd_types_custom.h index 446a2dead7..fcbe844b22 100644 --- a/src/flamenco/types/fd_types_custom.h +++ b/src/flamenco/types/fd_types_custom.h @@ -210,6 +210,179 @@ fd_flamenco_txn_walk( void * w, /* Represents the lamport balance associated with an account. */ typedef ulong fd_acc_lamports_t; +#if FD_HAS_INT128 +/********************* Rewards types **************************************************/ +/* TODO: move these into fd_types, but first we need to add dlist support to fd_types */ +struct __attribute__((aligned(8UL))) fd_stake_reward { + /* dlist */ + ulong prev; + ulong next; + /* pool */ + ulong parent; + /* data */ + fd_pubkey_t stake_pubkey; + ulong credits_observed; + ulong lamports; +}; +typedef struct fd_stake_reward fd_stake_reward_t; +#define FD_STAKE_REWARD_FOOTPRINT sizeof(fd_stake_reward_t) +#define FD_STAKE_REWARD_ALIGN (8UL) + +/* Encoded Size: Fixed (42 bytes) */ +struct __attribute__((aligned(8UL))) fd_vote_reward { + fd_pubkey_t pubkey; + ulong vote_rewards; + uchar commission; + uchar needs_store; +}; +typedef struct fd_vote_reward fd_vote_reward_t; +#define FD_VOTE_REWARD_FOOTPRINT sizeof(fd_vote_reward_t) +#define FD_VOTE_REWARD_ALIGN (8UL) + +#define DLIST_NAME fd_stake_reward_dlist +#define DLIST_ELE_T fd_stake_reward_t +#include "../../util/tmpl/fd_dlist.c" +#undef DLIST_NAME +#undef DLIST_ELE_T + +#define POOL_NAME fd_stake_reward_pool +#define POOL_T fd_stake_reward_t +#define POOL_NEXT parent +#include "../../util/tmpl/fd_pool.c" +#undef POOL_NAME +#undef POOL_T +#undef POOL_NEXT + +typedef struct fd_vote_reward_t_mapnode fd_vote_reward_t_mapnode_t; +#define REDBLK_T fd_vote_reward_t_mapnode_t +#define REDBLK_NAME fd_vote_reward_t_map +#define REDBLK_IMPL_STYLE 1 +#include "../../util/tmpl/fd_redblack.c" +#undef REDBLK_T +#undef REDBLK_NAME +struct fd_vote_reward_t_mapnode { + fd_vote_reward_t elem; + ulong redblack_parent; + ulong redblack_left; + ulong redblack_right; + int redblack_color; +}; + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L56 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_partitioned_stake_rewards { + ulong partitions_len; + fd_stake_reward_dlist_t * partitions; + fd_stake_reward_t * pool; +}; +typedef struct fd_partitioned_stake_rewards fd_partitioned_stake_rewards_t; +#define FD_PARTITIONED_STAKE_REWARDS_FOOTPRINT sizeof(fd_partitioned_stake_rewards_t) +#define FD_PARTITIONED_STAKE_REWARDS_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L131 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_stake_reward_calculation_partitioned { + fd_partitioned_stake_rewards_t partitioned_stake_rewards; + ulong total_stake_rewards_lamports; +}; +typedef struct fd_stake_reward_calculation_partitioned fd_stake_reward_calculation_partitioned_t; +#define FD_STAKE_REWARD_CALCULATION_PARTITIONED_FOOTPRINT sizeof(fd_stake_reward_calculation_partitioned_t) +#define FD_STAKE_REWARD_CALCULATION_PARTITIONED_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L94 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_stake_reward_calculation { + fd_stake_reward_dlist_t stake_rewards; + ulong stake_rewards_len; + fd_stake_reward_t * pool; + ulong total_stake_rewards_lamports; +}; +typedef struct fd_stake_reward_calculation fd_stake_reward_calculation_t; +#define FD_STAKE_REWARD_CALCULATION_FOOTPRINT sizeof(fd_stake_reward_calculation_t) +#define FD_STAKE_REWARD_CALCULATION_ALIGN (8UL) + +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_calculate_stake_vote_rewards_result { + fd_stake_reward_calculation_t stake_reward_calculation; + fd_vote_reward_t_mapnode_t * vote_reward_map_pool; + fd_vote_reward_t_mapnode_t * vote_reward_map_root; +}; +typedef struct fd_calculate_stake_vote_rewards_result fd_calculate_stake_vote_rewards_result_t; +#define FD_CALCULATE_STAKE_VOTE_REWARDS_RESULT_FOOTPRINT sizeof(fd_calculate_stake_vote_rewards_result_t) +#define FD_CALCULATE_STAKE_VOTE_REWARDS_RESULT_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L102 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_calculate_validator_rewards_result { + fd_calculate_stake_vote_rewards_result_t calculate_stake_vote_rewards_result; + uint128 total_points; +}; +typedef struct fd_calculate_validator_rewards_result fd_calculate_validator_rewards_result_t; +#define FD_CALCULATE_VALIDATOR_REWARDS_RESULT_FOOTPRINT sizeof(fd_calculate_validator_rewards_result_t) +#define FD_CALCULATE_VALIDATOR_REWARDS_RESULT_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L138 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_calculate_rewards_and_distribute_vote_rewards_result { + ulong total_rewards; + ulong distributed_rewards; + uint128 total_points; + fd_stake_reward_calculation_partitioned_t stake_rewards_by_partition; +}; +typedef struct fd_calculate_rewards_and_distribute_vote_rewards_result fd_calculate_rewards_and_distribute_vote_rewards_result_t; +#define FD_CALCULATE_REWARDS_AND_DISTRIBUTE_VOTE_REWARDS_RESULT_FOOTPRINT sizeof(fd_calculate_rewards_and_distribute_vote_rewards_result_t) +#define FD_CALCULATE_REWARDS_AND_DISTRIBUTE_VOTE_REWARDS_RESULT_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L118 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_partitioned_rewards_calculation { + fd_vote_reward_t_mapnode_t * vote_reward_map_pool; + fd_vote_reward_t_mapnode_t * vote_reward_map_root; + fd_stake_reward_calculation_partitioned_t stake_rewards_by_partition; + ulong old_vote_balance_and_staked; + ulong validator_rewards; + double validator_rate; + double foundation_rate; + double prev_epoch_duration_in_years; + ulong capitalization; + uint128 total_points; +}; +typedef struct fd_partitioned_rewards_calculation fd_partitioned_rewards_calculation_t; +#define FD_PARTITIONED_REWARDS_CALCULATION_FOOTPRINT sizeof(fd_partitioned_rewards_calculation_t) +#define FD_PARTITIONED_REWARDS_CALCULATION_ALIGN (8UL) + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L60 */ +/* Encoded Size: Dynamic */ +struct __attribute__((aligned(8UL))) fd_start_block_height_and_rewards { + ulong distribution_starting_block_height; + fd_partitioned_stake_rewards_t partitioned_stake_rewards; +}; +typedef struct fd_start_block_height_and_rewards fd_start_block_height_and_rewards_t; +#define FD_START_BLOCK_HEIGHT_AND_REWARDS_FOOTPRINT sizeof(fd_start_block_height_and_rewards_t) +#define FD_START_BLOCK_HEIGHT_AND_REWARDS_ALIGN (8UL) + +union fd_epoch_reward_status_inner { + fd_start_block_height_and_rewards_t Active; +}; +typedef union fd_epoch_reward_status_inner fd_epoch_reward_status_inner_t; + +/* https://github.com/anza-xyz/agave/blob/7117ed9653ce19e8b2dea108eff1f3eb6a3378a7/runtime/src/bank/partitioned_epoch_rewards/mod.rs#L70 */ +struct fd_epoch_reward_status { + uint discriminant; + fd_epoch_reward_status_inner_t inner; +}; +typedef struct fd_epoch_reward_status fd_epoch_reward_status_t; +#define FD_EPOCH_REWARD_STATUS_FOOTPRINT sizeof(fd_epoch_reward_status_t) +#define FD_EPOCH_REWARD_STATUS_ALIGN (8UL) + +enum { +fd_epoch_reward_status_enum_Active = 0, +fd_epoch_reward_status_enum_Inactive = 1, +}; + +/*******************************************************************************************/ +#endif + FD_PROTOTYPES_END #endif /* HEADER_fd_src_flamenco_runtime_fd_types_custom */