Skip to content

Commit 3458dd5

Browse files
authored
[solana] Post update atomic (#1210)
* Post update atomic * Checkpoint cli * Fix bug * Atomic * Cleanup * Cleanup * Cleanup * Cleanup * Comment * Add wormhole errors * refactor trim signatures
1 parent 2d5e030 commit 3458dd5

File tree

6 files changed

+368
-61
lines changed

6 files changed

+368
-61
lines changed

target_chains/solana/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/solana/cli/src/cli.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ pub enum Action {
4040
#[clap(short = 'p', long, help = "Payload from Hermes")]
4141
payload: String,
4242
},
43+
#[clap(about = "Post a price update from Hermes to Solana in one transaction")]
44+
PostPriceUpdateAtomic {
45+
#[clap(short = 'p', long, help = "Payload from Hermes")]
46+
payload: String,
47+
#[clap(
48+
short = 'n',
49+
default_value = "5",
50+
help = "Number of signatures to verify. If n >= 5 this will fail because of the transaction size limit."
51+
)]
52+
n_signatures: usize,
53+
},
4354
#[clap(
4455
about = "Initialize a wormhole receiver contract by sequentially replaying the guardian set updates"
4556
)]

target_chains/solana/cli/src/main.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#![deny(warnings)]
22

3-
43
pub mod cli;
4+
5+
56
use {
67
anchor_client::anchor_lang::{
78
InstructionData,
@@ -14,7 +15,10 @@ use {
1415
Action,
1516
Cli,
1617
},
17-
pyth_solana_receiver::state::config::DataSource,
18+
pyth_solana_receiver::{
19+
state::config::DataSource,
20+
PostUpdatesAtomicParams,
21+
},
1822
pythnet_sdk::wire::v1::{
1923
AccumulatorUpdateData,
2024
MerklePriceUpdate,
@@ -102,6 +106,25 @@ fn main() -> Result<()> {
102106
&merkle_price_updates[0],
103107
)?;
104108
}
109+
Action::PostPriceUpdateAtomic {
110+
payload,
111+
n_signatures,
112+
} => {
113+
let rpc_client = RpcClient::new(url);
114+
let payer =
115+
read_keypair_file(&*shellexpand::tilde(&keypair)).expect("Keypair not found");
116+
117+
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(&payload)?;
118+
119+
process_post_price_update_atomic(
120+
&rpc_client,
121+
&vaa,
122+
n_signatures,
123+
&wormhole,
124+
&payer,
125+
&merkle_price_updates[0],
126+
)?;
127+
}
105128

106129
Action::InitializeWormholeReceiver {} => {
107130
let rpc_client = RpcClient::new(url);
@@ -140,6 +163,7 @@ fn main() -> Result<()> {
140163
false,
141164
)?;
142165
}
166+
143167
Action::InitializePythReceiver {
144168
fee,
145169
emitter,
@@ -221,6 +245,55 @@ pub fn process_upgrade_guardian_set(
221245
Ok(())
222246
}
223247

248+
pub fn process_post_price_update_atomic(
249+
rpc_client: &RpcClient,
250+
vaa: &[u8],
251+
n_signatures: usize,
252+
wormhole: &Pubkey,
253+
payer: &Keypair,
254+
merkle_price_update: &MerklePriceUpdate,
255+
) -> Result<Pubkey> {
256+
let price_update_keypair = Keypair::new();
257+
258+
let (mut header, body): (Header, Body<&RawMessage>) = serde_wormhole::from_slice(vaa).unwrap();
259+
trim_signatures(&mut header, n_signatures);
260+
261+
let request_compute_units_instruction: Instruction =
262+
ComputeBudgetInstruction::set_compute_unit_limit(400_000);
263+
264+
265+
let post_update_accounts = pyth_solana_receiver::accounts::PostUpdatesAtomic::populate(
266+
payer.pubkey(),
267+
price_update_keypair.pubkey(),
268+
*wormhole,
269+
header.guardian_set_index,
270+
)
271+
.to_account_metas(None);
272+
273+
let post_update_instruction = Instruction {
274+
program_id: pyth_solana_receiver::id(),
275+
accounts: post_update_accounts,
276+
data: pyth_solana_receiver::instruction::PostUpdatesAtomic {
277+
params: PostUpdatesAtomicParams {
278+
merkle_price_update: merkle_price_update.clone(),
279+
vaa: serde_wormhole::to_vec(&(header, body)).unwrap(),
280+
},
281+
}
282+
.data(),
283+
};
284+
285+
process_transaction(
286+
rpc_client,
287+
vec![request_compute_units_instruction, post_update_instruction],
288+
&vec![payer, &price_update_keypair],
289+
)?;
290+
Ok(price_update_keypair.pubkey())
291+
}
292+
293+
fn trim_signatures(header: &mut Header, n_signatures: usize) {
294+
header.signatures = header.signatures[..(n_signatures)].to_vec();
295+
}
296+
224297
fn deserialize_guardian_set(buf: &mut &[u8], legacy_guardian_set: bool) -> Result<GuardianSet> {
225298
if !legacy_guardian_set {
226299
// Skip anchor discriminator

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk", version = "2.0.0" }
2121
solana-program = "1.16.20"
2222
byteorder = "1.4.3"
2323
wormhole-core-bridge-solana = {git = "https://github.com/guibescos/wormhole", branch = "variable-sigs"}
24+
wormhole-raw-vaas = {version = "0.0.1-alpha.1", features = ["ruint", "on-chain"], default-features = false }
2425
wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.17.1" }
2526
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.17.1"}
2627

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,15 @@ pub enum ReceiverError {
2828
NonexistentGovernanceAuthorityTransferRequest,
2929
#[msg("Funds are insufficient to pay the receiving fee")]
3030
InsufficientFunds,
31+
// Wormhole errors
32+
#[msg("Invalid VAA version")]
33+
InvalidVaaVersion,
34+
#[msg("Guardian set version in the VAA doesn't match the guardian set passed")]
35+
GuardianSetMismatch,
36+
#[msg("Guardian index exceeds the number of guardians in the set")]
37+
InvalidGuardianIndex,
38+
#[msg("A VAA signature is invalid")]
39+
InvalidSignature,
40+
#[msg("The recovered guardian public key doesn't match the guardian set")]
41+
InvalidGuardianKeyRecovery,
3142
}

0 commit comments

Comments
 (0)