Skip to content

Commit c1cc712

Browse files
authored
Use official wormhole, introducing VerificationLevel (#1212)
* Do it * Do it * Cleanup deps * Delete this
1 parent 6c45ed8 commit c1cc712

File tree

7 files changed

+99
-40
lines changed

7 files changed

+99
-40
lines changed

target_chains/solana/Cargo.lock

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/solana/cli/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ fn main() -> Result<()> {
186186
wormhole,
187187
valid_data_sources: vec![DataSource { chain, emitter }],
188188
single_update_fee_in_lamports: fee,
189+
minimum_signatures: 5,
189190
},
190191
}
191192
.data(),

target_chains/solana/programs/pyth-solana-receiver/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ anchor-lang = "0.28.0"
2020
pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk", version = "2.0.0" }
2121
solana-program = "1.16.20"
2222
byteorder = "1.4.3"
23-
wormhole-core-bridge-solana = {git = "https://github.com/guibescos/wormhole", branch = "variable-sigs"}
23+
wormhole-core-bridge-solana = {git = "https://github.com/wormhole-foundation/wormhole", branch = "wen/solana-rewrite", features = ["cpi"]}
2424
wormhole-raw-vaas = {version = "0.0.1-alpha.1", features = ["ruint", "on-chain"], default-features = false }
25-
wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.17.1" }
26-
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.17.1"}
2725

2826
[dev-dependencies]
2927
pyth-sdk = "0.8.0"

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub enum ReceiverError {
2828
NonexistentGovernanceAuthorityTransferRequest,
2929
#[msg("Funds are insufficient to pay the receiving fee")]
3030
InsufficientFunds,
31+
#[msg("The number of guardian signatures is below the minimum")]
32+
InsufficientGuardianSignatures,
3133
// Wormhole errors
3234
#[msg("Invalid VAA version")]
3335
InvalidVaaVersion,

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

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use {
1717
},
1818
},
1919
},
20-
serde_wormhole::RawMessage,
2120
solana_program::{
2221
keccak,
2322
program_memory::sol_memcpy,
@@ -29,23 +28,22 @@ use {
2928
Config,
3029
DataSource,
3130
},
32-
price_update::PriceUpdateV1,
31+
price_update::{
32+
PriceUpdateV1,
33+
VerificationLevel,
34+
},
3335
},
3436
wormhole_core_bridge_solana::{
35-
sdk::legacy::AccountVariant,
36-
state::{
37-
EncodedVaa,
38-
GuardianSet,
37+
sdk::{
38+
legacy::AccountVariant,
39+
VaaAccount,
3940
},
41+
state::GuardianSet,
4042
},
4143
wormhole_raw_vaas::{
4244
GuardianSetSig,
4345
Vaa,
4446
},
45-
wormhole_sdk::vaa::{
46-
Body,
47-
Header,
48-
},
4947
};
5048

5149
pub mod error;
@@ -104,6 +102,11 @@ pub mod pyth_solana_receiver {
104102
Ok(())
105103
}
106104

105+
pub fn set_minimum_signatures(ctx: Context<Governance>, minimum_signatures: u8) -> Result<()> {
106+
let config = &mut ctx.accounts.config;
107+
config.minimum_signatures = minimum_signatures;
108+
Ok(())
109+
}
107110

108111
/// Post a price update using a VAA and a MerklePriceUpdate.
109112
/// This function allows you to post a price update in a single transaction.
@@ -126,7 +129,6 @@ pub mod pyth_solana_receiver {
126129
ReceiverError::GuardianSetMismatch
127130
);
128131

129-
// Do we have enough signatures for quorum?
130132
let guardian_keys = &guardian_set.keys;
131133

132134
// Generate the same message hash (using keccak) that the Guardians used to generate their
@@ -159,10 +161,16 @@ pub mod pyth_solana_receiver {
159161
let treasury = &ctx.accounts.treasury;
160162
let price_update_account = &mut ctx.accounts.price_update_account;
161163

164+
require_gte!(
165+
vaa.signature_count(),
166+
config.minimum_signatures,
167+
ReceiverError::InsufficientGuardianSignatures
168+
);
169+
162170
let vaa_components = VaaComponents {
163-
verified_signatures: vaa.signature_count(),
164-
emitter_address: vaa.body().emitter_address(),
165-
emitter_chain: vaa.body().emitter_chain(),
171+
verification_level: VerificationLevel::Partial(vaa.signature_count()),
172+
emitter_address: vaa.body().emitter_address(),
173+
emitter_chain: vaa.body().emitter_chain(),
166174
};
167175

168176
post_price_update_from_vaa(
@@ -185,18 +193,15 @@ pub mod pyth_solana_receiver {
185193
pub fn post_updates(ctx: Context<PostUpdates>, price_update: MerklePriceUpdate) -> Result<()> {
186194
let config = &ctx.accounts.config;
187195
let payer: &Signer<'_> = &ctx.accounts.payer;
188-
let encoded_vaa = &ctx.accounts.encoded_vaa;
196+
let encoded_vaa = VaaAccount::load(&ctx.accounts.encoded_vaa)?;
189197
let treasury: &AccountInfo<'_> = &ctx.accounts.treasury;
190198
let price_update_account: &mut Account<'_, PriceUpdateV1> =
191199
&mut ctx.accounts.price_update_account;
192200

193-
let (_, body): (Header, Body<&RawMessage>) =
194-
serde_wormhole::from_slice(&encoded_vaa.buf).unwrap();
195-
196201
let vaa_components = VaaComponents {
197-
verified_signatures: encoded_vaa.header.verified_signatures,
198-
emitter_address: body.emitter_address.0,
199-
emitter_chain: body.emitter_chain.into(),
202+
verification_level: VerificationLevel::Full,
203+
emitter_address: encoded_vaa.try_emitter_address()?,
204+
emitter_chain: encoded_vaa.try_emitter_chain()?,
200205
};
201206

202207
post_price_update_from_vaa(
@@ -205,7 +210,7 @@ pub mod pyth_solana_receiver {
205210
treasury,
206211
price_update_account,
207212
&vaa_components,
208-
body.payload,
213+
encoded_vaa.try_payload()?.as_ref(),
209214
&price_update,
210215
)?;
211216

@@ -253,7 +258,8 @@ pub struct PostUpdates<'info> {
253258
#[account(mut)]
254259
pub payer: Signer<'info>,
255260
#[account(owner = config.wormhole)]
256-
pub encoded_vaa: Account<'info, EncodedVaa>,
261+
/// CHECK: We aren't deserializing the VAA here but later with VaaAccount::load, which is the recommended way
262+
pub encoded_vaa: AccountInfo<'info>,
257263
#[account(seeds = [CONFIG_SEED.as_ref()], bump)]
258264
pub config: Account<'info, Config>,
259265
#[account(seeds = [TREASURY_SEED.as_ref()], bump)]
@@ -351,10 +357,17 @@ impl crate::accounts::PostUpdates {
351357
}
352358
}
353359

360+
impl crate::accounts::Governance {
361+
pub fn populate(payer: Pubkey) -> Self {
362+
let config = Pubkey::find_program_address(&[CONFIG_SEED.as_ref()], &crate::ID).0;
363+
crate::accounts::Governance { payer, config }
364+
}
365+
}
366+
354367
struct VaaComponents {
355-
verified_signatures: u8,
356-
emitter_address: [u8; 32],
357-
emitter_chain: u16,
368+
verification_level: VerificationLevel,
369+
emitter_address: [u8; 32],
370+
emitter_chain: u16,
358371
}
359372

360373
fn post_price_update_from_vaa<'info>(
@@ -410,7 +423,7 @@ fn post_price_update_from_vaa<'info>(
410423
match message {
411424
Message::PriceFeedMessage(price_feed_message) => {
412425
price_update_account.write_authority = payer.key();
413-
price_update_account.verified_signatures = vaa_components.verified_signatures;
426+
price_update_account.verification_level = vaa_components.verification_level;
414427
price_update_account.price_message = price_feed_message;
415428
}
416429
Message::TwapMessage(_) => {

target_chains/solana/programs/pyth-solana-receiver/src/state/config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub struct Config {
1010
pub wormhole: Pubkey, // The address of the wormhole receiver
1111
pub valid_data_sources: Vec<DataSource>, // The list of valid data sources for oracle price updates
1212
pub single_update_fee_in_lamports: u64, // The fee in lamports for a single price update
13+
pub minimum_signatures: u8, // The minimum number of signatures required to accept a VAA
1314
}
1415

1516
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
@@ -51,11 +52,12 @@ pub mod tests {
5152
},
5253
],
5354
single_update_fee_in_lamports: 0,
55+
minimum_signatures: 0,
5456
};
5557

5658
assert_eq!(
5759
test_config.try_to_vec().unwrap().len(),
58-
32 + 1 + 32 + 32 + 4 + 1 + 33 + 1 + 33 + 8
60+
32 + 1 + 32 + 32 + 4 + 1 + 33 + 1 + 33 + 8 + 1
5961
);
6062
assert!(
6163
Config::discriminator().len() + test_config.try_to_vec().unwrap().len() <= Config::LEN
Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,47 @@
11
use {
2-
anchor_lang::prelude::*,
2+
anchor_lang::prelude::{
3+
borsh::BorshSchema,
4+
*,
5+
},
36
pythnet_sdk::messages::PriceFeedMessage,
47
solana_program::pubkey::Pubkey,
58
};
69

10+
11+
/**
12+
* This enum represents how many guardian signatures were checked for a Pythnet price update
13+
* If full, guardian quorum has been attained
14+
* If partial, at least config.minimum signatures have been verified, but in the case config.minimum_signatures changes in the future we also include the number of signatures that were checked */
15+
#[derive(AnchorSerialize, AnchorDeserialize, Copy, Clone, PartialEq, BorshSchema)]
16+
pub enum VerificationLevel {
17+
Partial(u8),
18+
Full,
19+
}
720
#[account]
21+
#[derive(BorshSchema)]
822
pub struct PriceUpdateV1 {
9-
pub write_authority: Pubkey, // This write authority can close this account
10-
pub verified_signatures: u8, // The number of wormhole signatures that were verified by the wormhole receiver
11-
pub price_message: PriceFeedMessage,
23+
pub write_authority: Pubkey, // This write authority can close this account
24+
pub verification_level: VerificationLevel, // Whether all the guardian signatures have been checked, and if not, how many have been checked
25+
pub price_message: PriceFeedMessage,
1226
}
1327

1428
impl PriceUpdateV1 {
15-
pub const LEN: usize = 8 + 32 + 1 + 32 + 8 + 8 + 4 + 8 + 8 + 8 + 8;
29+
pub const LEN: usize = 8 + 32 + 2 + 32 + 8 + 8 + 4 + 8 + 8 + 8 + 8;
30+
}
31+
32+
#[cfg(test)]
33+
pub mod tests {
34+
use {
35+
crate::state::price_update::PriceUpdateV1,
36+
anchor_lang::Discriminator,
37+
solana_program::borsh0_10,
38+
};
39+
40+
#[test]
41+
fn check_size() {
42+
assert!(
43+
PriceUpdateV1::discriminator().len() + borsh0_10::get_packed_len::<PriceUpdateV1>()
44+
== PriceUpdateV1::LEN
45+
);
46+
}
1647
}

0 commit comments

Comments
 (0)