From cf7987f4c5883aadb5b2fe7f2e3069adea483a63 Mon Sep 17 00:00:00 2001 From: Daniel Chew Date: Thu, 9 May 2024 17:42:28 +0100 Subject: [PATCH] feat(target_chains/fuel): add governance contract (#1518) * add governance contract * add fuel ci * add rust-toolchain * add executes_governance_instruction test * add test for SetValidPeriod * add test for AuthorizeGovernanceDataSourceTransfer * remove SetWormholeAddress * add test for SetDataSources * remove WormholeAddressSetEvent * remove SetWormholeAddress * remove SetWormholeAddressPayload * remove SetWormholeAddressPayload and SetWormholeAddress imports * remove GovernanceAction::SetWormholeAddress * address comments * refactor test * add comments --- .github/workflows/ci-fuel-contract.yml | 35 ++ pythnet/pythnet_sdk/src/test_utils/mod.rs | 21 +- target_chains/fuel/contracts/Cargo.lock | 550 ++++++++++++++++-- target_chains/fuel/contracts/Cargo.toml | 6 + target_chains/fuel/contracts/Forc.lock | 4 +- .../fuel/contracts/fuel-toolchain.toml | 6 +- .../pyth-contract/src/data_structures.sw | 1 + .../src/data_structures/data_source.sw | 6 +- .../data_structures/governance_instruction.sw | 183 ++++++ .../src/data_structures/wormhole_light.sw | 12 +- .../contracts/pyth-contract/src/errors.sw | 7 + .../contracts/pyth-contract/src/events.sw | 27 +- .../fuel/contracts/pyth-contract/src/main.sw | 344 +++++++++-- .../pyth-interface/src/data_structures.sw | 2 + .../data_structures/governance_instruction.sw | 29 + .../src/data_structures/governance_payload.sw | 29 + .../src/data_structures/wormhole_light.sw | 5 - .../contracts/pyth-interface/src/interface.sw | 12 +- .../fuel/contracts/scripts/deploy_pyth.rs | 31 +- target_chains/fuel/contracts/src/constants.rs | 23 +- .../fuel/contracts/src/pyth_utils.rs | 150 ++++- .../fuel/contracts/tests/functions/mod.rs | 1 + .../tests/functions/pyth_core/ema_price.rs | 21 +- .../pyth_core/ema_price_no_older_than.rs | 19 +- .../functions/pyth_core/ema_price_unsafe.rs | 21 +- .../pyth_core/parse_price_feed_updates.rs | 19 +- .../tests/functions/pyth_core/price.rs | 22 +- .../pyth_core/price_no_older_than.rs | 19 +- .../tests/functions/pyth_core/price_unsafe.rs | 19 +- .../tests/functions/pyth_core/update_fee.rs | 19 +- .../functions/pyth_core/update_price_feeds.rs | 20 +- .../update_price_feeds_if_necessary.rs | 19 +- .../execute_governance_instruction.rs | 229 ++++++++ .../tests/functions/pyth_governance/mod.rs | 1 + .../functions/pyth_info/price_feed_unsafe.rs | 19 +- .../{constuctor.rs => constructor.rs} | 33 +- .../tests/functions/pyth_init/mod.rs | 2 +- .../contracts/tests/utils/interface/mod.rs | 1 + .../tests/utils/interface/pyth_governance.rs | 27 + .../tests/utils/interface/pyth_info.rs | 4 +- .../tests/utils/interface/pyth_init.rs | 15 +- .../utils/interface/wormhole_guardians.rs | 4 +- 42 files changed, 1803 insertions(+), 214 deletions(-) create mode 100644 .github/workflows/ci-fuel-contract.yml create mode 100644 target_chains/fuel/contracts/pyth-contract/src/data_structures/governance_instruction.sw create mode 100644 target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_instruction.sw create mode 100644 target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_payload.sw create mode 100644 target_chains/fuel/contracts/tests/functions/pyth_governance/execute_governance_instruction.rs create mode 100644 target_chains/fuel/contracts/tests/functions/pyth_governance/mod.rs rename target_chains/fuel/contracts/tests/functions/pyth_init/{constuctor.rs => constructor.rs} (77%) create mode 100644 target_chains/fuel/contracts/tests/utils/interface/pyth_governance.rs diff --git a/.github/workflows/ci-fuel-contract.yml b/.github/workflows/ci-fuel-contract.yml new file mode 100644 index 000000000..07c63403d --- /dev/null +++ b/.github/workflows/ci-fuel-contract.yml @@ -0,0 +1,35 @@ +name: Test Fuel Contract + +on: + pull_request: + paths: + - target_chains/fuel/** + push: + branches: + - main + paths: + - target_chains/fuel/** + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: target_chains/fuel/contracts/ + steps: + - uses: actions/checkout@v2 + - name: Install Fuel toolchain + run: | + curl https://install.fuel.network | sh + echo "$HOME/.fuelup/bin" >> $GITHUB_PATH + - name: Build with Forc + run: forc build --verbose + - name: Run tests with Forc + run: forc test --verbose + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/pythnet/pythnet_sdk/src/test_utils/mod.rs b/pythnet/pythnet_sdk/src/test_utils/mod.rs index bc58a9210..a7f3fba93 100644 --- a/pythnet/pythnet_sdk/src/test_utils/mod.rs +++ b/pythnet/pythnet_sdk/src/test_utils/mod.rs @@ -4,7 +4,11 @@ use { merkle::MerkleTree, Accumulator, }, - hashers::keccak256_160::Keccak160, + hashers::{ + keccak256::Keccak256, + keccak256_160::Keccak160, + Hasher, + }, messages::{ FeedId, Message, @@ -27,6 +31,7 @@ use { byteorder::BigEndian, libsecp256k1::{ Message as libsecp256k1Message, + PublicKey, RecoveryId, SecretKey, Signature, @@ -96,6 +101,19 @@ pub fn dummy_guardians() -> Vec { result } +pub fn dummy_guardians_addresses() -> Vec<[u8; 20]> { + let guardians = dummy_guardians(); + guardians + .iter() + .map(|x| { + let mut result: [u8; 20] = [0u8; 20]; + let pubkey = &PublicKey::from_secret_key(x).serialize()[1..]; + result.copy_from_slice(&Keccak256::hashv(&[&pubkey])[12..]); + result + }) + .collect() +} + pub fn create_dummy_feed_id(value: i64) -> FeedId { let mut dummy_id = [0; 32]; dummy_id[0] = value as u8; @@ -271,7 +289,6 @@ pub fn create_vaa_from_payload( ..Default::default() }; - (header, body).into() } diff --git a/target_chains/fuel/contracts/Cargo.lock b/target_chains/fuel/contracts/Cargo.lock index aabac6513..b34ba8062 100644 --- a/target_chains/fuel/contracts/Cargo.lock +++ b/target_chains/fuel/contracts/Cargo.lock @@ -134,6 +134,12 @@ version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "ascii" version = "0.9.3" @@ -182,7 +188,7 @@ dependencies = [ "Inflector", "async-graphql-parser", "darling 0.14.4", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -360,6 +366,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -387,6 +402,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -396,22 +420,98 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bs58" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2", + "sha2 0.10.8", "tinyvec", ] +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -517,11 +617,11 @@ checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ "bs58", "coins-core", - "digest", - "hmac", + "digest 0.10.7", + "hmac 0.12.1", "k256", "serde", - "sha2", + "sha2 0.10.8", "thiserror", ] @@ -533,11 +633,11 @@ checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ "bitvec", "coins-bip32", - "hmac", + "hmac 0.12.1", "once_cell", "pbkdf2 0.12.2", "rand", - "sha2", + "sha2 0.10.8", "thiserror", ] @@ -550,13 +650,13 @@ dependencies = [ "base64 0.21.7", "bech32", "bs58", - "digest", + "digest 0.10.7", "generic-array", "hex", "ripemd", "serde", "serde_derive", - "sha2", + "sha2 0.10.8", "sha3", "thiserror", ] @@ -713,6 +813,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "ct-logs" version = "0.8.0" @@ -740,7 +850,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest", + "digest 0.10.7", "fiat-crypto", "platforms", "rustc_version", @@ -911,13 +1021,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -935,6 +1054,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "ecdsa" version = "0.16.9" @@ -942,7 +1067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -966,7 +1091,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "sha2", + "sha2 0.10.8", "subtle", ] @@ -984,7 +1109,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -1054,15 +1179,15 @@ checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ "aes", "ctr", - "digest", + "digest 0.10.7", "hex", - "hmac", + "hmac 0.12.1", "pbkdf2 0.11.0", "rand", "scrypt", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "sha3", "thiserror", "uuid 0.8.2", @@ -1089,11 +1214,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "fast-math" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2465292146cdfc2011350fe3b1c616ac83cf0faeedb33463ba1c332ed8948d66" +dependencies = [ + "ieee754", +] + [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "ff" @@ -1451,7 +1585,7 @@ dependencies = [ "rand", "secp256k1", "serde", - "sha2", + "sha2 0.10.8", "zeroize", ] @@ -1474,12 +1608,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89143dd80b29dda305fbb033bc7f868834445ef6b361bf920f0077938fb6c0bc" dependencies = [ "derive_more", - "digest", + "digest 0.10.7", "fuel-storage", "hashbrown 0.13.2", "hex", "serde", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -1633,7 +1767,7 @@ dependencies = [ "itertools 0.12.1", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "thiserror", "uint", ] @@ -1694,7 +1828,7 @@ dependencies = [ "rand", "serde", "serde_json", - "serde_with 3.7.0", + "serde_with 3.8.0", "tempfile", "tokio", "which", @@ -1939,13 +2073,34 @@ dependencies = [ "serde", ] +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", ] [[package]] @@ -2048,7 +2203,7 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.21.11", + "rustls 0.21.12", "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", @@ -2129,6 +2284,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ieee754" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" + [[package]] name = "indexmap" version = "1.9.3" @@ -2208,7 +2369,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2", + "sha2 0.10.8", "signature", ] @@ -2239,6 +2400,54 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -2247,9 +2456,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2335,12 +2544,78 @@ dependencies = [ "tempfile", ] +[[package]] +name = "num" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -2375,6 +2650,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.64" @@ -2428,14 +2709,14 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.8", ] [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -2443,15 +2724,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -2466,7 +2747,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -2475,8 +2756,8 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", - "hmac", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -2602,6 +2883,15 @@ dependencies = [ "uint", ] +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2691,11 +2981,37 @@ dependencies = [ "dotenv", "fuels", "hex", + "libsecp256k1", + "pythnet-sdk", "rand", "reqwest", "serde", "serde_json", + "serde_wormhole", + "sha3", "tokio", + "wormhole-vaas-serde", +] + +[[package]] +name = "pythnet-sdk" +version = "2.0.0" +dependencies = [ + "bincode", + "borsh", + "bytemuck", + "byteorder", + "fast-math", + "hex", + "libsecp256k1", + "rand", + "rustc_version", + "serde", + "serde_wormhole", + "sha3", + "slow_primes", + "thiserror", + "wormhole-vaas-serde", ] [[package]] @@ -2765,11 +3081,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] @@ -2828,7 +3144,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.11", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -2853,7 +3169,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -2893,7 +3209,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -2913,9 +3229,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -2939,9 +3255,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", @@ -3048,6 +3364,30 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -3060,10 +3400,10 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ - "hmac", + "hmac 0.12.1", "pbkdf2 0.11.0", "salsa20", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -3159,24 +3499,44 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "serde_json" version = "1.0.116" @@ -3212,9 +3572,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.7.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +checksum = "2c85f8e96d1d6857f13768fcbd895fcb06225510022a2774ed8b5150581847b0" dependencies = [ "serde", "serde_derive", @@ -3232,6 +3592,32 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "serde_wormhole" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b022bf813578a06341fd453c3fd6e64945d9975191193d5d45e8dbd97d1d84" +dependencies = [ + "base64 0.13.1", + "itoa", + "serde", + "serde_bytes", + "thiserror", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.8" @@ -3240,7 +3626,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3249,7 +3635,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] @@ -3268,7 +3654,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core", ] @@ -3281,6 +3667,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slow_primes" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58267dd2fbaa6dceecba9e3e106d2d90a2b02497c0e8b01b8759beccf5113938" +dependencies = [ + "num", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -3604,7 +3999,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.11", + "rustls 0.21.12", "tokio", ] @@ -3634,6 +4029,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -4179,6 +4583,32 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wormhole-supported-chains" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f42a80a24212937cc7d7b0ab8115bb87d82f949a1a42f75d500807072c94ba4" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "wormhole-vaas-serde" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240c5a6136dc66ecc65097bb6d159e849b5df4ecbbbb220868d0edbdcc568ed3" +dependencies = [ + "anyhow", + "bstr", + "schemars", + "serde", + "serde_wormhole", + "sha3", + "thiserror", + "wormhole-supported-chains", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/target_chains/fuel/contracts/Cargo.toml b/target_chains/fuel/contracts/Cargo.toml index 9b2219389..b0b8a132e 100644 --- a/target_chains/fuel/contracts/Cargo.toml +++ b/target_chains/fuel/contracts/Cargo.toml @@ -16,6 +16,12 @@ reqwest = "0.11.27" serde_json = "1.0.114" serde = "1.0.197" dotenv = "0.15.0" +libsecp256k1 = "0.7.1" +pythnet-sdk = { path = "../../../pythnet/pythnet_sdk", features = ["test-utils"] } +sha3 = "0.10.8" +serde_wormhole = { version ="0.1.0" } +wormhole-vaas-serde = { version = "0.1.0" } + [[bin]] name = "deploy_pyth" diff --git a/target_chains/fuel/contracts/Forc.lock b/target_chains/fuel/contracts/Forc.lock index cd92e1896..856b773cd 100644 --- a/target_chains/fuel/contracts/Forc.lock +++ b/target_chains/fuel/contracts/Forc.lock @@ -1,6 +1,6 @@ [[package]] name = "core" -source = "path+from-root-C3992B43B72ADB8C" +source = "path+from-root-566CA1D5F8BEAFBF" [[package]] name = "ownership" @@ -40,5 +40,5 @@ dependencies = ["std"] [[package]] name = "std" -source = "git+https://github.com/fuellabs/sway?tag=v0.49.1#2ac7030570f22510b0ac2a7b5ddf7baa20bdc0e1" +source = "git+https://github.com/fuellabs/sway?tag=v0.49.3#0dc6570377ee9c4a6359ade597fa27351e02a728" dependencies = ["core"] diff --git a/target_chains/fuel/contracts/fuel-toolchain.toml b/target_chains/fuel/contracts/fuel-toolchain.toml index e75c0bb58..0ef187fe5 100644 --- a/target_chains/fuel/contracts/fuel-toolchain.toml +++ b/target_chains/fuel/contracts/fuel-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] -channel = "nightly-2024-01-24" +channel = "beta-5" [components] -forc = "0.49.1" -fuel-core = "0.22.0" +forc = "0.49.3" +fuel-core = "0.22.4" diff --git a/target_chains/fuel/contracts/pyth-contract/src/data_structures.sw b/target_chains/fuel/contracts/pyth-contract/src/data_structures.sw index 1c395c121..8177afcbb 100644 --- a/target_chains/fuel/contracts/pyth-contract/src/data_structures.sw +++ b/target_chains/fuel/contracts/pyth-contract/src/data_structures.sw @@ -6,3 +6,4 @@ pub mod price; pub mod accumulator_update; pub mod batch_attestation_update; pub mod update_type; +pub mod governance_instruction; diff --git a/target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw b/target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw index 007df47c4..804a4db6c 100644 --- a/target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw +++ b/target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw @@ -19,7 +19,7 @@ impl DataSource { } #[storage(read)] - pub fn is_valid( + pub fn is_valid_data_source( self, is_valid_data_source: StorageKey>, ) -> bool { @@ -28,4 +28,8 @@ impl DataSource { None => false, } } + + pub fn is_valid_governance_data_source(self, chain_id: u16, emitter_address: b256) -> bool { + self.chain_id == chain_id && self.emitter_address == emitter_address + } } diff --git a/target_chains/fuel/contracts/pyth-contract/src/data_structures/governance_instruction.sw b/target_chains/fuel/contracts/pyth-contract/src/data_structures/governance_instruction.sw new file mode 100644 index 000000000..e477aa8bb --- /dev/null +++ b/target_chains/fuel/contracts/pyth-contract/src/data_structures/governance_instruction.sw @@ -0,0 +1,183 @@ +library; + +use ::errors::PythError; +use ::data_structures::{data_source::*, price::*, wormhole_light::{StorageGuardianSet, WormholeVM}}; +use pyth_interface::data_structures::{data_source::DataSource, price::{PriceFeed, PriceFeedId}, governance_payload::{UpgradeContractPayload, AuthorizeGovernanceDataSourceTransferPayload, RequestGovernanceDataSourceTransferPayload, SetDataSourcesPayload, SetFeePayload, SetValidPeriodPayload}, governance_instruction::{GovernanceInstruction, GovernanceModule, GovernanceAction}}; +use std::{bytes::Bytes, hash::Hash}; +use std::math::*; +use std::primitive_conversions::{u32::*, u64::*}; + + +pub const MAGIC: u32 = 0x5054474d; + +impl GovernanceInstruction { + pub fn new(magic: u32, + module: GovernanceModule, + action: GovernanceAction, + target_chain_id: u16, + payload: Bytes + ) -> Self { + Self { magic, module, action, target_chain_id, payload } + } + + pub fn parse_governance_instruction(encoded_instruction: Bytes) -> Self { + let mut index = 0; + let magic = u32::from_be_bytes([ + encoded_instruction.get(index).unwrap(), + encoded_instruction.get(index + 1).unwrap(), + encoded_instruction.get(index + 2).unwrap(), + encoded_instruction.get(index + 3).unwrap(), + ]); + require(magic == MAGIC, PythError::InvalidMagic); + index += 4; + + let mod_number = encoded_instruction.get(index).unwrap(); + let module = match mod_number { + 0 => GovernanceModule::Executor, + 1 => GovernanceModule::Target, + 2 => GovernanceModule::EvmExecutor, + 3 => GovernanceModule::StacksTarget, + _ => GovernanceModule::Invalid, + }; + require(match module { + GovernanceModule::Target => true, + _ => false, + }, PythError::InvalidGovernanceTarget); + index += 1; + + let action_number = encoded_instruction.get(index).unwrap(); + let governance_action = match action_number { + 0 => GovernanceAction::UpgradeContract, // Not implemented + 1 => GovernanceAction::AuthorizeGovernanceDataSourceTransfer, + 2 => GovernanceAction::SetDataSources, + 3 => GovernanceAction::SetFee, + 4 => GovernanceAction::SetValidPeriod, + 5 => GovernanceAction::RequestGovernanceDataSourceTransfer, + _ => GovernanceAction::Invalid, + }; + require(match governance_action { + GovernanceAction::Invalid => false, + _ => true, + }, PythError::InvalidGovernanceAction); + index += 1; + + let target_chain_id = u16::from_be_bytes([ + encoded_instruction.get(index).unwrap(), + encoded_instruction.get(index + 1).unwrap(), + ]); + index += 2; + + let (_, payload) = encoded_instruction.split_at(index); + + GovernanceInstruction::new( + magic, + module, + governance_action, + target_chain_id, + payload, + ) + } + + /// Parse an AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation + pub fn parse_authorize_governance_data_source_transfer_payload(encoded_payload: Bytes) -> AuthorizeGovernanceDataSourceTransferPayload { + AuthorizeGovernanceDataSourceTransferPayload { + claim_vaa: encoded_payload, + } + } + + pub fn parse_request_governance_data_source_transfer_payload(encoded_payload: Bytes) -> RequestGovernanceDataSourceTransferPayload { + let mut index = 0; + let governance_data_source_index = u32::from_be_bytes([ + encoded_payload.get(index).unwrap(), + encoded_payload.get(index + 1).unwrap(), + encoded_payload.get(index + 2).unwrap(), + encoded_payload.get(index + 3).unwrap(), + ]); + index += 4; + require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage); + let rdgst = RequestGovernanceDataSourceTransferPayload { + governance_data_source_index, + }; + rdgst + } + + pub fn parse_set_data_sources_payload(encoded_payload: Bytes) -> SetDataSourcesPayload { + let mut index = 0; + let data_sources_length = encoded_payload.get(index).unwrap().as_u64(); + index += 1; + let mut data_sources = Vec::with_capacity(data_sources_length); + + let mut i = 0; + while i < data_sources_length { + let (_, slice) = encoded_payload.split_at(index); + let (slice, _) = slice.split_at(2); + let chain_id = u16::from_be_bytes([slice.get(0).unwrap(), slice.get(1).unwrap()]); + index += 2; + let (_, slice) = encoded_payload.split_at(index); + let (slice, _) = slice.split_at(32); + let emitter_address: b256 = slice.into(); + index += 32; + + data_sources.push(DataSource { + chain_id, + emitter_address, + }); + i += 1 + } + + require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage); + let sds = SetDataSourcesPayload { data_sources }; + sds + } + + pub fn parse_set_fee_payload(encoded_payload: Bytes) -> SetFeePayload { + let mut index = 0; + let val = u64::from_be_bytes([ + encoded_payload.get(index).unwrap(), + encoded_payload.get(index + 1).unwrap(), + encoded_payload.get(index + 2).unwrap(), + encoded_payload.get(index + 3).unwrap(), + encoded_payload.get(index + 4).unwrap(), + encoded_payload.get(index + 5).unwrap(), + encoded_payload.get(index + 6).unwrap(), + encoded_payload.get(index + 7).unwrap(), + ]); + index += 8; + let expo = u64::from_be_bytes([ + encoded_payload.get(index).unwrap(), + encoded_payload.get(index + 1).unwrap(), + encoded_payload.get(index + 2).unwrap(), + encoded_payload.get(index + 3).unwrap(), + encoded_payload.get(index + 4).unwrap(), + encoded_payload.get(index + 5).unwrap(), + encoded_payload.get(index + 6).unwrap(), + encoded_payload.get(index + 7).unwrap(), + ]); + index += 8; + require(encoded_payload.len() == index, PythError::InvalidGovernanceMessage); + let sf = SetFeePayload { + new_fee: val * 10u64.pow(expo.try_as_u32().unwrap()), + }; + sf + } + + pub fn parse_set_valid_period_payload(encoded_payload: Bytes) -> SetValidPeriodPayload { + let mut index = 0; + let valid_time_period_seconds = u64::from_be_bytes([ + encoded_payload.get(index).unwrap(), + encoded_payload.get(index + 1).unwrap(), + encoded_payload.get(index + 2).unwrap(), + encoded_payload.get(index + 3).unwrap(), + encoded_payload.get(index + 4).unwrap(), + encoded_payload.get(index + 5).unwrap(), + encoded_payload.get(index + 6).unwrap(), + encoded_payload.get(index + 7).unwrap(), + ]); + index += 8; + require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage); + let svp = SetValidPeriodPayload { + new_valid_period: valid_time_period_seconds, + }; + svp + } +} diff --git a/target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw b/target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw index 1f40014dd..db361c448 100644 --- a/target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw +++ b/target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw @@ -6,7 +6,6 @@ use pyth_interface::data_structures::{ data_source::DataSource, wormhole_light::{ GuardianSet, - WormholeProvider, }, }; use std::{ @@ -145,15 +144,6 @@ impl GuardianSetUpgrade { } } -impl WormholeProvider { - pub fn new(governance_chain_id: u16, governance_contract: b256) -> Self { - WormholeProvider { - governance_chain_id, - governance_contract, - } - } -} - pub struct GuardianSignature { guardian_index: u8, r: b256, @@ -582,7 +572,7 @@ impl WormholeVM { ); require( DataSource::new(vm.emitter_chain_id, vm.emitter_address) - .is_valid(is_valid_data_source), + .is_valid_data_source(is_valid_data_source), WormholeError::InvalidUpdateDataSource, ); vm diff --git a/target_chains/fuel/contracts/pyth-contract/src/errors.sw b/target_chains/fuel/contracts/pyth-contract/src/errors.sw index 9f3646120..13fc39b09 100644 --- a/target_chains/fuel/contracts/pyth-contract/src/errors.sw +++ b/target_chains/fuel/contracts/pyth-contract/src/errors.sw @@ -9,6 +9,11 @@ pub enum PythError { InvalidAttestationSize: (), InvalidDataSourcesLength: (), InvalidExponent: (), + InvalidGovernanceDataSource: (), + InvalidGovernanceAction: (), + InvalidGovernanceMessage: (), + InvalidGovernanceModule: (), + InvalidGovernanceTarget: (), InvalidHeaderSize: (), InvalidMagic: (), InvalidMajorVersion: (), @@ -21,9 +26,11 @@ pub enum PythError { InvalidUpdateDataLength: (), InvalidUpdateDataSource: (), InvalidUpgradeModule: (), + InvalidWormholeAddressToSet: (), LengthOfPriceFeedIdsAndPublishTimesMustMatch: (), NewGuardianSetIsEmpty: (), NumberOfUpdatesIrretrievable: (), + OldGovernanceMessage: (), /// Emitted when a Price's `publish_time` is stale. OutdatedPrice: (), /// Emitted when a PriceFeed could not be retrieved. diff --git a/target_chains/fuel/contracts/pyth-contract/src/events.sw b/target_chains/fuel/contracts/pyth-contract/src/events.sw index 8c4ca4c73..c1525a657 100644 --- a/target_chains/fuel/contracts/pyth-contract/src/events.sw +++ b/target_chains/fuel/contracts/pyth-contract/src/events.sw @@ -3,7 +3,6 @@ library; use pyth_interface::data_structures::{ data_source::DataSource, price::PriceFeedId, - wormhole_light::WormholeProvider, }; pub struct ConstructedEvent { @@ -19,3 +18,29 @@ pub struct NewGuardianSetEvent { pub struct UpdatedPriceFeedsEvent { updated_price_feeds: Vec, } + +pub struct ContractUpgradedEvent { + old_implementation: Identity, + new_implementation: Identity, +} + +pub struct GovernanceDataSourceSetEvent { + old_data_source: DataSource, + new_data_source: DataSource, + initial_sequence: u64, +} + +pub struct DataSourcesSetEvent { + old_data_sources: Vec, + new_data_sources: Vec, +} + +pub struct FeeSetEvent { + old_fee: u64, + new_fee: u64, +} + +pub struct ValidPeriodSetEvent { + old_valid_period: u64, + new_valid_period: u64, +} diff --git a/target_chains/fuel/contracts/pyth-contract/src/main.sw b/target_chains/fuel/contracts/pyth-contract/src/main.sw index 459336acf..4fd155f9b 100644 --- a/target_chains/fuel/contracts/pyth-contract/src/main.sw +++ b/target_chains/fuel/contracts/pyth-contract/src/main.sw @@ -15,12 +15,17 @@ use std::{ ZERO_B256, }, context::msg_amount, - hash::Hash, + hash::{ + Hash, + keccak256, + sha256, + }, storage::{ storage_map::StorageMap, storage_vec::*, }, u256::U256, + revert::revert, }; use ::errors::{PythError, WormholeError}; @@ -31,8 +36,9 @@ use ::data_structures::{ price::*, update_type::UpdateType, wormhole_light::*, + governance_instruction::*, }; -use ::events::{ConstructedEvent, NewGuardianSetEvent, UpdatedPriceFeedsEvent}; +use ::events::{ConstructedEvent, NewGuardianSetEvent, UpdatedPriceFeedsEvent, ContractUpgradedEvent, GovernanceDataSourceSetEvent, DataSourcesSetEvent, FeeSetEvent, ValidPeriodSetEvent}; use pyth_interface::{ data_structures::{ @@ -42,10 +48,11 @@ use pyth_interface::{ PriceFeed, PriceFeedId, }, + governance_payload::{UpgradeContractPayload, AuthorizeGovernanceDataSourceTransferPayload, SetDataSourcesPayload, SetFeePayload, SetValidPeriodPayload}, wormhole_light::{ GuardianSet, - WormholeProvider, }, + governance_instruction::{GovernanceInstruction, GovernanceModule, GovernanceAction}, }, PythCore, PythInfo, @@ -70,6 +77,7 @@ storage { // Mapping of cached price information // priceId => PriceInfo latest_price_feed: StorageMap = StorageMap {}, + // Fee required for each update single_update_fee: u64 = 0, // For tracking all active emitter/chain ID pairs valid_data_sources: StorageVec = StorageVec {}, @@ -77,20 +85,38 @@ storage { /// This includes attestation delay, block time, and potential clock drift /// between the source/target chains. valid_time_period_seconds: u64 = 0, - // | | - // --+-- WORMHOLE STATE --+-- - // | | - // Mapping of consumed governance actions + /// Governance data source. VAA messages from this source can change this contract + /// state. e.g., upgrade the contract, change the valid data sources, and more. + governance_data_source: DataSource = DataSource { + chain_id: 0u16, + emitter_address: ZERO_B256, + }, + /// Index of the governance data source, increased each time the governance data source changes. + governance_data_source_index: u32 = 0, + /// Sequence number of the last executed governance message. Any governance message + /// with a lower or equal sequence number will be discarded. This prevents double-execution, + /// and also makes sure that messages are executed in the right order. + last_executed_governance_sequence: u64 = 0, + /// Chain ID of the contract + chain_id: u16 = 0, + /// | | + /// --+-- WORMHOLE STATE --+-- + /// | | + /// Mapping of consumed governance actions wormhole_consumed_governance_actions: StorageMap = StorageMap {}, - // Mapping of guardian_set_index => guardian set + /// Mapping of guardian_set_index => guardian set wormhole_guardian_sets: StorageMap = StorageMap {}, - // Current active guardian set index + /// Current active guardian set index wormhole_guardian_set_index: u32 = 0, - // Using Ethereum's Wormhole governance - wormhole_provider: WormholeProvider = WormholeProvider { - governance_chain_id: 0u16, - governance_contract: ZERO_B256, + /// Using Ethereum's Wormhole governance + wormhole_governance_data_source: DataSource = DataSource { + chain_id: 0u16, + emitter_address: ZERO_B256, }, + /// | | + /// --+-- GOVERNANCE STATE --+-- + /// | | + current_implementation: Identity = Identity::Address(Address::from(ZERO_B256)), } impl SRC5 for Contract { @@ -409,15 +435,61 @@ fn valid_time_period() -> u64 { storage.valid_time_period_seconds.read() } +#[storage(read)] +fn governance_data_source() -> DataSource { + storage.governance_data_source.read() +} + +#[storage(write)] +fn set_governance_data_source(data_source: DataSource) { + storage.governance_data_source.write(data_source); +} + +#[storage(read)] +fn governance_data_source_index() -> u32 { + storage.governance_data_source_index.read() +} + +#[storage(write)] +fn set_governance_data_source_index(index: u32) { + storage.governance_data_source_index.write(index); +} + +#[storage(read)] +fn last_executed_governance_sequence() -> u64 { + storage.last_executed_governance_sequence.read() +} + +#[storage(write)] +fn set_last_executed_governance_sequence(sequence: u64) { + storage.last_executed_governance_sequence.write(sequence); +} + +#[storage(read)] +fn chain_id() -> u16 { + storage.chain_id.read() +} + +#[storage(read)] +fn current_implementation() -> Identity { + storage.current_implementation.read() +} + impl PythInit for Contract { #[storage(read, write)] fn constructor( data_sources: Vec, + governance_data_source: DataSource, + wormhole_governance_data_source: DataSource, single_update_fee: u64, valid_time_period_seconds: u64, - wormhole_guardian_set_upgrade: Bytes, + wormhole_guardian_set_addresses: Vec, + wormhole_guardian_set_index: u32, + chain_id: u16, ) { + // This function sets the passed identity as the initial owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L127-L138 initialize_ownership(DEPLOYER); + // This function ensures that the sender is the owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L59-L65 only_owner(); require(data_sources.len > 0, PythError::InvalidDataSourcesLength); @@ -436,26 +508,35 @@ impl PythInit for Contract { .write(valid_time_period_seconds); storage.single_update_fee.write(single_update_fee); - let vm = WormholeVM::parse_initial_wormhole_vm(wormhole_guardian_set_upgrade); - let upgrade = GuardianSetUpgrade::parse_encoded_upgrade(0, vm.payload); + let guardian_length: u8 = wormhole_guardian_set_addresses.len().try_as_u8().unwrap(); + let mut new_guardian_set = StorageGuardianSet::new( + 0, + StorageKey { + slot: sha256(("guardian_set_keys", wormhole_guardian_set_index)), + offset: 0, + field_id: ZERO_B256, + }, + ); + let mut i: u8 = 0; + while i < guardian_length { + let key: b256 = wormhole_guardian_set_addresses.get(i.as_u64()).unwrap(); + new_guardian_set.keys.push(key); + i += 1; + } + + storage.wormhole_guardian_set_index.write(wormhole_guardian_set_index); + storage.wormhole_guardian_sets.insert(wormhole_guardian_set_index, new_guardian_set); - storage - .wormhole_consumed_governance_actions - .insert(vm.governance_action_hash, true); - storage - .wormhole_guardian_sets - .insert(upgrade.new_guardian_set_index, upgrade.new_guardian_set); - storage - .wormhole_guardian_set_index - .write(upgrade.new_guardian_set_index); - storage - .wormhole_provider - .write(WormholeProvider::new(vm.emitter_chain_id, vm.emitter_address)); + storage.governance_data_source.write(governance_data_source); + storage.wormhole_governance_data_source.write(wormhole_governance_data_source); + + storage.chain_id.write(chain_id); + // This function revokes ownership of the current owner and disallows any new owners. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L89-L99 renounce_ownership(); log(ConstructedEvent { - guardian_set_index: upgrade.new_guardian_set_index, + guardian_set_index: wormhole_guardian_set_index, }) } } @@ -492,8 +573,8 @@ impl PythInfo for Contract { } #[storage(read)] - fn valid_data_source(data_source: DataSource) -> bool { - data_source.is_valid(storage.is_valid_data_source) + fn is_valid_data_source(data_source: DataSource) -> bool { + data_source.is_valid_data_source(storage.is_valid_data_source) } } @@ -513,7 +594,7 @@ impl WormholeGuardians for Contract { } #[storage(read)] - fn current_wormhole_provider() -> WormholeProvider { + fn current_wormhole_provider() -> DataSource { current_wormhole_provider() } @@ -546,8 +627,8 @@ fn current_guardian_set_index() -> u32 { } #[storage(read)] -fn current_wormhole_provider() -> WormholeProvider { - storage.wormhole_provider.read() +fn current_wormhole_provider() -> DataSource { + storage.wormhole_governance_data_source.read() } #[storage(read)] @@ -573,12 +654,12 @@ fn submit_new_guardian_set(encoded_vm: Bytes) { let current_wormhole_provider = current_wormhole_provider(); require( vm.emitter_chain_id == current_wormhole_provider - .governance_chain_id, + .chain_id, WormholeError::InvalidGovernanceChain, ); require( vm.emitter_address == current_wormhole_provider - .governance_contract, + .emitter_address, WormholeError::InvalidGovernanceContract, ); require( @@ -615,3 +696,194 @@ fn submit_new_guardian_set(encoded_vm: Bytes) { new_guardian_set_index: upgrade.new_guardian_set_index, }) } + +/// Transfer the governance data source to a new value with sanity checks to ensure the new governance data source can manage the contract. +#[storage(read, write)] +fn authorize_governance_data_source_transfer(payload: AuthorizeGovernanceDataSourceTransferPayload) { + let old_governance_data_source = governance_data_source(); + + // Parse and verify the VAA contained in the payload to ensure it's valid and can manage the contract + let vm = WormholeVM::parse_and_verify_wormhole_vm( + current_guardian_set_index(), + payload.claim_vaa, + storage.wormhole_guardian_sets, + ); + + let gi = GovernanceInstruction::parse_governance_instruction(vm.payload); + require(gi.target_chain_id == chain_id() || gi.target_chain_id == 0, PythError::InvalidGovernanceTarget); + + require(match gi.action { + GovernanceAction::RequestGovernanceDataSourceTransfer => true, + _ => false, + }, PythError::InvalidGovernanceMessage); + + let claim_payload = GovernanceInstruction::parse_request_governance_data_source_transfer_payload(gi.payload); + + require(governance_data_source_index() < claim_payload.governance_data_source_index, PythError::OldGovernanceMessage); + + set_governance_data_source_index(claim_payload.governance_data_source_index); + + let new_governance_data_source = DataSource { + chain_id: vm.emitter_chain_id, + emitter_address: vm.emitter_address, + }; + + set_governance_data_source(new_governance_data_source); + + // Setting the last executed governance to the claimVaa sequence to avoid using older sequences. + set_last_executed_governance_sequence(vm.sequence); + + log(GovernanceDataSourceSetEvent { + old_data_source: old_governance_data_source, + new_data_source: new_governance_data_source, + initial_sequence: vm.sequence, + }); +} + +#[storage(read, write)] +fn set_data_sources(payload: SetDataSourcesPayload) { + let old_data_sources = storage.valid_data_sources.load_vec(); + + let mut i = 0; + while i < old_data_sources.len { + let data_source = old_data_sources.get(i).unwrap(); + storage.is_valid_data_source.insert(data_source, false); + i += 1; + } + + // Clear the current list of valid data sources + storage.valid_data_sources.clear(); + + i = 0; + // Add new data sources from the payload and mark them as valid + while i < payload.data_sources.len { + let data_source = payload.data_sources.get(i).unwrap(); + storage.valid_data_sources.push(data_source); + storage.is_valid_data_source.insert(data_source, true); + + i += 1; + } + + // Emit an event with the old and new data sources + log(DataSourcesSetEvent { + old_data_sources: old_data_sources, + new_data_sources: storage.valid_data_sources.load_vec(), + }); +} + +#[storage(read, write)] +fn set_fee(payload: SetFeePayload) { + let old_fee = storage.single_update_fee.read(); + storage.single_update_fee.write(payload.new_fee); + + log(FeeSetEvent { + old_fee, + new_fee: payload.new_fee, + }); +} + +#[storage(read, write)] +fn set_valid_period(payload: SetValidPeriodPayload) { + let old_valid_period = storage.valid_time_period_seconds.read(); + storage.valid_time_period_seconds.write(payload.new_valid_period); + + log(ValidPeriodSetEvent { + old_valid_period, + new_valid_period: payload.new_valid_period, + }); +} + +abi PythGovernance { + #[storage(read)] + fn governance_data_source() -> DataSource; + + #[storage(read, write)] + fn execute_governance_instruction(encoded_vm: Bytes); +} + +impl PythGovernance for Contract { + #[storage(read)] + fn governance_data_source() -> DataSource { + governance_data_source() + } + + #[storage(read, write)] + fn execute_governance_instruction(encoded_vm: Bytes) { + let vm = verify_governance_vm(encoded_vm); + // Log so that the WormholeVM struct will show up in the ABI and can be used in the tests + log(vm); + + let gi = GovernanceInstruction::parse_governance_instruction(vm.payload); + // Log so that the GovernanceInstruction struct will show up in the ABI and can be used in the tests + log(gi); + + require(gi.target_chain_id == chain_id() || gi.target_chain_id == 0, PythError::InvalidGovernanceTarget); + + match gi.action { + GovernanceAction::UpgradeContract => { + require(gi.target_chain_id != 0, PythError::InvalidGovernanceTarget); + // TODO: implement upgrade_upgradeable_contract(uc) when Fuel releases the upgrade standard library; + log("Upgrade functionality not implemented"); + revert(0u64); + }, + GovernanceAction::AuthorizeGovernanceDataSourceTransfer => { + let agdst = GovernanceInstruction::parse_authorize_governance_data_source_transfer_payload(gi.payload); + log(agdst); + authorize_governance_data_source_transfer(agdst); + }, + GovernanceAction::SetDataSources => { + let sdsp = GovernanceInstruction::parse_set_data_sources_payload(gi.payload); + log(sdsp); + set_data_sources(sdsp); + }, + GovernanceAction::SetFee => { + let sf = GovernanceInstruction::parse_set_fee_payload(gi.payload); + log(sf); + set_fee(sf); + }, + GovernanceAction::SetValidPeriod => { + let svp = GovernanceInstruction::parse_set_valid_period_payload(gi.payload); + log(svp); + set_valid_period(svp); + }, + GovernanceAction::RequestGovernanceDataSourceTransfer => { + // RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message + // The `revert` function only accepts u64, so as + // a workaround we use require. + require(false, PythError::InvalidGovernanceMessage); + }, + _ => { + // The `revert` function only accepts u64, so as + // a workaround we use require. + require(false, PythError::InvalidGovernanceMessage); + } + } + } +} + + +#[storage(read, write)] +fn verify_governance_vm(encoded_vm: Bytes) -> WormholeVM { + let vm = WormholeVM::parse_and_verify_wormhole_vm( + current_guardian_set_index(), + encoded_vm, + storage + .wormhole_guardian_sets, + ); + + require( + storage + .governance_data_source + .read() + .is_valid_governance_data_source(vm.emitter_chain_id, vm.emitter_address), + PythError::InvalidGovernanceDataSource, + ); + + require( + vm.sequence > last_executed_governance_sequence(), + PythError::OldGovernanceMessage, + ); + + set_last_executed_governance_sequence(vm.sequence); + vm +} diff --git a/target_chains/fuel/contracts/pyth-interface/src/data_structures.sw b/target_chains/fuel/contracts/pyth-interface/src/data_structures.sw index f701c8196..cff97bdba 100644 --- a/target_chains/fuel/contracts/pyth-interface/src/data_structures.sw +++ b/target_chains/fuel/contracts/pyth-interface/src/data_structures.sw @@ -2,4 +2,6 @@ library; pub mod data_source; pub mod price; +pub mod governance_payload; +pub mod governance_instruction; pub mod wormhole_light; diff --git a/target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_instruction.sw b/target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_instruction.sw new file mode 100644 index 000000000..48b7dcc41 --- /dev/null +++ b/target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_instruction.sw @@ -0,0 +1,29 @@ +library; + +use std::bytes::Bytes; + +pub struct GovernanceInstruction { + magic: u32, + module: GovernanceModule, + action: GovernanceAction, + target_chain_id: u16, + payload: Bytes, +} + +pub enum GovernanceModule { + Executor: (), // 0 + Target: (), // 1 + EvmExecutor: (), // 2 + StacksTarget: (), // 3 + Invalid: (), +} + +pub enum GovernanceAction { + UpgradeContract: (), // 0 + AuthorizeGovernanceDataSourceTransfer: (), // 1 + SetDataSources: (), // 2 + SetFee: (), // 3 + SetValidPeriod: (), // 4 + RequestGovernanceDataSourceTransfer: (), // 5 + Invalid: (), +} diff --git a/target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_payload.sw b/target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_payload.sw new file mode 100644 index 000000000..c6da5ad29 --- /dev/null +++ b/target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_payload.sw @@ -0,0 +1,29 @@ +library; + +use std::bytes::Bytes; + +use ::data_structures::data_source::DataSource; + +pub struct UpgradeContractPayload { + new_implementation: Identity, +} + +pub struct AuthorizeGovernanceDataSourceTransferPayload { + claim_vaa: Bytes, +} + +pub struct RequestGovernanceDataSourceTransferPayload { + governance_data_source_index: u32, +} + +pub struct SetDataSourcesPayload { + data_sources: Vec, +} + +pub struct SetFeePayload { + new_fee: u64, +} + +pub struct SetValidPeriodPayload { + new_valid_period: u64, +} diff --git a/target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw b/target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw index 3e70a8ca9..29253ca78 100644 --- a/target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw +++ b/target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw @@ -4,8 +4,3 @@ pub struct GuardianSet { expiration_time: u64, keys: Vec, } - -pub struct WormholeProvider { - governance_chain_id: u16, - governance_contract: b256, -} diff --git a/target_chains/fuel/contracts/pyth-interface/src/interface.sw b/target_chains/fuel/contracts/pyth-interface/src/interface.sw index 29989cae0..5d94b8838 100644 --- a/target_chains/fuel/contracts/pyth-interface/src/interface.sw +++ b/target_chains/fuel/contracts/pyth-interface/src/interface.sw @@ -9,9 +9,9 @@ use ::data_structures::{ PriceFeed, PriceFeedId, }, + governance_payload::UpgradeContractPayload, wormhole_light::{ GuardianSet, - WormholeProvider, }, }; use std::{bytes::Bytes, storage::storage_vec::*}; @@ -259,9 +259,13 @@ abi PythInit { #[storage(read, write)] fn constructor( data_sources: Vec, + governance_data_source: DataSource, + wormhole_governance_data_source: DataSource, single_update_fee: u64, valid_time_period_seconds: u64, - wormhole_guardian_set_upgrade: Bytes, + wormhole_guardian_set_addresses: Vec, + wormhole_guardian_set_index: u32, + chain_id: u16, ); } @@ -284,7 +288,7 @@ abi PythInfo { fn single_update_fee() -> u64; #[storage(read)] - fn valid_data_source(data_source: DataSource) -> bool; + fn is_valid_data_source(data_source: DataSource) -> bool; #[storage(read)] fn valid_data_sources() -> Vec; @@ -295,7 +299,7 @@ abi WormholeGuardians { fn current_guardian_set_index() -> u32; #[storage(read)] - fn current_wormhole_provider() -> WormholeProvider; + fn current_wormhole_provider() -> DataSource; #[storage(read)] fn governance_action_is_consumed(hash: b256) -> bool; diff --git a/target_chains/fuel/contracts/scripts/deploy_pyth.rs b/target_chains/fuel/contracts/scripts/deploy_pyth.rs index a47e7a477..a6c004704 100644 --- a/target_chains/fuel/contracts/scripts/deploy_pyth.rs +++ b/target_chains/fuel/contracts/scripts/deploy_pyth.rs @@ -2,13 +2,13 @@ use fuels::{ prelude::{Address, Provider, WalletUnlocked}, types::Bits256, }; -use pyth_sdk::{constants::BETA_5_URL, pyth_utils::guardian_set_upgrade_4_vaa}; +use pyth_sdk::{constants::BETA_5_URL, pyth_utils::guardian_set_upgrade_4_addresses}; use pyth_sdk::{ constants::{ - BTC_USD_PRICE_FEED_ID, DEFAULT_VALID_TIME_PERIOD, ETH_USD_PRICE_FEED_ID, + BTC_USD_PRICE_FEED_ID, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, ETH_USD_PRICE_FEED_ID, USDC_USD_PRICE_FEED_ID, }, - pyth_utils::{update_data_bytes, Pyth}, + pyth_utils::{update_data_bytes, DataSource, Pyth}, }; #[tokio::main] @@ -26,8 +26,31 @@ async fn main() { let pyth = Pyth::deploy(admin).await.unwrap(); + let governance_data_source: DataSource = DataSource { + chain_id: 1, + emitter_address: Bits256::from_hex_str( + "5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e", + ) + .unwrap(), + }; + + let wormhole_governance_data_source: DataSource = DataSource { + chain_id: 1, + emitter_address: Bits256([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, + ]), + }; + let _ = pyth - .constructor(DEFAULT_VALID_TIME_PERIOD, guardian_set_upgrade_4_vaa()) + .constructor( + governance_data_source, + wormhole_governance_data_source, + DEFAULT_VALID_TIME_PERIOD, + guardian_set_upgrade_4_addresses(), + 4, + DUMMY_CHAIN_ID, + ) .await .unwrap(); diff --git a/target_chains/fuel/contracts/src/constants.rs b/target_chains/fuel/contracts/src/constants.rs index c0b02fd3b..d6f205902 100644 --- a/target_chains/fuel/contracts/src/constants.rs +++ b/target_chains/fuel/contracts/src/constants.rs @@ -1,6 +1,27 @@ -use crate::pyth_utils::{Price, PriceFeed}; +use crate::pyth_utils::{DataSource, Price, PriceFeed}; use fuels::types::Bits256; +pub const MAGIC: u32 = 0x5054474d; + +pub const GOVERNANCE_DATA_SOURCE: DataSource = DataSource { + chain_id: 1, + emitter_address: Bits256([ + 0x56, 0x35, 0x97, 0x9a, 0x22, 0x1c, 0x34, 0x93, 0x1e, 0x32, 0x62, 0x0b, 0x92, 0x93, 0xa4, 0x63, + 0x06, 0x55, 0x55, 0xea, 0x71, 0xfe, 0x97, 0xcd, 0x62, 0x37, 0xad, 0xe8, 0x75, 0xb1, 0x2e, 0x9e + ]), +}; + +// only used for updating guardian set +pub const WORMHOLE_GOVERNANCE_DATA_SOURCE: DataSource = DataSource { + chain_id: 1, + emitter_address: Bits256([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, + ]), +}; + +pub const DUMMY_CHAIN_ID: u16 = 1; + pub const BETA_5_URL: &str = "beta-5.fuel.network"; pub const BETA_5_PYTH_CONTRACT_ID: &str = "0xe69daeb9fcf4c536c0fe402403b4b9e9822cc8b1f296e5d754be12cc384554c5"; diff --git a/target_chains/fuel/contracts/src/pyth_utils.rs b/target_chains/fuel/contracts/src/pyth_utils.rs index 2add9a366..5e384498e 100644 --- a/target_chains/fuel/contracts/src/pyth_utils.rs +++ b/target_chains/fuel/contracts/src/pyth_utils.rs @@ -16,7 +16,9 @@ use fuels::{ use rand::Rng; use reqwest; use serde_json; +use serde_wormhole::RawMessage; use std::path::PathBuf; +use wormhole_sdk::Vaa; abigen!(Contract( name = "PythOracleContract", @@ -87,6 +89,80 @@ pub fn test_accumulator_update_data_bytes() -> Vec { )] } +pub fn create_set_fee_payload(new_fee: u64, exponent: u64) -> Vec { + let base = new_fee / 10u64.pow(exponent.try_into().unwrap()); + let base_bytes = base.to_be_bytes(); + let exponent_bytes = exponent.to_be_bytes(); + let mut payload = Vec::new(); + payload.extend_from_slice(&base_bytes); + payload.extend_from_slice(&exponent_bytes); + payload +} + +pub fn create_set_valid_period_payload(new_valid_period: u64) -> Vec { + let valid_period_bytes = new_valid_period.to_be_bytes(); + let mut payload = Vec::new(); + payload.extend_from_slice(&valid_period_bytes); + payload +} + +pub fn create_set_data_sources_payload(data_sources: Vec) -> Vec { + let mut payload = Vec::new(); + payload.push(data_sources.len() as u8); + for data_source in data_sources { + payload.extend_from_slice(&data_source.chain_id.to_be_bytes()); + payload.extend_from_slice(&data_source.emitter_address.0); + } + payload +} + +pub fn create_authorize_governance_data_source_transfer_payload( + claim_vaa: Vaa>, +) -> Vec { + serde_wormhole::to_vec(&claim_vaa).unwrap() +} + +pub fn create_request_governance_data_source_transfer_payload( + governance_data_source_index: u32, +) -> Vec { + let index_bytes = governance_data_source_index.to_be_bytes(); + let mut payload = Vec::new(); + payload.extend_from_slice(&index_bytes); + payload +} + +pub fn create_governance_instruction_payload( + magic: u32, + module: GovernanceModule, + action: GovernanceAction, + target_chain_id: u16, + payload: Vec, +) -> Vec { + let mut buffer = Vec::new(); + buffer.extend_from_slice(&magic.to_be_bytes()); + let module_number = match module { + GovernanceModule::Executor => 0, + GovernanceModule::Target => 1, + GovernanceModule::EvmExecutor => 2, + GovernanceModule::StacksTarget => 3, + GovernanceModule::Invalid => u8::MAX, // Typically 255 for invalid + }; + buffer.push(module_number); + let action_number = match action { + GovernanceAction::UpgradeContract => 0, + GovernanceAction::AuthorizeGovernanceDataSourceTransfer => 1, + GovernanceAction::SetDataSources => 2, + GovernanceAction::SetFee => 3, + GovernanceAction::SetValidPeriod => 4, + GovernanceAction::RequestGovernanceDataSourceTransfer => 5, + GovernanceAction::Invalid => u8::MAX, // Typically 255 for invalid + }; + buffer.push(action_number); + buffer.extend_from_slice(&target_chain_id.to_be_bytes()); + buffer.extend_from_slice(&payload); + buffer +} + impl Pyth { pub async fn price(&self, price_feed_id: Bits256) -> Result, Error> { self.instance @@ -119,16 +195,24 @@ impl Pyth { pub async fn constructor( &self, + governance_data_source: DataSource, + wormhole_governance_data_source: DataSource, valid_time_period_seconds: u64, - wormhole_guardian_set_upgrade: Bytes, + wormhole_guardian_set_addresses: Vec, + wormhole_guardian_set_index: u32, + chain_id: u16, ) -> Result, Error> { self.instance .methods() .constructor( default_data_sources(), + governance_data_source, + wormhole_governance_data_source, DEFAULT_SINGLE_UPDATE_FEE, valid_time_period_seconds, - wormhole_guardian_set_upgrade, + wormhole_guardian_set_addresses, + wormhole_guardian_set_index, + chain_id, ) .with_tx_policies(TxPolicies::default().with_gas_price(1)) .call() @@ -173,6 +257,68 @@ pub fn guardian_set_upgrade_4_vaa() -> Bytes { Bytes(hex::decode(GUARDIAN_SET_UPGRADE_4_VAA).unwrap()) } +// Full list of guardian set upgrade 3 addresses can be found here: https://github.com/wormhole-foundation/wormhole-networks/blob/master/mainnetv2/guardianset/v3.prototxt +pub fn guardian_set_upgrade_3_addresses() -> Vec { + let addresses = vec![ + "58CC3AE5C097b213cE3c81979e1B9f9570746AA5", // Certus One + "fF6CB952589BDE862c25Ef4392132fb9D4A42157", // Staked + "114De8460193bdf3A2fCf81f86a09765F4762fD1", // Figment + "107A0086b32d7A0977926A205131d8731D39cbEB", // ChainodeTech + "8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2", // Inotel + "11b39756C042441BE6D8650b69b54EbE715E2343", // HashQuark + "54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd", // Chainlayer + "15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20", // xLabs + "74a3bf913953D695260D88BC1aA25A4eeE363ef0", // Forbole + "000aC0076727b35FBea2dAc28fEE5cCB0fEA768e", // Staking Fund + "AF45Ced136b9D9e24903464AE889F5C8a723FC14", // Moonlet Wallet + "f93124b7c738843CBB89E864c862c38cddCccF95", // P2P + "D2CC37A4dc036a8D232b48f62cDD4731412f4890", // 01Node + "DA798F6896A3331F64b48c12D1D57Fd9cbe70811", // MCF + "71AA1BE1D36CaFE3867910F99C09e347899C19C3", // Everstake + "8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf", // Chorus One + "178e21ad2E77AE06711549CFBB1f9c7a9d8096e8", // Syncnode + "5E1487F35515d02A92753504a8D75471b9f49EdB", // Triton + "6FbEBc898F403E4773E95feB15E80C9A99c8348d", // Staking Facilities + ]; + + // Convert the addresses to Bits256 by padding the leftmost 12 bytes with zeros. This is done because the original 20-byte key is shorter than the 32-byte b256 type. + addresses + .iter() + .map(|&addr| Bits256::from_hex_str(&format!("{:0>64}", addr)).unwrap()) + .collect() +} + +// Full list of guardian set upgrade 4 addresses can be found here: https://github.com/wormhole-foundation/wormhole-networks/blob/master/mainnetv2/guardianset/v4.prototxt +pub fn guardian_set_upgrade_4_addresses() -> Vec { + let addresses = vec![ + "5893B5A76c3f739645648885bDCcC06cd70a3Cd3", // RockawayX + "fF6CB952589BDE862c25Ef4392132fb9D4A42157", // Staked + "114De8460193bdf3A2fCf81f86a09765F4762fD1", // Figment + "107A0086b32d7A0977926A205131d8731D39cbEB", // ChainodeTech + "8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2", // Inotel + "11b39756C042441BE6D8650b69b54EbE715E2343", // HashQuark + "54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd", // Chainlayer + "15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20", // xLabs + "74a3bf913953D695260D88BC1aA25A4eeE363ef0", // Forbole + "000aC0076727b35FBea2dAc28fEE5cCB0fEA768e", // Staking Fund + "AF45Ced136b9D9e24903464AE889F5C8a723FC14", // Moonlet Wallet + "f93124b7c738843CBB89E864c862c38cddCccF95", // P2P + "D2CC37A4dc036a8D232b48f62cDD4731412f4890", // 01Node + "DA798F6896A3331F64b48c12D1D57Fd9cbe70811", // MCF + "71AA1BE1D36CaFE3867910F99C09e347899C19C3", // Everstake + "8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf", // Chorus One + "178e21ad2E77AE06711549CFBB1f9c7a9d8096e8", // Syncnode + "5E1487F35515d02A92753504a8D75471b9f49EdB", // Triton + "6FbEBc898F403E4773E95feB15E80C9A99c8348d", // Staking Facilities + ]; + + // Convert the addresses to Bits256 by padding the leftmost 12 bytes with zeros. This is done because the original 20-byte key is shorter than the 32-byte b256 type. + addresses + .iter() + .map(|&addr| Bits256::from_hex_str(&format!("{:0>64}", addr)).unwrap()) + .collect() +} + pub fn default_price_feed_ids() -> Vec { vec![ Bits256( diff --git a/target_chains/fuel/contracts/tests/functions/mod.rs b/target_chains/fuel/contracts/tests/functions/mod.rs index 017aee482..277143fc1 100644 --- a/target_chains/fuel/contracts/tests/functions/mod.rs +++ b/target_chains/fuel/contracts/tests/functions/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod pyth_core; pub(crate) mod pyth_info; pub(crate) mod pyth_init; +pub(crate) mod pyth_governance; pub(crate) mod wormhole_guardians; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs index b41903ce9..70f5d9ff4 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs @@ -5,12 +5,13 @@ use crate::utils::interface::{ use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, - TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, + DEFAULT_SINGLE_UPDATE_FEE, DUMMY_CHAIN_ID, GOVERNANCE_DATA_SOURCE, + TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, + TEST_BATCH_ETH_USD_PRICE_FEED, TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, + WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -26,9 +27,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -64,9 +69,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs index dee0837f7..8e0887f64 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs @@ -4,12 +4,13 @@ use crate::utils::interface::{ }; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, + TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -26,9 +27,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -72,9 +77,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs index 08f440909..97d42c123 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs @@ -5,12 +5,13 @@ use crate::utils::interface::{ use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, - TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, GOVERNANCE_DATA_SOURCE, + TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, + TEST_BATCH_ETH_USD_PRICE_FEED, TEST_BATCH_USDC_USD_PRICE_FEED, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DUMMY_CHAIN_ID }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -26,9 +27,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -64,9 +69,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs index 7ffd7f478..9b1c97755 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs @@ -5,12 +5,13 @@ use crate::utils::interface::{ use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, + TEST_BATCH_USDC_USD_PRICE_FEED, WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -26,9 +27,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -59,9 +64,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/price.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/price.rs index aaa68f57c..b7d0e7ab7 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/price.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/price.rs @@ -2,16 +2,16 @@ use crate::utils::interface::{ pyth_core::{price, update_fee, update_price_feeds}, pyth_init::constructor, }; - use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, - TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, + DEFAULT_SINGLE_UPDATE_FEE, DUMMY_CHAIN_ID, GOVERNANCE_DATA_SOURCE, + TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, + TEST_BATCH_ETH_USD_PRICE_FEED, TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, + WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -26,9 +26,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -64,9 +68,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs index f07d20e54..10911c93c 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs @@ -5,12 +5,13 @@ use crate::utils::interface::{ use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, + TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -27,9 +28,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -73,9 +78,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs index dc0bb0f0d..c8b5d421b 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs @@ -5,12 +5,13 @@ use crate::utils::interface::{ use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, + TEST_BATCH_USDC_USD_PRICE_FEED, WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -25,9 +26,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -63,9 +68,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs index d3c424d85..cb5377faf 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs @@ -1,9 +1,12 @@ use crate::utils::interface::{pyth_core::update_fee, pyth_init::constructor}; use crate::utils::setup::setup_environment; use pyth_sdk::{ - constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD}, + constants::{ + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE, + }, pyth_utils::{ - default_data_sources, guardian_set_upgrade_3_vaa, test_accumulator_update_data_bytes, + default_data_sources, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -18,9 +21,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -38,9 +45,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs index 1cc92ef8f..06e8c2b09 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs @@ -3,12 +3,14 @@ use crate::utils::interface::{ pyth_info::price_feed_exists, pyth_init::constructor, }; - use crate::utils::setup::setup_environment; use pyth_sdk::{ - constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD}, + constants::{ + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE, + }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -23,9 +25,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -69,9 +75,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs b/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs index 645b9fb26..261d86f7c 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs @@ -5,9 +5,12 @@ use crate::utils::interface::{ }; use crate::utils::setup::setup_environment; use pyth_sdk::{ - constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD}, + constants::{ + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE, + }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -22,9 +25,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -75,9 +82,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_governance/execute_governance_instruction.rs b/target_chains/fuel/contracts/tests/functions/pyth_governance/execute_governance_instruction.rs new file mode 100644 index 000000000..acdfdd3ac --- /dev/null +++ b/target_chains/fuel/contracts/tests/functions/pyth_governance/execute_governance_instruction.rs @@ -0,0 +1,229 @@ +use { + crate::utils::{interface::pyth_init::constructor, setup::setup_environment}, + fuels::types::{Bits256, Bytes}, + pyth_sdk::{ + constants::{ + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE, + }, + pyth_utils::default_data_sources, + }, + pythnet_sdk::test_utils::{create_vaa_from_payload, dummy_guardians_addresses}, +}; + +mod success { + + use { + super::*, + crate::utils::interface::{ + pyth_core::valid_time_period, + pyth_governance::{execute_governance_instruction, governance_data_source}, + pyth_info::{single_update_fee, valid_data_sources}, + }, + pyth_sdk::{ + constants::MAGIC, + pyth_utils::{ + create_authorize_governance_data_source_transfer_payload, + create_governance_instruction_payload, + create_request_governance_data_source_transfer_payload, + create_set_data_sources_payload, create_set_fee_payload, + create_set_valid_period_payload, DataSource, GovernanceAction, GovernanceModule, + Pyth, + }, + }, + }; + + async fn setup_governance_test_environment() -> (Pyth, Vec) { + let (_oracle_contract_id, deployer) = setup_environment().await.unwrap(); + let dummy_guardians_addresses: Vec<[u8; 20]> = dummy_guardians_addresses(); + let bits256_guardians: Vec = dummy_guardians_addresses + .iter() + .map(|address| { + let mut full_address = [0u8; 32]; // Create a 32-byte array filled with zeros + full_address[12..].copy_from_slice(address); // Copy the 20-byte address into the rightmost part + Bits256(full_address) // Create Bits256 from the 32-byte array + }) + .collect(); + + constructor( + &deployer.instance, + default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, + DEFAULT_SINGLE_UPDATE_FEE, + DEFAULT_VALID_TIME_PERIOD, + bits256_guardians.clone(), + 0, + DUMMY_CHAIN_ID, + ) + .await; + + (deployer, bits256_guardians) + } + + #[tokio::test] + async fn test_set_fee() { + let (deployer, _bits256_guardians) = setup_governance_test_environment().await; + // Test SetFee logic here + let set_fee_payload = create_set_fee_payload(100, 1); + let governance_instruction_payload = create_governance_instruction_payload( + MAGIC, + GovernanceModule::Target, + GovernanceAction::SetFee, + 1, + set_fee_payload, + ); + + let vaa = create_vaa_from_payload( + &governance_instruction_payload, + wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0), + wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id), + 1, + ); + + execute_governance_instruction( + &deployer.instance, + Bytes(serde_wormhole::to_vec(&vaa).unwrap()), + ) + .await; + + let fee = single_update_fee(&deployer.instance).await.value; + + assert_eq!(fee, 100); + } + + #[tokio::test] + async fn test_set_valid_period() { + let (deployer, _bits256_guardians) = setup_governance_test_environment().await; + // Test SetValidPeriod logic here + let set_valid_period_payload = create_set_valid_period_payload(100); + let governance_instruction_payload = create_governance_instruction_payload( + MAGIC, + GovernanceModule::Target, + GovernanceAction::SetValidPeriod, + 1, + set_valid_period_payload, + ); + + let vaa = create_vaa_from_payload( + &governance_instruction_payload, + wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0), + wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id), + 2, + ); + + execute_governance_instruction( + &deployer.instance, + Bytes(serde_wormhole::to_vec(&vaa).unwrap()), + ) + .await; + + let valid_period = valid_time_period(&deployer.instance).await.value; + + assert_eq!(valid_period, 100); + } + + #[tokio::test] + async fn test_set_data_sources() { + let (deployer, _bits256_guardians) = setup_governance_test_environment().await; + // Test SetDataSources + let test_data_sources = vec![ + DataSource { + chain_id: 2, + emitter_address: Bits256([1u8; 32]), + }, + DataSource { + chain_id: 27, + emitter_address: Bits256([2u8; 32]), + }, + ]; + let set_data_sources_payload = create_set_data_sources_payload(test_data_sources.clone()); + let governance_instruction_payload = create_governance_instruction_payload( + MAGIC, + GovernanceModule::Target, + GovernanceAction::SetDataSources, + 1, + set_data_sources_payload, + ); + let vaa = create_vaa_from_payload( + &governance_instruction_payload, + wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0), + wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id), + 3, + ); + + execute_governance_instruction( + &deployer.instance, + Bytes(serde_wormhole::to_vec(&vaa).unwrap()), + ) + .await; + + let new_data_sources = valid_data_sources(&deployer.instance).await.value; + + assert_eq!(new_data_sources, test_data_sources); + } + + #[tokio::test] + async fn test_authorize_governance_data_source_transfer() { + let (deployer, _bits256_guardians) = setup_governance_test_environment().await; + // Test AuthorizeGovernanceDataSourceTransfer + let new_emitter_address = Bits256([3u8; 32]); + let new_emitter_chain = 2; + + // Simulate creating a RequestGovernanceDataSourceTransfer VAA + let request_governance_data_source_transfer_payload = + create_request_governance_data_source_transfer_payload(1); + let mut governance_instruction_payload = create_governance_instruction_payload( + MAGIC, + GovernanceModule::Target, + GovernanceAction::RequestGovernanceDataSourceTransfer, + 1, + request_governance_data_source_transfer_payload, + ); + let mut vaa = create_vaa_from_payload( + &governance_instruction_payload, + wormhole_sdk::Address(new_emitter_address.0), + wormhole_sdk::Chain::from(new_emitter_chain), + 4, + ); + + // Authorize the transfer + let authorize_governance_data_source_transfer_payload = + create_authorize_governance_data_source_transfer_payload(vaa); + governance_instruction_payload = create_governance_instruction_payload( + MAGIC, + GovernanceModule::Target, + GovernanceAction::AuthorizeGovernanceDataSourceTransfer, + 1, + authorize_governance_data_source_transfer_payload, + ); + vaa = create_vaa_from_payload( + &governance_instruction_payload, + wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0), + wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id), + 5, + ); + + let old_governance_data_source = governance_data_source(&deployer.instance).await; + execute_governance_instruction( + &deployer.instance, + Bytes(serde_wormhole::to_vec(&vaa).unwrap()), + ) + .await; + + let new_governance_data_source = governance_data_source(&deployer.instance).await; + assert_ne!( + old_governance_data_source.value.emitter_address, + new_governance_data_source.value.emitter_address + ); + assert_ne!( + old_governance_data_source.value.chain_id, + new_governance_data_source.value.chain_id + ); + assert_eq!( + new_governance_data_source.value.emitter_address, + new_emitter_address + ); + assert_eq!(new_governance_data_source.value.chain_id, new_emitter_chain); + } +} diff --git a/target_chains/fuel/contracts/tests/functions/pyth_governance/mod.rs b/target_chains/fuel/contracts/tests/functions/pyth_governance/mod.rs new file mode 100644 index 000000000..f71ef1ed6 --- /dev/null +++ b/target_chains/fuel/contracts/tests/functions/pyth_governance/mod.rs @@ -0,0 +1 @@ +pub(crate) mod execute_governance_instruction; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs b/target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs index 0e3625495..c06b6e808 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs @@ -6,12 +6,13 @@ use crate::utils::interface::{ use crate::utils::setup::setup_environment; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, + GOVERNANCE_DATA_SOURCE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED, - TEST_BATCH_USDC_USD_PRICE_FEED, + TEST_BATCH_USDC_USD_PRICE_FEED, WORMHOLE_GOVERNANCE_DATA_SOURCE, }, pyth_utils::{ - default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa, + default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes, test_batch_update_data_bytes, }, }; @@ -26,9 +27,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; @@ -57,9 +62,13 @@ mod success { constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID, ) .await; diff --git a/target_chains/fuel/contracts/tests/functions/pyth_init/constuctor.rs b/target_chains/fuel/contracts/tests/functions/pyth_init/constructor.rs similarity index 77% rename from target_chains/fuel/contracts/tests/functions/pyth_init/constuctor.rs rename to target_chains/fuel/contracts/tests/functions/pyth_init/constructor.rs index 69e719972..bd2c77a67 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_init/constuctor.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_init/constructor.rs @@ -1,6 +1,6 @@ use crate::utils::interface::{ pyth_core::valid_time_period, - pyth_info::{owner, single_update_fee, valid_data_source, valid_data_sources}, + pyth_info::{is_valid_data_source, owner, single_update_fee, valid_data_sources}, pyth_init::constructor, wormhole_guardians::{ current_guardian_set_index, current_wormhole_provider, governance_action_is_consumed, @@ -8,10 +8,10 @@ use crate::utils::interface::{ }; use pyth_sdk::{ constants::{ - DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH, + DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH, GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE, DUMMY_CHAIN_ID }, pyth_utils::{ - default_data_sources, guardian_set_upgrade_3_vaa, ConstructedEvent, State, WormholeProvider, + default_data_sources, guardian_set_upgrade_3_addresses, ConstructedEvent, DataSource, State, }, }; @@ -29,7 +29,7 @@ mod success { // Initial values assert!( - !valid_data_source(&deployer.instance, &default_data_sources()[0]) + !is_valid_data_source(&deployer.instance, &default_data_sources()[0]) .await .value ); @@ -50,9 +50,9 @@ mod success { ); assert_eq!( current_wormhole_provider(&deployer.instance,).await.value, - WormholeProvider { - governance_chain_id: 0, - governance_contract: Bits256::zeroed(), + DataSource { + chain_id: 0, + emitter_address: Bits256::zeroed(), } ); assert_eq!(owner(&deployer.instance,).await.value, State::Uninitialized); @@ -60,9 +60,13 @@ mod success { let response = constructor( &deployer.instance, default_data_sources(), + GOVERNANCE_DATA_SOURCE, + WORMHOLE_GOVERNANCE_DATA_SOURCE, DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, - guardian_set_upgrade_3_vaa(), + guardian_set_upgrade_3_addresses(), + 3, + DUMMY_CHAIN_ID ) .await; @@ -79,7 +83,7 @@ mod success { // Final values assert!( - valid_data_source(&deployer.instance, &default_data_sources()[0]) + is_valid_data_source(&deployer.instance, &default_data_sources()[0]) .await .value ); @@ -95,20 +99,15 @@ mod success { single_update_fee(&deployer.instance).await.value, DEFAULT_SINGLE_UPDATE_FEE ); - assert!( - governance_action_is_consumed(&deployer.instance, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH) - .await - .value - ); assert_eq!( current_guardian_set_index(&deployer.instance,).await.value, 3 ); assert_eq!( current_wormhole_provider(&deployer.instance,).await.value, - WormholeProvider { - governance_chain_id: 1, - governance_contract: Bits256([ + DataSource { + chain_id: 1, + emitter_address: Bits256([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4 ]) diff --git a/target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs b/target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs index f0bb23d7d..cf6228652 100644 --- a/target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs +++ b/target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs @@ -1 +1 @@ -pub(crate) mod constuctor; +pub(crate) mod constructor; diff --git a/target_chains/fuel/contracts/tests/utils/interface/mod.rs b/target_chains/fuel/contracts/tests/utils/interface/mod.rs index 017aee482..277143fc1 100644 --- a/target_chains/fuel/contracts/tests/utils/interface/mod.rs +++ b/target_chains/fuel/contracts/tests/utils/interface/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod pyth_core; pub(crate) mod pyth_info; pub(crate) mod pyth_init; +pub(crate) mod pyth_governance; pub(crate) mod wormhole_guardians; diff --git a/target_chains/fuel/contracts/tests/utils/interface/pyth_governance.rs b/target_chains/fuel/contracts/tests/utils/interface/pyth_governance.rs new file mode 100644 index 000000000..54145c80d --- /dev/null +++ b/target_chains/fuel/contracts/tests/utils/interface/pyth_governance.rs @@ -0,0 +1,27 @@ +use fuels::{ + accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, types::Bytes, +}; +use pyth_sdk::pyth_utils::{DataSource, PythOracleContract}; + +pub(crate) async fn execute_governance_instruction( + contract: &PythOracleContract, + encoded_vm: Bytes, +) -> FuelCallResponse<()> { + contract + .methods() + .execute_governance_instruction(encoded_vm) + .call() + .await + .unwrap() +} + +pub(crate) async fn governance_data_source( + contract: &PythOracleContract, +) -> FuelCallResponse { + contract + .methods() + .governance_data_source() + .call() + .await + .unwrap() +} diff --git a/target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs b/target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs index 11aa183a2..e7b737018 100644 --- a/target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs +++ b/target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs @@ -40,13 +40,13 @@ pub(crate) async fn single_update_fee( contract.methods().single_update_fee().call().await.unwrap() } -pub(crate) async fn valid_data_source( +pub(crate) async fn is_valid_data_source( contract: &PythOracleContract, data_source: &DataSource, ) -> FuelCallResponse { contract .methods() - .valid_data_source(data_source.clone()) + .is_valid_data_source(data_source.clone()) .call() .await .unwrap() diff --git a/target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs b/target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs index 0026ffe5c..3188ad2b1 100644 --- a/target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs +++ b/target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs @@ -1,5 +1,6 @@ use fuels::{ - accounts::wallet::WalletUnlocked, prelude::Bytes, programs::call_response::FuelCallResponse, + accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, + types::Bits256, }; use pyth_sdk::pyth_utils::{DataSource, PythOracleContract}; @@ -7,17 +8,25 @@ use pyth_sdk::pyth_utils::{DataSource, PythOracleContract}; pub(crate) async fn constructor( contract: &PythOracleContract, data_sources: Vec, + governance_data_source: DataSource, + wormhole_governance_data_source: DataSource, single_update_fee: u64, valid_time_period_seconds: u64, - wormhole_guardian_set_upgrade: Bytes, + wormhole_guardian_set_addresses: Vec, + wormhole_guardian_set_index: u32, + chain_id: u16, ) -> FuelCallResponse<()> { contract .methods() .constructor( data_sources, + governance_data_source, + wormhole_governance_data_source, single_update_fee, valid_time_period_seconds, - wormhole_guardian_set_upgrade, + wormhole_guardian_set_addresses, + wormhole_guardian_set_index, + chain_id, ) .call() .await diff --git a/target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs b/target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs index 477ed3512..e5a0f15bb 100644 --- a/target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs +++ b/target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs @@ -1,7 +1,7 @@ use fuels::{ accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, types::Bits256, }; -use pyth_sdk::pyth_utils::{GuardianSet, PythOracleContract, WormholeProvider}; +use pyth_sdk::pyth_utils::{GuardianSet, PythOracleContract, DataSource}; pub(crate) async fn current_guardian_set_index( contract: &PythOracleContract, @@ -16,7 +16,7 @@ pub(crate) async fn current_guardian_set_index( pub(crate) async fn current_wormhole_provider( contract: &PythOracleContract, -) -> FuelCallResponse { +) -> FuelCallResponse { contract .methods() .current_wormhole_provider()