From 8776bade203f0f3119fe094a9d6b20f640028539 Mon Sep 17 00:00:00 2001 From: Kyrylo Stepanov Date: Thu, 10 Oct 2024 13:43:37 +0300 Subject: [PATCH 1/3] Fix [P1-L-04] --- .../src/instructions/close_voter.rs | 25 ++++++++----------- .../tests/program_test/addin.rs | 1 - 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/programs/voter-stake-registry/src/instructions/close_voter.rs b/programs/voter-stake-registry/src/instructions/close_voter.rs index 573c2edb..7cbdb7ff 100644 --- a/programs/voter-stake-registry/src/instructions/close_voter.rs +++ b/programs/voter-stake-registry/src/instructions/close_voter.rs @@ -1,6 +1,6 @@ use crate::cpi_instructions; use anchor_lang::prelude::*; -use anchor_spl::token::{self, CloseAccount, Mint, Token, TokenAccount}; +use anchor_spl::token::{self, CloseAccount, Token, TokenAccount}; use bytemuck::bytes_of_mut; use mplx_staking_states::{ error::MplStakingError, @@ -43,8 +43,6 @@ pub struct CloseVoter<'info> { /// keep track of all rewards and staking logic. pub reward_pool: UncheckedAccount<'info>, - pub deposit_mint: Account<'info, Mint>, - #[account(mut)] /// CHECK: Destination may be any address. pub sol_destination: UncheckedAccount<'info>, @@ -56,17 +54,15 @@ pub struct CloseVoter<'info> { pub rewards_program: UncheckedAccount<'info>, } -/// Closes the voter account, and specified token vault if any provided, +/// Closes the voter account, and specified token vaults if provided in the remaining accounts, /// allowing to retrieve rent examption SOL. /// Only accounts with no remaining stakes can be closed. +/// +/// Remaining accounts should containt the complete list of ATA that must be closed, +/// the length of those accounts should be equal to the number of mint configs in the registrar. pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> Result<()> { let registrar = ctx.accounts.registrar.load()?; - require!( - ctx.accounts.deposit_mint.key() == registrar.realm_governing_token_mint, - MplStakingError::InvalidGoverningTokenMint, - ); - require!( ctx.accounts.rewards_program.key() == registrar.rewards_program, MplStakingError::InvalidRewardsProgram @@ -98,16 +94,15 @@ pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> // will close all the token accounts owned by the voter for deposit_vault_info in ctx.remaining_accounts { + let deposit_vault_ta = Account::::try_from(&deposit_vault_info) + .map_err(|_| MplStakingError::DeserializationError)?; + registrar.voting_mint_config_index(deposit_vault_ta.mint)?; + require_keys_eq!( *deposit_vault_info.key, - get_associated_token_address( - &ctx.accounts.voter.key(), - &ctx.accounts.deposit_mint.key() - ), + get_associated_token_address(&ctx.accounts.voter.key(), &deposit_vault_ta.mint), ); - let deposit_vault_ta = Account::::try_from(&deposit_vault_info) - .map_err(|_| MplStakingError::DeserializationError)?; require_keys_eq!( deposit_vault_ta.owner, ctx.accounts.voter.key(), diff --git a/programs/voter-stake-registry/tests/program_test/addin.rs b/programs/voter-stake-registry/tests/program_test/addin.rs index 827fb073..7d86d71b 100644 --- a/programs/voter-stake-registry/tests/program_test/addin.rs +++ b/programs/voter-stake-registry/tests/program_test/addin.rs @@ -620,7 +620,6 @@ impl AddinCookie { voter_authority: voter_authority.pubkey(), deposit_mining, reward_pool: registrar.reward_pool, - deposit_mint: voting_mint.mint.pubkey.unwrap(), sol_destination: voter_authority.pubkey(), token_program: spl_token::id(), rewards_program: *rewards_program, From cb8cd86ad27f7b68a06440a4c2c4b9bab9631cb8 Mon Sep 17 00:00:00 2001 From: Kyrylo Stepanov Date: Thu, 10 Oct 2024 14:12:28 +0300 Subject: [PATCH 2/3] Add double checks for PDA calcs in arbitrary accounts --- .../src/instructions/claim.rs | 16 +++++++++++++--- .../src/instructions/close_voter.rs | 11 ++++++++--- .../src/instructions/create_registrar.rs | 7 ++++++- .../src/instructions/create_voter.rs | 7 ++++++- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/programs/voter-stake-registry/src/instructions/claim.rs b/programs/voter-stake-registry/src/instructions/claim.rs index d56dea1c..aec24b9c 100644 --- a/programs/voter-stake-registry/src/instructions/claim.rs +++ b/programs/voter-stake-registry/src/instructions/claim.rs @@ -20,16 +20,26 @@ pub struct Claim<'info> { /// CHECK: Rewards mint addr will be checked in the rewards contract pub reward_mint: UncheckedAccount<'info>, - #[account(mut)] + #[account( + mut, + seeds = [b"vault", reward_pool.key().as_ref(), reward_mint.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] /// CHECK: Rewards vault is used as a source of rewards and /// is checked on the rewards contract /// PDA(["vault", reward_pool, reward_mint], reward_program) pub vault: UncheckedAccount<'info>, /// CHECK: mining PDA will be checked in the rewards contract - /// PDA(["mining", mining owner , reward_pool], + /// PDA(["mining", mining owner , reward_pool], /// reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"mining", mining_owner.key().as_ref(), reward_pool.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] pub deposit_mining: UncheckedAccount<'info>, pub mining_owner: Signer<'info>, diff --git a/programs/voter-stake-registry/src/instructions/close_voter.rs b/programs/voter-stake-registry/src/instructions/close_voter.rs index 7cbdb7ff..3b6a52a2 100644 --- a/programs/voter-stake-registry/src/instructions/close_voter.rs +++ b/programs/voter-stake-registry/src/instructions/close_voter.rs @@ -21,7 +21,7 @@ pub struct CloseVoter<'info> { // the other constraints must be exhaustive #[account( mut, - seeds = [voter.load()?.registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()], + seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()], bump = voter.load()?.voter_bump, has_one = voter_authority, close = sol_destination @@ -34,7 +34,12 @@ pub struct CloseVoter<'info> { /// CHECK: mining PDA will be checked in the rewards contract /// PDA(["mining", mining owner , reward_pool], /// reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"mining", voter_authority.key().as_ref(), reward_pool.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] pub deposit_mining: UncheckedAccount<'info>, /// CHECK: @@ -94,7 +99,7 @@ pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> // will close all the token accounts owned by the voter for deposit_vault_info in ctx.remaining_accounts { - let deposit_vault_ta = Account::::try_from(&deposit_vault_info) + let deposit_vault_ta = Account::::try_from(deposit_vault_info) .map_err(|_| MplStakingError::DeserializationError)?; registrar.voting_mint_config_index(deposit_vault_ta.mint)?; diff --git a/programs/voter-stake-registry/src/instructions/create_registrar.rs b/programs/voter-stake-registry/src/instructions/create_registrar.rs index 08d83c6b..d8fbbf48 100644 --- a/programs/voter-stake-registry/src/instructions/create_registrar.rs +++ b/programs/voter-stake-registry/src/instructions/create_registrar.rs @@ -45,7 +45,12 @@ pub struct CreateRegistrar<'info> { /// CHECK: any address is allowed /// This account is responsible for storing money for rewards /// PDA(["vault", reward_pool, reward_mint], reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"vault", reward_pool.key().as_ref(), realm_governing_token_mint.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] reward_vault: UncheckedAccount<'info>, #[account(mut)] diff --git a/programs/voter-stake-registry/src/instructions/create_voter.rs b/programs/voter-stake-registry/src/instructions/create_voter.rs index 1e068b75..b0902af8 100644 --- a/programs/voter-stake-registry/src/instructions/create_voter.rs +++ b/programs/voter-stake-registry/src/instructions/create_voter.rs @@ -55,7 +55,12 @@ pub struct CreateVoter<'info> { /// CHECK: mining PDA will be checked in the rewards contract /// PDA(["mining", mining owner , reward_pool], /// reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"mining", voter_authority.key().as_ref(), reward_pool.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] pub deposit_mining: UncheckedAccount<'info>, /// CHECK: Rewards program ID From fb6a32c86f51862aab073d747955dfe814f313e4 Mon Sep 17 00:00:00 2001 From: Kyrylo Stepanov Date: Thu, 10 Oct 2024 14:41:26 +0300 Subject: [PATCH 3/3] update testing fixture --- .../tests/fixtures/mplx_rewards.so | Bin 249288 -> 249480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/programs/voter-stake-registry/tests/fixtures/mplx_rewards.so b/programs/voter-stake-registry/tests/fixtures/mplx_rewards.so index eaea9c9f588f13fe90b71ab6d53528e32584f58f..079906842ec1c648d62c96b9ac324b887e78fe6b 100755 GIT binary patch delta 24180 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

vu#IqxZoxR+hSw~>Mx<* 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