| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146 |
- use {
- byteorder::BigEndian,
- near_sdk::json_types::U128,
- pyth::{
- governance::{
- GovernanceAction,
- GovernanceInstruction,
- GovernanceModule,
- },
- state::{
- Chain,
- Price,
- PriceIdentifier,
- Source,
- },
- },
- pyth_wormhole_attester_sdk::{
- BatchPriceAttestation,
- Identifier,
- 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,
- },
- },
- serde_json::json,
- std::io::{
- Cursor,
- Write,
- },
- wormhole::Chain as WormholeChain,
- };
- async fn initialize_chain() -> (
- workspaces::Worker<workspaces::network::Sandbox>,
- workspaces::Contract,
- workspaces::Contract,
- ) {
- let worker = workspaces::sandbox().await.expect("Workspaces Failed");
- // Deploy Pyth
- let contract = worker
- .dev_deploy(&std::fs::read("pyth.wasm").expect("Failed to find pyth.wasm"))
- .await
- .expect("Failed to deploy pyth.wasm");
- // Deploy Wormhole Stub, this is a dummy contract that always verifies VAA's correctly so we
- // can test the ext_wormhole API.
- let wormhole = worker
- .dev_deploy(
- &std::fs::read("wormhole_stub.wasm").expect("Failed to find wormhole_stub.wasm"),
- )
- .await
- .expect("Failed to deploy wormhole_stub.wasm");
- // Initialize Wormhole.
- let _ = wormhole
- .call("new")
- .args_json(&json!({}))
- .gas(300_000_000_000_000)
- .transact_async()
- .await
- .expect("Failed to initialize Wormhole")
- .await
- .unwrap();
- // Initialize Pyth, one time operation that sets the Wormhole contract address.
- let codehash = [0u8; 32];
- let _ = contract
- .call("new")
- .args_json(&json!({
- "wormhole": wormhole.id(),
- "codehash": codehash,
- "initial_source": Source::default(),
- "gov_source": Source::default(),
- "update_fee": U128::from(1u128),
- "stale_threshold": 32,
- }))
- .gas(300_000_000_000_000)
- .transact_async()
- .await
- .expect("Failed to initialize Pyth")
- .await
- .unwrap();
- (worker, contract, wormhole)
- }
- #[tokio::test]
- 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::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // There should now be a two sources in the contract state.
- assert_eq!(
- serde_json::from_slice::<Vec<Source>>(&contract.view("get_sources").await.unwrap().result)
- .unwrap(),
- &[
- Source::default(),
- Source {
- emitter: [1; 32],
- chain: Chain::from(WormholeChain::Solana),
- },
- ]
- );
- }
- #[tokio::test]
- async fn test_set_governance_source() {
- let (_, contract, _) = initialize_chain().await;
- // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::Address([0; 32]),
- payload: (),
- sequence: 2,
- ..Default::default()
- };
- let vaa = {
- let request_vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Solana,
- emitter_address: wormhole::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())
- };
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // An action from the new source should now be accepted.
- let vaa = wormhole::Vaa {
- sequence: 3, // NOTE: Incremented Governance Sequence
- emitter_chain: wormhole::Chain::Solana,
- emitter_address: wormhole::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())
- };
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // But not from the old source.
- let vaa = wormhole::Vaa {
- sequence: 4, // NOTE: Incremented Governance Sequence
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::Address([0; 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())
- };
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .outcome()
- .is_success());
- }
- #[tokio::test]
- async fn test_stale_threshold() {
- let (_, contract, _) = initialize_chain().await;
- // Submit a Price Attestation to the contract.
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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;
- 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())
- };
- let update_fee = serde_json::from_slice::<U128>(
- &contract
- .view("get_update_fee_estimate")
- .args_json(&json!({
- "data": vaa,
- }))
- .await
- .unwrap()
- .result,
- )
- .unwrap();
- // Submit price. As there are no prices this should succeed despite being old.
- assert!(contract
- .call("update_price_feeds")
- .gas(300_000_000_000_000)
- .deposit(update_fee.into())
- .args_json(&json!({
- "data": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Despite succeeding, assert Price cannot be requested, 60 seconds in the past should be
- // considered stale. [tag:failed_price_check]
- assert_eq!(
- None,
- serde_json::from_slice::<Option<Price>>(
- &contract
- .view("get_price")
- .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
- .await
- .unwrap()
- .result
- )
- .unwrap(),
- );
- // 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::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- // The update handler should now succeed even if price is old, but simply not update the price.
- assert!(contract
- .call("update_price_feeds")
- .gas(300_000_000_000_000)
- .deposit(update_fee.into())
- .args_json(&json!({
- "data": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // The price however should _not_ have updated and if we check the unsafe stored price the
- // timestamp and price should be unchanged.
- assert_eq!(
- Price {
- price: 100.into(),
- conf: 1.into(),
- expo: 8,
- timestamp: now.into(),
- },
- serde_json::from_slice::<Price>(
- &contract
- .view("get_price_unsafe")
- .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
- .await
- .unwrap()
- .result
- )
- .unwrap(),
- );
- // Now we extend the staleness threshold with a Governance VAA.
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // It should now be possible to request the price that previously returned None.
- // [ref:failed_price_check]
- assert_eq!(
- Some(Price {
- price: 100.into(),
- conf: 1.into(),
- expo: 8,
- timestamp: now.into(),
- }),
- serde_json::from_slice::<Option<Price>>(
- &contract
- .view("get_price")
- .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
- .await
- .unwrap()
- .result
- )
- .unwrap(),
- );
- }
- #[tokio::test]
- async fn test_contract_fees() {
- let (_, contract, _) = initialize_chain().await;
- let now = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .expect("Failed to get UNIX timestamp")
- .as_secs();
- // Set a high fee for the contract needed to submit a price.
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- // Fetch Update fee before changing it.
- let update_fee = serde_json::from_slice::<U128>(
- &contract
- .view("get_update_fee_estimate")
- .args_json(&json!({
- "data": vaa,
- }))
- .await
- .unwrap()
- .result,
- )
- .unwrap();
- // Now set the update_fee so that it is too high for the deposit to cover.
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Check the state has actually changed before we try and execute another VAA.
- assert_ne!(
- u128::from(update_fee),
- u128::from(
- serde_json::from_slice::<U128>(
- &contract
- .view("get_update_fee_estimate")
- .args_json(&json!({
- "data": vaa,
- }))
- .await
- .unwrap()
- .result,
- )
- .unwrap()
- )
- );
- // Attempt to update the price feed with a now too low deposit.
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- assert!(contract
- .call("update_price_feeds")
- .gas(300_000_000_000_000)
- .deposit(update_fee.into())
- .args_json(&json!({
- "data": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Submitting a Price should have failed because the fee was not enough.
- assert_eq!(
- None,
- serde_json::from_slice::<Option<Price>>(
- &contract
- .view("get_price")
- .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
- .await
- .unwrap()
- .result
- )
- .unwrap(),
- );
- }
- // A test that attempts to SetFee twice with the same governance action, the first should succeed,
- // the second should fail.
- #[tokio::test]
- 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::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- // Attempt our first SetFee.
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Attempt to run the same VAA again.
- assert!(!contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- }
- // A test that attempts to SetFee twice with the same governance action, the first should succeed,
- // the second should fail.
- #[tokio::test]
- 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::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- // Attempt our first SetFee.
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Generate another VAA with sequence 3.
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- // This should succeed.
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Generate another VAA with sequence 2.
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- // This should fail due to being out of order.
- assert!(!contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- }
- // A test that fails if the governance action payload target is not NEAR.
- #[tokio::test]
- async fn test_governance_target_fails_if_not_near() {
- let (_, contract, _) = initialize_chain().await;
- let vaa = wormhole::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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::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
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- }
- // 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<MerklePriceUpdate>,
- tree: MerkleTree<Keccak160>,
- emitter_address: [u8; 32],
- emitter_chain: u16,
- ) -> Vec<u8> {
- 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::Vaa {
- emitter_chain: emitter_chain.into(),
- emitter_address: wormhole::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<u8> {
- 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::<Keccak160>::new(all_feeds_bytes_refs.as_slice()).unwrap();
- let mut price_updates: Vec<MerklePriceUpdate> = 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::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::Vaa {
- emitter_chain: wormhole::Chain::Any,
- emitter_address: wormhole::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())
- };
- assert!(contract
- .call("execute_governance_instruction")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "vaa": vaa,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // 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 = hex::encode(message);
- // Call the usual UpdatePriceFeed function.
- assert!(contract
- .call("update_price_feeds")
- .gas(300_000_000_000_000)
- .deposit(300_000_000_000_000_000_000_000)
- .args_json(&json!({
- "data": message,
- }))
- .transact_async()
- .await
- .expect("Failed to submit VAA")
- .await
- .unwrap()
- .failures()
- .is_empty());
- // Check the price feed actually updated. Check both types of serialized PriceIdentifier.
- let mut identifier = [0; 32];
- identifier[0] = 100;
- assert_eq!(
- Some(Price {
- price: 100.into(),
- conf: 100.into(),
- expo: 100,
- timestamp: 100.into(),
- }),
- serde_json::from_slice::<Option<Price>>(
- &contract
- .view("get_price_unsafe")
- .args_json(&json!({ "price_identifier": PriceIdentifier(identifier) }))
- .await
- .unwrap()
- .result
- )
- .unwrap(),
- );
- }
|