Skip to content

Commit f78301d

Browse files
authored
[pyth-solana-receiver-cli] Initialize solana wormhole receiver (#1180)
* Checkpoint * Checkpoint * Checkpoint * Cleanup * More cleanup * Cleanup * Cleanup * Should work * reorder * Send it * Rename * Add comment
1 parent 1be8a02 commit f78301d

File tree

4 files changed

+207
-23
lines changed

4 files changed

+207
-23
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/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[features]
7-
default = []
7+
default = ["mainnet"]
88
mainnet = ["wormhole-anchor-sdk/mainnet", "pyth-solana-receiver/mainnet"]
99
devnet = ["wormhole-anchor-sdk/solana-devnet", "pyth-solana-receiver/devnet"]
1010

@@ -27,3 +27,4 @@ wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", tag =
2727
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.17.1"}
2828
wormhole-anchor-sdk = { version = "0.1.0-alpha.2" , default-features = false }
2929
cfg-if = "1.0.0"
30+
hex = "0.4.3"

target_chains/solana/cli/src/cli.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
use clap::{
2-
Parser,
3-
Subcommand,
1+
use {
2+
clap::{
3+
Parser,
4+
Subcommand,
5+
},
6+
solana_sdk::pubkey::Pubkey,
7+
std::str::FromStr,
48
};
59

610
#[derive(Parser, Debug)]
@@ -9,29 +13,35 @@ use clap::{
913
author = "Pyth Network Contributors"
1014
)]
1115
pub struct Cli {
16+
#[clap(
17+
short = 'k',
18+
long,
19+
default_value = "~/.config/solana/id.json",
20+
help = "Keypair of the payer of transactions"
21+
)]
22+
pub keypair: String,
23+
#[clap(
24+
short = 'u',
25+
long,
26+
default_value = "http://localhost:8899",
27+
help = "RPC endpoint of the solana"
28+
)]
29+
pub url: String,
30+
#[clap(short = 'w', long, parse(try_from_str = Pubkey::from_str), help = "Address of the wormhole contract")]
31+
pub wormhole: Pubkey,
1232
#[clap(subcommand)]
13-
pub action: Action,
33+
pub action: Action,
1434
}
1535

1636
#[derive(Subcommand, Debug)]
1737
pub enum Action {
1838
#[clap(about = "Verify, post and receive the price VAA on solana")]
1939
PostAndReceiveVAA {
2040
#[clap(short = 'v', long, help = "Price VAA from Pythnet")]
21-
vaa: String,
22-
#[clap(
23-
short = 'k',
24-
long,
25-
default_value = "~/.config/solana/id.json",
26-
help = "Keypair of the payer of transactions"
27-
)]
28-
keypair: String,
29-
#[clap(
30-
short = 'u',
31-
long,
32-
default_value = "http://localhost:8899",
33-
help = "RPC endpoint of the solana"
34-
)]
35-
url: String,
41+
vaa: String,
3642
},
43+
#[clap(
44+
about = "Initialize a wormhole receiver contract by sequentially replaying the guardian set updates"
45+
)]
46+
InitializeWormholeReceiver {},
3747
}

target_chains/solana/cli/src/main.rs

Lines changed: 175 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![deny(warnings)]
12
pub mod cli;
23

34
use {
@@ -45,7 +46,9 @@ use {
4546
},
4647
wormhole_solana::{
4748
instructions::{
49+
initialize,
4850
post_vaa,
51+
upgrade_guardian_set,
4952
verify_signatures_txs,
5053
PostVAAData,
5154
},
@@ -55,6 +58,13 @@ use {
5558
VAA as WormholeSolanaVAA,
5659
},
5760
};
61+
62+
const INITIAL_GUARDIAN: &str = "58cc3ae5c097b213ce3c81979e1b9f9570746aa5"; // Mainnet intial guardian set so we can use stable price feeds
63+
const UPGRADE_GUARDIAN_SET_VAA_1 : &str = "010000000001007ac31b282c2aeeeb37f3385ee0de5f8e421d30b9e5ae8ba3d4375c1c77a86e77159bb697d9c456d6f8c02d22a94b1279b65b0d6a9957e7d3857423845ac758e300610ac1d2000000030001000000000000000000000000000000000000000000000000000000000000000400000000000005390000000000000000000000000000000000000000000000000000000000436f7265020000000000011358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cdeb5f7389fa26941519f0863349c223b73a6ddee774a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d";
64+
const UPGRADE_GUARDIAN_SET_VAA_2 : &str = "01000000010d0012e6b39c6da90c5dfd3c228edbb78c7a4c97c488ff8a346d161a91db067e51d638c17216f368aa9bdf4836b8645a98018ca67d2fec87d769cabfdf2406bf790a0002ef42b288091a670ef3556596f4f47323717882881eaf38e03345078d07a156f312b785b64dae6e9a87e3d32872f59cb1931f728cecf511762981baf48303668f0103cef2616b84c4e511ff03329e0853f1bd7ee9ac5ba71d70a4d76108bddf94f69c2a8a84e4ee94065e8003c334e899184943634e12043d0dda78d93996da073d190104e76d166b9dac98f602107cc4b44ac82868faf00b63df7d24f177aa391e050902413b71046434e67c770b19aecdf7fce1d1435ea0be7262e3e4c18f50ddc8175c0105d9450e8216d741e0206a50f93b750a47e0a258b80eb8fed1314cc300b3d905092de25cd36d366097b7103ae2d184121329ba3aa2d7c6cc53273f11af14798110010687477c8deec89d36a23e7948feb074df95362fc8dcbd8ae910ac556a1dee1e755c56b9db5d710c940938ed79bc1895a3646523a58bc55f475a23435a373ecfdd0107fb06734864f79def4e192497362513171530daea81f07fbb9f698afe7e66c6d44db21323144f2657d4a5386a954bb94eef9f64148c33aef6e477eafa2c5c984c01088769e82216310d1827d9bd48645ec23e90de4ef8a8de99e2d351d1df318608566248d80cdc83bdcac382b3c30c670352be87f9069aab5037d0b747208eae9c650109e9796497ff9106d0d1c62e184d83716282870cef61a1ee13d6fc485b521adcce255c96f7d1bca8d8e7e7d454b65783a830bddc9d94092091a268d311ecd84c26010c468c9fb6d41026841ff9f8d7368fa309d4dbea3ea4bbd2feccf94a92cc8a20a226338a8e2126cd16f70eaf15b4fc9be2c3fa19def14e071956a605e9d1ac4162010e23fcb6bd445b7c25afb722250c1acbc061ed964ba9de1326609ae012acdfb96942b2a102a2de99ab96327859a34a2b49a767dbdb62e0a1fb26af60fe44fd496a00106bb0bac77ac68b347645f2fb1ad789ea9bd76fb9b2324f25ae06f97e65246f142df717f662e73948317182c62ce87d79c73def0dba12e5242dfc038382812cfe00126da03c5e56cb15aeeceadc1e17a45753ab4dc0ec7bf6a75ca03143ed4a294f6f61bc3f478a457833e43084ecd7c985bf2f55a55f168aac0e030fc49e845e497101626e9d9a5d9e343f00010000000000000000000000000000000000000000000000000000000000000004c1759167c43f501c2000000000000000000000000000000000000000000000000000000000436f7265020000000000021358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd66b9590e1c41e0b226937bf9217d1d67fd4e91f574a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d";
65+
const UPGRADE_GUARDIAN_SET_VAA_3 : &str = "01000000020d00ce45474d9e1b1e7790a2d210871e195db53a70ffd6f237cfe70e2686a32859ac43c84a332267a8ef66f59719cf91cc8df0101fd7c36aa1878d5139241660edc0010375cc906156ae530786661c0cd9aef444747bc3d8d5aa84cac6a6d2933d4e1a031cffa30383d4af8131e929d9f203f460b07309a647d6cd32ab1cc7724089392c000452305156cfc90343128f97e499311b5cae174f488ff22fbc09591991a0a73d8e6af3afb8a5968441d3ab8437836407481739e9850ad5c95e6acfcc871e951bc30105a7956eefc23e7c945a1966d5ddbe9e4be376c2f54e45e3d5da88c2f8692510c7429b1ea860ae94d929bd97e84923a18187e777aa3db419813a80deb84cc8d22b00061b2a4f3d2666608e0aa96737689e3ba5793810ff3a52ff28ad57d8efb20967735dc5537a2e43ef10f583d144c12a1606542c207f5b79af08c38656d3ac40713301086b62c8e130af3411b3c0d91b5b50dcb01ed5f293963f901fc36e7b0e50114dce203373b32eb45971cef8288e5d928d0ed51cd86e2a3006b0af6a65c396c009080009e93ab4d2c8228901a5f4525934000b2c26d1dc679a05e47fdf0ff3231d98fbc207103159ff4116df2832eea69b38275283434e6cd4a4af04d25fa7a82990b707010aa643f4cf615dfff06ffd65830f7f6cf6512dabc3690d5d9e210fdc712842dc2708b8b2c22e224c99280cd25e5e8bfb40e3d1c55b8c41774e287c1e2c352aecfc010b89c1e85faa20a30601964ccc6a79c0ae53cfd26fb10863db37783428cd91390a163346558239db3cd9d420cfe423a0df84c84399790e2e308011b4b63e6b8015010ca31dcb564ac81a053a268d8090e72097f94f366711d0c5d13815af1ec7d47e662e2d1bde22678113d15963da100b668ba26c0c325970d07114b83c5698f46097010dc9fda39c0d592d9ed92cd22b5425cc6b37430e236f02d0d1f8a2ef45a00bde26223c0a6eb363c8b25fd3bf57234a1d9364976cefb8360e755a267cbbb674b39501108db01e444ab1003dd8b6c96f8eb77958b40ba7a85fefecf32ad00b7a47c0ae7524216262495977e09c0989dd50f280c21453d3756843608eacd17f4fdfe47600001261025228ef5af837cb060bcd986fcfa84ccef75b3fa100468cfd24e7fadf99163938f3b841a33496c2706d0208faab088bd155b2e20fd74c625bb1cc8c43677a0163c53c409e0c5dfa000100000000000000000000000000000000000000000000000000000000000000046c5a054d7833d1e42000000000000000000000000000000000000000000000000000000000436f7265020000000000031358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd15e7caf07c4e3dc8e7c469f92c8cd88fb8005a2074a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d";
66+
67+
5868
// Note: this is a reimplementation of the GuardianSet from wormhole_solana
5969
// because the wormhole_solana crate does uses an older versions of the dependencies.
6070
// This can be removed once the GuardianSet is added to the wormhole_anchor_sdk
@@ -89,12 +99,16 @@ impl Owner for GuardianSet {
8999

90100
fn main() -> Result<()> {
91101
let cli = Cli::parse();
102+
let Cli {
103+
action,
104+
keypair,
105+
url,
106+
wormhole,
107+
} = cli;
92108

93-
match cli.action {
109+
match action {
94110
Action::PostAndReceiveVAA {
95111
vaa: accumulator_update_data_str,
96-
keypair,
97-
url,
98112
} => {
99113
let wormhole = wormhole_anchor_sdk::wormhole::program::id();
100114
let rpc_client = RpcClient::new(url);
@@ -233,11 +247,169 @@ fn main() -> Result<()> {
233247
}
234248
}
235249
}
250+
Action::InitializeWormholeReceiver {} => {
251+
let rpc_client = RpcClient::new(url);
252+
let payer =
253+
read_keypair_file(&*shellexpand::tilde(&keypair)).expect("Keypair not found");
254+
255+
let initialize_instruction = initialize(
256+
wormhole,
257+
payer.pubkey(),
258+
0,
259+
0,
260+
&[hex::decode(INITIAL_GUARDIAN).unwrap().try_into().unwrap()],
261+
)
262+
.unwrap();
263+
process_transaction(&rpc_client, vec![initialize_instruction], &vec![&payer])?;
264+
265+
process_upgrade_guardian_set(
266+
&rpc_client,
267+
&hex::decode(UPGRADE_GUARDIAN_SET_VAA_1).unwrap(),
268+
wormhole,
269+
&payer,
270+
true,
271+
)?;
272+
process_upgrade_guardian_set(
273+
&rpc_client,
274+
&hex::decode(UPGRADE_GUARDIAN_SET_VAA_2).unwrap(),
275+
wormhole,
276+
&payer,
277+
false,
278+
)?;
279+
process_upgrade_guardian_set(
280+
&rpc_client,
281+
&hex::decode(UPGRADE_GUARDIAN_SET_VAA_3).unwrap(),
282+
wormhole,
283+
&payer,
284+
false,
285+
)?;
286+
}
236287
}
237288

238289
Ok(())
239290
}
240291

292+
pub fn process_upgrade_guardian_set(
293+
rpc_client: &RpcClient,
294+
vaa: &[u8],
295+
wormhole: Pubkey,
296+
payer: &Keypair,
297+
legacy_guardian_set: bool,
298+
) -> Result<()> {
299+
let posted_vaa =
300+
process_post_vaa(rpc_client, vaa, wormhole, payer, legacy_guardian_set).unwrap();
301+
let parsed_vaa: Vaa<&RawMessage> = serde_wormhole::from_slice(vaa).unwrap();
302+
let (header, body): (Header, Body<&RawMessage>) = parsed_vaa.into();
303+
let guardian_set_index_old = header.guardian_set_index;
304+
let emitter = Pubkey::from(body.emitter_address.0);
305+
let sequence = body.sequence;
306+
307+
let update_guardian_set_instruction = upgrade_guardian_set(
308+
wormhole,
309+
payer.pubkey(),
310+
posted_vaa,
311+
guardian_set_index_old,
312+
emitter,
313+
sequence,
314+
)
315+
.unwrap();
316+
317+
process_transaction(
318+
rpc_client,
319+
vec![update_guardian_set_instruction],
320+
&vec![payer],
321+
)?;
322+
Ok(())
323+
}
324+
325+
fn deserialize_guardian_set(buf: &mut &[u8], legacy_guardian_set: bool) -> Result<GuardianSet> {
326+
if !legacy_guardian_set {
327+
// Skip anchor discriminator
328+
*buf = &buf[8..];
329+
}
330+
let guardian_set = GuardianSet::deserialize(buf)?;
331+
Ok(guardian_set)
332+
}
333+
334+
pub fn process_post_vaa(
335+
rpc_client: &RpcClient,
336+
vaa: &[u8],
337+
wormhole: Pubkey,
338+
payer: &Keypair,
339+
legacy_guardian_set: bool,
340+
) -> Result<Pubkey> {
341+
let parsed_vaa: Vaa<&RawMessage> = serde_wormhole::from_slice(vaa).unwrap();
342+
let (header, body): (Header, Body<&RawMessage>) = parsed_vaa.into();
343+
344+
let wormhole_config = WormholeConfig::key(&wormhole, ());
345+
346+
let wormhole_config_data =
347+
BridgeData::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
348+
349+
let guardian_set = Pubkey::find_program_address(
350+
&[
351+
b"GuardianSet",
352+
&wormhole_config_data.guardian_set_index.to_be_bytes(),
353+
],
354+
&wormhole,
355+
)
356+
.0;
357+
358+
let guardian_set_data = deserialize_guardian_set(
359+
&mut &rpc_client.get_account_data(&guardian_set)?[..],
360+
legacy_guardian_set,
361+
)?;
362+
363+
let vaa_hash = body.digest().unwrap().hash;
364+
let vaa_pubkey = WormholeSolanaVAA::key(&wormhole, vaa_hash);
365+
366+
let signature_set_keypair = Keypair::new();
367+
368+
let verify_txs = verify_signatures_txs(
369+
vaa,
370+
WormholeSolanaGuardianSet {
371+
index: guardian_set_data.index,
372+
keys: guardian_set_data.keys,
373+
creation_time: guardian_set_data.creation_time,
374+
expiration_time: guardian_set_data.expiration_time,
375+
},
376+
wormhole,
377+
payer.pubkey(),
378+
wormhole_config_data.guardian_set_index,
379+
signature_set_keypair.pubkey(),
380+
)?;
381+
382+
for tx in verify_txs {
383+
process_transaction(rpc_client, tx, &vec![payer, &signature_set_keypair])?;
384+
}
385+
let post_vaa_data = PostVAAData {
386+
version: header.version,
387+
guardian_set_index: header.guardian_set_index,
388+
timestamp: body.timestamp,
389+
nonce: body.nonce,
390+
emitter_chain: body.emitter_chain.into(),
391+
emitter_address: body.emitter_address.0,
392+
sequence: body.sequence,
393+
consistency_level: body.consistency_level,
394+
payload: body.payload.to_vec(),
395+
};
396+
397+
process_transaction(
398+
rpc_client,
399+
vec![post_vaa(
400+
wormhole,
401+
payer.pubkey(),
402+
signature_set_keypair.pubkey(),
403+
post_vaa_data,
404+
)?],
405+
&vec![payer],
406+
)?;
407+
408+
rpc_client.get_account_data(&vaa_pubkey).ok();
409+
410+
Ok(vaa_pubkey)
411+
}
412+
241413
pub fn process_transaction(
242414
rpc_client: &RpcClient,
243415
instructions: Vec<Instruction>,

0 commit comments

Comments
 (0)