Line data Source code
1 : #include "fd_vote_state_v3.h" 2 : #include "fd_vote_codec.h" 3 : #include "fd_authorized_voters.h" 4 : #include "fd_vote_utils.h" 5 : #include "fd_vote_state_versioned.h" 6 : #include "../fd_vote_program.h" 7 : #include "../../fd_runtime.h" 8 : 9 : /* to_vote_state_1_14_11 converts a "v3" vote state object into the 10 : older "v1.14.11" version. This destroys the "v3" object in the 11 : process. 12 : https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67 */ 13 : static void 14 : to_vote_state_1_14_11( fd_vote_state_v3_t * vote_state, 15 0 : fd_vote_state_1_14_11_t * vote_state_1_14_11 /* out */ ) { 16 0 : vote_state_1_14_11->node_pubkey = vote_state->node_pubkey; 17 0 : vote_state_1_14_11->authorized_withdrawer = vote_state->authorized_withdrawer; 18 0 : vote_state_1_14_11->commission = vote_state->commission; 19 0 : vote_state_1_14_11->votes = vote_state->votes; 20 0 : vote_state_1_14_11->has_root_slot = vote_state->has_root_slot; 21 0 : vote_state_1_14_11->root_slot = vote_state->root_slot; 22 0 : vote_state_1_14_11->authorized_voters = vote_state->authorized_voters; 23 0 : vote_state_1_14_11->prior_voters = vote_state->prior_voters; 24 0 : vote_state_1_14_11->epoch_credits = vote_state->epoch_credits; 25 0 : vote_state_1_14_11->last_timestamp = vote_state->last_timestamp; 26 0 : } 27 : 28 : void 29 : fd_vote_program_v3_create_new( fd_vote_init_t * const vote_init, 30 : fd_sol_sysvar_clock_t const * clock, 31 0 : fd_vote_state_versioned_t * versioned /* out */ ) { 32 0 : fd_vote_state_versioned_new( versioned, fd_vote_state_versioned_enum_v3 ); 33 : 34 0 : fd_vote_state_v3_t * vote_state = &versioned->v3; 35 0 : vote_state->node_pubkey = vote_init->node_pubkey; 36 0 : vote_state->authorized_withdrawer = vote_init->authorized_withdrawer; 37 0 : vote_state->commission = vote_init->commission; 38 0 : vote_state->prior_voters.idx = 31; 39 0 : vote_state->prior_voters.is_empty = 1; 40 : 41 : /* Insert the authorized voter */ 42 0 : fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( vote_state->authorized_voters.pool ); 43 0 : voter->epoch = clock->epoch; 44 0 : voter->pubkey = vote_init->authorized_voter; 45 0 : voter->prio = voter->pubkey.uc[0]; 46 0 : fd_vote_authorized_voters_treap_ele_insert( vote_state->authorized_voters.treap, voter, vote_state->authorized_voters.pool ); 47 0 : } 48 : 49 : int 50 : fd_vote_state_v3_set_vote_account_state( fd_exec_instr_ctx_t const * ctx, 51 : fd_borrowed_account_t * vote_account, 52 0 : fd_vote_state_versioned_t * versioned ) { 53 : /* This is a horrible conditional expression in Agave. 54 : The terms were broken up into their own variables. */ 55 0 : fd_vote_state_v3_t * v3_vote_state = &versioned->v3; 56 : 57 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L420-L424 */ 58 0 : fd_rent_t const * rent = &ctx->bank->f.rent; 59 0 : int resize_needed = fd_borrowed_account_get_data_len( vote_account ) < FD_VOTE_STATE_V3_SZ; 60 0 : int resize_rent_exempt = fd_rent_exempt_minimum_balance( rent, FD_VOTE_STATE_V3_SZ ) <= fd_borrowed_account_get_lamports( vote_account ); 61 : 62 : /* The resize operation itself is part of the horrible conditional, 63 : but behind a short-circuit operator. */ 64 0 : int resize_failed = 0; 65 0 : if( resize_needed && resize_rent_exempt ) { 66 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L422-L424 */ 67 0 : resize_failed = 68 0 : fd_borrowed_account_set_data_length( vote_account, FD_VOTE_STATE_V3_SZ ) != FD_EXECUTOR_INSTR_SUCCESS; 69 0 : } 70 : 71 0 : if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) { 72 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L426-L430 */ 73 0 : fd_vote_state_versioned_t vsv[1]; 74 0 : fd_vote_state_versioned_new( vsv, fd_vote_state_versioned_enum_v1_14_11 ); 75 0 : to_vote_state_1_14_11( v3_vote_state, &vsv->v1_14_11 ); 76 0 : return fd_vsv_set_state( vote_account, vsv ); 77 0 : } 78 : 79 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L432-L433 */ 80 0 : return fd_vsv_set_state( vote_account, versioned ); 81 0 : } 82 : 83 : int 84 : fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account, 85 0 : fd_vote_state_versioned_t * versioned ) { 86 : /* deserialize_into_ptr is essentially a call to get_state + 87 : try_convert_to_v3. It's written a little more verbosely in Agave 88 : as they try to optimize the decoding steps. 89 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L162-L202 */ 90 0 : int rc = fd_vsv_get_state( vote_account->acc, versioned ); 91 0 : if( FD_UNLIKELY( rc ) ) return rc; 92 : 93 : /* Unlike vote states v4 decoding, vote state v3 decoding will only 94 : pass for v1_14_11 and v3 vote states. 95 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.0.0/vote-interface/src/state/vote_state_v3.rs#L157-L164 */ 96 0 : if( FD_UNLIKELY( versioned->kind!=fd_vote_state_versioned_enum_v1_14_11 && 97 0 : versioned->kind!=fd_vote_state_versioned_enum_v3 ) ) { 98 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 99 0 : } 100 : 101 0 : return fd_vsv_try_convert_to_v3( versioned ); 102 0 : } 103 : 104 : int 105 : fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self, 106 : ulong current_epoch, 107 0 : fd_pubkey_t ** pubkey /* out */ ) { 108 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L832 109 0 : fd_vote_authorized_voter_t * authorized_voter = fd_authorized_voters_get_and_cache_authorized_voter_for_epoch( 110 0 : &self->authorized_voters, 111 0 : current_epoch 112 0 : ); 113 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L835 114 0 : if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 115 0 : *pubkey = &authorized_voter->pubkey; 116 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L837 117 0 : fd_authorized_voters_purge_authorized_voters( &self->authorized_voters, current_epoch ); 118 0 : return FD_EXECUTOR_INSTR_SUCCESS; 119 0 : } 120 : 121 : int 122 : fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t * ctx, 123 : fd_vote_state_v3_t * self, 124 : fd_pubkey_t const * authorized_pubkey, 125 : ulong current_epoch, 126 : ulong target_epoch, 127 : uchar const * bls_pubkey, 128 : int authorized_withdrawer_signer, 129 : fd_pubkey_t const * signers[ FD_TXN_SIG_MAX ], 130 0 : ulong signers_cnt ) { 131 0 : int rc; 132 0 : fd_pubkey_t * epoch_authorized_voter = NULL; 133 : 134 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L287-L292 */ 135 0 : if( FD_UNLIKELY( bls_pubkey!=NULL ) ) { 136 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 137 0 : } 138 : 139 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L778 140 0 : rc = fd_vote_state_v3_get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter ); 141 0 : if( FD_UNLIKELY( rc ) ) return rc; 142 : 143 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L779 144 0 : rc = fd_vote_signature_verify( epoch_authorized_voter, authorized_withdrawer_signer, signers, signers_cnt ); 145 0 : if( FD_UNLIKELY( rc ) ) return rc; 146 : 147 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L786 148 0 : if( FD_UNLIKELY( fd_authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) { 149 0 : ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE; 150 0 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR; 151 0 : } 152 : 153 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L791 154 0 : fd_vote_authorized_voter_t * latest_authorized = 155 0 : fd_authorized_voters_last( &self->authorized_voters ); 156 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L794 157 0 : if( FD_UNLIKELY( ( !latest_authorized ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 158 0 : ulong latest_epoch = latest_authorized->epoch; 159 0 : fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey; 160 : 161 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799 162 0 : if( !fd_pubkey_eq( latest_authorized_pubkey, authorized_pubkey ) ) { 163 0 : fd_vote_prior_voters_t * prior_voters = &self->prior_voters; 164 : 165 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801 166 0 : ulong epoch_of_last_authorized_switch = 0UL; 167 0 : if( (!prior_voters->is_empty) & (prior_voters->idx < 32) ) { 168 0 : epoch_of_last_authorized_switch = prior_voters->buf[prior_voters->idx].epoch_end; 169 0 : } 170 : 171 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L810 172 0 : if( target_epoch <= latest_epoch ) 173 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 174 : 175 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L815 176 0 : prior_voters->idx += 1UL; 177 0 : prior_voters->idx %= 32UL; 178 0 : prior_voters->buf[prior_voters->idx] = 179 0 : ( fd_vote_prior_voter_t ){ .pubkey = *latest_authorized_pubkey, 180 0 : .epoch_start = epoch_of_last_authorized_switch, 181 0 : .epoch_end = target_epoch }; 182 0 : prior_voters->is_empty = 0; 183 0 : } 184 : 185 : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822 186 0 : if( FD_UNLIKELY( !fd_vote_authorized_voters_pool_free( self->authorized_voters.pool ) ) ) { 187 0 : FD_LOG_CRIT(( "invariant violation: max authorized voter count of vote account exceeded" )); 188 0 : } 189 : 190 0 : fd_vote_authorized_voter_t * ele = 191 0 : fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool ); 192 0 : ele->epoch = target_epoch; 193 0 : ele->pubkey = *authorized_pubkey; 194 0 : ele->prio = ele->pubkey.uc[0]; 195 0 : fd_vote_authorized_voters_treap_ele_insert( 196 0 : self->authorized_voters.treap, ele, self->authorized_voters.pool ); 197 : 198 0 : return 0; 199 0 : } 200 :