diff --git a/pythnet/pythnet_sdk/Cargo.toml b/pythnet/pythnet_sdk/Cargo.toml index cd47cc93..87381912 100644 --- a/pythnet/pythnet_sdk/Cargo.toml +++ b/pythnet/pythnet_sdk/Cargo.toml @@ -10,6 +10,9 @@ edition = "2021" crate-type = ["lib"] name = "pythnet_sdk" +[features] +test-utils = ["dep:wormhole-sdk", "dep:serde_wormhole"] + [dependencies] bincode = "1.3.1" borsh = "0.10.3" @@ -23,6 +26,12 @@ quickcheck = { version = "1", optional = true} sha3 = "0.10.4" slow_primes = "0.1.14" thiserror = "1.0.40" +serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", optional = true, tag="v2.23.37"} +wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", optional = true, tag="v2.23.37"} + +[patch.crates-io] +serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag="v2.23.37"} + [dev-dependencies] base64 = "0.21.0" diff --git a/pythnet/pythnet_sdk/src/lib.rs b/pythnet/pythnet_sdk/src/lib.rs index c30a316c..2c3632c6 100644 --- a/pythnet/pythnet_sdk/src/lib.rs +++ b/pythnet/pythnet_sdk/src/lib.rs @@ -5,6 +5,9 @@ pub mod messages; pub mod wire; pub mod wormhole; +#[cfg(feature = "test-utils")] +pub mod test_utils; + pub(crate) type Pubkey = [u8; 32]; /// Official Message Buffer Program Id diff --git a/pythnet/pythnet_sdk/src/test_utils/mod.rs b/pythnet/pythnet_sdk/src/test_utils/mod.rs new file mode 100644 index 00000000..be382037 --- /dev/null +++ b/pythnet/pythnet_sdk/src/test_utils/mod.rs @@ -0,0 +1,166 @@ +use { + crate::{ + accumulators::{ + merkle::MerkleTree, + Accumulator, + }, + hashers::keccak256_160::Keccak160, + messages::{ + Message, + PriceFeedMessage, + }, + wire::{ + to_vec, + v1::{ + AccumulatorUpdateData, + MerklePriceUpdate, + Proof, + WormholeMerkleRoot, + WormholeMessage, + WormholePayload, + }, + PrefixedVec, + }, + }, + byteorder::BigEndian, + serde_wormhole::RawMessage, + wormhole_sdk::{ + Address, + Chain, + Vaa, + }, +}; + +pub struct DataSource { + pub address: Address, + pub chain: Chain, +} + +pub const DEFAULT_DATA_SOURCE: DataSource = DataSource { + address: Address([1u8; 32]), + chain: Chain::Solana, +}; + +pub const DEFAULT_GOVERNANCE_SOURCE: DataSource = DataSource { + address: Address([2u8; 32]), + chain: Chain::Ethereum, +}; + +pub const WRONG_SOURCE: DataSource = DataSource { + address: Address([3u8; 32]), + chain: Chain::Bsc, +}; + +pub const SECONDARY_DATA_SOURCE: DataSource = DataSource { + address: Address([4u8; 32]), + chain: Chain::Polygon, +}; + +pub const SECONDARY_GOVERNANCE_SOURCE: DataSource = DataSource { + address: Address([5u8; 32]), + chain: Chain::Avalanche, +}; + +pub const DEFAULT_CHAIN_ID: Chain = Chain::Oasis; +pub const WRONG_CHAIN_ID: Chain = Chain::Algorand; +pub const DEFAULT_VALID_TIME_PERIOD: u64 = 180; + +const DEFAULT_SEQUENCE: u64 = 2; + + +pub fn create_dummy_price_feed_message(value: i64) -> Message { + let mut dummy_id = [0; 32]; + dummy_id[0] = value as u8; + let msg = PriceFeedMessage { + feed_id: dummy_id, + price: value, + conf: value as u64, + exponent: value as i32, + publish_time: value, + prev_publish_time: value, + ema_price: value, + ema_conf: value as u64, + }; + Message::PriceFeedMessage(msg) +} + +pub fn create_accumulator_message( + all_feeds: &[Message], + updates: &[Message], + corrupt_wormhole_message: bool, +) -> Vec { + let all_feeds_bytes: Vec<_> = all_feeds + .iter() + .map(|f| to_vec::<_, BigEndian>(f).unwrap()) + .collect(); + let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect(); + let tree = MerkleTree::::new(all_feeds_bytes_refs.as_slice()).unwrap(); + let mut price_updates: Vec = vec![]; + for update in updates { + let proof = tree + .prove(&to_vec::<_, BigEndian>(update).unwrap()) + .unwrap(); + price_updates.push(MerklePriceUpdate { + message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()), + proof, + }); + } + create_accumulator_message_from_updates( + price_updates, + tree, + corrupt_wormhole_message, + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, + ) +} + +pub fn create_accumulator_message_from_updates( + price_updates: Vec, + tree: MerkleTree, + corrupt_wormhole_message: bool, + emitter_address: Address, + emitter_chain: Chain, +) -> Vec { + let mut root_hash = [0u8; 20]; + root_hash.copy_from_slice(&to_vec::<_, BigEndian>(&tree.root).unwrap()[..20]); + let wormhole_message = WormholeMessage::new(WormholePayload::Merkle(WormholeMerkleRoot { + slot: 0, + ring_size: 0, + root: root_hash, + })); + + let mut vaa_payload = to_vec::<_, BigEndian>(&wormhole_message).unwrap(); + if corrupt_wormhole_message { + vaa_payload[0] = 0; + } + + let vaa = create_vaa_from_payload( + &vaa_payload, + emitter_address, + emitter_chain, + DEFAULT_SEQUENCE, + ); + + let accumulator_update_data = AccumulatorUpdateData::new(Proof::WormholeMerkle { + vaa: PrefixedVec::from(serde_wormhole::to_vec(&vaa).unwrap()), + updates: price_updates, + }); + + to_vec::<_, BigEndian>(&accumulator_update_data).unwrap() +} + +pub fn create_vaa_from_payload( + payload: &[u8], + emitter_address: Address, + emitter_chain: Chain, + sequence: u64, +) -> Vaa> { + let vaa: Vaa> = Vaa { + emitter_chain: emitter_chain, + emitter_address: emitter_address, + sequence, + payload: >::from(payload.to_vec()), + ..Default::default() + }; + vaa +} diff --git a/target_chains/cosmwasm/Cargo.lock b/target_chains/cosmwasm/Cargo.lock index 716fc000..89de8427 100644 --- a/target_chains/cosmwasm/Cargo.lock +++ b/target_chains/cosmwasm/Cargo.lock @@ -213,6 +213,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -1483,10 +1494,12 @@ dependencies = [ "serde_derive", "serde_json", "serde_repr", + "serde_wormhole", "sha3 0.9.1", "terraswap", "thiserror", "wormhole-cosmwasm", + "wormhole-sdk", ] [[package]] @@ -1546,9 +1559,11 @@ dependencies = [ "hex", "rustc_version", "serde", + "serde_wormhole", "sha3 0.10.8", "slow_primes", "thiserror", + "wormhole-sdk", ] [[package]] @@ -1617,6 +1632,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" + [[package]] name = "region" version = "3.0.0" @@ -1857,6 +1878,18 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "serde_wormhole" +version = "0.1.0" +source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.23.37#846c2e9c9dce18a48745e79ba2ee7eaa5acaf1f4" +dependencies = [ + "base64", + "itoa", + "serde", + "serde_bytes", + "thiserror", +] + [[package]] name = "sha2" version = "0.9.9" @@ -2661,6 +2694,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "wormhole-sdk" +version = "0.1.0" +source = "git+https://github.com/wormhole-foundation/wormhole?tag=v2.23.37#846c2e9c9dce18a48745e79ba2ee7eaa5acaf1f4" +dependencies = [ + "anyhow", + "bstr", + "schemars", + "serde", + "serde_wormhole", + "sha3 0.10.8", + "thiserror", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/target_chains/cosmwasm/Cargo.toml b/target_chains/cosmwasm/Cargo.toml index 969a4801..aebf9f95 100644 --- a/target_chains/cosmwasm/Cargo.toml +++ b/target_chains/cosmwasm/Cargo.toml @@ -14,3 +14,6 @@ codegen-units = 1 panic = 'abort' incremental = false overflow-checks = true + +[patch.crates-io] +serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag="v2.23.37"} diff --git a/target_chains/cosmwasm/contracts/pyth/Cargo.toml b/target_chains/cosmwasm/contracts/pyth/Cargo.toml index 0ea3b041..4a41e47a 100644 --- a/target_chains/cosmwasm/contracts/pyth/Cargo.toml +++ b/target_chains/cosmwasm/contracts/pyth/Cargo.toml @@ -45,3 +45,6 @@ wormhole-cosmwasm = {git = "https://github.com/wormhole-foundation/wormhole", t [dev-dependencies] cosmwasm-vm = { version = "1.0.0", default-features = false } serde_json = "1.0" +pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk", features = ["test-utils"] } +serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag="v2.23.37"} +wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", tag="v2.23.37"} diff --git a/target_chains/cosmwasm/contracts/pyth/src/contract.rs b/target_chains/cosmwasm/contracts/pyth/src/contract.rs index 055afa00..6dc33980 100644 --- a/target_chains/cosmwasm/contracts/pyth/src/contract.rs +++ b/target_chains/cosmwasm/contracts/pyth/src/contract.rs @@ -835,31 +835,44 @@ mod test { PriceFeedMessage, TwapMessage, }, + test_utils::{ + create_accumulator_message, + create_accumulator_message_from_updates, + create_dummy_price_feed_message, + create_vaa_from_payload, + DEFAULT_CHAIN_ID, + DEFAULT_DATA_SOURCE, + DEFAULT_GOVERNANCE_SOURCE, + DEFAULT_VALID_TIME_PERIOD, + SECONDARY_GOVERNANCE_SOURCE, + WRONG_CHAIN_ID, + WRONG_SOURCE, + }, wire::{ to_vec, - v1::{ - MerklePriceUpdate, - WormholeMerkleRoot, - }, + v1::MerklePriceUpdate, PrefixedVec, }, }, + serde_wormhole::RawMessage, std::time::Duration, + wormhole_sdk::{ + Address, + Chain, + Vaa, + }, }; /// Default valid time period for testing purposes. - const VALID_TIME_PERIOD: Duration = Duration::from_secs(3 * 60); const WORMHOLE_ADDR: &str = "Wormhole"; - const EMITTER_CHAIN: u16 = 3; - - fn default_emitter_addr() -> Vec { - vec![0, 1, 80] - } fn default_config_info() -> ConfigInfo { ConfigInfo { wormhole_contract: Addr::unchecked(WORMHOLE_ADDR), - data_sources: create_data_sources(default_emitter_addr(), EMITTER_CHAIN), + data_sources: create_data_sources( + DEFAULT_DATA_SOURCE.address.0.to_vec(), + DEFAULT_DATA_SOURCE.chain.into(), + ), ..create_zero_config_info() } } @@ -871,25 +884,22 @@ mod test { let mut config = config(dependencies.as_mut().storage); config .save(&ConfigInfo { - valid_time_period: VALID_TIME_PERIOD, + valid_time_period: Duration::from_secs(DEFAULT_VALID_TIME_PERIOD), ..create_zero_config_info() }) .unwrap(); (dependencies, mock_env()) } - /// Mock handler for wormhole queries. - /// Warning: the interface for the `VerifyVAA` action is slightly different than the real wormhole contract. - /// In the mock, you pass in a binary-encoded `ParsedVAA`, and that exact vaa will be returned by wormhole. - /// The real contract uses a different binary VAA format (see `ParsedVAA::deserialize`) which includes - /// the guardian signatures. fn handle_wasm_query(wasm_query: &WasmQuery) -> QuerierResult { match wasm_query { WasmQuery::Smart { contract_addr, msg } if *contract_addr == WORMHOLE_ADDR => { let query_msg = from_binary::(msg); match query_msg { Ok(WormholeQueryMsg::VerifyVAA { vaa, .. }) => { - SystemResult::Ok(ContractResult::Ok(vaa)) + SystemResult::Ok(ContractResult::Ok( + to_binary(&ParsedVAA::deserialize(&vaa).unwrap()).unwrap(), + )) } Err(_e) => SystemResult::Err(SystemError::InvalidRequest { error: "Invalid message".into(), @@ -919,45 +929,30 @@ mod test { } } - fn create_zero_vaa() -> ParsedVAA { - ParsedVAA { - version: 0, - guardian_set_index: 0, - timestamp: 0, - nonce: 0, - len_signers: 0, - emitter_chain: 0, - emitter_address: vec![], - sequence: 0, - consistency_level: 0, - payload: vec![], - hash: vec![], - } - } - fn create_batch_price_update_msg( - emitter_address: &[u8], - emitter_chain: u16, + emitter_address: Address, + emitter_chain: Chain, attestations: Vec, ) -> Binary { let batch_attestation = BatchPriceAttestation { price_attestations: attestations, }; - let mut vaa = create_zero_vaa(); - vaa.emitter_address = emitter_address.to_vec(); - vaa.emitter_chain = emitter_chain; - vaa.payload = batch_attestation.serialize().unwrap(); - - to_binary(&vaa).unwrap() + let vaa = create_vaa_from_payload( + &batch_attestation.serialize().unwrap(), + emitter_address, + emitter_chain, + 0, + ); + serde_wormhole::to_vec(&vaa).unwrap().into() } fn create_batch_price_update_msg_from_attestations( attestations: Vec, ) -> Binary { create_batch_price_update_msg( - default_emitter_addr().as_slice(), - EMITTER_CHAIN, + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, attestations, ) } @@ -1012,8 +1007,8 @@ mod test { fn apply_price_update( config_info: &ConfigInfo, - emitter_address: &[u8], - emitter_chain: u16, + emitter_address: Address, + emitter_chain: Chain, attestations: Vec, ) -> StdResult<(usize, Vec)> { let (mut deps, env) = setup_test(); @@ -1160,20 +1155,16 @@ mod test { let feed1 = create_dummy_price_feed_message(100); let feed2 = create_dummy_price_feed_message(200); let feed3 = create_dummy_price_feed_message(300); - let data = [create_accumulator_message( - &[feed1, feed2, feed3], - &[feed1], - false, - )]; - check_sufficient_fee(&deps.as_ref(), &data) + let data = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false); + check_sufficient_fee(&deps.as_ref(), &[data.into()]) } #[test] fn test_parse_batch_attestation_empty_array() { let (num_attestations, new_attestations) = apply_price_update( &default_config_info(), - default_emitter_addr().as_slice(), - EMITTER_CHAIN, + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, vec![], ) .unwrap(); @@ -1182,85 +1173,6 @@ mod test { assert_eq!(new_attestations.len(), 0); } - fn create_dummy_price_feed_message(value: i64) -> Message { - let mut dummy_id = [0; 32]; - dummy_id[0] = value as u8; - let msg = PriceFeedMessage { - feed_id: dummy_id, - price: value, - conf: value as u64, - exponent: value as i32, - publish_time: value, - prev_publish_time: value, - ema_price: value, - ema_conf: value as u64, - }; - Message::PriceFeedMessage(msg) - } - - fn create_accumulator_message_from_updates( - price_updates: Vec, - tree: MerkleTree, - corrupt_wormhole_message: bool, - emitter_address: Vec, - emitter_chain: u16, - ) -> Binary { - let mut root_hash = [0u8; 20]; - root_hash.copy_from_slice(&to_vec::<_, BigEndian>(&tree.root).unwrap()[..20]); - let wormhole_message = WormholeMessage::new(WormholePayload::Merkle(WormholeMerkleRoot { - slot: 0, - ring_size: 0, - root: root_hash, - })); - - let mut vaa = create_zero_vaa(); - vaa.emitter_address = emitter_address; - vaa.emitter_chain = emitter_chain; - vaa.payload = to_vec::<_, BigEndian>(&wormhole_message).unwrap(); - if corrupt_wormhole_message { - vaa.payload[0] = 0; - } - - let vaa_binary = to_binary(&vaa).unwrap(); - let accumulator_update_data = AccumulatorUpdateData::new(Proof::WormholeMerkle { - vaa: PrefixedVec::from(vaa_binary.to_vec()), - updates: price_updates, - }); - - Binary::from(to_vec::<_, BigEndian>(&accumulator_update_data).unwrap()) - } - - fn create_accumulator_message( - all_feeds: &[Message], - updates: &[Message], - corrupt_wormhole_message: bool, - ) -> Binary { - let all_feeds_bytes: Vec<_> = all_feeds - .iter() - .map(|f| to_vec::<_, BigEndian>(f).unwrap()) - .collect(); - let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect(); - let tree = MerkleTree::::new(all_feeds_bytes_refs.as_slice()).unwrap(); - let mut price_updates: Vec = vec![]; - for update in updates { - let proof = tree - .prove(&to_vec::<_, BigEndian>(update).unwrap()) - .unwrap(); - price_updates.push(MerklePriceUpdate { - message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()), - proof, - }); - } - create_accumulator_message_from_updates( - price_updates, - tree, - corrupt_wormhole_message, - default_emitter_addr(), - EMITTER_CHAIN, - ) - } - - fn check_price_match(deps: &OwnedDeps, msg: &Message) { match msg { Message::PriceFeedMessage(feed_msg) => { @@ -1283,7 +1195,7 @@ mod test { }; } - fn test_accumulator_wrong_source(emitter_address: Vec, emitter_chain: u16) { + fn test_accumulator_wrong_source(emitter_address: Address, emitter_chain: Chain) { let (mut deps, env) = setup_test(); config(&mut deps.storage) .save(&default_config_info()) @@ -1306,20 +1218,19 @@ mod test { emitter_chain, ); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); assert_eq!(result, Err(PythContractError::InvalidUpdateEmitter.into())); } #[test] fn test_accumulator_verify_vaa_sender_fail_wrong_emitter_address() { - let emitter_address = [17, 23, 14]; - test_accumulator_wrong_source(emitter_address.to_vec(), EMITTER_CHAIN); + test_accumulator_wrong_source(WRONG_SOURCE.address, DEFAULT_DATA_SOURCE.chain); } #[test] fn test_accumulator_verify_vaa_sender_fail_wrong_emitter_chain() { - test_accumulator_wrong_source(default_emitter_addr(), EMITTER_CHAIN + 1); + test_accumulator_wrong_source(DEFAULT_DATA_SOURCE.address, WRONG_SOURCE.chain); } #[test] @@ -1336,23 +1247,32 @@ mod test { let feed3 = create_dummy_price_feed_message(300); let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false); - assert_eq!(get_update_fee_amount(&deps.as_ref(), &[msg]).unwrap(), 200); + assert_eq!( + get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(), + 200 + ); let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false); - assert_eq!(get_update_fee_amount(&deps.as_ref(), &[msg]).unwrap(), 100); + assert_eq!( + get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(), + 100 + ); let msg = create_accumulator_message( &[feed1, feed2, feed3], &[feed1, feed2, feed3, feed1, feed3], false, ); - assert_eq!(get_update_fee_amount(&deps.as_ref(), &[msg]).unwrap(), 500); + assert_eq!( + get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(), + 500 + ); let batch_msg = create_batch_price_update_msg_from_attestations(vec![PriceAttestation::default()]); let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false); assert_eq!( - get_update_fee_amount(&deps.as_ref(), &[msg, batch_msg]).unwrap(), + get_update_fee_amount(&deps.as_ref(), &[msg.into(), batch_msg]).unwrap(), 400 ); } @@ -1369,7 +1289,7 @@ mod test { let feed2 = create_dummy_price_feed_message(200); let msg = create_accumulator_message(&[feed1, feed2], &[feed1], false); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_ok()); check_price_match(&deps, &feed1); } @@ -1386,7 +1306,7 @@ mod test { } let msg = create_accumulator_message(&all_feeds, &all_feeds[100..110], false); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_ok()); for i in 100..110 { check_price_match(&deps, &all_feeds[i]); @@ -1421,7 +1341,7 @@ mod test { let msg2 = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg, msg2]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into(), msg2.into()]); assert!(result.is_ok()); check_price_match(&deps, &feed1); @@ -1442,7 +1362,7 @@ mod test { as_mut_price_feed(&mut feed2).price *= 2; let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_ok()); check_price_match(&deps, &feed1); @@ -1464,7 +1384,7 @@ mod test { let msg2 = create_accumulator_message(&[feed1, feed2, feed3], &[feed2, feed3], false); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg, msg2]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into(), msg2.into()]); assert!(result.is_ok()); check_price_match(&deps, &feed1); @@ -1480,9 +1400,9 @@ mod test { let feed1 = create_dummy_price_feed_message(100); let mut msg = create_accumulator_message(&[feed1], &[feed1], false); - msg.0[4] = 3; // major version + msg[4] = 3; // major version let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); assert_eq!( result.unwrap_err(), @@ -1500,7 +1420,7 @@ mod test { let feed1 = create_dummy_price_feed_message(100); let msg = create_accumulator_message(&[feed1], &[feed1], true); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); assert_eq!( result.unwrap_err(), @@ -1528,7 +1448,7 @@ mod test { }); let msg = create_accumulator_message(&[feed1], &[feed1], false); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); assert_eq!( result.unwrap_err(), @@ -1560,11 +1480,11 @@ mod test { price_updates, tree, false, - default_emitter_addr(), - EMITTER_CHAIN, + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, ); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); assert_eq!( result.unwrap_err(), @@ -1594,11 +1514,11 @@ mod test { price_updates, tree, false, - default_emitter_addr(), - EMITTER_CHAIN, + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, ); let info = mock_info("123", &[]); - let result = update_price_feeds(deps.as_mut(), env, info, &[msg]); + let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]); assert!(result.is_err()); assert_eq!( result.unwrap_err(), @@ -1779,8 +1699,8 @@ mod test { fn test_verify_vaa_sender_ok() { let result = apply_price_update( &default_config_info(), - default_emitter_addr().as_slice(), - EMITTER_CHAIN, + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, vec![PriceAttestation::default()], ); assert!(result.is_ok()); @@ -1788,11 +1708,10 @@ mod test { #[test] fn test_verify_vaa_sender_fail_wrong_emitter_address() { - let emitter_address = [17, 23, 14]; let result = apply_price_update( &default_config_info(), - emitter_address.as_slice(), - EMITTER_CHAIN, + WRONG_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, vec![PriceAttestation::default()], ); assert_eq!(result, Err(PythContractError::InvalidUpdateEmitter.into())); @@ -1802,8 +1721,8 @@ mod test { fn test_verify_vaa_sender_fail_wrong_emitter_chain() { let result = apply_price_update( &default_config_info(), - default_emitter_addr().as_slice(), - EMITTER_CHAIN + 1, + DEFAULT_DATA_SOURCE.address, + WRONG_SOURCE.chain, vec![PriceAttestation::default()], ); assert_eq!(result, Err(PythContractError::InvalidUpdateEmitter.into())); @@ -2091,14 +2010,19 @@ mod test { /// against it. Returns the response of the governance instruction along with the resulting config. fn apply_governance_vaa( initial_config: &ConfigInfo, - vaa: &ParsedVAA, + vaa: &Vaa>, ) -> StdResult<(Response, ConfigInfo)> { let (mut deps, env) = setup_test(); config(&mut deps.storage).save(initial_config).unwrap(); let info = mock_info("123", &[]); - let result = execute_governance_instruction(deps.as_mut(), env, info, &to_binary(&vaa)?); + let result = execute_governance_instruction( + deps.as_mut(), + env, + info, + &serde_wormhole::to_vec(vaa).unwrap().into(), + ); result.and_then(|response| config_read(&deps.storage).load().map(|c| (response, c))) } @@ -2107,23 +2031,22 @@ mod test { ConfigInfo { wormhole_contract: Addr::unchecked(WORMHOLE_ADDR), governance_source: PythDataSource { - emitter: Binary(vec![1u8, 2u8]), - chain_id: 3, + emitter: Binary(DEFAULT_GOVERNANCE_SOURCE.address.0.to_vec()), + chain_id: DEFAULT_GOVERNANCE_SOURCE.chain.into(), }, governance_sequence_number: 4, - chain_id: 5, + chain_id: DEFAULT_CHAIN_ID.into(), ..create_zero_config_info() } } - fn governance_vaa(instruction: &GovernanceInstruction) -> ParsedVAA { - let mut vaa = create_zero_vaa(); - vaa.emitter_address = vec![1u8, 2u8]; - vaa.emitter_chain = 3; - vaa.sequence = 7; - vaa.payload = instruction.serialize().unwrap(); - - vaa + fn governance_vaa(instruction: &GovernanceInstruction) -> Vaa> { + create_vaa_from_payload( + &instruction.serialize().unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 7, + ) } #[test] @@ -2132,7 +2055,7 @@ mod test { let test_instruction = GovernanceInstruction { module: Target, - target_chain_id: 5, + target_chain_id: DEFAULT_CHAIN_ID.into(), action: SetFee { val: 6, expo: 0 }, }; let test_vaa = governance_vaa(&test_instruction); @@ -2142,12 +2065,12 @@ mod test { // Wrong emitter address let mut vaa_copy = test_vaa.clone(); - vaa_copy.emitter_address = vec![2u8, 3u8]; + vaa_copy.emitter_address = WRONG_SOURCE.address; assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err()); // wrong source chain let mut vaa_copy = test_vaa.clone(); - vaa_copy.emitter_chain = 4; + vaa_copy.emitter_chain = WRONG_SOURCE.chain; assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err()); // sequence number too low @@ -2157,64 +2080,68 @@ mod test { // wrong magic number let mut vaa_copy = test_vaa.clone(); - vaa_copy.payload[0] = 0; + let mut new_payload = vaa_copy.payload.to_vec(); + new_payload[0] = 0; + vaa_copy.payload = >::from(new_payload); assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err()); // wrong target chain let mut instruction_copy = test_instruction.clone(); - instruction_copy.target_chain_id = 6; + instruction_copy.target_chain_id = WRONG_CHAIN_ID.into(); let mut vaa_copy = test_vaa.clone(); - vaa_copy.payload = instruction_copy.serialize().unwrap(); + vaa_copy.payload = >::from(instruction_copy.serialize().unwrap()); assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err()); // target chain == 0 is allowed let mut instruction_copy = test_instruction.clone(); instruction_copy.target_chain_id = 0; let mut vaa_copy = test_vaa.clone(); - vaa_copy.payload = instruction_copy.serialize().unwrap(); + vaa_copy.payload = >::from(instruction_copy.serialize().unwrap()); assert!(apply_governance_vaa(&test_config, &vaa_copy).is_ok()); // wrong module - let mut instruction_copy = test_instruction.clone(); + let mut instruction_copy = test_instruction; instruction_copy.module = Executor; let mut vaa_copy = test_vaa; - vaa_copy.payload = instruction_copy.serialize().unwrap(); + vaa_copy.payload = >::from(instruction_copy.serialize().unwrap()); assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err()); // invalid action index - let _instruction_copy = test_instruction; - vaa_copy.payload[9] = 100; + let mut new_payload = vaa_copy.payload.to_vec(); + new_payload[9] = 100; + vaa_copy.payload = >::from(new_payload); assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err()); } #[test] fn test_authorize_governance_transfer_success() { let source_2 = PythDataSource { - emitter: Binary::from([2u8; 32]), - chain_id: 4, + emitter: Binary::from(SECONDARY_GOVERNANCE_SOURCE.address.0), + chain_id: SECONDARY_GOVERNANCE_SOURCE.chain.into(), }; let test_config = governance_test_config(); + + let claim_vaa = create_vaa_from_payload( + &GovernanceInstruction { + module: Target, + target_chain_id: test_config.chain_id, + action: RequestGovernanceDataSourceTransfer { + governance_data_source_index: 1, + }, + } + .serialize() + .unwrap(), + SECONDARY_GOVERNANCE_SOURCE.address, + SECONDARY_GOVERNANCE_SOURCE.chain, + 12, + ); + let test_instruction = GovernanceInstruction { module: Target, target_chain_id: test_config.chain_id, action: AuthorizeGovernanceDataSourceTransfer { - claim_vaa: to_binary(&ParsedVAA { - emitter_address: source_2.emitter.to_vec(), - emitter_chain: source_2.chain_id, - sequence: 12, - payload: GovernanceInstruction { - module: Target, - target_chain_id: test_config.chain_id, - action: RequestGovernanceDataSourceTransfer { - governance_data_source_index: 1, - }, - } - .serialize() - .unwrap(), - ..create_zero_vaa() - }) - .unwrap(), + claim_vaa: serde_wormhole::to_vec(&claim_vaa).unwrap().into(), }, }; @@ -2227,33 +2154,29 @@ mod test { #[test] fn test_authorize_governance_transfer_bad_source_index() { - let source_2 = PythDataSource { - emitter: Binary::from([2u8; 32]), - chain_id: 4, - }; - let mut test_config = governance_test_config(); test_config.governance_source_index = 10; + + let claim_vaa = create_vaa_from_payload( + &GovernanceInstruction { + module: Target, + target_chain_id: test_config.chain_id, + action: RequestGovernanceDataSourceTransfer { + governance_data_source_index: 10, + }, + } + .serialize() + .unwrap(), + SECONDARY_GOVERNANCE_SOURCE.address, + SECONDARY_GOVERNANCE_SOURCE.chain, + 12, + ); + let test_instruction = GovernanceInstruction { module: Target, target_chain_id: test_config.chain_id, action: AuthorizeGovernanceDataSourceTransfer { - claim_vaa: to_binary(&ParsedVAA { - emitter_address: source_2.emitter.to_vec(), - emitter_chain: source_2.chain_id, - sequence: 12, - payload: GovernanceInstruction { - module: Target, - target_chain_id: test_config.chain_id, - action: RequestGovernanceDataSourceTransfer { - governance_data_source_index: 10, - }, - } - .serialize() - .unwrap(), - ..create_zero_vaa() - }) - .unwrap(), + claim_vaa: serde_wormhole::to_vec(&claim_vaa).unwrap().into(), }, }; @@ -2266,32 +2189,28 @@ mod test { #[test] fn test_authorize_governance_transfer_bad_target_chain() { - let source_2 = PythDataSource { - emitter: Binary::from([2u8; 32]), - chain_id: 4, - }; - let test_config = governance_test_config(); + + let claim_vaa = create_vaa_from_payload( + &GovernanceInstruction { + module: Target, + target_chain_id: WRONG_CHAIN_ID.into(), + action: RequestGovernanceDataSourceTransfer { + governance_data_source_index: 11, + }, + } + .serialize() + .unwrap(), + SECONDARY_GOVERNANCE_SOURCE.address, + SECONDARY_GOVERNANCE_SOURCE.chain, + 12, + ); + let test_instruction = GovernanceInstruction { module: Target, target_chain_id: test_config.chain_id, action: AuthorizeGovernanceDataSourceTransfer { - claim_vaa: to_binary(&ParsedVAA { - emitter_address: source_2.emitter.to_vec(), - emitter_chain: source_2.chain_id, - sequence: 12, - payload: GovernanceInstruction { - module: Target, - target_chain_id: test_config.chain_id + 1, - action: RequestGovernanceDataSourceTransfer { - governance_data_source_index: 11, - }, - } - .serialize() - .unwrap(), - ..create_zero_vaa() - }) - .unwrap(), + claim_vaa: serde_wormhole::to_vec(&claim_vaa).unwrap().into(), }, }; @@ -2354,7 +2273,7 @@ mod test { let test_instruction = GovernanceInstruction { module: Target, - target_chain_id: 5, + target_chain_id: DEFAULT_CHAIN_ID.into(), action: SetFee { val: 6, expo: 1 }, }; let test_vaa = governance_vaa(&test_instruction); @@ -2366,7 +2285,7 @@ mod test { let test_instruction = GovernanceInstruction { module: Target, - target_chain_id: 5, + target_chain_id: DEFAULT_CHAIN_ID.into(), action: SetFee { val: 6, expo: 0 }, }; let test_vaa = governance_vaa(&test_instruction); @@ -2384,7 +2303,7 @@ mod test { let test_instruction = GovernanceInstruction { module: Target, - target_chain_id: 5, + target_chain_id: DEFAULT_CHAIN_ID.into(), action: SetValidPeriod { valid_seconds: 20 }, }; let test_vaa = governance_vaa(&test_instruction); diff --git a/target_chains/near/receiver/Cargo.lock b/target_chains/near/receiver/Cargo.lock index c80ecfd6..51ae912a 100644 --- a/target_chains/near/receiver/Cargo.lock +++ b/target_chains/near/receiver/Cargo.lock @@ -2239,9 +2239,11 @@ dependencies = [ "hex 0.4.3", "rustc_version", "serde", + "serde_wormhole", "sha3", "slow_primes", "thiserror", + "wormhole-sdk", ] [[package]] diff --git a/target_chains/near/receiver/Cargo.toml b/target_chains/near/receiver/Cargo.toml index e3f01372..2ec47eb5 100644 --- a/target_chains/near/receiver/Cargo.toml +++ b/target_chains/near/receiver/Cargo.toml @@ -38,6 +38,7 @@ tokio = { version = "1.23.0", features = ["full"] } serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag="v2.23.37" } workspaces = { version = "0.7.0" } wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", tag="v2.23.37" } +pythnet-sdk = { path = "../../../pythnet/pythnet_sdk", features = ["test-utils"] } diff --git a/target_chains/near/receiver/tests/workspaces.rs b/target_chains/near/receiver/tests/workspaces.rs index e4b4b7c1..a56f5221 100644 --- a/target_chains/near/receiver/tests/workspaces.rs +++ b/target_chains/near/receiver/tests/workspaces.rs @@ -1,5 +1,4 @@ use { - byteorder::BigEndian, near_sdk::json_types::U128, pyth::{ governance::{ @@ -20,34 +19,17 @@ use { PriceAttestation, PriceStatus, }, - pythnet_sdk::{ - accumulators::{ - merkle::MerkleTree, - Accumulator, - }, - hashers::keccak256_160::Keccak160, - messages::{ - Message, - PriceFeedMessage, - }, - wire::{ - to_vec, - v1::{ - AccumulatorUpdateData, - MerklePriceUpdate, - Proof, - WormholeMerkleRoot, - WormholeMessage, - WormholePayload, - }, - PrefixedVec, - }, + pythnet_sdk::test_utils::{ + create_accumulator_message, + create_dummy_price_feed_message, + create_vaa_from_payload, + DEFAULT_DATA_SOURCE, + DEFAULT_GOVERNANCE_SOURCE, + DEFAULT_VALID_TIME_PERIOD, + SECONDARY_DATA_SOURCE, + SECONDARY_GOVERNANCE_SOURCE, }, serde_json::json, - std::io::{ - Cursor, - Write, - }, wormhole_sdk::Chain as WormholeChain, }; @@ -92,10 +74,16 @@ async fn initialize_chain() -> ( .args_json(&json!({ "wormhole": wormhole.id(), "codehash": codehash, - "initial_source": Source::default(), - "gov_source": Source::default(), + "initial_source": Source { + emitter: DEFAULT_DATA_SOURCE.address.0, + chain: DEFAULT_DATA_SOURCE.chain.into(), + }, + "gov_source": Source { + emitter: DEFAULT_GOVERNANCE_SOURCE.address.0, + chain: DEFAULT_GOVERNANCE_SOURCE.chain.into(), + }, "update_fee": U128::from(1u128), - "stale_threshold": 32, + "stale_threshold": DEFAULT_VALID_TIME_PERIOD, })) .gas(300_000_000_000_000) .transact_async() @@ -112,37 +100,30 @@ async fn test_set_sources() { let (_, contract, _) = initialize_chain().await; // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - sequence: 1, - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Any), - module: GovernanceModule::Target, - action: GovernanceAction::SetDataSources { - data_sources: vec![ - Source::default(), - Source { - emitter: [1; 32], - chain: Chain::from(WormholeChain::Solana), - }, - ], - }, - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Any), + module: GovernanceModule::Target, + action: GovernanceAction::SetDataSources { + data_sources: vec![ + Source { + emitter: DEFAULT_DATA_SOURCE.address.0, + chain: DEFAULT_DATA_SOURCE.chain.into(), + }, + Source { + emitter: SECONDARY_DATA_SOURCE.address.0, + chain: SECONDARY_DATA_SOURCE.chain.into(), + }, + ], + }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); assert!(contract .call("execute_governance_instruction") @@ -164,10 +145,13 @@ async fn test_set_sources() { serde_json::from_slice::>(&contract.view("get_sources").await.unwrap().result) .unwrap(), &[ - Source::default(), Source { - emitter: [1; 32], - chain: Chain::from(WormholeChain::Solana), + emitter: DEFAULT_DATA_SOURCE.address.0, + chain: DEFAULT_DATA_SOURCE.chain.into(), + }, + Source { + emitter: SECONDARY_DATA_SOURCE.address.0, + chain: SECONDARY_DATA_SOURCE.chain.into(), }, ] ); @@ -177,60 +161,39 @@ async fn test_set_sources() { async fn test_set_governance_source() { let (_, contract, _) = initialize_chain().await; + // Data Source Upgrades are submitted with an embedded VAA, generate that one here first + // before we embed it. + let request_vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::RequestGovernanceDataSourceTransfer { + governance_data_source_index: 1, + }, + } + .serialize() + .unwrap(), + SECONDARY_GOVERNANCE_SOURCE.address, + SECONDARY_GOVERNANCE_SOURCE.chain, + 1, + ); + // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 2, - ..Default::default() - }; - - let vaa = { - let request_vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Solana, - emitter_address: wormhole_sdk::Address([1; 32]), - payload: (), - sequence: 1, - ..Default::default() - }; - - // Data Source Upgrades are submitted with an embedded VAA, generate that one here first - // before we embed it. - let request_vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &request_vaa).expect("Failed to serialize VAA"); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::RequestGovernanceDataSourceTransfer { - governance_data_source_index: 1, - }, - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - cur.into_inner() - }; - - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::AuthorizeGovernanceDataSourceTransfer { - claim_vaa: request_vaa, - }, - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::AuthorizeGovernanceDataSourceTransfer { + claim_vaa: serde_wormhole::to_vec(&request_vaa).unwrap(), + }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 2, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); assert!(contract .call("execute_governance_instruction") @@ -247,37 +210,30 @@ async fn test_set_governance_source() { .is_empty()); // An action from the new source should now be accepted. - let vaa = wormhole_sdk::Vaa { - sequence: 3, // NOTE: Incremented Governance Sequence - emitter_chain: wormhole_sdk::Chain::Solana, - emitter_address: wormhole_sdk::Address([1; 32]), - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetDataSources { - data_sources: vec![ - Source::default(), - Source { - emitter: [2; 32], - chain: Chain::from(WormholeChain::Solana), - }, - ], - }, - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetDataSources { + data_sources: vec![ + Source { + emitter: DEFAULT_DATA_SOURCE.address.0, + chain: DEFAULT_DATA_SOURCE.chain.into(), + }, + Source { + emitter: SECONDARY_DATA_SOURCE.address.0, + chain: SECONDARY_DATA_SOURCE.chain.into(), + }, + ], + }, + } + .serialize() + .unwrap(), + SECONDARY_GOVERNANCE_SOURCE.address, + SECONDARY_GOVERNANCE_SOURCE.chain, + 2, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); assert!(contract .call("execute_governance_instruction") @@ -295,37 +251,32 @@ async fn test_set_governance_source() { .is_empty()); // But not from the old source. - let vaa = wormhole_sdk::Vaa { - sequence: 4, // NOTE: Incremented Governance Sequence - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - ..Default::default() - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetDataSources { + data_sources: vec![ + Source::default(), + Source { + emitter: DEFAULT_DATA_SOURCE.address.0, + chain: DEFAULT_DATA_SOURCE.chain.into(), + }, + Source { + emitter: SECONDARY_DATA_SOURCE.address.0, + chain: SECONDARY_DATA_SOURCE.chain.into(), + }, + ], + }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 4, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetDataSources { - data_sources: vec![ - Source::default(), - Source { - emitter: [2; 32], - chain: Chain::from(WormholeChain::Solana), - }, - ], - }, - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; assert!(contract .call("execute_governance_instruction") @@ -347,53 +298,43 @@ async fn test_set_governance_source() { async fn test_stale_threshold() { let (_, contract, _) = initialize_chain().await; - // Submit a Price Attestation to the contract. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 1, - ..Default::default() - }; - // Get current UNIX timestamp and subtract a minute from it to place the price attestation in // the past. This should be accepted but untrusted. let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("Failed to get UNIX timestamp") .as_secs() - - 60; + - DEFAULT_VALID_TIME_PERIOD; - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &BatchPriceAttestation { - price_attestations: vec![PriceAttestation { - product_id: Identifier::default(), - price_id: Identifier::default(), - price: 100, - conf: 1, - expo: 8, - ema_price: 100, - ema_conf: 1, - status: PriceStatus::Trading, - num_publishers: 8, - max_num_publishers: 8, - attestation_time: now.try_into().unwrap(), - publish_time: now.try_into().unwrap(), - prev_publish_time: now.try_into().unwrap(), - prev_price: 100, - prev_conf: 1, - last_attested_publish_time: now.try_into().unwrap(), - }], - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + // Submit a Price Attestation to the contract. + let vaa = create_vaa_from_payload( + &BatchPriceAttestation { + price_attestations: vec![PriceAttestation { + product_id: Identifier::default(), + price_id: Identifier::default(), + price: 100, + conf: 1, + expo: 8, + ema_price: 100, + ema_conf: 1, + status: PriceStatus::Trading, + num_publishers: 8, + max_num_publishers: 8, + attestation_time: now.try_into().unwrap(), + publish_time: now.try_into().unwrap(), + prev_publish_time: now.try_into().unwrap(), + prev_price: 100, + prev_conf: 1, + last_attested_publish_time: now.try_into().unwrap(), + }], + } + .serialize() + .unwrap(), + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); let update_fee = serde_json::from_slice::( &contract @@ -440,44 +381,34 @@ async fn test_stale_threshold() { // Submit another Price Attestation to the contract with an even older timestamp. Which // should now fail due to the existing newer price. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - sequence: 2, - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &BatchPriceAttestation { - price_attestations: vec![PriceAttestation { - product_id: Identifier::default(), - price_id: Identifier::default(), - price: 1000, - conf: 1, - expo: 8, - ema_price: 1000, - ema_conf: 1, - status: PriceStatus::Trading, - num_publishers: 8, - max_num_publishers: 8, - attestation_time: (now - 1024).try_into().unwrap(), - publish_time: (now - 1024).try_into().unwrap(), - prev_publish_time: (now - 1024).try_into().unwrap(), - prev_price: 90, - prev_conf: 1, - last_attested_publish_time: (now - 1024).try_into().unwrap(), - }], - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &BatchPriceAttestation { + price_attestations: vec![PriceAttestation { + product_id: Identifier::default(), + price_id: Identifier::default(), + price: 1000, + conf: 1, + expo: 8, + ema_price: 1000, + ema_conf: 1, + status: PriceStatus::Trading, + num_publishers: 8, + max_num_publishers: 8, + attestation_time: (now - 1024).try_into().unwrap(), + publish_time: (now - 1024).try_into().unwrap(), + prev_publish_time: (now - 1024).try_into().unwrap(), + prev_price: 90, + prev_conf: 1, + last_attested_publish_time: (now - 1024).try_into().unwrap(), + }], + } + .serialize() + .unwrap(), + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, + 2, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); // The update handler should now succeed even if price is old, but simply not update the price. assert!(contract @@ -516,29 +447,19 @@ async fn test_stale_threshold() { ); // Now we extend the staleness threshold with a Governance VAA. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - sequence: 3, - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetValidPeriod { valid_seconds: 256 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetValidPeriod { valid_seconds: 256 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 3, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); assert!(contract .call("execute_governance_instruction") @@ -586,29 +507,19 @@ async fn test_contract_fees() { .as_secs(); // Set a high fee for the contract needed to submit a price. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 1, - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetFee { base: 128, expo: 8 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetFee { base: 128, expo: 8 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); // Fetch Update fee before changing it. let update_fee = serde_json::from_slice::( @@ -658,44 +569,34 @@ async fn test_contract_fees() { ); // Attempt to update the price feed with a now too low deposit. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - sequence: 2, - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &BatchPriceAttestation { - price_attestations: vec![PriceAttestation { - product_id: Identifier::default(), - price_id: Identifier::default(), - price: 1000, - conf: 1, - expo: 8, - ema_price: 1000, - ema_conf: 1, - status: PriceStatus::Trading, - num_publishers: 8, - max_num_publishers: 8, - attestation_time: (now - 1024).try_into().unwrap(), - publish_time: (now - 1024).try_into().unwrap(), - prev_publish_time: (now - 1024).try_into().unwrap(), - prev_price: 90, - prev_conf: 1, - last_attested_publish_time: (now - 1024).try_into().unwrap(), - }], - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &BatchPriceAttestation { + price_attestations: vec![PriceAttestation { + product_id: Identifier::default(), + price_id: Identifier::default(), + price: 1000, + conf: 1, + expo: 8, + ema_price: 1000, + ema_conf: 1, + status: PriceStatus::Trading, + num_publishers: 8, + max_num_publishers: 8, + attestation_time: (now - 1024).try_into().unwrap(), + publish_time: (now - 1024).try_into().unwrap(), + prev_publish_time: (now - 1024).try_into().unwrap(), + prev_price: 90, + prev_conf: 1, + last_attested_publish_time: (now - 1024).try_into().unwrap(), + }], + } + .serialize() + .unwrap(), + DEFAULT_DATA_SOURCE.address, + DEFAULT_DATA_SOURCE.chain, + 2, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); assert!(contract .call("update_price_feeds") @@ -734,29 +635,20 @@ async fn test_same_governance_sequence_fails() { let (_, contract, _) = initialize_chain().await; // Set a high fee for the contract needed to submit a price. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 1, - ..Default::default() - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetFee { base: 128, expo: 8 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetFee { base: 128, expo: 8 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; // Attempt our first SetFee. assert!(contract @@ -798,29 +690,19 @@ async fn test_out_of_order_sequences_fail() { let (_, contract, _) = initialize_chain().await; // Set a high fee for the contract needed to submit a price. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 1, - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetFee { base: 128, expo: 8 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetFee { base: 128, expo: 8 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); // Attempt our first SetFee. assert!(contract @@ -839,29 +721,19 @@ async fn test_out_of_order_sequences_fail() { .is_empty()); // Generate another VAA with sequence 3. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 3, - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetFee { base: 128, expo: 8 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetFee { base: 128, expo: 8 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 3, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); // This should succeed. assert!(contract @@ -880,29 +752,19 @@ async fn test_out_of_order_sequences_fail() { .is_empty()); // Generate another VAA with sequence 2. - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 2, - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Near), - module: GovernanceModule::Target, - action: GovernanceAction::SetFee { base: 128, expo: 8 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Near), + module: GovernanceModule::Target, + action: GovernanceAction::SetFee { base: 128, expo: 8 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 2, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); // This should fail due to being out of order. assert!(!contract @@ -926,29 +788,20 @@ async fn test_out_of_order_sequences_fail() { async fn test_governance_target_fails_if_not_near() { let (_, contract, _) = initialize_chain().await; - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - payload: (), - sequence: 1, - ..Default::default() - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Solana), + module: GovernanceModule::Target, + action: GovernanceAction::SetFee { base: 128, expo: 8 }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).unwrap(); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Solana), - module: GovernanceModule::Target, - action: GovernanceAction::SetFee { base: 128, expo: 8 }, - } - .serialize() - .unwrap(), - ) - .unwrap(); - hex::encode(cur.into_inner()) - }; // This should fail as the target is Solana, when Near is expected. assert!(!contract @@ -970,119 +823,27 @@ async fn test_governance_target_fails_if_not_near() { // A test to check accumulator style updates work as intended. #[tokio::test] async fn test_accumulator_updates() { - fn create_dummy_price_feed_message(value: i64) -> Message { - let mut dummy_id = [0; 32]; - dummy_id[0] = value as u8; - let msg = PriceFeedMessage { - feed_id: dummy_id, - price: value, - conf: value as u64, - exponent: value as i32, - publish_time: value, - prev_publish_time: value, - ema_price: value, - ema_conf: value as u64, - }; - Message::PriceFeedMessage(msg) - } - - fn create_accumulator_message_from_updates( - price_updates: Vec, - tree: MerkleTree, - emitter_address: [u8; 32], - emitter_chain: u16, - ) -> Vec { - let mut root_hash = [0u8; 20]; - root_hash.copy_from_slice(&to_vec::<_, BigEndian>(&tree.root).unwrap()[..20]); - let wormhole_message = WormholeMessage::new(WormholePayload::Merkle(WormholeMerkleRoot { - slot: 0, - ring_size: 0, - root: root_hash, - })); - - let vaa = wormhole_sdk::Vaa { - emitter_chain: emitter_chain.into(), - emitter_address: wormhole_sdk::Address(emitter_address), - sequence: 2, - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all(&to_vec::<_, BigEndian>(&wormhole_message).unwrap()) - .expect("Failed to write Payload"); - cur.into_inner() - }; - - let accumulator_update_data = AccumulatorUpdateData::new(Proof::WormholeMerkle { - vaa: PrefixedVec::from(vaa), - updates: price_updates, - }); - - to_vec::<_, BigEndian>(&accumulator_update_data).unwrap() - } - - fn create_accumulator_message(all_feeds: &[Message], updates: &[Message]) -> Vec { - let all_feeds_bytes: Vec<_> = all_feeds - .iter() - .map(|f| to_vec::<_, BigEndian>(f).unwrap()) - .collect(); - let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect(); - let tree = MerkleTree::::new(all_feeds_bytes_refs.as_slice()).unwrap(); - let mut price_updates: Vec = vec![]; - for update in updates { - let proof = tree - .prove(&to_vec::<_, BigEndian>(update).unwrap()) - .unwrap(); - price_updates.push(MerklePriceUpdate { - message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()), - proof, - }); - } - create_accumulator_message_from_updates( - price_updates, - tree, - [1; 32], - wormhole_sdk::Chain::Any.into(), - ) - } - let (_, contract, _) = initialize_chain().await; // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole - let vaa = wormhole_sdk::Vaa { - emitter_chain: wormhole_sdk::Chain::Any, - emitter_address: wormhole_sdk::Address([0; 32]), - sequence: 1, - payload: (), - ..Default::default() - }; - - let vaa = { - let mut cur = Cursor::new(Vec::new()); - serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA"); - cur.write_all( - &GovernanceInstruction { - target: Chain::from(WormholeChain::Any), - module: GovernanceModule::Target, - action: GovernanceAction::SetDataSources { - data_sources: vec![ - Source::default(), - Source { - emitter: [1; 32], - chain: Chain::from(WormholeChain::Any), - }, - ], - }, - } - .serialize() - .unwrap(), - ) - .expect("Failed to write Payload"); - hex::encode(cur.into_inner()) - }; + let vaa = create_vaa_from_payload( + &GovernanceInstruction { + target: Chain::from(WormholeChain::Any), + module: GovernanceModule::Target, + action: GovernanceAction::SetDataSources { + data_sources: vec![Source { + emitter: DEFAULT_DATA_SOURCE.address.0, + chain: DEFAULT_DATA_SOURCE.chain.into(), + }], + }, + } + .serialize() + .unwrap(), + DEFAULT_GOVERNANCE_SOURCE.address, + DEFAULT_GOVERNANCE_SOURCE.chain, + 1, + ); + let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap()); assert!(contract .call("execute_governance_instruction") @@ -1102,7 +863,7 @@ async fn test_accumulator_updates() { // Create a couple of test feeds. let feed_1 = create_dummy_price_feed_message(100); let feed_2 = create_dummy_price_feed_message(200); - let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1]); + let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1], false); let message = hex::encode(message); // Call the usual UpdatePriceFeed function. @@ -1197,8 +958,8 @@ async fn test_borsh_field_cmopat() { assert_eq!( decoded_price, PriceTester { - price: i64::MAX.into(), - conf: u64::MAX.into(), + price: i64::MAX, + conf: u64::MAX, expo: 100, bad_field_name: 100, }