From e65dfdb70593b3f33a1c3d011844ff71ab33ab84 Mon Sep 17 00:00:00 2001 From: Holly Hodgkinson <1hhodgk1@gmail.com> Date: Sun, 23 Jun 2024 20:49:19 +0100 Subject: [PATCH 1/4] Latest version of parse_price_feed_update with tests --- .../aptos/contracts/sources/pyth.move | 298 ++++++++++++++++++ 1 file changed, 298 insertions(+) diff --git a/target_chains/aptos/contracts/sources/pyth.move b/target_chains/aptos/contracts/sources/pyth.move index a831643061..939d72346c 100644 --- a/target_chains/aptos/contracts/sources/pyth.move +++ b/target_chains/aptos/contracts/sources/pyth.move @@ -30,6 +30,12 @@ module pyth::pyth { const PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813; const ACCUMULATOR_UPDATE_WORMHOLE_VERIFICATION_MAGIC: u64 = 1096111958; + struct ParseConfig has copy, drop { + min_publish_time: u64, + max_publish_time: u64, + check_uniqueness: bool, + } + // ----------------------------------------------------------------------------- // Initialisation functions @@ -395,6 +401,119 @@ module pyth::pyth { update_timestamp > cached_timestamp } +// ----------------------------------------------------------------------------- +// Parse Benchmark Prices +// + fun parse_and_validate_price_feeds( + price_infos: &vector, + price_ids: vector>, + min_publish_time: u64, + max_publish_time: u64 + ): vector { + let parsed_price_feeds = vector::empty(); + let parsed_price_ids = vector::empty>(); + + // validate publish times and populate parsed price feed vector + let i = 0; + while(i < vector::length(price_infos)) { + let price_info = vector::borrow(price_infos, i); + let price_feed = price_info::get_price_feed(price_info); + let price: price::Price = price_feed::get_price(price_feed); + let timestamp = price::get_timestamp(&price); + if (timestamp >= min_publish_time && timestamp <= max_publish_time) { + let price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(price_feed); + let price_id_bytes = price_identifier::get_bytes(price_id); + vector::push_back(&mut parsed_price_ids, price_id_bytes); + vector::push_back(&mut parsed_price_feeds, *price_feed); + }; + i = i + 1; + }; + + // ensure all requested price IDs have corresponding valid updates + let k = 0; + while (k < vector::length(&price_ids)) { + let requested_price_id = vector::borrow(&price_ids, k); + let found = false; + + let j = 0; + while (j < vector::length(&parsed_price_ids)) { + let parsed_price_id = vector::borrow(&parsed_price_ids, j); + if (requested_price_id == parsed_price_id) { + found = true; + }; + j = j + 1; + }; + + if (!found) { + abort error::unknown_price_feed() // or replace with more suitable error + }; + k = k + 1; + }; + + return parsed_price_feeds + } + + // parse a single VAA and return vector of price feeds + fun parse_price_feed_updates_single_vaa ( + vaa: vector, + price_ids: vector>, + min_publish_time: u64, + max_publish_time: u64 + ): vector { + let cur = cursor::init(vaa); + let header: u64 = deserialize::deserialize_u32(&mut cur); + if (header == PYTHNET_ACCUMULATOR_UPDATE_MAGIC) { + let price_infos = parse_and_verify_accumulator_message(&mut cur); + cursor::rest(cur); + return parse_and_validate_price_feeds(&price_infos, price_ids, min_publish_time, max_publish_time) + } else { + let vaa = vaa::parse_and_verify(vaa); + verify_data_source(&vaa); + let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::destroy(vaa))); + cursor::rest(cur); + return parse_and_validate_price_feeds(&price_infos, price_ids, min_publish_time, max_publish_time) + } + } + + public fun parse_price_feed_updates( + update_data: vector>, + price_ids: vector>, + min_publish_time: u64, + max_publish_time: u64, + fee: Coin + ): vector { + // validate and deposit the update fee + let update_fee = get_update_fee(&update_data); + assert!(update_fee <= coin::value(&fee), error::insufficient_fee()); + coin::deposit(@pyth, fee); + let pyth_balance = coin::balance(@pyth); + assert!(pyth_balance >= update_fee, error::insufficient_fee()); + + let price_feeds = vector::empty(); + + // iterate through the update_data vector + let i = 0; + while (i < vector::length(&update_data)) { + // pass single VAA into the helper function + let single_vaa = vector::borrow(&update_data, i); + let single_price_feeds = parse_price_feed_updates_single_vaa(*single_vaa, price_ids, min_publish_time, max_publish_time); + + // iterate through the vector of price feeds from the single parsed VAA + let j = 0; + while (j < vector::length(&single_price_feeds)) { + // add each parsed price feed into the price feeds vector + let price_feed = vector::borrow(&single_price_feeds, j); + vector::push_back(&mut price_feeds, *price_feed); + j = j + 1; + }; + + i = i + 1; + }; + + price_feeds + } + + // ----------------------------------------------------------------------------- // Query the cached prices // @@ -1430,4 +1549,183 @@ module pyth::pyth_test { cleanup_test(burn_capability, mint_capability); } + + + #[test(aptos_framework = @aptos_framework)] + fun test_parse_price_feed_updates_success(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 100; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + data_sources_for_test_vaa(), + update_fee, + initial_balance); + + let update_data = TEST_VAAS; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + let mock_price_infos: vector = get_mock_price_infos(); + + assert!(vector::length(&test_price_feeds) > 0, 1); + let i: u64 = 0; + while (i < vector::length(&mock_price_infos)) { + let mock_price_info: &pyth::price_info::PriceInfo = vector::borrow(&mock_price_infos, i); + let test_price_feed: &pyth::price_feed::PriceFeed = vector::borrow(&test_price_feeds, i); + let mock_price_feed: &pyth::price_feed::PriceFeed = price_info::get_price_feed(mock_price_info); + + let test_price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(test_price_feed); + let mock_price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(mock_price_feed); + let test_price: price::Price = price_feed::get_price(test_price_feed); + let mock_price: price::Price = price_feed::get_price(mock_price_feed); + let test_ema_price: price::Price = price_feed::get_ema_price(test_price_feed); + let mock_ema_price: price::Price = price_feed::get_ema_price(mock_price_feed); + + assert!(test_price_id == mock_price_id, 1); + assert!(test_price == mock_price, 1); + assert!(test_ema_price == mock_ema_price, 1); + + i = i + 1; + }; + + cleanup_test(burn_capability, mint_capability); + } + + + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 65542, location = pyth::pyth)] + fun test_parse_price_feed_updates_insufficient_fee(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 20; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + data_sources_for_test_vaa(), + update_fee, + initial_balance); + + let update_data = TEST_VAAS; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 6, location = wormhole::vaa)] + fun test_parse_price_feed_updates_corrupt_vaa(aptos_framework: &signer) { + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], 50, 100); + + let corrupt_vaa = x"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + pyth::parse_price_feed_updates(vector[corrupt_vaa], price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 65539, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_data_source(aptos_framework: &signer) { + let update_data = TEST_VAAS; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + let data_sources = vector[ + data_source::new( + 4, external_address::from_bytes(x"0000000000000000000000000000000000000000000000000000000000007742")), + data_source::new( + 5, external_address::from_bytes(x"0000000000000000000000000000000000000000000000000000000000007637")) + ]; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources, 50, 100); + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 393224, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_price_id(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 100; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + data_sources_for_test_vaa(), + update_fee, + initial_balance); + + let update_data = TEST_VAAS; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d2", // invalid price id + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 393224, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_publish_times(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 100; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + data_sources_for_test_vaa(), + update_fee, + initial_balance); + + let update_data = TEST_VAAS; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + // invalid publish times: max_publish_time is less than min_publish_time + let min_publish_time: u64 = 1663680750; + let max_publish_time: u64 = 1663074345; + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + + + + } From ba26645a5d086db4edb8fb2db934703bf71853c7 Mon Sep 17 00:00:00 2001 From: Holly Hodgkinson <1hhodgk1@gmail.com> Date: Mon, 24 Jun 2024 12:26:25 +0100 Subject: [PATCH 2/4] Minor changes, removal of Config struct --- target_chains/aptos/contracts/sources/pyth.move | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/target_chains/aptos/contracts/sources/pyth.move b/target_chains/aptos/contracts/sources/pyth.move index 939d72346c..79b82ad66e 100644 --- a/target_chains/aptos/contracts/sources/pyth.move +++ b/target_chains/aptos/contracts/sources/pyth.move @@ -30,13 +30,6 @@ module pyth::pyth { const PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813; const ACCUMULATOR_UPDATE_WORMHOLE_VERIFICATION_MAGIC: u64 = 1096111958; - struct ParseConfig has copy, drop { - min_publish_time: u64, - max_publish_time: u64, - check_uniqueness: bool, - } - - // ----------------------------------------------------------------------------- // Initialisation functions @@ -1723,9 +1716,5 @@ module pyth::pyth_test { cleanup_test(burn_capability, mint_capability); } - - - - } From 77803b9e987af24e4e2bcc66202fa96f83ac4ab2 Mon Sep 17 00:00:00 2001 From: Holly Hodgkinson <1hhodgk1@gmail.com> Date: Sat, 6 Jul 2024 19:25:11 +0100 Subject: [PATCH 3/4] Updated parse_price_feed_updates and related test cases based on requested amendments --- .../aptos/contracts/sources/error.move | 8 + .../aptos/contracts/sources/pyth.move | 158 +++++++----------- 2 files changed, 69 insertions(+), 97 deletions(-) diff --git a/target_chains/aptos/contracts/sources/error.move b/target_chains/aptos/contracts/sources/error.move index f5eae1b9da..8fed41f36b 100644 --- a/target_chains/aptos/contracts/sources/error.move +++ b/target_chains/aptos/contracts/sources/error.move @@ -122,4 +122,12 @@ module pyth::error { public fun invalid_keccak160_length(): u64 { error::invalid_argument(30) } + + public fun price_feed_outside_time_range(): u64 { + error::invalid_argument(31) + } + + public fun invalid_publish_time_range(): u64 { + error::invalid_argument(32) + } } diff --git a/target_chains/aptos/contracts/sources/pyth.move b/target_chains/aptos/contracts/sources/pyth.move index 79b82ad66e..100326c20f 100644 --- a/target_chains/aptos/contracts/sources/pyth.move +++ b/target_chains/aptos/contracts/sources/pyth.move @@ -399,7 +399,6 @@ module pyth::pyth { // fun parse_and_validate_price_feeds( price_infos: &vector, - price_ids: vector>, min_publish_time: u64, max_publish_time: u64 ): vector { @@ -408,88 +407,69 @@ module pyth::pyth { // validate publish times and populate parsed price feed vector let i = 0; - while(i < vector::length(price_infos)) { + while (i < vector::length(price_infos)) { let price_info = vector::borrow(price_infos, i); let price_feed = price_info::get_price_feed(price_info); - let price: price::Price = price_feed::get_price(price_feed); + let price = price_feed::get_price(price_feed); let timestamp = price::get_timestamp(&price); - if (timestamp >= min_publish_time && timestamp <= max_publish_time) { - let price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(price_feed); - let price_id_bytes = price_identifier::get_bytes(price_id); - vector::push_back(&mut parsed_price_ids, price_id_bytes); - vector::push_back(&mut parsed_price_feeds, *price_feed); - }; - i = i + 1; - }; - // ensure all requested price IDs have corresponding valid updates - let k = 0; - while (k < vector::length(&price_ids)) { - let requested_price_id = vector::borrow(&price_ids, k); - let found = false; + assert!(timestamp >= min_publish_time && timestamp <= max_publish_time, error::price_feed_outside_time_range()); - let j = 0; - while (j < vector::length(&parsed_price_ids)) { - let parsed_price_id = vector::borrow(&parsed_price_ids, j); - if (requested_price_id == parsed_price_id) { - found = true; - }; - j = j + 1; - }; - - if (!found) { - abort error::unknown_price_feed() // or replace with more suitable error - }; - k = k + 1; + let price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(price_feed); + let price_id_bytes = price_identifier::get_bytes(price_id); + vector::push_back(&mut parsed_price_ids, price_id_bytes); + vector::push_back(&mut parsed_price_feeds, *price_feed); + i = i + 1; }; - return parsed_price_feeds + parsed_price_feeds } // parse a single VAA and return vector of price feeds fun parse_price_feed_updates_single_vaa ( vaa: vector, - price_ids: vector>, min_publish_time: u64, max_publish_time: u64 - ): vector { + ): (u64, vector) { let cur = cursor::init(vaa); let header: u64 = deserialize::deserialize_u32(&mut cur); + let total_updates; + let price_infos; if (header == PYTHNET_ACCUMULATOR_UPDATE_MAGIC) { - let price_infos = parse_and_verify_accumulator_message(&mut cur); + price_infos = parse_and_verify_accumulator_message(&mut cur); + total_updates = vector::length(&price_infos); cursor::rest(cur); - return parse_and_validate_price_feeds(&price_infos, price_ids, min_publish_time, max_publish_time) } else { let vaa = vaa::parse_and_verify(vaa); verify_data_source(&vaa); - let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::destroy(vaa))); + price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::destroy(vaa))); + total_updates = 1; cursor::rest(cur); - return parse_and_validate_price_feeds(&price_infos, price_ids, min_publish_time, max_publish_time) - } + }; + let parsed_price_feeds = parse_and_validate_price_feeds(&price_infos, min_publish_time, max_publish_time); + (total_updates, parsed_price_feeds) } public fun parse_price_feed_updates( update_data: vector>, - price_ids: vector>, min_publish_time: u64, max_publish_time: u64, fee: Coin ): vector { - // validate and deposit the update fee - let update_fee = get_update_fee(&update_data); - assert!(update_fee <= coin::value(&fee), error::insufficient_fee()); - coin::deposit(@pyth, fee); - let pyth_balance = coin::balance(@pyth); - assert!(pyth_balance >= update_fee, error::insufficient_fee()); + // validate the publish times + assert!(min_publish_time <= max_publish_time, error::invalid_publish_time_range()); let price_feeds = vector::empty(); + let total_updates = 0; // iterate through the update_data vector let i = 0; while (i < vector::length(&update_data)) { // pass single VAA into the helper function let single_vaa = vector::borrow(&update_data, i); - let single_price_feeds = parse_price_feed_updates_single_vaa(*single_vaa, price_ids, min_publish_time, max_publish_time); + let (updates, single_price_feeds) = parse_price_feed_updates_single_vaa(*single_vaa, min_publish_time, max_publish_time); + + total_updates = total_updates + updates; // iterate through the vector of price feeds from the single parsed VAA let j = 0; @@ -502,6 +482,11 @@ module pyth::pyth { i = i + 1; }; + + // calculate and charge the message update fee + let update_fee = state::get_base_update_fee() * total_updates; + assert!(update_fee <= coin::value(&fee), error::insufficient_fee()); + coin::deposit(@pyth, fee); price_feeds } @@ -1555,17 +1540,18 @@ module pyth::pyth_test { initial_balance); let update_data = TEST_VAAS; - let price_ids = vector[ - x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", - x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", - x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", - x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", - ]; let min_publish_time: u64 = 1663074345; let max_publish_time: u64 = 1663680750; - let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + let initial_pyth_balance = coin::balance(@pyth); + let fee_value = coin::value(&coins); + + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); let mock_price_infos: vector = get_mock_price_infos(); + + // update fee transfer validation + let final_pyth_balance = coin::balance(@pyth); + assert!(final_pyth_balance == initial_pyth_balance + fee_value, 1); assert!(vector::length(&test_price_feeds) > 0, 1); let i: u64 = 0; @@ -1604,16 +1590,11 @@ module pyth::pyth_test { initial_balance); let update_data = TEST_VAAS; - let price_ids = vector[ - x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", - x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", - x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", - x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", - ]; let min_publish_time: u64 = 1663074345; let max_publish_time: u64 = 1663680750; - pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); + assert!(vector::length(&test_price_feeds) == 0, 1); cleanup_test(burn_capability, mint_capability); } @@ -1624,15 +1605,11 @@ module pyth::pyth_test { let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], 50, 100); let corrupt_vaa = x"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"; - let price_ids = vector[ - x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", - x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", - x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", - x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", - ]; let min_publish_time: u64 = 1663074345; let max_publish_time: u64 = 1663680750; - pyth::parse_price_feed_updates(vector[corrupt_vaa], price_ids, min_publish_time, max_publish_time, coins); + + let test_price_feeds: vector = pyth::parse_price_feed_updates(vector[corrupt_vaa], min_publish_time, max_publish_time, coins); + assert!(vector::length(&test_price_feeds) == 0, 1); cleanup_test(burn_capability, mint_capability); } @@ -1641,12 +1618,6 @@ module pyth::pyth_test { #[expected_failure(abort_code = 65539, location = pyth::pyth)] fun test_parse_price_feed_updates_invalid_data_source(aptos_framework: &signer) { let update_data = TEST_VAAS; - let price_ids = vector[ - x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", - x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", - x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", - x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", - ]; let min_publish_time: u64 = 1663074345; let max_publish_time: u64 = 1663680750; @@ -1658,15 +1629,15 @@ module pyth::pyth_test { ]; let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources, 50, 100); - pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); + assert!(vector::length(&test_price_feeds) == 0, 1); cleanup_test(burn_capability, mint_capability); } - #[test(aptos_framework = @aptos_framework)] - #[expected_failure(abort_code = 393224, location = pyth::pyth)] - fun test_parse_price_feed_updates_invalid_price_id(aptos_framework: &signer) { + #[expected_failure(abort_code = 65568, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_publish_times(aptos_framework: &signer) { let update_fee = 50; let initial_balance = 100; let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, @@ -1676,23 +1647,19 @@ module pyth::pyth_test { initial_balance); let update_data = TEST_VAAS; - let price_ids = vector[ - x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d2", // invalid price id - x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", - x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", - x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", - ]; - let min_publish_time: u64 = 1663074345; - let max_publish_time: u64 = 1663680750; + // invalid publish times: max_publish_time is less than min_publish_time + let min_publish_time: u64 = 1663680750; + let max_publish_time: u64 = 1663074345; - pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); + assert!(vector::length(&test_price_feeds) == 0, 1); cleanup_test(burn_capability, mint_capability); } #[test(aptos_framework = @aptos_framework)] - #[expected_failure(abort_code = 393224, location = pyth::pyth)] - fun test_parse_price_feed_updates_invalid_publish_times(aptos_framework: &signer) { + #[expected_failure(abort_code = 65567, location = pyth::pyth)] + fun test_parse_price_feed_updates_publish_times_outside_timestamps(aptos_framework: &signer) { let update_fee = 50; let initial_balance = 100; let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, @@ -1702,19 +1669,16 @@ module pyth::pyth_test { initial_balance); let update_data = TEST_VAAS; - let price_ids = vector[ - x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", - x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", - x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", - x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", - ]; - // invalid publish times: max_publish_time is less than min_publish_time - let min_publish_time: u64 = 1663680750; - let max_publish_time: u64 = 1663074345; + + // publish times that are outside the range of all price feeds + let min_publish_time: u64 = 2000000000; // Future timestamp + let max_publish_time: u64 = 2000000500; // Future timestamp - pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); + assert!(vector::length(&test_price_feeds) == 0, 1); cleanup_test(burn_capability, mint_capability); } + } From 84d6f1465a7e84e114a5708a2bb31d15e16b3a42 Mon Sep 17 00:00:00 2001 From: Holly Hodgkinson <1hhodgk1@gmail.com> Date: Thu, 15 Aug 2024 13:27:03 +0100 Subject: [PATCH 4/4] Minor amendments --- .../aptos/contracts/sources/pyth.move | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/target_chains/aptos/contracts/sources/pyth.move b/target_chains/aptos/contracts/sources/pyth.move index 100326c20f..d49db3c8db 100644 --- a/target_chains/aptos/contracts/sources/pyth.move +++ b/target_chains/aptos/contracts/sources/pyth.move @@ -403,7 +403,6 @@ module pyth::pyth { max_publish_time: u64 ): vector { let parsed_price_feeds = vector::empty(); - let parsed_price_ids = vector::empty>(); // validate publish times and populate parsed price feed vector let i = 0; @@ -415,9 +414,6 @@ module pyth::pyth { assert!(timestamp >= min_publish_time && timestamp <= max_publish_time, error::price_feed_outside_time_range()); - let price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(price_feed); - let price_id_bytes = price_identifier::get_bytes(price_id); - vector::push_back(&mut parsed_price_ids, price_id_bytes); vector::push_back(&mut parsed_price_feeds, *price_feed); i = i + 1; }; @@ -1593,8 +1589,7 @@ module pyth::pyth_test { let min_publish_time: u64 = 1663074345; let max_publish_time: u64 = 1663680750; - let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); - assert!(vector::length(&test_price_feeds) == 0, 1); + pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); cleanup_test(burn_capability, mint_capability); } @@ -1608,8 +1603,7 @@ module pyth::pyth_test { let min_publish_time: u64 = 1663074345; let max_publish_time: u64 = 1663680750; - let test_price_feeds: vector = pyth::parse_price_feed_updates(vector[corrupt_vaa], min_publish_time, max_publish_time, coins); - assert!(vector::length(&test_price_feeds) == 0, 1); + pyth::parse_price_feed_updates(vector[corrupt_vaa], min_publish_time, max_publish_time, coins); cleanup_test(burn_capability, mint_capability); } @@ -1629,8 +1623,7 @@ module pyth::pyth_test { ]; let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources, 50, 100); - let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); - assert!(vector::length(&test_price_feeds) == 0, 1); + pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); cleanup_test(burn_capability, mint_capability); } @@ -1651,8 +1644,7 @@ module pyth::pyth_test { let min_publish_time: u64 = 1663680750; let max_publish_time: u64 = 1663074345; - let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); - assert!(vector::length(&test_price_feeds) == 0, 1); + pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); cleanup_test(burn_capability, mint_capability); } @@ -1674,8 +1666,7 @@ module pyth::pyth_test { let min_publish_time: u64 = 2000000000; // Future timestamp let max_publish_time: u64 = 2000000500; // Future timestamp - let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); - assert!(vector::length(&test_price_feeds) == 0, 1); + pyth::parse_price_feed_updates(update_data, min_publish_time, max_publish_time, coins); cleanup_test(burn_capability, mint_capability); }