Bladeren bron

feat: test-stuff

swimricky 2 jaren geleden
bovenliggende
commit
d5ec624759

+ 1 - 0
target_chains/solana/Cargo.lock

@@ -3055,6 +3055,7 @@ dependencies = [
  "serde",
  "serde_wormhole",
  "sha3 0.10.6",
+ "solana-bpf-loader-program",
  "solana-program",
  "solana-program-test",
  "solana-sdk",

+ 10 - 7
target_chains/solana/cli/src/main.rs

@@ -64,6 +64,9 @@ use {
     },
 };
 
+// Note: this is a reimplementation of the GuardianSet from wormhole_solana
+// because the wormhole_solana crate does uses an older versions of the dependencies.
+// This can be removed once the GuardianSet is added to the wormhole_anchor_sdk
 #[derive(Default, AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq)]
 pub struct GuardianSet {
     /// Index representing an incrementing version number for this guardian set.
@@ -213,6 +216,7 @@ fn main() -> Result<()> {
                             WormholePayload::Merkle(merkle_root) => merkle_root.root,
                         });
 
+                    // verify each merkle update using the posted vaa account.
                     let mut verify_count = 0;
                     for update in updates {
                         let message_vec = Vec::from(update.message.clone());
@@ -227,6 +231,12 @@ fn main() -> Result<()> {
                     println!("[5/5] Post updates from AccumulatorUpdateData and use the PostedVAA on solana using pyth-solana-receiver::PostUpdates");
                     // TODO need to figure out max number of updates that can be sent in 1 txn
 
+                    let post_updates_accounts = pyth_solana_receiver::accounts::PostUpdates {
+                        payer:         payer.pubkey(),
+                        posted_vaa:    vaa_pubkey,
+                        signature_set: *posted_vaa_data.vaa.signature_set(),
+                    }
+                    .to_account_metas(None);
                     // update_bytes_len: 288 (1 price feed)
                     let update_bytes = updates
                         .iter()
@@ -241,13 +251,6 @@ fn main() -> Result<()> {
                         .sum();
 
                     println!("update_bytes_len: {}", update_bytes_len);
-
-                    let post_updates_accounts = pyth_solana_receiver::accounts::PostUpdates {
-                        payer:         payer.pubkey(),
-                        posted_vaa:    vaa_pubkey,
-                        signature_set: *posted_vaa_data.vaa.signature_set(),
-                    }
-                    .to_account_metas(None);
                     let post_updates_ix_data = pyth_solana_receiver::instruction::PostUpdates {
                         price_updates: update_bytes,
                     }

+ 2 - 0
target_chains/solana/programs/pyth-solana-receiver/Cargo.toml

@@ -24,6 +24,7 @@ wormhole-core = { git = "https://github.com/guibescos/wormhole", branch = "reise
 wormhole-solana = { git = "https://github.com/guibescos/wormhole", branch = "reisen/sdk-solana"}
 pyth-wormhole-attester-sdk = { path = "../../../../wormhole_attester/sdk/rust" }
 pythnet-sdk = { git = "https://github.com/pyth-network/pyth-crosschain", version = "2.0.0", features = ["strum"] }
+
 solana-program = "1.16.20"
 hex = "0.4.3"
 serde = { version = "1.0.152", features = ["derive"] }
@@ -42,3 +43,4 @@ tokio = "1.14.1"
 bincode = "1.3.3"
 libsecp256k1 = "0.7.1"
 rand = "0.8.5"
+solana-bpf-loader-program = "1.16.20"

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

@@ -9,7 +9,7 @@ pub enum ReceiverError {
     #[msg("The posted VAA has wrong magic number.")]
     PostedVaaHeaderWrongMagicNumber,
     #[msg("An error occured when deserializing the VAA.")]
-    DeserializeVAAFailed,
+    DeserializeVaaFailed,
     #[msg("An error occurred when deserializing the updates.")]
     DeserializeUpdateFailed,
     #[msg("An error occurred when deserializing the message")]

+ 53 - 2
target_chains/solana/programs/pyth-solana-receiver/src/lib.rs

@@ -31,15 +31,66 @@ use {
 declare_id!("DvPfMBZJJwKgJsv2WJA8bFwUMn8nFd5Xpioc6foC3rse");
 pub const POST_VAA: u8 = 2;
 
+#[derive(Accounts)]
+pub struct Update<'info> {
+    #[account(mut)]
+    pub payer: Signer<'info>,
+}
+
 #[program]
 pub mod pyth_solana_receiver {
     use super::*;
 
+    pub fn update(
+        _ctx: Context<Update>,
+        data: Vec<u8>,
+        recovery_id: u8,
+        signature: [u8; 64],
+    ) -> Result<()> {
+        use {
+            hex::ToHex,
+            solana_program::{
+                keccak,
+                secp256k1_recover::secp256k1_recover,
+            },
+        };
+
+        // This costs about 10k compute units
+        let message_hash = {
+            let mut hasher = keccak::Hasher::default();
+            hasher.hash(&data);
+            hasher.result()
+        };
+
+        // This costs about 25k compute units
+        let recovered_pubkey = secp256k1_recover(&message_hash.0, recovery_id, &signature)
+            .map_err(|_| ProgramError::InvalidArgument)?;
+
+        msg!(
+            "Recovered key: {}",
+            recovered_pubkey.0.encode_hex::<String>()
+        );
+
+        // TODO: Check the pubkey is an expected value.
+        // Here we are checking the secp256k1 pubkey against a known authorized pubkey.
+        //
+        // if recovered_pubkey.0 != AUTHORIZED_PUBLIC_KEY {
+        //  return Err(ProgramError::InvalidArgument);
+        // }
+
+        Ok(())
+    }
+
     /// Verify the updates using the posted_vaa account
-    ///  * `vaa_hash` hash post of the post_vaa data to derive the address of the post_vaa account
     ///  * `price_updates` Vec of bytes for the updates to verify and post on-chain
     #[allow(unused_variables)]
-    pub fn post_updates(ctx: Context<PostUpdates>, price_updates: Vec<Vec<u8>>) -> Result<()> {
+    pub fn post_updates(
+        ctx: Context<PostUpdates>,
+        // TODO: update pythnet_sdk to implement BorshSerialize, BorshDeserialize
+        // for MerklePriceUpdate as well as Keccak160 price_updates can be passed
+        // in as Vec<MerklePriceUpdate>
+        price_updates: Vec<Vec<u8>>,
+    ) -> Result<()> {
         let vaa = &ctx.accounts.posted_vaa;
         let signature_set = &ctx.accounts.signature_set;
         require_keys_eq!(

+ 3 - 3
target_chains/solana/programs/pyth-solana-receiver/src/state.rs

@@ -24,13 +24,13 @@ impl AccountDeserialize for AnchorVaa {
             *given_disc == *b"vaa",
             ReceiverError::PostedVaaHeaderWrongMagicNumber
         );
-        Self::try_deserialize_unchecked(&mut &buf[3..])
+        Self::try_deserialize_unchecked(buf)
     }
 
     // Manual implementation because this account does not have an anchor discriminator
     fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
-        AnchorDeserialize::deserialize(buf)
-            .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into())
+        let mut data: &[u8] = &buf[3..];
+        AnchorDeserialize::deserialize(&mut data).map_err(Into::into)
     }
 }
 

+ 2 - 1
target_chains/solana/programs/pyth-solana-receiver/tests/cases/mod.rs

@@ -1 +1,2 @@
-pub use super::program_test::*;
+mod test_update_price;
+// mod test_upgrade_program;

+ 60 - 0
target_chains/solana/programs/pyth-solana-receiver/tests/cases/test_upgrade_program.rs

@@ -0,0 +1,60 @@
+use crate::program_test::ProgramSimulator;
+use solana_program::bpf_loader_upgradeable;
+use solana_program::rent::Rent;
+use solana_program_test::read_file;
+use solana_sdk::signature::{Keypair, Signer};
+use std::path::Path;
+use std::process::Stdio;
+
+#[tokio::test]
+async fn test_update_program() {
+    let mut sim = ProgramSimulator::new().await;
+
+    let buffer_keypair = Keypair::new();
+    let upgrade_authority = sim.upgrade_authority;
+    let program_filepath = std::env::current_dir()
+        .unwrap()
+        .join(Path::new("../../target/deploy/pyth_solana_receiver.so"));
+    let mut program_data = read_file(
+        std::env::current_dir()
+            .unwrap()
+            .join(Path::new("../../target/deploy/pyth_solana_receiver.so")),
+    );
+
+    let mut target_tmp_dir = std::env::current_dir()
+        .unwrap()
+        .join(Path::new("../../target/tmp/genesis_keypair.so"));
+    println!("target_tmp_dir: {:?}", target_tmp_dir);
+
+    let minimum_balance = Rent::default().minimum_balance(program_data.len());
+    let data_len = program_data.len();
+    let initial_ixs = bpf_loader_upgradeable::create_buffer(
+        &sim.genesis_keypair.pubkey(),
+        &buffer_keypair.pubkey(),
+        &upgrade_authority.pubkey(),
+        minimum_balance,
+        data_len,
+    )
+    .unwrap();
+
+    // let exit = std::process::Command::new("solana")
+    //     .arg("program")
+    //     .arg("deploy")
+    //     .arg("--url")
+    //     .arg("http://127.0.0.1:8899")
+    //     .arg("--keypair")
+    //     .arg(&ProgramSimulator::keypair_filepath("genesis_keypair"))
+    //     .arg("--upgrade-authority")
+    //     .arg(&ProgramSimulator::keypair_filepath("upgrade_authority"))
+    //     .arg("--program-id")
+    //     .arg(&sim.program_id.to_string())
+    //     .arg(program_filepath)
+    //     .stdout(Stdio::inherit())
+    //     .stderr(Stdio::inherit())
+    //     .output()
+    //     .expect("Must deploy");
+    // if !exit.status.success() {
+    //     println!("There was a problem deploying: {exit:?}.");
+    //     std::process::exit(exit.status.code().unwrap_or(1));
+    // }
+}

+ 154 - 7
target_chains/solana/programs/pyth-solana-receiver/tests/program_test/mod.rs

@@ -1,5 +1,6 @@
 use {
     pyth_solana_receiver::ID,
+    solana_bpf_loader_program::process_instruction,
     solana_program::{
         bpf_loader_upgradeable::{
             self,
@@ -19,9 +20,14 @@ use {
         BanksClientError,
         ProgramTest,
         ProgramTestBanksClientExt,
+        ProgramTestContext,
     },
     solana_sdk::{
-        account::Account,
+        account::{
+            Account,
+            AccountSharedData,
+        },
+        account_utils::StateMut,
         signature::{
             Keypair,
             Signer,
@@ -45,23 +51,136 @@ pub struct ProgramSimulator {
 }
 
 impl ProgramSimulator {
+    pub async fn setup_test_context() -> ProgramTestContext {
+        let program_test =
+            ProgramTest::new("", bpf_loader_upgradeable::ID, Some(process_instruction));
+        program_test.start_with_context().await
+    }
     /// Deploys the target chain contract as upgradable
     pub async fn new() -> ProgramSimulator {
-        let mut program_test = ProgramTest::new("pyth_solana_receiver", ID, None);
+        // let mut program_test = ProgramTest::new("pyth_solana_receiver", ID, None);
+        //
+        // let upgrade_authority_keypair = Keypair::new();
+        // let mut program_test = Self::setup_test_context().await;
+        // let program_file =
+        //     find_file("pyth_solana_receiver.so").expect("Failed to locate program file");
+        // let mut bpf_data = read_file(program_file);
+        let mut bpf_data = read_file(
+            std::env::current_dir()
+                .unwrap()
+                .join(Path::new("../../target/deploy/pyth_solana_receiver.so")),
+        );
+        println!("got bpf data");
+
+        let mut program_test = ProgramTest::default();
+        // let mut program_test =
+        //     ProgramTest::new("", bpf_loader_upgradeable::ID, Some(process_instruction));
+
+        println!("created program_test");
+
+        let program_key = Pubkey::try_from(ID).unwrap();
+        // This PDA is the actual address in the real world
+        // https://docs.rs/solana-program/1.6.4/solana_program/bpf_loader_upgradeable/index.html
+        let (programdata_address, _) =
+            Pubkey::find_program_address(&[&program_key.to_bytes()], &bpf_loader_upgradeable::id());
 
         let upgrade_authority_keypair = Keypair::new();
 
+        let program_deserialized = UpgradeableLoaderState::Program {
+            programdata_address,
+        };
+        let programdata_deserialized = UpgradeableLoaderState::ProgramData {
+            slot:                      1,
+            upgrade_authority_address: Some(upgrade_authority_keypair.pubkey()),
+        };
+        //
+        // // Program contains a pointer to progradata
+        let program_vec = bincode::serialize(&program_deserialized).unwrap();
+        // Programdata contains a header and the binary of the program
+        let mut programdata_vec = bincode::serialize(&programdata_deserialized).unwrap();
+        programdata_vec.append(&mut bpf_data);
+
+        let program_account = Account {
+            lamports:   Rent::default().minimum_balance(program_vec.len()),
+            data:       program_vec,
+            owner:      bpf_loader_upgradeable::ID,
+            executable: true,
+            rent_epoch: Epoch::default(),
+        };
+        let programdata_account = Account {
+            lamports:   Rent::default().minimum_balance(programdata_vec.len()),
+            data:       programdata_vec,
+            owner:      bpf_loader_upgradeable::ID,
+            executable: false,
+            rent_epoch: Epoch::default(),
+        };
+        //
+        // // Add to both accounts to program test, now the program is deploy as upgradable
+        program_test.add_account(program_key, program_account);
+        program_test.add_account(programdata_address, programdata_account);
+
         // Start validator
-        let (banks_client, genesis_keypair, recent_blockhash) = program_test.start().await;
+        // let (banks_client, genesis_keypair, recent_blockhash) = program_test.start().await;
+
+        let program_test_context = program_test.start_with_context().await;
+        println!("started program test context");
+
+        // Self::add_upgradeable_loader_account(
+        //     &mut program_test_context,
+        //     &pyth_solana_receiver::ID,
+        //     &UpgradeableLoaderState::Program {
+        //         programdata_address,
+        //     },
+        //     UpgradeableLoaderState::size_of_program(),
+        //     |_| {},
+        // )
+        // .await;
+        // let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
+        // let program_data_len = UpgradeableLoaderState::size_of_programdata(bpf_data.len());
+        // Self::add_upgradeable_loader_account(
+        //     &mut program_test_context,
+        //     &programdata_address,
+        //     &UpgradeableLoaderState::ProgramData {
+        //         slot: 0,
+        //         upgrade_authority_address: Some(upgrade_authority_keypair.pubkey()),
+        //     },
+        //     program_data_len,
+        //     |account| {
+        //         account.data_as_mut_slice()[programdata_data_offset..].copy_from_slice(&bpf_data)
+        //     },
+        // )
+        // .await;
+        // let genesis_keypair_filepath = Self::keypair_filepath("genesis_keypair");
+        // println!("genesis_keypair_filepath: {:?}", genesis_keypair_filepath);
+        //
+        // program_test_context
+        //     .payer
+        //     .write_to_file(genesis_keypair_filepath)
+        //     .unwrap();
+        // println!("wrote genesis keypair to file");
+        //
+        // let upgrade_authority_keypair_filepath = Self::keypair_filepath("upgrade_authority");
+        // upgrade_authority_keypair
+        //     .write_to_file(upgrade_authority_keypair_filepath)
+        //     .unwrap();
+        // println!("wrote genesis keypair to file");
 
         let mut result = ProgramSimulator {
-            program_id: pyth_solana_receiver::ID,
-            banks_client,
-            last_blockhash: recent_blockhash,
+            program_id:        pyth_solana_receiver::ID,
+            banks_client:      program_test_context.banks_client,
+            last_blockhash:    program_test_context.last_blockhash,
             upgrade_authority: upgrade_authority_keypair,
-            genesis_keypair,
+            genesis_keypair:   program_test_context.payer,
         };
 
+        // let mut result = ProgramSimulator {
+        //     program_id: pyth_solana_receiver::ID,
+        //     banks_client,
+        //     last_blockhash: recent_blockhash,
+        //     upgrade_authority: upgrade_authority_keypair,
+        //     genesis_keypair,
+        // };
+
         // Transfer money to upgrade_authority so it can call the instructions
         result
             .airdrop(&result.upgrade_authority.pubkey(), 1000 * LAMPORTS_PER_SOL)
@@ -102,4 +221,32 @@ impl ProgramSimulator {
         self.process_ix(instruction, &vec![], &self.genesis_keypair.insecure_clone())
             .await
     }
+
+    pub async fn add_upgradeable_loader_account(
+        context: &mut ProgramTestContext,
+        account_address: &Pubkey,
+        account_state: &UpgradeableLoaderState,
+        account_data_len: usize,
+        account_callback: impl Fn(&mut AccountSharedData),
+    ) {
+        let rent = context.banks_client.get_rent().await.unwrap();
+        let mut account = AccountSharedData::new(
+            rent.minimum_balance(account_data_len),
+            account_data_len,
+            &bpf_loader_upgradeable::ID,
+        );
+        account
+            .set_state(account_state)
+            .expect("state failed to serialize into account data");
+        account_callback(&mut account);
+        context.set_account(account_address, &account);
+    }
+
+    // pub fn keypair_filepath(keypair_name: &str) -> PathBuf {
+    //     let mut tmp_keypath_dir = std::env::current_dir()
+    //         .unwrap()
+    //         .join(Path::new("../../target/tmp"));
+    //     tmp_keypath_dir.push(format!("{}.json", keypair_name));
+    //     tmp_keypath_dir
+    // }
 }

+ 1 - 1
target_chains/solana/programs/pyth-solana-receiver/tests/test_all.rs

@@ -1,4 +1,4 @@
-#![cfg(feature = "test-bpf")]
+// #![cfg(feature = "test-bpf")]
 
 mod cases;