diff --git a/solana/pyth2wormhole/client/src/cli.rs b/solana/pyth2wormhole/client/src/cli.rs index 6794cf91..c21e6770 100644 --- a/solana/pyth2wormhole/client/src/cli.rs +++ b/solana/pyth2wormhole/client/src/cli.rs @@ -35,10 +35,10 @@ pub struct Cli { pub enum Action { #[clap(about = "Initialize a pyth2wormhole program freshly deployed under ")] Init { - /// The bridge program account - #[clap(short = 'w', long = "wh-prog")] - wh_prog: Pubkey, - #[clap(short = 'o', long = "owner")] + /// The bridge program account + #[clap(short = 'w', long = "wh-prog")] + wh_prog: Pubkey, + #[clap(short = 'o', long = "owner")] owner_addr: Pubkey, #[clap(short = 'p', long = "pyth-owner")] pyth_owner_addr: Pubkey, @@ -47,20 +47,22 @@ pub enum Action { about = "Use an existing pyth2wormhole program to attest product price information to another chain" )] Attest { - #[clap(short = 'f', long = "--config", about = "Attestation YAML config")] - attestation_cfg: PathBuf, + #[clap(short = 'f', long = "--config", about = "Attestation YAML config")] + attestation_cfg: PathBuf, }, - #[clap(about = "Update an existing pyth2wormhole program's settings (currently set owner only)")] + #[clap( + about = "Update an existing pyth2wormhole program's settings (currently set owner only)" + )] SetConfig { - /// Current owner keypair path + /// Current owner keypair path #[clap(long = "owner", default_value = "~/.config/solana/id.json")] - owner: String, - /// New owner to set + owner: String, + /// New owner to set #[clap(long = "new-owner")] - new_owner_addr: Pubkey, + new_owner_addr: Pubkey, #[clap(long = "new-wh-prog")] - new_wh_prog: Pubkey, + new_wh_prog: Pubkey, #[clap(long = "new-pyth-owner")] - new_pyth_owner_addr: Pubkey, + new_pyth_owner_addr: Pubkey, }, } diff --git a/solana/pyth2wormhole/program/src/attest.rs b/solana/pyth2wormhole/program/src/attest.rs index c8783c36..50fb45aa 100644 --- a/solana/pyth2wormhole/program/src/attest.rs +++ b/solana/pyth2wormhole/program/src/attest.rs @@ -1,6 +1,9 @@ use crate::{ config::P2WConfigAccount, - types::{PriceAttestation, batch_serialize}, + types::{ + batch_serialize, + PriceAttestation, + }, }; use borsh::{ BorshDeserialize, @@ -97,7 +100,6 @@ pub struct Attest<'b> { // pub pyth_product10: Option>, // pub pyth_price10: Option>, - pub clock: Sysvar<'b, Clock>, /// Wormhole program address - must match the config value @@ -108,7 +110,6 @@ pub struct Attest<'b> { // This contract makes no attempt to exhaustively validate // Wormhole's account inputs. Only the wormhole contract address // is validated (see above). - /// Bridge config needed for fee calculation pub wh_bridge: Mut>, @@ -162,9 +163,8 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So accs.pyth_price4.as_ref(), accs.pyth_product5.as_ref(), accs.pyth_price5.as_ref(), - - // Did you read the comment near `pyth_product`? - // accs.pyth_product6.as_ref(), + // Did you read the comment near `pyth_product`? + // accs.pyth_product6.as_ref(), // accs.pyth_price6.as_ref(), // accs.pyth_product7.as_ref(), // accs.pyth_price7.as_ref(), @@ -192,45 +192,41 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So let mut attestations = Vec::with_capacity(price_pairs.len() / 2); for pair in price_pairs.as_slice().chunks_exact(2) { + let product = pair[0]; + let price = pair[1]; - let product = pair[0]; - let price = pair[1]; - - if accs.config.pyth_owner != *price.owner - || accs.config.pyth_owner != *product.owner - { + if accs.config.pyth_owner != *price.owner || accs.config.pyth_owner != *product.owner { trace!(&format!( "Pair {:?} - {:?}: pyth_owner pubkey mismatch (expected {:?}, got product owner {:?} and price owner {:?}", - - product, price, + product, price, accs.config.pyth_owner, product.owner, price.owner )); return Err(SolitaireError::InvalidOwner(accs.pyth_price.owner.clone()).into()); } - let attestation = PriceAttestation::from_pyth_price_bytes( - price.key.clone(), - accs.clock.unix_timestamp, - &*price.try_borrow_data()?, - )?; + let attestation = PriceAttestation::from_pyth_price_bytes( + price.key.clone(), + accs.clock.unix_timestamp, + &*price.try_borrow_data()?, + )?; - // The following check is crucial against poorly ordered - // account inputs, e.g. [Some(prod1), Some(price1), - // Some(prod2), None, None, Some(price)], interpreted by - // earlier logic as [(prod1, price1), (prod2, price3)]. - // - // Failing to verify the product/price relationship could lead - // to mismatched product/price metadata, which would result in - // a false attestation. - if &attestation.product_id != product.key { - trace!(&format!( - "Price's product_id does not match the pased account (points at {:?} instead)", - attestation.product_id - )); - return Err(ProgramError::InvalidAccountData.into()); - } + // The following check is crucial against poorly ordered + // account inputs, e.g. [Some(prod1), Some(price1), + // Some(prod2), None, None, Some(price)], interpreted by + // earlier logic as [(prod1, price1), (prod2, price3)]. + // + // Failing to verify the product/price relationship could lead + // to mismatched product/price metadata, which would result in + // a false attestation. + if &attestation.product_id != product.key { + trace!(&format!( + "Price's product_id does not match the pased account (points at {:?} instead)", + attestation.product_id + )); + return Err(ProgramError::InvalidAccountData.into()); + } - attestations.push(attestation); + attestations.push(attestation); } trace!("Attestations successfully created"); @@ -249,12 +245,12 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So let post_message_data = ( bridge::instruction::Instruction::PostMessage, PostMessageData { - nonce: 0, // Superseded by the sequence number + nonce: 0, // Superseded by the sequence number payload: batch_serialize(attestations.as_slice().iter()).map_err(|e| { - trace!(e.to_string()); - ProgramError::InvalidAccountData - })?, - consistency_level: data.consistency_level, + trace!(e.to_string()); + ProgramError::InvalidAccountData + })?, + consistency_level: data.consistency_level, }, ); diff --git a/solana/pyth2wormhole/program/src/config.rs b/solana/pyth2wormhole/program/src/config.rs index fb342590..a247d613 100644 --- a/solana/pyth2wormhole/program/src/config.rs +++ b/solana/pyth2wormhole/program/src/config.rs @@ -5,9 +5,18 @@ //! problems related to max batch size mismatches between config and //! contract logic. See attest.rs for details. -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::{ + BorshDeserialize, + BorshSerialize, +}; use solana_program::pubkey::Pubkey; -use solitaire::{processors::seeded::AccountOwner, AccountState, Data, Derive, Owned}; +use solitaire::{ + processors::seeded::AccountOwner, + AccountState, + Data, + Derive, + Owned, +}; #[derive(Default, BorshDeserialize, BorshSerialize)] pub struct Pyth2WormholeConfig { diff --git a/solana/pyth2wormhole/program/src/initialize.rs b/solana/pyth2wormhole/program/src/initialize.rs index 94ae4357..e09308f0 100644 --- a/solana/pyth2wormhole/program/src/initialize.rs +++ b/solana/pyth2wormhole/program/src/initialize.rs @@ -1,11 +1,27 @@ use solana_program::pubkey::Pubkey; -use solitaire::{AccountState, CreationLamports, ExecutionContext, FromAccounts, Info, InstructionContext, Keyed, Mut, Peel, Result as SoliResult, Signer, ToInstruction}; +use solitaire::{ + AccountState, + CreationLamports, + ExecutionContext, + FromAccounts, + Info, + InstructionContext, + Keyed, + Mut, + Peel, + Result as SoliResult, + Signer, + ToInstruction, +}; -use crate::config::{P2WConfigAccount, Pyth2WormholeConfig}; +use crate::config::{ + P2WConfigAccount, + Pyth2WormholeConfig, +}; #[derive(FromAccounts, ToInstruction)] pub struct Initialize<'b> { - pub new_config: Mut>, + pub new_config: Mut>, pub payer: Mut>>, } diff --git a/solana/pyth2wormhole/program/src/lib.rs b/solana/pyth2wormhole/program/src/lib.rs index 92ff361c..d4e6b7b8 100644 --- a/solana/pyth2wormhole/program/src/lib.rs +++ b/solana/pyth2wormhole/program/src/lib.rs @@ -1,4 +1,3 @@ - #![feature(adt_const_params)] pub mod attest; pub mod config; @@ -29,7 +28,6 @@ solitaire! { SetConfig(Pyth2WormholeConfig) => set_config, } - #[cfg(feature = "wasm")] #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] pub mod wasm; diff --git a/solana/pyth2wormhole/program/src/types/mod.rs b/solana/pyth2wormhole/program/src/types/mod.rs index aa5643b8..5e749d91 100644 --- a/solana/pyth2wormhole/program/src/types/mod.rs +++ b/solana/pyth2wormhole/program/src/types/mod.rs @@ -47,7 +47,6 @@ use self::pyth_extensions::{ P2WPriceType, }; - /// Precedes every message implementing the p2w serialization format pub const P2W_MAGIC: &'static [u8] = b"P2WH"; diff --git a/solana/pyth2wormhole/program/src/types/pyth_extensions.rs b/solana/pyth2wormhole/program/src/types/pyth_extensions.rs index ee056403..5c2c1ced 100644 --- a/solana/pyth2wormhole/program/src/types/pyth_extensions.rs +++ b/solana/pyth2wormhole/program/src/types/pyth_extensions.rs @@ -1,7 +1,11 @@ //! This module contains 1:1 (or close) copies of selected Pyth types //! with quick and dirty enhancements. -use std::{convert::TryInto, io::Read, mem}; +use std::{ + convert::TryInto, + io::Read, + mem, +}; use pyth_client::{ CorpAction, @@ -13,7 +17,10 @@ use solitaire::ErrBox; /// 1:1 Copy of pyth_client::PriceType with derived additional traits. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))] +#[cfg_attr( + feature = "wasm", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] #[repr(u8)] pub enum P2WPriceType { Unknown, @@ -37,7 +44,10 @@ impl Default for P2WPriceType { /// 1:1 Copy of pyth_client::PriceStatus with derived additional traits. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))] +#[cfg_attr( + feature = "wasm", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] pub enum P2WPriceStatus { Unknown, Trading, @@ -64,7 +74,10 @@ impl Default for P2WPriceStatus { /// 1:1 Copy of pyth_client::CorpAction with derived additional traits. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))] +#[cfg_attr( + feature = "wasm", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] pub enum P2WCorpAction { NoCorpAct, } @@ -85,7 +98,10 @@ impl From<&CorpAction> for P2WCorpAction { /// 1:1 Copy of pyth_client::Ema with all-pub fields. #[derive(Clone, Default, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))] +#[cfg_attr( + feature = "wasm", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] #[repr(C)] pub struct P2WEma { pub val: i64, @@ -146,7 +162,7 @@ impl P2WEma { let mut val_vec = vec![0u8; mem::size_of::()]; bytes.read_exact(val_vec.as_mut_slice())?; let val = i64::from_be_bytes(val_vec.as_slice().try_into()?); - + let mut numer_vec = vec![0u8; mem::size_of::()]; bytes.read_exact(numer_vec.as_mut_slice())?; let numer = i64::from_be_bytes(numer_vec.as_slice().try_into()?); diff --git a/solana/pyth2wormhole/program/src/wasm.rs b/solana/pyth2wormhole/program/src/wasm.rs index a6dafbef..ebc99126 100644 --- a/solana/pyth2wormhole/program/src/wasm.rs +++ b/solana/pyth2wormhole/program/src/wasm.rs @@ -1,10 +1,13 @@ -use solitaire::Seeded; use solana_program::pubkey::Pubkey; +use solitaire::Seeded; use wasm_bindgen::prelude::*; use std::str::FromStr; -use crate::{attest::P2WEmitter, types}; +use crate::{ + attest::P2WEmitter, + types, +}; #[wasm_bindgen] pub fn get_emitter_address(program_id: String) -> Vec { @@ -17,14 +20,13 @@ pub fn get_emitter_address(program_id: String) -> Vec { #[wasm_bindgen] pub fn parse_attestation(bytes: Vec) -> JsValue { let a = types::PriceAttestation::deserialize(bytes.as_slice()).unwrap(); - + JsValue::from_serde(&a).unwrap() } #[wasm_bindgen] pub fn parse_batch_attestation(bytes: Vec) -> JsValue { let a = types::batch_deserialize(bytes.as_slice()).unwrap(); - JsValue::from_serde(&a).unwrap() }