Skip to content

Commit 7bf2782

Browse files
authored
fix: audit feedback 1 (#1331)
* Audit 1 * Pin wh sdk * Typo * Typo * Add constraint to initialize
1 parent 736a202 commit 7bf2782

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

target_chains/solana/programs/pyth-solana-receiver/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,6 @@ pub enum ReceiverError {
5151
TargetGovernanceAuthorityMismatch,
5252
#[msg("The governance authority needs to request a transfer first")]
5353
NonexistentGovernanceAuthorityTransferRequest,
54+
#[msg("The minimum number of signatures should be at least 1")]
55+
ZeroMinimumSignatures,
5456
}

target_chains/solana/programs/pyth-solana-receiver/src/lib.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pub mod pyth_solana_receiver {
5353
use super::*;
5454

5555
pub fn initialize(ctx: Context<Initialize>, initial_config: Config) -> Result<()> {
56+
require!(
57+
initial_config.minimum_signatures > 0,
58+
ReceiverError::ZeroMinimumSignatures
59+
);
5660
let config = &mut ctx.accounts.config;
5761
**config = initial_config;
5862
Ok(())
@@ -101,14 +105,21 @@ pub mod pyth_solana_receiver {
101105

102106
pub fn set_minimum_signatures(ctx: Context<Governance>, minimum_signatures: u8) -> Result<()> {
103107
let config = &mut ctx.accounts.config;
108+
require!(minimum_signatures > 0, ReceiverError::ZeroMinimumSignatures);
104109
config.minimum_signatures = minimum_signatures;
105110
Ok(())
106111
}
107112

108113
/// Post a price update using a VAA and a MerklePriceUpdate.
109114
/// This function allows you to post a price update in a single transaction.
110-
/// Compared to post_update, it is less secure since you won't be able to verify all guardian signatures if you use this function because of transaction size limitations.
111-
/// Typically, you can fit 5 guardian signatures in a transaction that uses this.
115+
/// Compared to `post_update`, it only checks whatever signatures are present in the provided VAA and doesn't fail if the number of signatures is lower than the Wormhole quorum of two thirds of the guardians.
116+
/// The number of signatures that were in the VAA is stored in the `VerificationLevel` of the `PriceUpdateV1` account.
117+
///
118+
/// We recommend using `post_update_atomic` with 5 signatures. This is close to the maximum signatures you can verify in one transaction without exceeding the transaction size limit.
119+
///
120+
/// # Warning
121+
///
122+
/// Using partially verified price updates is dangerous, as it lowers the threshold of guardians that need to collude to produce a malicious price update.
112123
pub fn post_update_atomic(
113124
ctx: Context<PostUpdateAtomic>,
114125
params: PostUpdateAtomicParams,
@@ -195,7 +206,7 @@ pub mod pyth_solana_receiver {
195206
pub fn post_update(ctx: Context<PostUpdate>, params: PostUpdateParams) -> Result<()> {
196207
let config = &ctx.accounts.config;
197208
let payer: &Signer<'_> = &ctx.accounts.payer;
198-
let encoded_vaa = VaaAccount::load(&ctx.accounts.encoded_vaa)?;
209+
let encoded_vaa = VaaAccount::load(&ctx.accounts.encoded_vaa)?; // IMPORTANT: This line checks that the encoded_vaa has ProcessingStatus::Verified. This check is critical otherwise the program could be tricked into accepting unverified VAAs.
199210
let treasury: &AccountInfo<'_> = &ctx.accounts.treasury;
200211
let price_update_account: &mut Account<'_, PriceUpdateV1> =
201212
&mut ctx.accounts.price_update_account;
@@ -269,9 +280,8 @@ pub struct PostUpdate<'info> {
269280
pub encoded_vaa: AccountInfo<'info>,
270281
#[account(seeds = [CONFIG_SEED.as_ref()], bump)]
271282
pub config: Account<'info, Config>,
272-
#[account(seeds = [TREASURY_SEED.as_ref(), &[params.treasury_id]], bump)]
273283
/// CHECK: This is just a PDA controlled by the program. There is currently no way to withdraw funds from it.
274-
#[account(mut)]
284+
#[account(mut, seeds = [TREASURY_SEED.as_ref(), &[params.treasury_id]], bump)]
275285
pub treasury: AccountInfo<'info>,
276286
/// The contraint is such that either the price_update_account is uninitialized or the payer is the write_authority.
277287
/// Pubkey::default() is the SystemProgram on Solana and it can't sign so it's impossible that price_update_account.write_authority == Pubkey::default() once the account is initialized

target_chains/solana/programs/pyth-solana-receiver/tests/test_governance.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,20 @@ async fn test_governance() {
253253
initial_config.minimum_signatures
254254
);
255255

256+
// Minimum signatures can't be 0
257+
assert_eq!(
258+
program_simulator
259+
.process_ix_with_default_compute_limit(
260+
SetMinimumSignatures::populate(governance_authority.pubkey(), 0,),
261+
&vec![&governance_authority],
262+
None,
263+
)
264+
.await
265+
.unwrap_err()
266+
.unwrap(),
267+
into_transaction_error(ReceiverError::ZeroMinimumSignatures)
268+
);
269+
256270
program_simulator
257271
.process_ix_with_default_compute_limit(
258272
SetMinimumSignatures::populate(

0 commit comments

Comments
 (0)