diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index 19a6a71d7..5bafd226e 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -81,7 +81,8 @@ jobs: mkdir -p "${BPF_OUT_DIR}" cargo build-sbf --features "mainnet" - cargo test-sbf --features "mainnet" + cargo test-sbf -p "example-native-token-transfers" --features "mainnet" + cargo test-sbf -p "ntt-transceiver" --features "mainnet,testing" cargo test check-version: diff --git a/solana/Anchor.toml b/solana/Anchor.toml index 312c9954f..27cb226d4 100644 --- a/solana/Anchor.toml +++ b/solana/Anchor.toml @@ -30,15 +30,15 @@ upgradeable = true [[test.genesis]] address = "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth" -program = "programs/example-native-token-transfers/tests/fixtures/mainnet_core_bridge.so" +program = "tests/fixtures/mainnet_core_bridge.so" [[test.genesis]] address = "EtZMZM22ViKMo4r5y4Anovs3wKQ2owUmDpjygnMMcdEX" -program = "programs/example-native-token-transfers/tests/fixtures/mainnet_wormhole_post_message_shim.so" +program = "tests/fixtures/mainnet_wormhole_post_message_shim.so" [[test.genesis]] address = "EFaNWErqAtVWufdNb7yofSHHfWFos843DFpu4JBw24at" -program = "programs/example-native-token-transfers/tests/fixtures/mainnet_wormhole_verify_vaa_shim.so" +program = "tests/fixtures/mainnet_wormhole_verify_vaa_shim.so" [test.validator] bind_address = "0.0.0.0" diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 6030b4278..7216c7243 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -545,7 +545,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -704,11 +704,11 @@ dependencies = [ [[package]] name = "borsh" -version = "1.4.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0901fc8eb0aca4c83be0106d6f2db17d86a08dfc2c25f0e84464bf381158add6" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ - "borsh-derive 1.4.0", + "borsh-derive 1.5.7", "cfg_aliases", ] @@ -740,16 +740,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.4.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51670c3aa053938b0ee3bd67c3817e471e626151131b934038e83c5bf8de48f5" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.48", - "syn_derive", + "syn 2.0.109", ] [[package]] @@ -876,7 +875,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -940,9 +939,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" @@ -1232,7 +1231,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -1243,7 +1242,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -1370,7 +1369,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -1393,7 +1392,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -1510,7 +1509,7 @@ checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -1523,7 +1522,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -1567,29 +1566,20 @@ version = "3.0.0" dependencies = [ "anchor-lang", "anchor-spl", - "base64 0.21.7", - "bincode", "bitmaps 3.2.1", "cfg-if", - "hex", - "libsecp256k1", "ntt-messages", - "serde", - "serde_json", - "serde_wormhole", - "sha3 0.10.8", "solana-address-lookup-table-program", "solana-program", - "solana-program-runtime", "solana-program-test", "solana-sdk", - "spl-associated-token-account 3.0.2", + "spl-associated-token-account 3.0.4", "spl-token", - "spl-token-2022 3.0.2", + "spl-token-2022 3.0.5", + "test-utils", "wormhole-anchor-sdk", "wormhole-governance", "wormhole-io", - "wormhole-raw-vaas", "wormhole-sdk", "wormhole-solana-utils", ] @@ -1714,7 +1704,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -2520,9 +2510,13 @@ dependencies = [ "anchor-spl", "cfg-if", "example-native-token-transfers", - "hex", "ntt-messages", "solana-program", + "solana-program-test", + "solana-sdk", + "spl-associated-token-account 3.0.4", + "spl-token", + "test-utils", "wormhole-anchor-sdk", "wormhole-io", "wormhole-post-message-shim-interface", @@ -2602,7 +2596,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -2684,7 +2678,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -2696,7 +2690,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -2877,7 +2871,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -3029,9 +3023,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -3053,7 +3047,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -3519,7 +3513,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -3596,7 +3590,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -3652,7 +3646,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -3914,7 +3908,7 @@ version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e58fa66e1e240097665e7f87b267aa8e976ea3fcbd86918c8fd218c875395ada" dependencies = [ - "borsh 1.4.0", + "borsh 1.5.7", "futures", "solana-banks-interface", "solana-program", @@ -4147,7 +4141,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -4272,7 +4266,7 @@ dependencies = [ "blake3", "borsh 0.10.3", "borsh 0.9.3", - "borsh 1.4.0", + "borsh 1.5.7", "bs58 0.4.0", "bv", "bytemuck", @@ -4598,7 +4592,7 @@ dependencies = [ "base64 0.21.7", "bincode", "bitflags 2.4.2", - "borsh 1.4.0", + "borsh 1.5.7", "bs58 0.4.0", "bytemuck", "byteorder", @@ -4653,7 +4647,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -4978,17 +4972,17 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2e688554bac5838217ffd1fab7845c573ff106b6336bf7d290db7c98d5a8efd" +checksum = "143109d789171379e6143ef23191786dfaac54289ad6e7917cfb26b36c432b10" dependencies = [ "assert_matches", - "borsh 1.4.0", + "borsh 1.5.7", "num-derive 0.4.1", "num-traits", "solana-program", "spl-token", - "spl-token-2022 3.0.2", + "spl-token-2022 3.0.5", "thiserror", ] @@ -5022,7 +5016,7 @@ checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" dependencies = [ "quote", "spl-discriminator-syn 0.1.1", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5033,7 +5027,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn 0.2.0", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5045,7 +5039,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.109", "thiserror", ] @@ -5058,7 +5052,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.109", "thiserror", ] @@ -5090,7 +5084,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046ce669f48cf2eca1ec518916d8725596bfb655beb1c74374cf71dc6cb773c9" dependencies = [ - "borsh 1.4.0", + "borsh 1.5.7", "bytemuck", "solana-program", "solana-zk-token-sdk", @@ -5132,7 +5126,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5144,7 +5138,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5252,9 +5246,9 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "3.0.2" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5412f99ae7ee6e0afde00defaa354e6228e47e30c0e3adf553e2e01e6abb584" +checksum = "4c39e416aeb1ea0b22f3b2bbecaf7e38a92a1aa8f4a0c5785c94179694e846a0" dependencies = [ "arrayref", "bytemuck", @@ -5320,7 +5314,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30179c47e93625680dabb620c6e7931bd12d62af390f447bc7beb4a3a9b5feee" dependencies = [ - "borsh 1.4.0", + "borsh 1.5.7", "solana-program", "spl-discriminator 0.2.2", "spl-pod 0.2.2", @@ -5467,27 +5461,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "sync_wrapper" version = "0.1.2" @@ -5618,7 +5600,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5629,10 +5611,45 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", "test-case-core", ] +[[package]] +name = "test-utils" +version = "3.0.0" +dependencies = [ + "anchor-lang", + "anchor-spl", + "base64 0.21.7", + "bincode", + "cfg-if", + "example-native-token-transfers", + "hex", + "libsecp256k1", + "ntt-messages", + "ntt-transceiver", + "serde", + "serde_json", + "serde_wormhole", + "sha3 0.10.8", + "solana-banks-interface", + "solana-program", + "solana-program-runtime", + "solana-program-test", + "solana-sdk", + "spl-associated-token-account 3.0.4", + "spl-token", + "spl-token-2022 3.0.5", + "wormhole-anchor-sdk", + "wormhole-governance", + "wormhole-io", + "wormhole-sdk", + "wormhole-solana-utils", + "wormhole-svm-definitions 0.1.0 (git+https://github.com/wormholelabs-xyz/wormhole?branch=svm-shims-fr-env-addr)", + "wormhole-svm-shim", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -5665,7 +5682,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5770,7 +5787,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -5917,7 +5934,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -6160,7 +6177,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", "wasm-bindgen-shared", ] @@ -6194,7 +6211,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6458,12 +6475,6 @@ dependencies = [ "anchor-lang", ] -[[package]] -name = "wormhole-raw-vaas" -version = "0.2.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b8fed51f54543a57b009ce057df3380269c70971d4d096cc9f5dcc2cb1fdb7" - [[package]] name = "wormhole-sdk" version = "0.1.0" @@ -6509,6 +6520,15 @@ dependencies = [ "solana-program", ] +[[package]] +name = "wormhole-svm-shim" +version = "0.1.0" +source = "git+https://github.com/wormhole-foundation/wormhole?branch=svm/anchor-v0.29.0-shim-interface#63481c6486f8747529f7df692bab8b7fdf85146b" +dependencies = [ + "solana-program", + "wormhole-svm-definitions 0.1.0 (git+https://github.com/wormhole-foundation/wormhole?branch=svm/anchor-v0.29.0-shim-interface)", +] + [[package]] name = "wormhole-verify-vaa-shim-interface" version = "0.0.0" @@ -6573,7 +6593,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] @@ -6593,7 +6613,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.109", ] [[package]] diff --git a/solana/Cargo.toml b/solana/Cargo.toml index ae4e05d0d..d3397518e 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -50,6 +50,7 @@ anchor-spl = "0.29.0" solana-program = "=1.18.26" solana-program-runtime = "=1.18.26" solana-program-test = "=1.18.26" +solana-banks-interface = "=1.18.26" solana-address-lookup-table-program = "=1.18.26" spl-token = "4.0.0" spl-token-2022 = "3.0.2" diff --git a/solana/Dockerfile b/solana/Dockerfile index a0796df10..7daf648fb 100644 --- a/solana/Dockerfile +++ b/solana/Dockerfile @@ -7,6 +7,7 @@ COPY solana/Cargo.lock Cargo.lock COPY solana/Cargo.toml Cargo.toml COPY solana/modules modules COPY solana/programs programs +COPY solana/tests tests COPY solana/rust-toolchain rust-toolchain ENV RUST_BACKTRACE=1 diff --git a/solana/Makefile b/solana/Makefile index 6d0e9c53b..dc83dabb3 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -44,7 +44,9 @@ idl: anchor-build mkdir -p ts/idl/$(VERSION)/json mkdir -p ts/idl/$(VERSION)/ts cp -r target/idl/* ts/idl/$(VERSION)/json/ - for jsonfile in ts/idl/$(VERSION)/json/*.json; do \ + for src in target/idl/*.json; do \ + filename=$$(basename $$src); \ + jsonfile=ts/idl/$(VERSION)/json/$$filename; \ tsfile=$$(echo $$jsonfile | sed 's/json\/\(.*\)\.json/ts\/\1.ts/'); \ tsx scripts/regenerateIdl.ts $$jsonfile > $$tsfile; \ done @@ -67,9 +69,12 @@ node_modules: test: cargo-test anchor-test +# ntt-transceiver is built separately with `testing` so post message shim ix data is set as return_data +# See: ntt-transceiver/src/wormhole/accounts.rs::post_message (#[cfg(feature = "testing")]) cargo-test: cargo build-sbf --features "mainnet" - cargo test-sbf --features "mainnet" + cargo test-sbf -p "example-native-token-transfers" --features "mainnet" + cargo test-sbf -p "ntt-transceiver" --features "mainnet,testing" cargo test anchor-test: idl sdk node_modules diff --git a/solana/jest.config.ts b/solana/jest.config.ts index dff632b02..4dc22e90f 100644 --- a/solana/jest.config.ts +++ b/solana/jest.config.ts @@ -4,7 +4,7 @@ const jestConfig: JestConfigWithTsJest = { verbose: true, testTimeout: 10000000, modulePathIgnorePatterns: ["mocks"], - roots: ["./tests"], + roots: ["./tests/anchor"], testMatch: ["**/*.test.ts"], preset: "ts-jest", moduleNameMapper: { diff --git a/solana/programs/example-native-token-transfers/Cargo.toml b/solana/programs/example-native-token-transfers/Cargo.toml index c42bf3b6d..513f1c8a5 100644 --- a/solana/programs/example-native-token-transfers/Cargo.toml +++ b/solana/programs/example-native-token-transfers/Cargo.toml @@ -31,12 +31,9 @@ tilt-devnet2 = [ "tilt-devnet" ] workspace = true [dependencies] - -ntt-messages = { path = "../../modules/ntt-messages", features = ["anchor", "hash"] } anchor-lang = { workspace = true, features = ["init-if-needed"] } anchor-spl.workspace = true bitmaps = "3.2.1" -hex.workspace = true cfg-if.workspace = true solana-program.workspace = true solana-address-lookup-table-program.workspace = true @@ -45,19 +42,14 @@ wormhole-anchor-sdk.workspace = true wormhole-io.workspace = true wormhole-solana-utils.workspace = true +ntt-messages = { path = "../../modules/ntt-messages", features = ["anchor", "hash"] } + [dev-dependencies] -wormhole-governance = { path = "../wormhole-governance", features = ["no-entrypoint"] } solana-program-test.workspace = true -serde_json = "1.0.113" -serde = "1.0.196" -base64 = "0.21.7" solana-sdk = "*" -spl-token = "4" spl-associated-token-account = { version = "3.0.0", features = ["no-entrypoint"] } -sha3 = "0.10.4" -wormhole-raw-vaas = "0.2.0-alpha.2" -libsecp256k1 = "=0.6.0" +spl-token.workspace = true wormhole-sdk.workspace = true -serde_wormhole.workspace = true -solana-program-runtime.workspace = true -bincode = "1.3.3" + +test-utils = { path = "../../tests/cargo" } +wormhole-governance = { path = "../wormhole-governance", features = ["no-entrypoint"] } \ No newline at end of file diff --git a/solana/programs/example-native-token-transfers/src/bitmap.rs b/solana/programs/example-native-token-transfers/src/bitmap.rs index b05d11f7c..7ac2aa0b9 100644 --- a/solana/programs/example-native-token-transfers/src/bitmap.rs +++ b/solana/programs/example-native-token-transfers/src/bitmap.rs @@ -1,7 +1,9 @@ -use crate::error::NTTError; use anchor_lang::prelude::*; use bitmaps::Bitmap as BM; use std::result::Result as StdResult; + +use crate::error::NTTError; + #[derive(PartialEq, Eq, Clone, Copy, Debug, AnchorDeserialize, AnchorSerialize, InitSpace)] pub struct Bitmap { map: u128, diff --git a/solana/programs/example-native-token-transfers/src/config.rs b/solana/programs/example-native-token-transfers/src/config.rs index ee07ed5f1..efd374a53 100644 --- a/solana/programs/example-native-token-transfers/src/config.rs +++ b/solana/programs/example-native-token-transfers/src/config.rs @@ -1,7 +1,6 @@ -use std::ops::{Deref, DerefMut}; - use anchor_lang::prelude::*; use ntt_messages::{chain_id::ChainId, mode::Mode}; +use std::ops::{Deref, DerefMut}; use crate::bitmap::Bitmap; diff --git a/solana/programs/example-native-token-transfers/src/instructions/mark_outbox_item_as_released.rs b/solana/programs/example-native-token-transfers/src/instructions/mark_outbox_item_as_released.rs index d460049f6..a74225b5b 100644 --- a/solana/programs/example-native-token-transfers/src/instructions/mark_outbox_item_as_released.rs +++ b/solana/programs/example-native-token-transfers/src/instructions/mark_outbox_item_as_released.rs @@ -1,8 +1,9 @@ +use anchor_lang::prelude::*; + use crate::{ config::*, error::NTTError, queue::outbox::OutboxItem, registered_transceiver::RegisteredTransceiver, }; -use anchor_lang::prelude::*; pub const OUTBOX_ITEM_SIGNER_SEED: &[u8] = b"outbox_item_signer"; diff --git a/solana/programs/example-native-token-transfers/src/queue/inbox.rs b/solana/programs/example-native-token-transfers/src/queue/inbox.rs index 396ed6221..e05cf3b26 100644 --- a/solana/programs/example-native-token-transfers/src/queue/inbox.rs +++ b/solana/programs/example-native-token-transfers/src/queue/inbox.rs @@ -1,6 +1,5 @@ -use std::ops::{Deref, DerefMut}; - use anchor_lang::prelude::*; +use std::ops::{Deref, DerefMut}; use crate::{bitmap::Bitmap, clock::current_timestamp, error::NTTError}; diff --git a/solana/programs/example-native-token-transfers/src/queue/outbox.rs b/solana/programs/example-native-token-transfers/src/queue/outbox.rs index cd1aa205f..efdde23a9 100644 --- a/solana/programs/example-native-token-transfers/src/queue/outbox.rs +++ b/solana/programs/example-native-token-transfers/src/queue/outbox.rs @@ -1,7 +1,6 @@ -use std::ops::{Deref, DerefMut}; - use anchor_lang::prelude::*; use ntt_messages::{chain_id::ChainId, trimmed_amount::TrimmedAmount}; +use std::ops::{Deref, DerefMut}; use crate::{bitmap::*, clock::current_timestamp, error::NTTError}; diff --git a/solana/programs/example-native-token-transfers/tests/admin.rs b/solana/programs/example-native-token-transfers/tests/admin.rs index 84357b97d..1884afaff 100644 --- a/solana/programs/example-native-token-transfers/tests/admin.rs +++ b/solana/programs/example-native-token-transfers/tests/admin.rs @@ -1,46 +1,24 @@ #![cfg(feature = "test-sbf")] #![feature(type_changing_struct_update)] -use anchor_lang::{prelude::Pubkey, system_program::System, Id}; -use example_native_token_transfers::{ - config::Config, error::NTTError, registered_transceiver::RegisteredTransceiver, -}; +use anchor_lang::{system_program::System, Id}; +use example_native_token_transfers::error::NTTError; use ntt_messages::mode::Mode; use solana_program_test::*; use solana_sdk::{instruction::InstructionError, signer::Signer, transaction::TransactionError}; - -use crate::{ - common::{query::GetAccountDataAnchor, setup::setup, submit::Submittable}, - sdk::accounts::{good_ntt, NTTAccounts}, - sdk::instructions::admin::{ - deregister_transceiver, register_transceiver, set_threshold, DeregisterTransceiver, - RegisterTransceiver, SetThreshold, +use test_utils::{ + common::submit::Submittable, + helpers::{assert_threshold, assert_transceiver_id, setup}, + sdk::{ + accounts::good_ntt, + instructions::admin::{ + deregister_transceiver, register_transceiver, set_threshold, DeregisterTransceiver, + RegisterTransceiver, SetThreshold, + }, + transceivers::accounts::{good_ntt_transceiver, NTTTransceiverAccounts}, }, }; -pub mod common; -pub mod sdk; - -async fn assert_threshold(ctx: &mut ProgramTestContext, expected_threshold: u8) { - let config_account: Config = ctx.get_account_data_anchor(good_ntt.config()).await; - assert_eq!(config_account.threshold, expected_threshold); -} - -async fn assert_transceiver_id( - ctx: &mut ProgramTestContext, - transceiver: &Pubkey, - expected_id: u8, -) { - let registered_transceiver_account: RegisteredTransceiver = ctx - .get_account_data_anchor(good_ntt.registered_transceiver(transceiver)) - .await; - assert_eq!( - registered_transceiver_account.transceiver_address, - *transceiver - ); - assert_eq!(registered_transceiver_account.id, expected_id); -} - #[tokio::test] async fn test_invalid_transceiver() { let (mut ctx, test_data) = setup(Mode::Locking).await; @@ -91,7 +69,7 @@ async fn test_reregister_all_transceivers() { .submit_with_signers(&[&test_data.program_owner], &mut ctx) .await .unwrap(); - assert_transceiver_id(&mut ctx, transceiver, idx as u8 + 1).await; + assert_transceiver_id(&good_ntt, &mut ctx, transceiver, idx as u8 + 1).await; } // set threshold = 1 (for baked-in transceiver) + num_dummy_transceivers @@ -119,7 +97,7 @@ async fn test_reregister_all_transceivers() { .await .unwrap(); // assert threshold decreases - assert_threshold(&mut ctx, num_dummy_transceivers - idx as u8).await; + assert_threshold(&good_ntt, &mut ctx, num_dummy_transceivers - idx as u8).await; } // reregister dummy transceivers @@ -136,8 +114,8 @@ async fn test_reregister_all_transceivers() { .await .unwrap(); // assert transceiver_id and threshold are retained - assert_transceiver_id(&mut ctx, transceiver, idx as u8 + 1).await; - assert_threshold(&mut ctx, 1).await; + assert_transceiver_id(&good_ntt, &mut ctx, transceiver, idx as u8 + 1).await; + assert_threshold(&good_ntt, &mut ctx, 1).await; } // reregister baked-in transceiver @@ -146,15 +124,15 @@ async fn test_reregister_all_transceivers() { RegisterTransceiver { payer: ctx.payer.pubkey(), owner: test_data.program_owner.pubkey(), - transceiver: example_native_token_transfers::ID, + transceiver: good_ntt_transceiver.program(), }, ) .submit_with_signers(&[&test_data.program_owner], &mut ctx) .await .unwrap(); // assert transceiver_id and threshold are retained - assert_transceiver_id(&mut ctx, &example_native_token_transfers::ID, 0).await; - assert_threshold(&mut ctx, 1).await; + assert_transceiver_id(&good_ntt, &mut ctx, &good_ntt_transceiver.program(), 0).await; + assert_threshold(&good_ntt, &mut ctx, 1).await; } #[tokio::test] @@ -166,7 +144,7 @@ async fn test_deregister_last_enabled_transceiver() { &good_ntt, DeregisterTransceiver { owner: test_data.program_owner.pubkey(), - transceiver: example_native_token_transfers::ID, + transceiver: good_ntt_transceiver.program(), }, ) .submit_with_signers(&[&test_data.program_owner], &mut ctx) @@ -199,7 +177,7 @@ async fn test_deregister_last_enabled_transceiver() { &good_ntt, DeregisterTransceiver { owner: test_data.program_owner.pubkey(), - transceiver: example_native_token_transfers::ID, + transceiver: good_ntt_transceiver.program(), }, ) .submit_with_signers(&[&test_data.program_owner], &mut ctx) diff --git a/solana/programs/example-native-token-transfers/tests/broadcast.rs b/solana/programs/example-native-token-transfers/tests/broadcast.rs index 321ba69b7..eeccf1918 100644 --- a/solana/programs/example-native-token-transfers/tests/broadcast.rs +++ b/solana/programs/example-native-token-transfers/tests/broadcast.rs @@ -1,27 +1,32 @@ #![cfg(feature = "test-sbf")] #![feature(type_changing_struct_update)] -use common::setup::OTHER_CHAIN; -use ntt_messages::chain_id::ChainId; -use ntt_messages::mode::Mode; -use ntt_messages::transceivers::wormhole::{ - WormholeTransceiverInfo, WormholeTransceiverRegistration, +use ntt_messages::{ + chain_id::ChainId, + mode::Mode, + transceivers::wormhole::{WormholeTransceiverInfo, WormholeTransceiverRegistration}, }; use solana_program_test::*; use solana_sdk::{signature::Keypair, signer::Signer}; -use wormhole_anchor_sdk::wormhole::PostedVaa; - -use crate::common::query::GetAccountDataAnchor; -use crate::common::setup::{setup, OTHER_TRANSCEIVER}; -use crate::sdk::accounts::{good_ntt, NTTAccounts}; -use crate::sdk::transceivers::wormhole::instructions::broadcast_id::{broadcast_id, BroadcastId}; -use crate::{ - common::submit::Submittable, - sdk::transceivers::wormhole::instructions::broadcast_peer::{broadcast_peer, BroadcastPeer}, +use test_utils::{ + common::{ + fixtures::{OTHER_CHAIN, OTHER_TRANSCEIVER}, + query::GetAccountDataAnchor, + submit::Submittable, + }, + helpers::setup, + sdk::{ + accounts::{good_ntt, NTTAccounts}, + transceivers::{ + accounts::good_ntt_transceiver, + instructions::{ + broadcast_id::{broadcast_id, BroadcastId}, + broadcast_peer::{broadcast_peer, BroadcastPeer}, + }, + }, + }, }; - -pub mod common; -pub mod sdk; +use wormhole_anchor_sdk::wormhole::PostedVaa; #[tokio::test] async fn test_broadcast_peer() { @@ -31,6 +36,7 @@ async fn test_broadcast_peer() { broadcast_peer( &good_ntt, + &good_ntt_transceiver, BroadcastPeer { payer: ctx.payer.pubkey(), wormhole_message: wh_message.pubkey(), @@ -62,6 +68,7 @@ async fn test_broadcast_id() { broadcast_id( &good_ntt, + &good_ntt_transceiver, BroadcastId { payer: ctx.payer.pubkey(), wormhole_message: wh_message.pubkey(), diff --git a/solana/programs/example-native-token-transfers/tests/cancel_flow.rs b/solana/programs/example-native-token-transfers/tests/cancel_flow.rs index b5dfcf0c7..8f1e4a4a4 100644 --- a/solana/programs/example-native-token-transfers/tests/cancel_flow.rs +++ b/solana/programs/example-native-token-transfers/tests/cancel_flow.rs @@ -1,121 +1,31 @@ #![cfg(feature = "test-sbf")] #![feature(type_changing_struct_update)] -use anchor_lang::prelude::*; -use common::setup::{TestData, OTHER_CHAIN}; -use example_native_token_transfers::{ - instructions::{RedeemArgs, TransferArgs}, - queue::{inbox::InboxRateLimit, outbox::OutboxRateLimit}, - transfer::Payload, -}; -use ntt_messages::{ - chain_id::ChainId, mode::Mode, ntt::NativeTokenTransfer, ntt_manager::NttManagerMessage, -}; -use sdk::{ - accounts::{good_ntt, NTTAccounts}, - transceivers::wormhole::instructions::receive_message::ReceiveMessage, -}; +use example_native_token_transfers::instructions::RedeemArgs; +use ntt_messages::mode::Mode; use solana_program_test::*; use solana_sdk::{signature::Keypair, signer::Signer}; -use wormhole_sdk::Address; - -use crate::{ +use test_utils::{ common::{ - query::GetAccountDataAnchor, - setup::{setup, OTHER_TRANSCEIVER}, - utils::make_transfer_message, + fixtures::{OTHER_CHAIN, OTHER_TRANSCEIVER}, + submit::Submittable, + }, + helpers::{ + inbound_capacity, init_receive_message_accs, init_redeem_accs, init_transfer_accs_args, + make_transfer_message, outbound_capacity, post_vaa_helper, setup, }, sdk::{ + accounts::good_ntt, instructions::{ - redeem::{redeem, Redeem}, - transfer::Transfer, + redeem::redeem, + transfer::{approve_token_authority, transfer}, + }, + transceivers::{ + accounts::good_ntt_transceiver, instructions::receive_message::receive_message, }, - transceivers::wormhole::instructions::receive_message::receive_message, }, }; -use crate::{ - common::{submit::Submittable, utils::post_vaa_helper}, - sdk::instructions::transfer::{approve_token_authority, transfer}, -}; - -pub mod common; -pub mod sdk; - -fn init_transfer_accs_args( - ctx: &mut ProgramTestContext, - test_data: &TestData, - outbox_item: Pubkey, - amount: u64, - should_queue: bool, -) -> (Transfer, TransferArgs) { - let accs = Transfer { - payer: ctx.payer.pubkey(), - peer: good_ntt.peer(OTHER_CHAIN), - mint: test_data.mint, - from: test_data.user_token_account, - from_authority: test_data.user.pubkey(), - outbox_item, - }; - - let args = TransferArgs { - amount, - recipient_chain: ChainId { id: OTHER_CHAIN }, - recipient_address: [1u8; 32], - should_queue, - }; - - (accs, args) -} - -fn init_redeem_accs( - ctx: &mut ProgramTestContext, - test_data: &TestData, - chain_id: u16, - ntt_manager_message: NttManagerMessage>, -) -> Redeem { - Redeem { - payer: ctx.payer.pubkey(), - peer: good_ntt.peer(chain_id), - transceiver: good_ntt.program(), - transceiver_message: good_ntt.transceiver_message(chain_id, ntt_manager_message.id), - inbox_item: good_ntt.inbox_item(chain_id, ntt_manager_message), - inbox_rate_limit: good_ntt.inbox_rate_limit(chain_id), - mint: test_data.mint, - } -} - -fn init_receive_message_accs( - ctx: &mut ProgramTestContext, - vaa: Pubkey, - chain_id: u16, - id: [u8; 32], -) -> ReceiveMessage { - ReceiveMessage { - payer: ctx.payer.pubkey(), - peer: good_ntt.transceiver_peer(chain_id), - vaa, - chain_id, - id, - } -} - -async fn outbound_capacity(ctx: &mut ProgramTestContext) -> u64 { - let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); - let rate_limit: OutboxRateLimit = ctx - .get_account_data_anchor(good_ntt.outbox_rate_limit()) - .await; - - rate_limit.rate_limit.capacity_at(clock.unix_timestamp) -} - -async fn inbound_capacity(ctx: &mut ProgramTestContext) -> u64 { - let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); - let rate_limit: InboxRateLimit = ctx - .get_account_data_anchor(good_ntt.inbox_rate_limit(OTHER_CHAIN)) - .await; - - rate_limit.rate_limit.capacity_at(clock.unix_timestamp) -} +use wormhole_sdk::Address; #[tokio::test] async fn test_cancel() { @@ -141,12 +51,19 @@ async fn test_cancel() { ) .await; - let inbound_limit_before = inbound_capacity(&mut ctx).await; - let outbound_limit_before = outbound_capacity(&mut ctx).await; + let inbound_limit_before = inbound_capacity(&good_ntt, &mut ctx).await; + let outbound_limit_before = outbound_capacity(&good_ntt, &mut ctx).await; receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -155,6 +72,8 @@ async fn test_cancel() { redeem( &good_ntt, init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, &mut ctx, &test_data, OTHER_CHAIN, @@ -166,17 +85,26 @@ async fn test_cancel() { .await .unwrap(); - assert_eq!(outbound_limit_before, outbound_capacity(&mut ctx).await); + assert_eq!( + outbound_limit_before, + outbound_capacity(&good_ntt, &mut ctx).await + ); assert_eq!( inbound_limit_before - 1000, - inbound_capacity(&mut ctx).await + inbound_capacity(&good_ntt, &mut ctx).await ); let outbox_item = Keypair::new(); - let (accs, args) = - init_transfer_accs_args(&mut ctx, &test_data, outbox_item.pubkey(), 7000, true); + let (accs, args) = init_transfer_accs_args( + &good_ntt, + &mut ctx, + &test_data, + outbox_item.pubkey(), + 7000, + true, + ); approve_token_authority( &good_ntt, @@ -194,15 +122,25 @@ async fn test_cancel() { assert_eq!( outbound_limit_before - 7000, - outbound_capacity(&mut ctx).await + outbound_capacity(&good_ntt, &mut ctx).await ); // fully replenished - assert_eq!(inbound_limit_before, inbound_capacity(&mut ctx).await); + assert_eq!( + inbound_limit_before, + inbound_capacity(&good_ntt, &mut ctx).await + ); receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa1, OTHER_CHAIN, [1u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa1, + OTHER_CHAIN, + [1u8; 32], + ), ) .submit(&mut ctx) .await @@ -211,6 +149,8 @@ async fn test_cancel() { redeem( &good_ntt, init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, &mut ctx, &test_data, OTHER_CHAIN, @@ -224,11 +164,11 @@ async fn test_cancel() { assert_eq!( outbound_limit_before - 5000, - outbound_capacity(&mut ctx).await + outbound_capacity(&good_ntt, &mut ctx).await ); assert_eq!( inbound_limit_before - 2000, - inbound_capacity(&mut ctx).await + inbound_capacity(&good_ntt, &mut ctx).await ); } diff --git a/solana/programs/example-native-token-transfers/tests/common/submit.rs b/solana/programs/example-native-token-transfers/tests/common/submit.rs deleted file mode 100644 index d81fbe4bb..000000000 --- a/solana/programs/example-native-token-transfers/tests/common/submit.rs +++ /dev/null @@ -1,51 +0,0 @@ -use solana_program_test::{BanksClientError, ProgramTestContext}; -use solana_sdk::{ - instruction::Instruction, signature::Keypair, signer::Signer, signers::Signers, - transaction::Transaction, -}; - -pub trait Submittable { - async fn submit(self, ctx: &mut ProgramTestContext) -> Result<(), BanksClientError> - where - Self: Sized, - { - let no_signers: &[&Keypair] = &[]; - self.submit_with_signers(no_signers, ctx).await - } - - async fn submit_with_signers( - self, - signers: &T, - ctx: &mut ProgramTestContext, - ) -> Result<(), BanksClientError>; -} - -impl Submittable for Instruction { - async fn submit_with_signers( - self, - signers: &T, - ctx: &mut ProgramTestContext, - ) -> Result<(), BanksClientError> { - let blockhash = ctx.banks_client.get_latest_blockhash().await.unwrap(); - - let mut transaction = Transaction::new_with_payer(&[self], Some(&ctx.payer.pubkey())); - transaction.partial_sign(&[&ctx.payer], blockhash); - transaction.partial_sign(signers, blockhash); - - ctx.banks_client.process_transaction(transaction).await - } -} - -impl Submittable for Transaction { - async fn submit_with_signers( - mut self, - signers: &T, - ctx: &mut ProgramTestContext, - ) -> Result<(), BanksClientError> { - let blockhash = ctx.banks_client.get_latest_blockhash().await.unwrap(); - - self.partial_sign(&[&ctx.payer], blockhash); - self.partial_sign(signers, blockhash); - ctx.banks_client.process_transaction(self).await - } -} diff --git a/solana/programs/example-native-token-transfers/tests/governance.rs b/solana/programs/example-native-token-transfers/tests/governance.rs index 7b88c0d76..ec188591c 100644 --- a/solana/programs/example-native-token-transfers/tests/governance.rs +++ b/solana/programs/example-native-token-transfers/tests/governance.rs @@ -1,39 +1,33 @@ #![cfg(feature = "test-sbf")] #![feature(type_changing_struct_update)] -use std::sync::atomic::AtomicU64; - use anchor_lang::{prelude::*, InstructionData}; -use common::setup::TestData; use example_native_token_transfers::config::Config; use ntt_messages::mode::Mode; -use sdk::accounts::{Governance, Wormhole}; use solana_program::{ instruction::{Instruction, InstructionError}, system_instruction::SystemError, }; use solana_program_test::*; use solana_sdk::{pubkey::Pubkey, signer::Signer, transaction::TransactionError}; -use wormhole_governance::{ - error::GovernanceError, - instructions::{GovernanceMessage, ReplayProtection, OWNER}, -}; -use wormhole_sdk::{Address, Vaa, GOVERNANCE_EMITTER}; -use wormhole_solana_utils::cpi::bpf_loader_upgradeable; - -use crate::{ - common::{query::GetAccountDataAnchor, setup::setup, submit::Submittable}, +use std::sync::atomic::AtomicU64; +use test_utils::{ + common::{fixtures::TestData, query::GetAccountDataAnchor, submit::Submittable}, + helpers::setup, sdk::{ - accounts::{good_ntt, NTTAccounts}, + accounts::{good_ntt, Governance, NTTAccounts, Wormhole}, instructions::{ admin::{set_paused, SetPaused}, post_vaa::post_vaa, }, }, }; - -pub mod common; -pub mod sdk; +use wormhole_governance::{ + error::GovernanceError, + instructions::{GovernanceMessage, ReplayProtection, OWNER}, +}; +use wormhole_sdk::{Address, Vaa, GOVERNANCE_EMITTER}; +use wormhole_solana_utils::cpi::bpf_loader_upgradeable; async fn post_governance_vaa( ctx: &mut ProgramTestContext, diff --git a/solana/programs/example-native-token-transfers/tests/receive.rs b/solana/programs/example-native-token-transfers/tests/receive.rs index f7eeef820..8b7fa0ce0 100644 --- a/solana/programs/example-native-token-transfers/tests/receive.rs +++ b/solana/programs/example-native-token-transfers/tests/receive.rs @@ -3,77 +3,38 @@ use anchor_lang::prelude::*; use anchor_spl::token::{Token, TokenAccount}; -use common::{ - setup::{TestData, ANOTHER_CHAIN, OTHER_CHAIN}, - utils::make_transfer_message, -}; use example_native_token_transfers::{ error::NTTError, instructions::{RedeemArgs, ReleaseInboundArgs}, - transfer::Payload, -}; -use ntt_messages::{mode::Mode, ntt::NativeTokenTransfer, ntt_manager::NttManagerMessage}; -use sdk::{ - accounts::NTTAccounts, transceivers::wormhole::instructions::receive_message::ReceiveMessage, }; +use ntt_messages::mode::Mode; use solana_program::instruction::InstructionError; use solana_program_test::*; use solana_sdk::{ pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::TransactionError, }; use spl_associated_token_account::get_associated_token_address_with_program_id; -use wormhole_sdk::Address; - -use crate::{ +use test_utils::{ common::{ + fixtures::{ANOTHER_CHAIN, OTHER_CHAIN, OTHER_TRANSCEIVER}, query::GetAccountDataAnchor, - setup::{setup, OTHER_TRANSCEIVER}, + submit::Submittable, + }, + helpers::{ + init_receive_message_accs, init_redeem_accs, make_transfer_message, post_vaa_helper, setup, }, sdk::{ - accounts::good_ntt, - instructions::redeem::{redeem, Redeem}, - transceivers::wormhole::instructions::receive_message::receive_message, + accounts::{good_ntt, NTTAccounts}, + instructions::{ + redeem::redeem, + release_inbound::{release_inbound_unlock, ReleaseInbound}, + }, + transceivers::{ + accounts::good_ntt_transceiver, instructions::receive_message::receive_message, + }, }, }; -use crate::{ - common::{submit::Submittable, utils::post_vaa_helper}, - sdk::instructions::release_inbound::{release_inbound_unlock, ReleaseInbound}, -}; - -pub mod common; -pub mod sdk; - -fn init_redeem_accs( - ctx: &mut ProgramTestContext, - test_data: &TestData, - chain_id: u16, - ntt_manager_message: NttManagerMessage>, -) -> Redeem { - Redeem { - payer: ctx.payer.pubkey(), - peer: good_ntt.peer(chain_id), - transceiver: good_ntt.program(), - transceiver_message: good_ntt.transceiver_message(chain_id, ntt_manager_message.id), - inbox_item: good_ntt.inbox_item(chain_id, ntt_manager_message), - inbox_rate_limit: good_ntt.inbox_rate_limit(chain_id), - mint: test_data.mint, - } -} - -fn init_receive_message_accs( - ctx: &mut ProgramTestContext, - vaa: Pubkey, - chain_id: u16, - id: [u8; 32], -) -> ReceiveMessage { - ReceiveMessage { - payer: ctx.payer.pubkey(), - peer: good_ntt.transceiver_peer(chain_id), - vaa, - chain_id, - id, - } -} +use wormhole_sdk::Address; #[tokio::test] async fn test_receive() { @@ -125,7 +86,14 @@ async fn test_receive() { receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -134,6 +102,8 @@ async fn test_receive() { redeem( &good_ntt, init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, &mut ctx, &test_data, OTHER_CHAIN, @@ -220,7 +190,14 @@ async fn test_double_receive() { receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -228,7 +205,14 @@ async fn test_double_receive() { let err = receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa1, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa1, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -261,7 +245,14 @@ async fn test_wrong_recipient_ntt_manager() { receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -270,6 +261,8 @@ async fn test_wrong_recipient_ntt_manager() { let err = redeem( &good_ntt, init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, &mut ctx, &test_data, OTHER_CHAIN, @@ -308,7 +301,14 @@ async fn test_wrong_transceiver_peer() { let err = receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -343,7 +343,14 @@ async fn test_wrong_manager_peer() { receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -352,6 +359,8 @@ async fn test_wrong_manager_peer() { let err = redeem( &good_ntt, init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, &mut ctx, &test_data, OTHER_CHAIN, @@ -390,7 +399,14 @@ async fn test_wrong_inbox_item() { receive_message( &good_ntt, - init_receive_message_accs(&mut ctx, vaa0, OTHER_CHAIN, [0u8; 32]), + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt_transceiver, + &mut ctx, + vaa0, + OTHER_CHAIN, + [0u8; 32], + ), ) .submit(&mut ctx) .await @@ -398,6 +414,8 @@ async fn test_wrong_inbox_item() { // use 'ANOTHER_CHAIN' inbox item account here let mut redeem_accs = init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, &mut ctx, &test_data, OTHER_CHAIN, diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/mod.rs b/solana/programs/example-native-token-transfers/tests/sdk/transceivers/mod.rs deleted file mode 100644 index 3ab4966d3..000000000 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod wormhole; diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/accounts/mod.rs b/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/accounts/mod.rs deleted file mode 100644 index 3ab4966d3..000000000 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/accounts/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod wormhole; diff --git a/solana/programs/example-native-token-transfers/tests/transfer.rs b/solana/programs/example-native-token-transfers/tests/transfer.rs index 8bd7b986e..f1ca9dc26 100644 --- a/solana/programs/example-native-token-transfers/tests/transfer.rs +++ b/solana/programs/example-native-token-transfers/tests/transfer.rs @@ -3,7 +3,6 @@ use anchor_lang::prelude::{Clock, ErrorCode, Pubkey}; use anchor_spl::token::{Mint, TokenAccount}; -use common::setup::{TestData, OTHER_CHAIN}; use example_native_token_transfers::{ bitmap::Bitmap, error::NTTError, @@ -17,27 +16,20 @@ use ntt_messages::{ transceiver::TransceiverMessage, transceivers::wormhole::WormholeTransceiver, trimmed_amount::TrimmedAmount, }; -use sdk::accounts::NTT; use solana_program_test::*; use solana_sdk::{ instruction::InstructionError, signature::Keypair, signer::Signer, transaction::TransactionError, }; -use wormhole_anchor_sdk::wormhole::PostedVaa; - -use crate::{ +use test_utils::{ common::{ + fixtures::{TestData, ANOTHER_CHAIN, OTHER_MANAGER, OUTBOUND_LIMIT, UNREGISTERED_CHAIN}, query::GetAccountDataAnchor, - setup::{ANOTHER_CHAIN, OUTBOUND_LIMIT, UNREGISTERED_CHAIN}, + submit::Submittable, }, + helpers::{assert_queued, init_transfer_accs_args, setup, setup_with_transfer_fee}, sdk::{ accounts::{good_ntt, NTTAccounts}, - instructions::transfer::Transfer, - }, -}; -use crate::{ - common::{setup::OTHER_MANAGER, submit::Submittable}, - sdk::{ instructions::{ admin::{set_outbound_limit, set_paused, SetOutboundLimit, SetPaused}, transfer::{ @@ -45,46 +37,13 @@ use crate::{ transfer_with_token_program_id, }, }, - transceivers::wormhole::instructions::release_outbound::{ - release_outbound, ReleaseOutbound, + transceivers::{ + accounts::{good_ntt_transceiver, NTTTransceiverAccounts}, + instructions::release_outbound::{release_outbound, ReleaseOutbound}, }, }, }; - -pub mod common; -pub mod sdk; - -use crate::common::setup::{setup, setup_with_transfer_fee}; - -/// Helper function for setting up transfer accounts and args. -/// It sets the accounts up properly, so for negative testing we just modify the -/// result. -fn init_accs_args( - ntt: &NTT, - ctx: &mut ProgramTestContext, - test_data: &TestData, - outbox_item: Pubkey, - amount: u64, - should_queue: bool, -) -> (Transfer, TransferArgs) { - let accs = Transfer { - payer: ctx.payer.pubkey(), - mint: test_data.mint, - from: test_data.user_token_account, - from_authority: test_data.user.pubkey(), - peer: ntt.peer(OTHER_CHAIN), - outbox_item, - }; - - let args = TransferArgs { - amount, - recipient_chain: ChainId { id: OTHER_CHAIN }, - recipient_address: [1u8; 32], - should_queue, - }; - - (accs, args) -} +use wormhole_anchor_sdk::wormhole::PostedVaa; #[tokio::test] pub async fn test_transfer_locking() { @@ -129,7 +88,8 @@ async fn test_transfer(ctx: &mut ProgramTestContext, test_data: &TestData, mode: let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); - let (accs, args) = init_accs_args(&good_ntt, ctx, test_data, outbox_item.pubkey(), 154, false); + let (accs, args) = + init_transfer_accs_args(&good_ntt, ctx, test_data, outbox_item.pubkey(), 154, false); approve_token_authority( &good_ntt, @@ -165,6 +125,7 @@ async fn test_transfer(ctx: &mut ProgramTestContext, test_data: &TestData, mode: release_outbound( &good_ntt, + &good_ntt_transceiver, ReleaseOutbound { payer: ctx.payer.pubkey(), outbox_item: outbox_item.pubkey(), @@ -189,7 +150,7 @@ async fn test_transfer(ctx: &mut ProgramTestContext, test_data: &TestData, mode: outbox_item_account_after, ); - let wh_message = good_ntt.wormhole_message(&outbox_item.pubkey()); + let wh_message = good_ntt_transceiver.wormhole_message(&outbox_item.pubkey()); // NOTE: technically this is not a PostedVAA but a PostedMessage, but the // sdk does not export that type, so we parse it as a PostedVAA instead. @@ -233,7 +194,8 @@ async fn test_transfer_with_transfer_fee( ) { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args(&good_ntt, ctx, test_data, outbox_item.pubkey(), 154, false); + let (accs, args) = + init_transfer_accs_args(&good_ntt, ctx, test_data, outbox_item.pubkey(), 154, false); approve_token_authority_with_token_program_id( &good_ntt, @@ -261,7 +223,7 @@ async fn test_burn_mode_burns_tokens() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -312,7 +274,7 @@ async fn locking_mode_locks_tokens() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -357,17 +319,14 @@ async fn locking_mode_locks_tokens() { // NOTE: we transfer 1050, but only 1000 gets locked (token is 9 decimals, and // gets trimmed to 7 because of the target chain's decimals) - assert_eq!( token_account_before.amount - 1000, token_account_after.amount ); - assert_eq!( custody_account_before.amount + 1000, custody_account_after.amount ); - assert_eq!(mint_before.supply, mint_after.supply); } @@ -377,7 +336,7 @@ async fn test_bad_mint() { let outbox_item = Keypair::new(); - let (mut accs, args) = init_accs_args( + let (mut accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -442,7 +401,7 @@ async fn test_invalid_peer() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &BadNTT {}, &mut ctx, &test_data, @@ -491,7 +450,7 @@ async fn test_unregistered_peer_cant_transfer() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &BadNTT {}, &mut ctx, &test_data, @@ -531,7 +490,7 @@ async fn test_cant_transfer_to_unregistered_peer() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -580,7 +539,7 @@ async fn test_rate_limit() { let outbox_item = Keypair::new(); let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -622,7 +581,7 @@ async fn test_transfer_wrong_mode() { let (mut ctx, test_data) = setup(Mode::Burning).await; let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -681,7 +640,7 @@ async fn test_cant_transfer_more_than_balance() { .unwrap(); // try to transfer more than balance - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -713,15 +672,6 @@ async fn test_cant_transfer_more_than_balance() { ); } -async fn assert_queued(ctx: &mut ProgramTestContext, outbox_item: Pubkey) { - let outbox_item_account: OutboxItem = ctx.get_account_data_anchor(outbox_item).await; - - let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); - - assert!(!outbox_item_account.released.get(0).unwrap()); - assert!(outbox_item_account.release_timestamp > clock.unix_timestamp); -} - #[tokio::test] async fn test_large_tx_queue() { let (mut ctx, test_data) = setup(Mode::Locking).await; @@ -730,7 +680,7 @@ async fn test_large_tx_queue() { let too_much = OUTBOUND_LIMIT + 1000; let should_queue = true; - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -773,7 +723,7 @@ async fn test_cant_transfer_when_paused() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -847,7 +797,7 @@ async fn test_large_tx_no_queue() { let too_much = OUTBOUND_LIMIT + 1000; let should_queue = false; - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -886,7 +836,7 @@ async fn test_cant_release_queued() { let outbox_item = Keypair::new(); let too_much = OUTBOUND_LIMIT + 1000; - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -914,6 +864,7 @@ async fn test_cant_release_queued() { // check that 'revert_on_delay = true' returns correct error let err = release_outbound( &good_ntt, + &good_ntt_transceiver, ReleaseOutbound { payer: ctx.payer.pubkey(), outbox_item: outbox_item.pubkey(), @@ -937,6 +888,7 @@ async fn test_cant_release_queued() { // check that 'revert_on_delay = false' succeeds but does not release release_outbound( &good_ntt, + &good_ntt_transceiver, ReleaseOutbound { payer: ctx.payer.pubkey(), outbox_item: outbox_item.pubkey(), @@ -952,7 +904,7 @@ async fn test_cant_release_queued() { assert_queued(&mut ctx, outbox_item.pubkey()).await; // just to be safe, let's make sure the wormhole message account wasn't initialised - let wh_message = good_ntt.wormhole_message(&outbox_item.pubkey()); + let wh_message = good_ntt_transceiver.wormhole_message(&outbox_item.pubkey()); assert!(ctx .banks_client .get_account(wh_message) @@ -967,7 +919,7 @@ async fn test_cant_release_twice() { let outbox_item = Keypair::new(); - let (accs, args) = init_accs_args( + let (accs, args) = init_transfer_accs_args( &good_ntt, &mut ctx, &test_data, @@ -992,6 +944,7 @@ async fn test_cant_release_twice() { release_outbound( &good_ntt, + &good_ntt_transceiver, ReleaseOutbound { payer: ctx.payer.pubkey(), outbox_item: outbox_item.pubkey(), @@ -1007,6 +960,7 @@ async fn test_cant_release_twice() { // make sure we can't release again let err = release_outbound( &good_ntt, + &good_ntt_transceiver, ReleaseOutbound { payer: ctx.payer.pubkey(), outbox_item: outbox_item.pubkey(), diff --git a/solana/programs/ntt-transceiver/Cargo.toml b/solana/programs/ntt-transceiver/Cargo.toml index fd9462f1c..9b0333362 100644 --- a/solana/programs/ntt-transceiver/Cargo.toml +++ b/solana/programs/ntt-transceiver/Cargo.toml @@ -9,22 +9,26 @@ crate-type = ["cdylib", "lib"] name = "ntt_transceiver" [features] +default = ["mainnet"] no-entrypoint = ["example-native-token-transfers/no-entrypoint"] no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint", "example-native-token-transfers/cpi"] idl-build = [ "anchor-lang/idl-build", "anchor-spl/idl-build", "example-native-token-transfers/idl-build", "wormhole-anchor-sdk/mainnet" # TODO: is this kosher? builds idl without passing on our feature flags ] -no-log-ix-name = [] -cpi = ["no-entrypoint", "example-native-token-transfers/cpi"] -default = ["mainnet"] wormhole-transceiver = ["example-native-token-transfers/cpi"] transceiver-type-from-env = [] - -bridge-address-from-env = [ "wormhole-anchor-sdk/bridge-address-from-env", "wormhole-svm-definitions/from-env", "example-native-token-transfers/bridge-address-from-env" ] +# cargo-test-sbf will pass this along +test-sbf = [] +# only enable for tests +testing = [] +# networks mainnet = [ "wormhole-transceiver", "wormhole-anchor-sdk/mainnet", "example-native-token-transfers/mainnet" ] +bridge-address-from-env = [ "wormhole-anchor-sdk/bridge-address-from-env", "wormhole-svm-definitions/from-env", "example-native-token-transfers/bridge-address-from-env" ] solana-devnet = [ "wormhole-transceiver", "wormhole-anchor-sdk/solana-devnet", "wormhole-svm-definitions/testnet", "example-native-token-transfers/solana-devnet" ] tilt-devnet = [ "wormhole-transceiver", "wormhole-anchor-sdk/tilt-devnet", "wormhole-svm-definitions/localnet", "example-native-token-transfers/tilt-devnet" ] tilt-devnet2 = [ "wormhole-transceiver", "tilt-devnet", "wormhole-svm-definitions/localnet", "example-native-token-transfers/tilt-devnet2" ] @@ -33,20 +37,23 @@ tilt-devnet2 = [ "wormhole-transceiver", "tilt-devnet", "wormhole-svm-definition workspace = true [dependencies] -ntt-messages = { path = "../../modules/ntt-messages", features = ["anchor", "hash"] } anchor-lang.workspace = true anchor-spl.workspace = true +cfg-if.workspace = true solana-program.workspace = true -wormhole-post-message-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface", features = ["no-entrypoint", "cpi"] } -wormhole-verify-vaa-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface", features = ["no-entrypoint", "cpi"] } -wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole", branch = "svm-shims-fr-env-addr" } -cfg-if = "1.0.0" - wormhole-anchor-sdk.workspace = true wormhole-io.workspace = true wormhole-sdk.workspace = true example-native-token-transfers = { path = "../example-native-token-transfers", default-features = false } +ntt-messages = { path = "../../modules/ntt-messages", features = ["anchor", "hash"] } +wormhole-post-message-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface", features = ["no-entrypoint", "cpi"] } +wormhole-verify-vaa-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface", features = ["no-entrypoint", "cpi"] } +wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole", branch = "svm-shims-fr-env-addr" } [dev-dependencies] -hex.workspace = true +solana-program-test.workspace = true +spl-associated-token-account = { version = "3.0.0", features = ["no-entrypoint"] } +solana-sdk = "*" +spl-token.workspace = true +test-utils = { path = "../../tests/cargo", features = ["shim"] } diff --git a/solana/programs/ntt-transceiver/src/wormhole/accounts.rs b/solana/programs/ntt-transceiver/src/wormhole/accounts.rs index 1b0430178..30a455a5f 100644 --- a/solana/programs/ntt-transceiver/src/wormhole/accounts.rs +++ b/solana/programs/ntt-transceiver/src/wormhole/accounts.rs @@ -72,6 +72,23 @@ pub fn post_message<'info, A: TypePrefixedPayload>( TypePrefixedPayload::to_vec_payload(payload), )?; + // Instruction data passed onto the Post Message Shim program is used to recreate the VAA. + // Cargo tests, however, do not expose a way to fetch inner instructions. As a workaround during + // testing, we build with this feature enabled and set the instruction data as the return data. + // The test first simulates this instruction to fetch the return data before submitting it. + #[cfg(feature = "testing")] + { + use anchor_lang::InstructionData; + + let ix_data = wormhole_post_message_shim_interface::instruction::PostMessage { + nonce: batch_id, + consistency_level: Finality::Finalized, + payload: TypePrefixedPayload::to_vec_payload(payload), + } + .data(); + solana_program::program::set_return_data(&ix_data); + } + Ok(()) } diff --git a/solana/programs/ntt-transceiver/tests/admin.rs b/solana/programs/ntt-transceiver/tests/admin.rs new file mode 100644 index 000000000..0e150a7dd --- /dev/null +++ b/solana/programs/ntt-transceiver/tests/admin.rs @@ -0,0 +1,208 @@ +#![cfg(feature = "test-sbf")] +#![feature(type_changing_struct_update)] + +use anchor_lang::{system_program::System, Id}; +use example_native_token_transfers::error::NTTError; +use ntt_messages::mode::Mode; +use solana_program_test::*; +use solana_sdk::{instruction::InstructionError, signer::Signer, transaction::TransactionError}; +use test_utils::{ + common::submit::Submittable, + helpers::{assert_threshold, assert_transceiver_id, setup}, + sdk::{ + accounts::good_ntt, + instructions::admin::{ + deregister_transceiver, register_transceiver, set_threshold, DeregisterTransceiver, + RegisterTransceiver, SetThreshold, + }, + transceivers::accounts::{good_ntt_transceiver, NTTTransceiverAccounts}, + }, +}; +use wormhole_svm_definitions::solana::{POST_MESSAGE_SHIM_PROGRAM_ID, VERIFY_VAA_SHIM_PROGRAM_ID}; + +#[tokio::test] +async fn test_invalid_transceiver() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + + // try registering system program + let err = register_transceiver( + &good_ntt, + RegisterTransceiver { + payer: ctx.payer.pubkey(), + owner: test_data.program_owner.pubkey(), + transceiver: System::id(), + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap_err(); + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::InvalidTransceiverProgram.into()) + ) + ); +} + +#[tokio::test] +async fn test_reregister_all_transceivers() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + + // Transceivers are expected to be executable which requires them to be added on setup + // Thus, we pass all available executable program IDs as dummy_transceivers + let dummy_transceivers = vec![ + example_native_token_transfers::ID, + wormhole_anchor_sdk::wormhole::program::Wormhole::id(), + POST_MESSAGE_SHIM_PROGRAM_ID, + VERIFY_VAA_SHIM_PROGRAM_ID, + ]; + let num_dummy_transceivers: u8 = dummy_transceivers.len().try_into().unwrap(); + + // register dummy transceivers + for (idx, transceiver) in dummy_transceivers.iter().enumerate() { + register_transceiver( + &good_ntt, + RegisterTransceiver { + payer: ctx.payer.pubkey(), + owner: test_data.program_owner.pubkey(), + transceiver: *transceiver, + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + assert_transceiver_id(&good_ntt, &mut ctx, transceiver, idx as u8 + 1).await; + } + + // set threshold = 1 (for ntt_transceiver) + num_dummy_transceivers + set_threshold( + &good_ntt, + SetThreshold { + owner: test_data.program_owner.pubkey(), + }, + 1 + num_dummy_transceivers, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + + // deregister dummy transceivers + for (idx, transceiver) in dummy_transceivers.iter().enumerate() { + deregister_transceiver( + &good_ntt, + DeregisterTransceiver { + owner: test_data.program_owner.pubkey(), + transceiver: *transceiver, + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + // assert threshold decreases + assert_threshold(&good_ntt, &mut ctx, num_dummy_transceivers - idx as u8).await; + } + + // reregister dummy transceiver + for (idx, transceiver) in dummy_transceivers.iter().enumerate() { + register_transceiver( + &good_ntt, + RegisterTransceiver { + payer: ctx.payer.pubkey(), + owner: test_data.program_owner.pubkey(), + transceiver: *transceiver, + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + // assert transceiver_id and threshold are retained + assert_transceiver_id(&good_ntt, &mut ctx, transceiver, idx as u8 + 1).await; + assert_threshold(&good_ntt, &mut ctx, 1).await; + } + + // reregister standalone transceiver + register_transceiver( + &good_ntt, + RegisterTransceiver { + payer: ctx.payer.pubkey(), + owner: test_data.program_owner.pubkey(), + transceiver: good_ntt_transceiver.program(), + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + // assert transceiver_id and threshold are retained + assert_transceiver_id(&good_ntt, &mut ctx, &good_ntt_transceiver.program(), 0).await; + assert_threshold(&good_ntt, &mut ctx, 1).await; +} + +#[tokio::test] +async fn test_deregister_last_enabled_transceiver() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + + // attempt to deregister only enabled transceiver (standalone transceiver) + let err = deregister_transceiver( + &good_ntt, + DeregisterTransceiver { + owner: test_data.program_owner.pubkey(), + transceiver: good_ntt_transceiver.program(), + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap_err(); + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::ZeroThreshold.into()) + ) + ); + + // register arbitrary executable program as dummy transceiver + let dummy_transceiver = wormhole_anchor_sdk::wormhole::program::Wormhole::id(); + register_transceiver( + &good_ntt, + RegisterTransceiver { + payer: ctx.payer.pubkey(), + owner: test_data.program_owner.pubkey(), + transceiver: dummy_transceiver, + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + + // deregister standalone transceiver + deregister_transceiver( + &good_ntt, + DeregisterTransceiver { + owner: test_data.program_owner.pubkey(), + transceiver: good_ntt_transceiver.program(), + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap(); + + // attempt to deregister last enabled transceiver (dummy transceiver) + let err = deregister_transceiver( + &good_ntt, + DeregisterTransceiver { + owner: test_data.program_owner.pubkey(), + transceiver: dummy_transceiver, + }, + ) + .submit_with_signers(&[&test_data.program_owner], &mut ctx) + .await + .unwrap_err(); + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::ZeroThreshold.into()) + ) + ); +} diff --git a/solana/programs/ntt-transceiver/tests/broadcast.rs b/solana/programs/ntt-transceiver/tests/broadcast.rs new file mode 100644 index 000000000..ed0763f48 --- /dev/null +++ b/solana/programs/ntt-transceiver/tests/broadcast.rs @@ -0,0 +1,99 @@ +#![cfg(feature = "test-sbf")] +#![feature(type_changing_struct_update)] + +use anchor_lang::AnchorDeserialize; +use ntt_messages::{ + chain_id::ChainId, + mode::Mode, + transceivers::wormhole::{WormholeTransceiverInfo, WormholeTransceiverRegistration}, +}; +use solana_program_test::*; +use solana_sdk::signer::Signer; +use test_utils::{ + common::{ + fixtures::{OTHER_CHAIN, OTHER_TRANSCEIVER}, + submit::Submittable, + }, + helpers::{get_message_data, setup}, + sdk::{ + accounts::{good_ntt, NTTAccounts}, + transceivers::{ + accounts::good_ntt_transceiver, + instructions::{ + broadcast_id::{broadcast_id, BroadcastId}, + broadcast_peer::{broadcast_peer, BroadcastPeer}, + }, + }, + }, +}; +use wormhole_svm_definitions::{solana::Finality::Finalized, EncodeFinality}; + +#[tokio::test] +async fn test_broadcast_peer() { + let (mut ctx, _test_data) = setup(Mode::Locking).await; + + let ix = broadcast_peer( + &good_ntt, + &good_ntt_transceiver, + BroadcastPeer { + payer: ctx.payer.pubkey(), + chain_id: OTHER_CHAIN, + }, + ); + + // simulate to fetch data before submitting ix + let msg = get_message_data( + &good_ntt.wormhole(), + &good_ntt_transceiver, + &mut ctx, + ix.clone(), + ) + .await; + ix.submit(&mut ctx).await.unwrap(); + + assert_eq!(msg.nonce, 0); // hardcoded + assert_eq!(msg.consistency_level, Finalized.encode()); // hardcoded + assert_eq!( + WormholeTransceiverRegistration::deserialize(&mut &msg.payload[..]).unwrap(), + WormholeTransceiverRegistration { + chain_id: ChainId { id: OTHER_CHAIN }, + transceiver_address: OTHER_TRANSCEIVER + } + ); +} + +#[tokio::test] +async fn test_broadcast_id() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let ix = broadcast_id( + &good_ntt, + &good_ntt_transceiver, + BroadcastId { + payer: ctx.payer.pubkey(), + mint: test_data.mint, + }, + ); + + // simulate to fetch data before submitting ix + let msg = get_message_data( + &good_ntt.wormhole(), + &good_ntt_transceiver, + &mut ctx, + ix.clone(), + ) + .await; + ix.submit(&mut ctx).await.unwrap(); + + assert_eq!(msg.nonce, 0); // hardcoded + assert_eq!(msg.consistency_level, Finalized.encode()); // hardcoded + assert_eq!( + WormholeTransceiverInfo::deserialize(&mut &msg.payload[..]).unwrap(), + WormholeTransceiverInfo { + manager_address: good_ntt.program().to_bytes(), + manager_mode: Mode::Locking, + token_address: test_data.mint.to_bytes(), + token_decimals: 9, + } + ); +} diff --git a/solana/programs/ntt-transceiver/tests/cancel_flow.rs b/solana/programs/ntt-transceiver/tests/cancel_flow.rs new file mode 100644 index 000000000..f54e0dc66 --- /dev/null +++ b/solana/programs/ntt-transceiver/tests/cancel_flow.rs @@ -0,0 +1,187 @@ +#![cfg(feature = "test-sbf")] +#![feature(type_changing_struct_update)] + +use example_native_token_transfers::instructions::RedeemArgs; +use ntt_messages::mode::Mode; +use ntt_transceiver::vaa_body::VaaBodyData; +use solana_program_test::*; +use solana_sdk::{signature::Keypair, signer::Signer}; +use test_utils::{ + common::{ + fixtures::{OTHER_CHAIN, OTHER_TRANSCEIVER}, + submit::Submittable, + }, + helpers::{ + inbound_capacity, init_receive_message_accs, init_redeem_accs, init_transfer_accs_args, + make_transfer_message, outbound_capacity, post_vaa_helper, setup, + }, + sdk::{ + accounts::good_ntt, + instructions::{ + post_vaa::close_signatures, + redeem::redeem, + transfer::{approve_token_authority, transfer}, + }, + transceivers::{ + accounts::good_ntt_transceiver, + instructions::receive_message::receive_message_instruction_data, + }, + }, +}; +use wormhole_sdk::Address; + +#[tokio::test] +async fn test_cancel() { + let recipient = Keypair::new(); + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let msg0 = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + let msg1 = make_transfer_message(&good_ntt, [1u8; 32], 2000, &recipient.pubkey()); + let (guardian_signatures0, guardian_set_index0, span0) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg0.clone(), + &mut ctx, + ) + .await; + let (guardian_signatures1, guardian_set_index1, span1) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg1.clone(), + &mut ctx, + ) + .await; + + let inbound_limit_before = inbound_capacity(&good_ntt, &mut ctx).await; + let outbound_limit_before = outbound_capacity(&good_ntt, &mut ctx).await; + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index0, + guardian_signatures0, + ), + VaaBodyData { span: span0 }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures0).await; + + redeem( + &good_ntt, + init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg0.ntt_manager_payload.clone(), + ), + RedeemArgs {}, + ) + .submit(&mut ctx) + .await + .unwrap(); + + assert_eq!( + outbound_limit_before, + outbound_capacity(&good_ntt, &mut ctx).await + ); + + assert_eq!( + inbound_limit_before - 1000, + inbound_capacity(&good_ntt, &mut ctx).await + ); + + let outbox_item = Keypair::new(); + + let (accs, args) = init_transfer_accs_args( + &good_ntt, + &mut ctx, + &test_data, + outbox_item.pubkey(), + 7000, + true, + ); + + approve_token_authority( + &good_ntt, + &test_data.user_token_account, + &test_data.user.pubkey(), + &args, + ) + .submit_with_signers(&[&test_data.user], &mut ctx) + .await + .unwrap(); + transfer(&good_ntt, accs, args, Mode::Locking) + .submit_with_signers(&[&outbox_item], &mut ctx) + .await + .unwrap(); + + assert_eq!( + outbound_limit_before - 7000, + outbound_capacity(&good_ntt, &mut ctx).await + ); + + // fully replenished + assert_eq!( + inbound_limit_before, + inbound_capacity(&good_ntt, &mut ctx).await + ); + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [1u8; 32], + guardian_set_index1, + guardian_signatures1, + ), + VaaBodyData { span: span1 }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures1).await; + + redeem( + &good_ntt, + init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg1.ntt_manager_payload.clone(), + ), + RedeemArgs {}, + ) + .submit(&mut ctx) + .await + .unwrap(); + + assert_eq!( + outbound_limit_before - 5000, + outbound_capacity(&good_ntt, &mut ctx).await + ); + + assert_eq!( + inbound_limit_before - 2000, + inbound_capacity(&good_ntt, &mut ctx).await + ); +} diff --git a/solana/programs/ntt-transceiver/tests/receive.rs b/solana/programs/ntt-transceiver/tests/receive.rs new file mode 100644 index 000000000..d456c2725 --- /dev/null +++ b/solana/programs/ntt-transceiver/tests/receive.rs @@ -0,0 +1,628 @@ +#![cfg(feature = "test-sbf")] +#![feature(type_changing_struct_update)] + +use anchor_lang::prelude::*; +use anchor_spl::token::{Token, TokenAccount}; +use example_native_token_transfers::{ + error::NTTError, + instructions::{RedeemArgs, ReleaseInboundArgs}, +}; +use ntt_messages::mode::Mode; +use ntt_transceiver::vaa_body::VaaBodyData; +use solana_program::instruction::InstructionError; +use solana_program_test::*; +use solana_sdk::{ + pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::TransactionError, +}; +use spl_associated_token_account::get_associated_token_address_with_program_id; +use test_utils::{ + common::{ + fixtures::{ANOTHER_CHAIN, OTHER_CHAIN, OTHER_TRANSCEIVER}, + query::GetAccountDataAnchor, + submit::Submittable, + }, + helpers::{ + init_receive_message_accs, init_redeem_accs, make_transfer_message, post_vaa_helper, setup, + }, + sdk::{ + accounts::{good_ntt, NTTAccounts}, + instructions::{ + post_vaa::close_signatures, + redeem::redeem, + release_inbound::{release_inbound_unlock, ReleaseInbound}, + }, + transceivers::{ + accounts::good_ntt_transceiver, + instructions::{ + receive_message::{receive_message_account, receive_message_instruction_data}, + unverified_message_account::{ + post_unverified_message_account, UnverifiedMessageAccount, + }, + }, + }, + }, +}; +use wormhole_sdk::{vaa::digest, Address}; + +#[tokio::test] +async fn test_receive_instruction_data() { + let recipient = Keypair::new(); + let (mut ctx, test_data) = setup(Mode::Locking).await; + + // transfer tokens to custody account + spl_token::instruction::transfer_checked( + &Token::id(), + &test_data.user_token_account, + &test_data.mint, + &good_ntt.custody(&test_data.mint), + &test_data.user.pubkey(), + &[], + 1000, + 9, + ) + .unwrap() + .submit_with_signers(&[&test_data.user], &mut ctx) + .await + .unwrap(); + + spl_associated_token_account::instruction::create_associated_token_account( + &ctx.payer.pubkey(), + &recipient.pubkey(), + &test_data.mint, + &Token::id(), + ) + .submit(&mut ctx) + .await + .unwrap(); + + let recipient_token_account = get_associated_token_address_with_program_id( + &recipient.pubkey(), + &test_data.mint, + &Token::id(), + ); + + let msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + let (guardian_signatures, guardian_set_index, span) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg.clone(), + &mut ctx, + ) + .await; + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index, + guardian_signatures, + ), + VaaBodyData { span }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures).await; + + redeem( + &good_ntt, + init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg.ntt_manager_payload.clone(), + ), + RedeemArgs {}, + ) + .submit(&mut ctx) + .await + .unwrap(); + + let token_account: TokenAccount = ctx.get_account_data_anchor(recipient_token_account).await; + + assert_eq!(token_account.amount, 0); + + release_inbound_unlock( + &good_ntt, + ReleaseInbound { + payer: ctx.payer.pubkey(), + inbox_item: good_ntt.inbox_item(OTHER_CHAIN, msg.ntt_manager_payload.clone()), + mint: test_data.mint, + recipient: recipient_token_account, + }, + ReleaseInboundArgs { + revert_when_not_ready: false, + }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + let token_account: TokenAccount = ctx.get_account_data_anchor(recipient_token_account).await; + assert_eq!(token_account.amount, 1000); + + // let's make sure we can't redeem again. + let err = release_inbound_unlock( + &good_ntt, + ReleaseInbound { + payer: ctx.payer.pubkey(), + inbox_item: good_ntt.inbox_item(OTHER_CHAIN, msg.ntt_manager_payload.clone()), + mint: test_data.mint, + recipient: recipient_token_account, + }, + ReleaseInboundArgs { + revert_when_not_ready: false, + }, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::TransferAlreadyRedeemed.into()) + ) + ); +} + +#[tokio::test] +async fn test_receive_message_account() { + let recipient = Keypair::new(); + let (mut ctx, test_data) = setup(Mode::Locking).await; + + // transfer tokens to custody account + spl_token::instruction::transfer_checked( + &Token::id(), + &test_data.user_token_account, + &test_data.mint, + &good_ntt.custody(&test_data.mint), + &test_data.user.pubkey(), + &[], + 1000, + 9, + ) + .unwrap() + .submit_with_signers(&[&test_data.user], &mut ctx) + .await + .unwrap(); + + spl_associated_token_account::instruction::create_associated_token_account( + &ctx.payer.pubkey(), + &recipient.pubkey(), + &test_data.mint, + &Token::id(), + ) + .submit(&mut ctx) + .await + .unwrap(); + + let recipient_token_account = get_associated_token_address_with_program_id( + &recipient.pubkey(), + &test_data.mint, + &Token::id(), + ); + + let msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + let (guardian_signatures, guardian_set_index, vaa_body) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg.clone(), + &mut ctx, + ) + .await; + + // arbitrary seed to identify this message + let seed = u64::from_be_bytes(digest(&vaa_body).unwrap().hash[24..].try_into().unwrap()); + + post_unverified_message_account( + &good_ntt_transceiver, + UnverifiedMessageAccount { + payer: ctx.payer.pubkey(), + }, + seed, + vaa_body, + ) + .submit(&mut ctx) + .await + .unwrap(); + + receive_message_account( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index, + guardian_signatures, + ), + seed, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures).await; + + redeem( + &good_ntt, + init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg.ntt_manager_payload.clone(), + ), + RedeemArgs {}, + ) + .submit(&mut ctx) + .await + .unwrap(); + + let token_account: TokenAccount = ctx.get_account_data_anchor(recipient_token_account).await; + + assert_eq!(token_account.amount, 0); + + release_inbound_unlock( + &good_ntt, + ReleaseInbound { + payer: ctx.payer.pubkey(), + inbox_item: good_ntt.inbox_item(OTHER_CHAIN, msg.ntt_manager_payload.clone()), + mint: test_data.mint, + recipient: recipient_token_account, + }, + ReleaseInboundArgs { + revert_when_not_ready: false, + }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + let token_account: TokenAccount = ctx.get_account_data_anchor(recipient_token_account).await; + assert_eq!(token_account.amount, 1000); + + // let's make sure we can't redeem again. + let err = release_inbound_unlock( + &good_ntt, + ReleaseInbound { + payer: ctx.payer.pubkey(), + inbox_item: good_ntt.inbox_item(OTHER_CHAIN, msg.ntt_manager_payload.clone()), + mint: test_data.mint, + recipient: recipient_token_account, + }, + ReleaseInboundArgs { + revert_when_not_ready: false, + }, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::TransferAlreadyRedeemed.into()) + ) + ); +} + +#[tokio::test] +async fn test_double_receive() { + let recipient = Keypair::new(); + let (mut ctx, _test_data) = setup(Mode::Locking).await; + + let msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + let (guardian_signatures0, guardian_set_index0, span0) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg.clone(), + &mut ctx, + ) + .await; + let (guardian_signatures1, guardian_set_index1, span1) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg, + &mut ctx, + ) + .await; + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index0, + guardian_signatures0, + ), + VaaBodyData { span: span0 }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + let err = receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index1, + guardian_signatures1, + ), + VaaBodyData { span: span1 }, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures0).await; + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures1).await; + + assert_eq!( + err.unwrap(), + // AccountAlreadyInUse + TransactionError::InstructionError(0, InstructionError::Custom(0)) + ); +} + +#[tokio::test] +async fn test_wrong_recipient_ntt_manager() { + let recipient = Keypair::new(); + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let mut msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + msg.recipient_ntt_manager = Pubkey::new_unique().to_bytes(); + + let (guardian_signatures, guardian_set_index, span) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg.clone(), + &mut ctx, + ) + .await; + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index, + guardian_signatures, + ), + VaaBodyData { span }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures).await; + + let err = redeem( + &good_ntt, + init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg.ntt_manager_payload.clone(), + ), + RedeemArgs {}, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::InvalidRecipientNttManager.into()) + ) + ); +} + +#[tokio::test] +async fn test_wrong_transceiver_peer() { + let recipient = Keypair::new(); + let (mut ctx, _test_data) = setup(Mode::Locking).await; + + let msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + let (guardian_signatures, guardian_set_index, span) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(Pubkey::new_unique().to_bytes()), // not the expected transceiver + msg.clone(), + &mut ctx, + ) + .await; + + let err = receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index, + guardian_signatures, + ), + VaaBodyData { span }, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures).await; + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::InvalidTransceiverPeer.into()) + ) + ); +} + +#[tokio::test] +async fn test_wrong_manager_peer() { + let recipient = Keypair::new(); + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let mut msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + msg.source_ntt_manager = Pubkey::new_unique().to_bytes(); // not the expected source manager + + let (guardian_signatures, guardian_set_index, span) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg.clone(), + &mut ctx, + ) + .await; + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index, + guardian_signatures, + ), + VaaBodyData { span }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures).await; + + let err = redeem( + &good_ntt, + init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg.ntt_manager_payload.clone(), + ), + RedeemArgs {}, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::InvalidNttManagerPeer.into()) + ) + ); +} + +#[tokio::test] +async fn test_wrong_inbox_item() { + let recipient = Keypair::new(); + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let msg = make_transfer_message(&good_ntt, [0u8; 32], 1000, &recipient.pubkey()); + + let (guardian_signatures, guardian_set_index, span) = post_vaa_helper( + &good_ntt_transceiver, + OTHER_CHAIN.into(), + Address(OTHER_TRANSCEIVER), + msg.clone(), + &mut ctx, + ) + .await; + + receive_message_instruction_data( + &good_ntt, + &good_ntt_transceiver, + init_receive_message_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + OTHER_CHAIN, + [0u8; 32], + guardian_set_index, + guardian_signatures, + ), + VaaBodyData { span }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + close_signatures(&good_ntt_transceiver, &mut ctx, &guardian_signatures).await; + + // use 'ANOTHER_CHAIN' inbox item account here + let mut redeem_accs = init_redeem_accs( + &good_ntt, + &good_ntt_transceiver, + &mut ctx, + &test_data, + OTHER_CHAIN, + msg.ntt_manager_payload.clone(), + ); + redeem_accs.inbox_item = good_ntt.inbox_item(ANOTHER_CHAIN, msg.ntt_manager_payload.clone()); + + let err = redeem(&good_ntt, redeem_accs, RedeemArgs {}) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(ErrorCode::ConstraintSeeds.into()) + ) + ); +} diff --git a/solana/programs/ntt-transceiver/tests/transfer.rs b/solana/programs/ntt-transceiver/tests/transfer.rs new file mode 100644 index 000000000..ded9d5f57 --- /dev/null +++ b/solana/programs/ntt-transceiver/tests/transfer.rs @@ -0,0 +1,289 @@ +#![cfg(feature = "test-sbf")] +#![feature(type_changing_struct_update)] + +use anchor_lang::{prelude::Clock, AnchorDeserialize}; +use example_native_token_transfers::{ + bitmap::Bitmap, error::NTTError, queue::outbox::OutboxItem, transfer::Payload, +}; +use ntt_messages::{ + chain_id::ChainId, mode::Mode, ntt::NativeTokenTransfer, ntt_manager::NttManagerMessage, + transceiver::TransceiverMessage, transceivers::wormhole::WormholeTransceiver, + trimmed_amount::TrimmedAmount, +}; +use ntt_transceiver::wormhole::instructions::release_outbound::ReleaseOutboundArgs; +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, signature::Keypair, signer::Signer, + transaction::TransactionError, +}; +use test_utils::{ + common::{ + fixtures::{TestData, OTHER_MANAGER, OUTBOUND_LIMIT}, + query::GetAccountDataAnchor, + submit::Submittable, + }, + helpers::{assert_queued, get_message_data, init_transfer_accs_args, setup}, + sdk::{ + accounts::{good_ntt, NTTAccounts}, + instructions::transfer::{approve_token_authority, transfer}, + transceivers::{ + accounts::good_ntt_transceiver, + instructions::release_outbound::{release_outbound, ReleaseOutbound}, + }, + }, +}; +use wormhole_svm_definitions::{solana::Finality::Finalized, EncodeFinality}; + +#[tokio::test] +pub async fn test_transfer_locking() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + test_transfer(&mut ctx, &test_data, Mode::Locking).await; +} + +#[tokio::test] +pub async fn test_transfer_burning() { + let (mut ctx, test_data) = setup(Mode::Burning).await; + test_transfer(&mut ctx, &test_data, Mode::Burning).await; +} + +/// This tests the happy path of a transfer, with all the relevant account checks. +/// Written as a helper function so both modes can be tested. +async fn test_transfer(ctx: &mut ProgramTestContext, test_data: &TestData, mode: Mode) { + let outbox_item = Keypair::new(); + + let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); + + let (accs, args) = + init_transfer_accs_args(&good_ntt, ctx, test_data, outbox_item.pubkey(), 154, false); + + approve_token_authority( + &good_ntt, + &test_data.user_token_account, + &test_data.user.pubkey(), + &args, + ) + .submit_with_signers(&[&test_data.user], ctx) + .await + .unwrap(); + transfer(&good_ntt, accs, args, mode) + .submit_with_signers(&[&outbox_item], ctx) + .await + .unwrap(); + + let outbox_item_account: OutboxItem = ctx.get_account_data_anchor(outbox_item.pubkey()).await; + + assert_eq!( + outbox_item_account, + OutboxItem { + amount: TrimmedAmount { + amount: 1, + decimals: 7 + }, + sender: test_data.user.pubkey(), + recipient_chain: ChainId { id: 2 }, + recipient_ntt_manager: OTHER_MANAGER, + recipient_address: [1u8; 32], + release_timestamp: clock.unix_timestamp, + released: Bitmap::new(), + } + ); + + let ix = release_outbound( + &good_ntt, + &good_ntt_transceiver, + ReleaseOutbound { + payer: ctx.payer.pubkey(), + outbox_item: outbox_item.pubkey(), + }, + ReleaseOutboundArgs { + revert_on_delay: true, + }, + ); + + // simulate to fetch data before submitting ix + let msg = get_message_data(&good_ntt.wormhole(), &good_ntt_transceiver, ctx, ix.clone()).await; + ix.submit(ctx).await.unwrap(); + + // make sure the outbox item is now released, but nothing else has changed + let outbox_item_account_after: OutboxItem = + ctx.get_account_data_anchor(outbox_item.pubkey()).await; + assert_eq!( + OutboxItem { + released: Bitmap::from_value(1), + ..outbox_item_account + }, + outbox_item_account_after, + ); + + assert_eq!(msg.nonce, 0); // hardcoded + assert_eq!(msg.consistency_level, Finalized.encode()); // hardcoded + assert_eq!( + TransceiverMessage::>::deserialize( + &mut &msg.payload[..], + ) + .unwrap(), + TransceiverMessage::new( + example_native_token_transfers::ID.to_bytes(), + OTHER_MANAGER, + NttManagerMessage { + id: outbox_item.pubkey().to_bytes(), + sender: test_data.user.pubkey().to_bytes(), + payload: NativeTokenTransfer { + amount: TrimmedAmount { + amount: 1, + decimals: 7 + }, + source_token: test_data.mint.to_bytes(), + to: [1u8; 32], + to_chain: ChainId { id: 2 }, + additional_payload: Payload {} + } + }, + vec![] + ) + ); +} + +#[tokio::test] +async fn test_cant_release_queued() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let outbox_item = Keypair::new(); + + let too_much = OUTBOUND_LIMIT + 1000; + let (accs, args) = init_transfer_accs_args( + &good_ntt, + &mut ctx, + &test_data, + outbox_item.pubkey(), + too_much, + true, + ); + + approve_token_authority( + &good_ntt, + &test_data.user_token_account, + &test_data.user.pubkey(), + &args, + ) + .submit_with_signers(&[&test_data.user], &mut ctx) + .await + .unwrap(); + transfer(&good_ntt, accs, args, Mode::Locking) + .submit_with_signers(&[&outbox_item], &mut ctx) + .await + .unwrap(); + + assert_queued(&mut ctx, outbox_item.pubkey()).await; + + // check that 'revert_on_delay = true' returns correct error + let err = release_outbound( + &good_ntt, + &good_ntt_transceiver, + ReleaseOutbound { + payer: ctx.payer.pubkey(), + outbox_item: outbox_item.pubkey(), + }, + ReleaseOutboundArgs { + revert_on_delay: true, + }, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::CantReleaseYet.into()) + ) + ); + + // check that 'revert_on_delay = false' succeeds but does not release + release_outbound( + &good_ntt, + &good_ntt_transceiver, + ReleaseOutbound { + payer: ctx.payer.pubkey(), + outbox_item: outbox_item.pubkey(), + }, + ReleaseOutboundArgs { + revert_on_delay: false, + }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + assert_queued(&mut ctx, outbox_item.pubkey()).await; +} + +#[tokio::test] +async fn test_cant_release_twice() { + let (mut ctx, test_data) = setup(Mode::Locking).await; + + let outbox_item = Keypair::new(); + + let (accs, args) = init_transfer_accs_args( + &good_ntt, + &mut ctx, + &test_data, + outbox_item.pubkey(), + 100, + false, + ); + + approve_token_authority( + &good_ntt, + &test_data.user_token_account, + &test_data.user.pubkey(), + &args, + ) + .submit_with_signers(&[&test_data.user], &mut ctx) + .await + .unwrap(); + transfer(&good_ntt, accs, args, Mode::Locking) + .submit_with_signers(&[&outbox_item], &mut ctx) + .await + .unwrap(); + + release_outbound( + &good_ntt, + &good_ntt_transceiver, + ReleaseOutbound { + payer: ctx.payer.pubkey(), + outbox_item: outbox_item.pubkey(), + }, + ReleaseOutboundArgs { + revert_on_delay: true, + }, + ) + .submit(&mut ctx) + .await + .unwrap(); + + // make sure we can't release again + let err = release_outbound( + &good_ntt, + &good_ntt_transceiver, + ReleaseOutbound { + payer: ctx.payer.pubkey(), + outbox_item: outbox_item.pubkey(), + }, + ReleaseOutboundArgs { + revert_on_delay: true, + }, + ) + .submit(&mut ctx) + .await + .unwrap_err(); + + assert_eq!( + err.unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(NTTError::MessageAlreadySent.into()) + ) + ); +} diff --git a/solana/tests/anchor.test.ts b/solana/tests/anchor/anchor.test.ts similarity index 99% rename from solana/tests/anchor.test.ts rename to solana/tests/anchor/anchor.test.ts index 30ba2c8d5..cc08dcd5d 100644 --- a/solana/tests/anchor.test.ts +++ b/solana/tests/anchor/anchor.test.ts @@ -25,8 +25,8 @@ import { SolanaWormholeCore, utils, } from "@wormhole-foundation/sdk-solana-core"; -import { IdlVersion, NTT, getTransceiverProgram } from "../ts/index.js"; -import { SolanaNtt } from "../ts/sdk/index.js"; +import { IdlVersion, NTT, getTransceiverProgram } from "../../ts/index.js"; +import { SolanaNtt } from "../../ts/sdk/index.js"; import { TestDummyTransferHook, TestHelper, @@ -39,7 +39,7 @@ import { /** * Test Config Constants */ -const SOLANA_ROOT_DIR = `${__dirname}/../`; +const SOLANA_ROOT_DIR = `${__dirname}/../../`; const VERSION: IdlVersion = "3.0.0"; const TOKEN_PROGRAM = spl.TOKEN_2022_PROGRAM_ID; const GUARDIAN_KEY = diff --git a/solana/tests/utils/helpers.ts b/solana/tests/anchor/utils/helpers.ts similarity index 98% rename from solana/tests/utils/helpers.ts rename to solana/tests/anchor/utils/helpers.ts index 3345cf637..3f70112c3 100644 --- a/solana/tests/utils/helpers.ts +++ b/solana/tests/anchor/utils/helpers.ts @@ -10,10 +10,10 @@ import { signSendWait as ssw, UniversalAddress, } from "@wormhole-foundation/sdk"; -import { DummyTransferHook } from "../../ts/idl/1_0_0/ts/dummy_transfer_hook.js"; -import { type WormholePostMessageShim } from "../../ts/idl/wormhole_shim/ts/wormhole_post_message_shim.js"; -import { IDL as WormholePostMessageShimIdl } from "../../ts/idl/wormhole_shim/ts/wormhole_post_message_shim.js"; -import { derivePda } from "../../ts/lib/utils.js"; +import { DummyTransferHook } from "../../../ts/idl/1_0_0/ts/dummy_transfer_hook.js"; +import { type WormholePostMessageShim } from "../../../ts/idl/wormhole_shim/ts/wormhole_post_message_shim.js"; +import { IDL as WormholePostMessageShimIdl } from "../../../ts/idl/wormhole_shim/ts/wormhole_post_message_shim.js"; +import { derivePda } from "../../../ts/lib/utils.js"; import { TxHash } from "@wormhole-foundation/sdk-definitions"; export interface ErrorConstructor { diff --git a/solana/tests/cargo/Cargo.toml b/solana/tests/cargo/Cargo.toml new file mode 100644 index 000000000..298e90a2b --- /dev/null +++ b/solana/tests/cargo/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "test-utils" +version = "3.0.0" +description = "Test utilities" +edition = "2021" + +[features] +# cargo-test-sbf will pass this along +test-sbf = [] +# enable to use standalone shim transceiver +shim = ["ntt-transceiver"] +# networks +mainnet = [ "wormhole-anchor-sdk/mainnet", "example-native-token-transfers/mainnet", "ntt-transceiver/mainnet" ] +bridge-address-from-env = [ "wormhole-anchor-sdk/bridge-address-from-env", "wormhole-svm-definitions/from-env", "example-native-token-transfers/bridge-address-from-env", "ntt-transceiver/bridge-address-from-env" ] +solana-devnet = [ "wormhole-anchor-sdk/solana-devnet", "wormhole-svm-definitions/testnet", "example-native-token-transfers/solana-devnet", "ntt-transceiver/solana-devnet" ] +tilt-devnet = [ "wormhole-anchor-sdk/tilt-devnet", "wormhole-svm-definitions/localnet", "example-native-token-transfers/tilt-devnet", "ntt-transceiver/tilt-devnet" ] +tilt-devnet2 = [ "tilt-devnet", "wormhole-svm-definitions/localnet", "example-native-token-transfers/tilt-devnet2", "ntt-transceiver/tilt-devnet2" ] + +[lints] +workspace = true + +[dependencies] +anchor-lang.workspace = true +anchor-spl.workspace = true +base64 = "0.21.7" +bincode = "1.3.3" +cfg-if.workspace = true +hex.workspace = true +libsecp256k1 = "=0.6.0" +serde = "1.0.196" +serde_json = "1.0.113" +serde_wormhole.workspace = true +sha3 = "0.10.4" +solana-banks-interface.workspace = true +solana-program.workspace = true +solana-program-runtime.workspace = true +solana-program-test.workspace = true +solana-sdk = "*" +spl-associated-token-account = { version = "3.0.0", features = ["no-entrypoint"] } +spl-token.workspace = true +spl-token-2022 = { workspace = true, features = ["no-entrypoint"] } +wormhole-io.workspace = true +wormhole-anchor-sdk.workspace = true +wormhole-sdk.workspace = true +wormhole-solana-utils.workspace = true + +example-native-token-transfers = { path = "../../programs/example-native-token-transfers", default-features = false } +ntt-messages = { path = "../../modules/ntt-messages", features = ["anchor", "hash"] } +ntt-transceiver = { path = "../../programs/ntt-transceiver", features = ["testing"], optional = true } +wormhole-governance = { path = "../../programs/wormhole-governance", features = ["no-entrypoint"] } +wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole", branch = "svm-shims-fr-env-addr" } +wormhole-svm-shim = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface" } diff --git a/solana/programs/example-native-token-transfers/tests/common/account_json_utils.rs b/solana/tests/cargo/src/common/account_json_utils.rs similarity index 99% rename from solana/programs/example-native-token-transfers/tests/common/account_json_utils.rs rename to solana/tests/cargo/src/common/account_json_utils.rs index ac0fac5aa..1e7624026 100644 --- a/solana/programs/example-native-token-transfers/tests/common/account_json_utils.rs +++ b/solana/tests/cargo/src/common/account_json_utils.rs @@ -1,9 +1,8 @@ -use std::{io::Error, str::FromStr}; - use anchor_lang::{prelude::Pubkey, AnchorDeserialize}; use base64::Engine; use serde::{Deserialize, Serialize}; use solana_program_test::ProgramTest; +use std::{io::Error, str::FromStr}; #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] diff --git a/solana/tests/cargo/src/common/fixtures.rs b/solana/tests/cargo/src/common/fixtures.rs new file mode 100644 index 000000000..5a8e911fe --- /dev/null +++ b/solana/tests/cargo/src/common/fixtures.rs @@ -0,0 +1,32 @@ +use anchor_lang::prelude::Pubkey; +use solana_sdk::signature::Keypair; + +use crate::sdk::accounts::Governance; + +// TODO: maybe make these configurable? I think it's fine like this: +// the mint amount is more than the limits, so we can test the rate limits +pub const MINT_AMOUNT: u64 = 100000; +pub const OUTBOUND_LIMIT: u64 = 10000; +pub const INBOUND_LIMIT: u64 = 50000; + +pub const OTHER_TRANSCEIVER: [u8; 32] = [7u8; 32]; +pub const ANOTHER_TRANSCEIVER: [u8; 32] = [8u8; 32]; +pub const OTHER_MANAGER: [u8; 32] = [9u8; 32]; +pub const ANOTHER_MANAGER: [u8; 32] = [5u8; 32]; + +pub const THIS_CHAIN: u16 = 1; +pub const OTHER_CHAIN: u16 = 2; +pub const ANOTHER_CHAIN: u16 = 3; +pub const UNREGISTERED_CHAIN: u16 = u16::MAX; + +pub struct TestData { + pub governance: Governance, + pub program_owner: Keypair, + pub mint_authority: Keypair, + pub mint: Pubkey, + pub bad_mint_authority: Keypair, + pub bad_mint: Pubkey, + pub user: Keypair, + pub user_token_account: Pubkey, + pub bad_user_token_account: Pubkey, +} diff --git a/solana/programs/example-native-token-transfers/tests/common/mod.rs b/solana/tests/cargo/src/common/mod.rs similarity index 74% rename from solana/programs/example-native-token-transfers/tests/common/mod.rs rename to solana/tests/cargo/src/common/mod.rs index ecf9020a8..37876be32 100644 --- a/solana/programs/example-native-token-transfers/tests/common/mod.rs +++ b/solana/tests/cargo/src/common/mod.rs @@ -1,6 +1,5 @@ #![allow(async_fn_in_trait)] pub mod account_json_utils; +pub mod fixtures; pub mod query; -pub mod setup; pub mod submit; -pub mod utils; diff --git a/solana/programs/example-native-token-transfers/tests/common/query.rs b/solana/tests/cargo/src/common/query.rs similarity index 100% rename from solana/programs/example-native-token-transfers/tests/common/query.rs rename to solana/tests/cargo/src/common/query.rs diff --git a/solana/tests/cargo/src/common/submit.rs b/solana/tests/cargo/src/common/submit.rs new file mode 100644 index 000000000..0cdb6b3bf --- /dev/null +++ b/solana/tests/cargo/src/common/submit.rs @@ -0,0 +1,132 @@ +use solana_banks_interface::BanksTransactionResultWithSimulation; +use solana_program_test::{BanksClientError, ProgramTestBanksClientExt, ProgramTestContext}; +use solana_sdk::{ + instruction::Instruction, signature::Keypair, signer::Signer, signers::Signers, + transaction::Transaction, +}; + +pub trait Submittable { + async fn submit(self, ctx: &mut ProgramTestContext) -> Result<(), BanksClientError> + where + Self: Sized, + { + let no_signers: &[&Keypair] = &[]; + self.submit_with_signers(no_signers, ctx).await + } + + async fn submit_with_signers( + self, + signers: &T, + ctx: &mut ProgramTestContext, + ) -> Result<(), BanksClientError>; + + async fn simulate( + self, + ctx: &mut ProgramTestContext, + ) -> Result + where + Self: Sized, + { + let no_signers: &[&Keypair] = &[]; + self.simulate_with_signers(no_signers, ctx).await + } + + async fn simulate_with_signers( + self, + signers: &T, + ctx: &mut ProgramTestContext, + ) -> Result; +} + +impl Submittable for Instruction { + async fn submit_with_signers( + self, + signers: &T, + ctx: &mut ProgramTestContext, + ) -> Result<(), BanksClientError> { + let blockhash = ctx.banks_client.get_latest_blockhash().await.unwrap(); + + let mut transaction = Transaction::new_with_payer(&[self], Some(&ctx.payer.pubkey())); + transaction.partial_sign(&[&ctx.payer], blockhash); + transaction.partial_sign(signers, blockhash); + + // force a new blockhash in case the transaction status is cached + // this can occur when the same instruction has been executed recently + if ctx + .banks_client + .get_transaction_status(transaction.signatures[0]) + .await + .unwrap() + .is_some() + { + let blockhash = ctx + .banks_client + .get_new_latest_blockhash(&blockhash) + .await + .unwrap(); + transaction.partial_sign(&[&ctx.payer], blockhash); + transaction.partial_sign(signers, blockhash); + } + + ctx.banks_client.process_transaction(transaction).await + } + + async fn simulate_with_signers( + self, + signers: &T, + ctx: &mut ProgramTestContext, + ) -> Result { + let blockhash = ctx.banks_client.get_latest_blockhash().await.unwrap(); + + let mut transaction = Transaction::new_with_payer(&[self], Some(&ctx.payer.pubkey())); + transaction.partial_sign(&[&ctx.payer], blockhash); + transaction.partial_sign(signers, blockhash); + + ctx.banks_client.simulate_transaction(transaction).await + } +} + +impl Submittable for Transaction { + async fn submit_with_signers( + mut self, + signers: &T, + ctx: &mut ProgramTestContext, + ) -> Result<(), BanksClientError> { + let blockhash = ctx.banks_client.get_latest_blockhash().await.unwrap(); + + self.partial_sign(&[&ctx.payer], blockhash); + self.partial_sign(signers, blockhash); + + // force a new blockhash in case the transaction status is cached + // this can occur when the same transaction has been executed recently + if ctx + .banks_client + .get_transaction_status(self.signatures[0]) + .await + .unwrap() + .is_some() + { + let blockhash = ctx + .banks_client + .get_new_latest_blockhash(&blockhash) + .await + .unwrap(); + self.partial_sign(&[&ctx.payer], blockhash); + self.partial_sign(signers, blockhash); + } + + ctx.banks_client.process_transaction(self).await + } + + async fn simulate_with_signers( + mut self, + signers: &T, + ctx: &mut ProgramTestContext, + ) -> Result { + let blockhash = ctx.banks_client.get_latest_blockhash().await.unwrap(); + + self.partial_sign(&[&ctx.payer], blockhash); + self.partial_sign(signers, blockhash); + ctx.banks_client.simulate_transaction(self).await + } +} diff --git a/solana/tests/cargo/src/helpers/admin.rs b/solana/tests/cargo/src/helpers/admin.rs new file mode 100644 index 000000000..e68e01338 --- /dev/null +++ b/solana/tests/cargo/src/helpers/admin.rs @@ -0,0 +1,28 @@ +use anchor_lang::prelude::Pubkey; +use example_native_token_transfers::{ + config::Config, registered_transceiver::RegisteredTransceiver, +}; +use solana_program_test::ProgramTestContext; + +use crate::{common::query::GetAccountDataAnchor, sdk::accounts::NTT}; + +pub async fn assert_threshold(ntt: &NTT, ctx: &mut ProgramTestContext, expected_threshold: u8) { + let config_account: Config = ctx.get_account_data_anchor(ntt.config()).await; + assert_eq!(config_account.threshold, expected_threshold); +} + +pub async fn assert_transceiver_id( + ntt: &NTT, + ctx: &mut ProgramTestContext, + transceiver: &Pubkey, + expected_id: u8, +) { + let registered_transceiver_account: RegisteredTransceiver = ctx + .get_account_data_anchor(ntt.registered_transceiver(transceiver)) + .await; + assert_eq!( + registered_transceiver_account.transceiver_address, + *transceiver + ); + assert_eq!(registered_transceiver_account.id, expected_id); +} diff --git a/solana/tests/cargo/src/helpers/mod.rs b/solana/tests/cargo/src/helpers/mod.rs new file mode 100644 index 000000000..583e20ca1 --- /dev/null +++ b/solana/tests/cargo/src/helpers/mod.rs @@ -0,0 +1,21 @@ +mod admin; +#[cfg(feature = "shim")] +mod post_message_shim; +mod post_vaa; +mod queue; +mod rate_limit; +mod receive_message; +mod redeem; +mod setup; +mod transfer; + +pub use admin::*; +#[cfg(feature = "shim")] +pub use post_message_shim::*; +pub use post_vaa::*; +pub use queue::*; +pub use rate_limit::*; +pub use receive_message::*; +pub use redeem::*; +pub use setup::*; +pub use transfer::*; diff --git a/solana/tests/cargo/src/helpers/post_message_shim.rs b/solana/tests/cargo/src/helpers/post_message_shim.rs new file mode 100644 index 000000000..3bc23c3ae --- /dev/null +++ b/solana/tests/cargo/src/helpers/post_message_shim.rs @@ -0,0 +1,70 @@ +use solana_program_test::ProgramTestContext; +use solana_sdk::instruction::Instruction; + +use crate::{ + common::submit::Submittable, + sdk::{accounts::Wormhole, transceivers::accounts::NTTTransceiver}, +}; + +pub struct PostMessageShimInstructionData { + pub nonce: u32, + pub consistency_level: u8, + pub payload: Vec, +} + +// TODO: Figure out how to get CPI event that can be parsed to re-create the VAA message. +// `inner_instructions` is always `None` even though CPIs happen. This limits the +// testing that can be done as we can no longer parse the CPI event from it. +pub async fn get_message_data( + wh: &Wormhole, + ntt_transceiver: &NTTTransceiver, + ctx: &mut ProgramTestContext, + ix: Instruction, +) -> PostMessageShimInstructionData { + // simulate ix + let out = ix.simulate(ctx).await.unwrap(); + assert!(out.result.unwrap().is_ok()); + + let details = out.simulation_details.unwrap(); + + // verify logs + let logs = details.logs; + let is_core_bridge_cpi_log = + |line: &String| line.contains(format!("Program {} invoke [3]", wh.program).as_str()); + assert_eq!( + logs.iter() + .filter(|line| { line.contains("Program log: Sequence: 0") }) + .count(), + 1 + ); + let core_bridge_log_index = logs.iter().position(is_core_bridge_cpi_log).unwrap(); + assert_eq!( + logs.iter() + .skip(core_bridge_log_index) + .filter(|line| { + line.contains( + format!( + "Program {} invoke [3]", + ntt_transceiver.post_message_shim().program + ) + .as_str(), + ) + }) + .count(), + 1 + ); + + // parse return data + let ix_data = details.return_data.unwrap().data; + // 8-byte instruction discriminator + let nonce = u32::from_le_bytes(ix_data[8..12].try_into().unwrap()); + let consistency_level: u8 = ix_data[12]; + // 4-byte Vec length + let payload = ix_data[17..].to_vec(); + + PostMessageShimInstructionData { + nonce, + consistency_level, + payload, + } +} diff --git a/solana/tests/cargo/src/helpers/post_vaa.rs b/solana/tests/cargo/src/helpers/post_vaa.rs new file mode 100644 index 000000000..9be1e09c9 --- /dev/null +++ b/solana/tests/cargo/src/helpers/post_vaa.rs @@ -0,0 +1,94 @@ +use anchor_lang::AnchorSerialize; +use solana_program::pubkey::Pubkey; +use solana_program_test::ProgramTestContext; +use std::sync::atomic::AtomicU64; +use wormhole_sdk::{Address, Chain, Vaa}; + +cfg_if! { + if #[cfg(feature = "shim")] { + use crate::sdk::{transceivers::accounts::NTTTransceiver, + instructions::post_vaa::{ + get_guardian_signature, post_signatures, GUARDIAN_INDEX, GUARDIAN_SET_INDEX, + } + }; + use solana_sdk::{signature::Keypair, signer::Signer}; + + pub async fn post_vaa_helper( + ntt_transceiver: &NTTTransceiver, + emitter_chain: Chain, + emitter_address: Address, + msg: A, + ctx: &mut ProgramTestContext, + ) -> (Pubkey, u32, Vec) { + static I: AtomicU64 = AtomicU64::new(0); + + let sequence = I.fetch_add(1, std::sync::atomic::Ordering::Acquire); + + let mut vaa = Vaa { + version: 1, + guardian_set_index: GUARDIAN_SET_INDEX, + signatures: vec![], + timestamp: 123232, + nonce: 0, + emitter_chain, + emitter_address, + sequence, + consistency_level: 0, + payload: msg, + }; + vaa.signatures + .push(get_guardian_signature(vaa.clone(), GUARDIAN_INDEX)); + + let guardian_signatures = Keypair::new(); + post_signatures(ntt_transceiver, ctx, &guardian_signatures, &vaa).await; + + ( + guardian_signatures.pubkey(), + GUARDIAN_SET_INDEX, + vaa_body(&vaa), + ) + } + + fn vaa_body(vaa: &Vaa) -> Vec { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&vaa.timestamp.to_be_bytes()); + bytes.extend_from_slice(&vaa.nonce.to_be_bytes()); + bytes.extend_from_slice(&u16::from(vaa.emitter_chain).to_be_bytes()); + bytes.extend_from_slice(&vaa.emitter_address.0); + bytes.extend_from_slice(&vaa.sequence.to_be_bytes()); + bytes.push(vaa.consistency_level); + let payload_bytes = vaa.payload.try_to_vec().unwrap(); + bytes.extend_from_slice(&payload_bytes); + bytes + } + } else { + use crate::sdk::{instructions::post_vaa::post_vaa, accounts::NTT}; + + pub async fn post_vaa_helper( + ntt: &NTT, + emitter_chain: Chain, + emitter_address: Address, + msg: A, + ctx: &mut ProgramTestContext, + ) -> Pubkey { + static I: AtomicU64 = AtomicU64::new(0); + + let sequence = I.fetch_add(1, std::sync::atomic::Ordering::Acquire); + + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![], + timestamp: 123232, + nonce: 0, + emitter_chain, + emitter_address, + sequence, + consistency_level: 0, + payload: msg, + }; + + post_vaa(&ntt.wormhole(), ctx, vaa).await + } + } +} diff --git a/solana/tests/cargo/src/helpers/queue.rs b/solana/tests/cargo/src/helpers/queue.rs new file mode 100644 index 000000000..2408109ec --- /dev/null +++ b/solana/tests/cargo/src/helpers/queue.rs @@ -0,0 +1,14 @@ +use anchor_lang::prelude::{Clock, Pubkey}; +use example_native_token_transfers::queue::outbox::OutboxItem; +use solana_program_test::ProgramTestContext; + +use crate::common::query::GetAccountDataAnchor; + +pub async fn assert_queued(ctx: &mut ProgramTestContext, outbox_item: Pubkey) { + let outbox_item_account: OutboxItem = ctx.get_account_data_anchor(outbox_item).await; + + let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); + + assert!(!outbox_item_account.released.get(0).unwrap()); + assert!(outbox_item_account.release_timestamp > clock.unix_timestamp); +} diff --git a/solana/tests/cargo/src/helpers/rate_limit.rs b/solana/tests/cargo/src/helpers/rate_limit.rs new file mode 100644 index 000000000..94061b712 --- /dev/null +++ b/solana/tests/cargo/src/helpers/rate_limit.rs @@ -0,0 +1,24 @@ +use anchor_lang::prelude::Clock; +use example_native_token_transfers::queue::{inbox::InboxRateLimit, outbox::OutboxRateLimit}; +use solana_program_test::ProgramTestContext; + +use crate::{ + common::{fixtures::OTHER_CHAIN, query::GetAccountDataAnchor}, + sdk::accounts::NTT, +}; + +pub async fn outbound_capacity(ntt: &NTT, ctx: &mut ProgramTestContext) -> u64 { + let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); + let rate_limit: OutboxRateLimit = ctx.get_account_data_anchor(ntt.outbox_rate_limit()).await; + + rate_limit.rate_limit.capacity_at(clock.unix_timestamp) +} + +pub async fn inbound_capacity(ntt: &NTT, ctx: &mut ProgramTestContext) -> u64 { + let clock: Clock = ctx.banks_client.get_sysvar().await.unwrap(); + let rate_limit: InboxRateLimit = ctx + .get_account_data_anchor(ntt.inbox_rate_limit(OTHER_CHAIN)) + .await; + + rate_limit.rate_limit.capacity_at(clock.unix_timestamp) +} diff --git a/solana/tests/cargo/src/helpers/receive_message.rs b/solana/tests/cargo/src/helpers/receive_message.rs new file mode 100644 index 000000000..ac78bbc5a --- /dev/null +++ b/solana/tests/cargo/src/helpers/receive_message.rs @@ -0,0 +1,50 @@ +use anchor_lang::prelude::Pubkey; +use solana_program_test::ProgramTestContext; +use solana_sdk::signer::Signer; + +use crate::sdk::transceivers::{ + accounts::NTTTransceiver, instructions::receive_message::ReceiveMessage, +}; + +cfg_if! { + if #[cfg(feature = "shim")] { + use crate::sdk::accounts::NTT; + + pub fn init_receive_message_accs( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + ctx: &mut ProgramTestContext, + chain_id: u16, + id: [u8; 32], + guardian_set_index: u32, + guardian_signatures: Pubkey, + ) -> ReceiveMessage { + ReceiveMessage { + payer: ctx.payer.pubkey(), + peer: ntt_transceiver.transceiver_peer(chain_id), + chain_id, + id, + guardian_set: ntt + .wormhole() + .guardian_set_with_bump(guardian_set_index), + guardian_signatures, + } + } + } else { + pub fn init_receive_message_accs( + ntt_transceiver: &NTTTransceiver, + ctx: &mut ProgramTestContext, + vaa: Pubkey, + chain_id: u16, + id: [u8; 32], + ) -> ReceiveMessage { + ReceiveMessage { + payer: ctx.payer.pubkey(), + peer: ntt_transceiver.transceiver_peer(chain_id), + vaa, + chain_id, + id, + } + } + } +} diff --git a/solana/tests/cargo/src/helpers/redeem.rs b/solana/tests/cargo/src/helpers/redeem.rs new file mode 100644 index 000000000..fadf1f81c --- /dev/null +++ b/solana/tests/cargo/src/helpers/redeem.rs @@ -0,0 +1,28 @@ +use example_native_token_transfers::transfer::Payload; +use ntt_messages::{ntt::NativeTokenTransfer, ntt_manager::NttManagerMessage}; +use solana_program_test::ProgramTestContext; +use solana_sdk::signer::Signer; + +use crate::{ + common::fixtures::TestData, + sdk::{accounts::NTT, instructions::redeem::Redeem, transceivers::accounts::NTTTransceiver}, +}; + +pub fn init_redeem_accs( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + ctx: &mut ProgramTestContext, + test_data: &TestData, + chain_id: u16, + ntt_manager_message: NttManagerMessage>, +) -> Redeem { + Redeem { + payer: ctx.payer.pubkey(), + peer: ntt.peer(chain_id), + transceiver: ntt_transceiver.program(), + transceiver_message: ntt_transceiver.transceiver_message(chain_id, ntt_manager_message.id), + inbox_item: ntt.inbox_item(chain_id, ntt_manager_message), + inbox_rate_limit: ntt.inbox_rate_limit(chain_id), + mint: test_data.mint, + } +} diff --git a/solana/programs/example-native-token-transfers/tests/common/setup.rs b/solana/tests/cargo/src/helpers/setup.rs similarity index 87% rename from solana/programs/example-native-token-transfers/tests/common/setup.rs rename to solana/tests/cargo/src/helpers/setup.rs index f32833b35..e3ccf799e 100644 --- a/solana/programs/example-native-token-transfers/tests/common/setup.rs +++ b/solana/tests/cargo/src/helpers/setup.rs @@ -1,64 +1,44 @@ -use std::path::PathBuf; - use anchor_lang::prelude::{Error, Id, Pubkey}; -use anchor_spl::token::{Mint, Token}; -use example_native_token_transfers::{ - instructions::{InitializeArgs, SetPeerArgs}, - transceivers::wormhole::SetTransceiverPeerArgs, +use anchor_spl::{ + associated_token::get_associated_token_address_with_program_id, + token::{Mint, Token}, }; +use example_native_token_transfers::instructions::{InitializeArgs, SetPeerArgs}; use ntt_messages::{chain_id::ChainId, mode::Mode}; use solana_program::{bpf_loader_upgradeable::UpgradeableLoaderState, rent::Rent}; use solana_program_runtime::log_collector::log::{trace, warn}; -use solana_program_test::{find_file, read_file, ProgramTest, ProgramTestContext}; +use solana_program_test::{read_file, ProgramTest, ProgramTestContext}; use solana_sdk::{ account::Account, signature::Keypair, signer::Signer, system_instruction, transaction::Transaction, }; -use spl_associated_token_account::get_associated_token_address_with_program_id; +use std::path::PathBuf; use wormhole_anchor_sdk::wormhole::{BridgeData, FeeCollector}; -use crate::sdk::{ - accounts::{good_ntt, Governance, NTTAccounts}, - instructions::{ - admin::{register_transceiver, set_peer, RegisterTransceiver, SetPeer}, - initialize::{initialize_with_token_program_id, Initialize}, +use crate::{ + common::{ + account_json_utils::{add_account_unchecked, AccountLoadable}, + fixtures::{ + TestData, ANOTHER_CHAIN, ANOTHER_MANAGER, INBOUND_LIMIT, MINT_AMOUNT, OTHER_CHAIN, + OTHER_MANAGER, OTHER_TRANSCEIVER, OUTBOUND_LIMIT, THIS_CHAIN, + }, + submit::Submittable, + }, + sdk::{ + accounts::{good_ntt, Governance, NTTAccounts}, + instructions::{ + admin::{register_transceiver, set_peer, RegisterTransceiver, SetPeer}, + initialize::{initialize_with_token_program_id, Initialize}, + }, + transceivers::{ + accounts::{good_ntt_transceiver, NTTTransceiverAccounts}, + instructions::admin::{ + set_transceiver_peer, SetTransceiverPeer, SetTransceiverPeerArgs, + }, + }, }, - transceivers::wormhole::instructions::admin::{set_transceiver_peer, SetTransceiverPeer}, -}; - -use super::{ - account_json_utils::{add_account_unchecked, AccountLoadable}, - submit::Submittable, }; -// TODO: maybe make these configurable? I think it's fine like this: -// the mint amount is more than the limits, so we can test the rate limits -pub const MINT_AMOUNT: u64 = 100000; -pub const OUTBOUND_LIMIT: u64 = 10000; -pub const INBOUND_LIMIT: u64 = 50000; - -pub const OTHER_TRANSCEIVER: [u8; 32] = [7u8; 32]; -pub const ANOTHER_TRANSCEIVER: [u8; 32] = [8u8; 32]; -pub const OTHER_MANAGER: [u8; 32] = [9u8; 32]; -pub const ANOTHER_MANAGER: [u8; 32] = [5u8; 32]; - -pub const THIS_CHAIN: u16 = 1; -pub const OTHER_CHAIN: u16 = 2; -pub const ANOTHER_CHAIN: u16 = 3; -pub const UNREGISTERED_CHAIN: u16 = u16::MAX; - -pub struct TestData { - pub governance: Governance, - pub program_owner: Keypair, - pub mint_authority: Keypair, - pub mint: Pubkey, - pub bad_mint_authority: Keypair, - pub bad_mint: Pubkey, - pub user: Keypair, - pub user_token_account: Pubkey, - pub bad_user_token_account: Pubkey, -} - pub async fn setup_with_extra_accounts( mode: Mode, accounts: &[(Pubkey, Account)], @@ -132,6 +112,33 @@ pub async fn setup_programs(program_owner: Pubkey) -> Result None, ); + cfg_if! { + if #[cfg(feature = "shim")] { + use wormhole_svm_definitions::solana::{POST_MESSAGE_SHIM_PROGRAM_ID, VERIFY_VAA_SHIM_PROGRAM_ID}; + + add_program_upgradeable( + &mut program_test, + "ntt_transceiver", + ntt_transceiver::ID, + Some(program_owner), + ); + + add_program_upgradeable( + &mut program_test, + "mainnet_wormhole_post_message_shim", + POST_MESSAGE_SHIM_PROGRAM_ID, + None, + ); + + add_program_upgradeable( + &mut program_test, + "mainnet_wormhole_verify_vaa_shim", + VERIFY_VAA_SHIM_PROGRAM_ID, + None, + ); + } + } + BridgeData::add_account( &mut program_test, "../../tests/accounts/mainnet/core_bridge_config.json", @@ -204,7 +211,7 @@ pub async fn setup_ntt_with_token_program_id( RegisterTransceiver { payer: ctx.payer.pubkey(), owner: test_data.program_owner.pubkey(), - transceiver: example_native_token_transfers::ID, // standalone ntt_manager&transceiver + transceiver: good_ntt_transceiver.program(), // standalone shim transceiver }, ) .submit_with_signers(&[&test_data.program_owner], ctx) @@ -213,6 +220,7 @@ pub async fn setup_ntt_with_token_program_id( set_transceiver_peer( &good_ntt, + &good_ntt_transceiver, SetTransceiverPeer { payer: ctx.payer.pubkey(), owner: test_data.program_owner.pubkey(), @@ -225,7 +233,6 @@ pub async fn setup_ntt_with_token_program_id( .submit_with_signers(&[&test_data.program_owner], ctx) .await .unwrap(); - set_peer( &good_ntt, SetPeer { @@ -599,7 +606,6 @@ pub fn add_program_upgradeable( }, ); }; - let warn_invalid_program_name = || { let valid_program_names = default_shared_object_dirs() .iter() @@ -646,6 +652,15 @@ pub fn add_program_upgradeable( // Invalid: `test-sbf` invocation with no matching SBF shared object. (true, None) => { warn_invalid_program_name(); + if true { + panic!( + "{:?} curr: {:?}, bpf {:?}; sbf {:?}", + default_shared_object_dirs(), + std::env::current_dir().unwrap(), + std::env::var("BPF_OUT_DIR"), + std::env::var("SBF_OUT_DIR"), + ); + } panic!("Program file data not available for {program_name} ({program_id})"); } @@ -656,6 +671,16 @@ pub fn add_program_upgradeable( } } +pub fn find_file(filename: &str) -> Option { + for dir in default_shared_object_dirs() { + let candidate = dir.join(filename); + if candidate.exists() { + return Some(candidate); + } + } + None +} + fn default_shared_object_dirs() -> Vec { let mut search_path = vec![]; if let Ok(bpf_out_dir) = std::env::var("BPF_OUT_DIR") { @@ -663,7 +688,7 @@ fn default_shared_object_dirs() -> Vec { } else if let Ok(bpf_out_dir) = std::env::var("SBF_OUT_DIR") { search_path.push(PathBuf::from(bpf_out_dir)); } - search_path.push(PathBuf::from("tests/fixtures")); + search_path.push(PathBuf::from("../../tests/fixtures")); if let Ok(dir) = std::env::current_dir() { search_path.push(dir); } diff --git a/solana/programs/example-native-token-transfers/tests/common/utils.rs b/solana/tests/cargo/src/helpers/transfer.rs similarity index 52% rename from solana/programs/example-native-token-transfers/tests/common/utils.rs rename to solana/tests/cargo/src/helpers/transfer.rs index 42f5ea399..9fce2085a 100644 --- a/solana/programs/example-native-token-transfers/tests/common/utils.rs +++ b/solana/tests/cargo/src/helpers/transfer.rs @@ -1,20 +1,48 @@ -use std::sync::atomic::AtomicU64; - -use anchor_lang::AnchorSerialize; +use anchor_lang::prelude::Pubkey; +use example_native_token_transfers::instructions::TransferArgs; use example_native_token_transfers::transfer::Payload; use ntt_messages::{ chain_id::ChainId, ntt::NativeTokenTransfer, ntt_manager::NttManagerMessage, transceiver::TransceiverMessage, transceivers::wormhole::WormholeTransceiver, trimmed_amount::TrimmedAmount, }; -use solana_program::pubkey::Pubkey; use solana_program_test::ProgramTestContext; -use wormhole_sdk::{Address, Chain, Vaa}; +use solana_sdk::signer::Signer; + +use crate::{ + common::fixtures::{TestData, OTHER_CHAIN, OTHER_MANAGER, THIS_CHAIN}, + sdk::{accounts::NTT, instructions::transfer::Transfer}, +}; -use crate::sdk::accounts::NTT; +/// Helper function for setting up transfer accounts and args. +/// It sets the accounts up properly, so for negative testing we just modify the +/// result. +pub fn init_transfer_accs_args( + ntt: &NTT, + ctx: &mut ProgramTestContext, + test_data: &TestData, + outbox_item: Pubkey, + amount: u64, + should_queue: bool, +) -> (Transfer, TransferArgs) { + let accs = Transfer { + payer: ctx.payer.pubkey(), + mint: test_data.mint, + from: test_data.user_token_account, + from_authority: test_data.user.pubkey(), + peer: ntt.peer(OTHER_CHAIN), + outbox_item, + }; + + let args = TransferArgs { + amount, + recipient_chain: ChainId { id: OTHER_CHAIN }, + recipient_address: [1u8; 32], + should_queue, + }; -use super::setup::{OTHER_MANAGER, THIS_CHAIN}; -use crate::sdk::instructions::post_vaa::post_vaa; + (accs, args) +} pub fn make_transfer_message( ntt: &NTT, @@ -44,30 +72,3 @@ pub fn make_transfer_message( vec![], ) } - -pub async fn post_vaa_helper( - ntt: &NTT, - emitter_chain: Chain, - emitter_address: Address, - msg: A, - ctx: &mut ProgramTestContext, -) -> Pubkey { - static I: AtomicU64 = AtomicU64::new(0); - - let sequence = I.fetch_add(1, std::sync::atomic::Ordering::Acquire); - - let vaa = Vaa { - version: 1, - guardian_set_index: 0, - signatures: vec![], - timestamp: 123232, - nonce: 0, - emitter_chain, - emitter_address, - sequence, - consistency_level: 0, - payload: msg, - }; - - post_vaa(&ntt.wormhole(), ctx, vaa).await -} diff --git a/solana/programs/example-native-token-transfers/tests/lib.rs b/solana/tests/cargo/src/lib.rs similarity index 57% rename from solana/programs/example-native-token-transfers/tests/lib.rs rename to solana/tests/cargo/src/lib.rs index 7a3c7c60b..87f45e634 100644 --- a/solana/programs/example-native-token-transfers/tests/lib.rs +++ b/solana/tests/cargo/src/lib.rs @@ -1,3 +1,7 @@ #![feature(type_changing_struct_update)] pub mod common; +pub mod helpers; pub mod sdk; + +#[macro_use] +extern crate cfg_if; diff --git a/solana/tests/cargo/src/sdk/accounts/governance.rs b/solana/tests/cargo/src/sdk/accounts/governance.rs new file mode 100644 index 000000000..908e1faad --- /dev/null +++ b/solana/tests/cargo/src/sdk/accounts/governance.rs @@ -0,0 +1,12 @@ +use anchor_lang::prelude::Pubkey; + +pub struct Governance { + pub program: Pubkey, +} + +impl Governance { + pub fn governance(&self) -> Pubkey { + let (gov, _) = Pubkey::find_program_address(&[b"governance"], &self.program); + gov + } +} diff --git a/solana/tests/cargo/src/sdk/accounts/mod.rs b/solana/tests/cargo/src/sdk/accounts/mod.rs new file mode 100644 index 000000000..b1f04fdca --- /dev/null +++ b/solana/tests/cargo/src/sdk/accounts/mod.rs @@ -0,0 +1,7 @@ +pub mod governance; +pub mod ntt; +pub mod wormhole; + +pub use governance::*; +pub use ntt::*; +pub use wormhole::*; diff --git a/solana/programs/example-native-token-transfers/tests/sdk/accounts.rs b/solana/tests/cargo/src/sdk/accounts/ntt.rs similarity index 63% rename from solana/programs/example-native-token-transfers/tests/sdk/accounts.rs rename to solana/tests/cargo/src/sdk/accounts/ntt.rs index 5b388304e..323a8652a 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/accounts.rs +++ b/solana/tests/cargo/src/sdk/accounts/ntt.rs @@ -12,60 +12,12 @@ use example_native_token_transfers::{ }; use ntt_messages::{ntt::NativeTokenTransfer, ntt_manager::NttManagerMessage}; use sha3::{Digest, Keccak256}; -use wormhole_anchor_sdk::wormhole; use wormhole_io::TypePrefixedPayload; use wormhole_solana_utils::cpi::bpf_loader_upgradeable; -pub struct Wormhole { - pub program: Pubkey, -} - -impl Wormhole { - pub fn bridge(&self) -> Pubkey { - let (bridge, _) = - Pubkey::find_program_address(&[wormhole::BridgeData::SEED_PREFIX], &self.program); - bridge - } - - pub fn fee_collector(&self) -> Pubkey { - let (fee_collector, _) = - Pubkey::find_program_address(&[wormhole::FeeCollector::SEED_PREFIX], &self.program); - fee_collector - } +use crate::sdk::transceivers::accounts::ntt_transceiver::NTTTransceiver; - pub fn sequence(&self, emitter: &Pubkey) -> Pubkey { - let (sequence, _) = Pubkey::find_program_address( - &[wormhole::SequenceTracker::SEED_PREFIX, emitter.as_ref()], - &self.program, - ); - sequence - } - - pub fn guardian_set(&self, guardian_set_index: u32) -> Pubkey { - let (guardian_set, _) = Pubkey::find_program_address( - &[b"GuardianSet", &guardian_set_index.to_be_bytes()], - &self.program, - ); - guardian_set - } - - pub fn posted_vaa(&self, vaa_hash: &[u8]) -> Pubkey { - let (posted_vaa, _) = - Pubkey::find_program_address(&[b"PostedVAA", vaa_hash], &self.program); - posted_vaa - } -} - -pub struct Governance { - pub program: Pubkey, -} - -impl Governance { - pub fn governance(&self) -> Pubkey { - let (gov, _) = Pubkey::find_program_address(&[b"governance"], &self.program); - gov - } -} +use super::wormhole::Wormhole; pub type NTT = dyn NTTAccounts; @@ -150,23 +102,6 @@ pub trait NTTAccounts { registered_transceiver } - fn emitter(&self) -> Pubkey { - let (emitter, _) = Pubkey::find_program_address(&[b"emitter".as_ref()], &self.program()); - emitter - } - - fn wormhole_message(&self, outbox_item: &Pubkey) -> Pubkey { - let (wormhole_message, _) = Pubkey::find_program_address( - &[b"message".as_ref(), outbox_item.as_ref()], - &self.program(), - ); - wormhole_message - } - - fn wormhole_sequence(&self) -> Pubkey { - self.wormhole().sequence(&self.emitter()) - } - fn peer(&self, chain: u16) -> Pubkey { let (peer, _) = Pubkey::find_program_address( &[b"peer".as_ref(), &chain.to_be_bytes()], @@ -175,24 +110,8 @@ pub trait NTTAccounts { peer } - fn transceiver_peer(&self, chain: u16) -> Pubkey { - let (peer, _) = Pubkey::find_program_address( - &[b"transceiver_peer".as_ref(), &chain.to_be_bytes()], - &self.program(), - ); - peer - } - - fn transceiver_message(&self, chain: u16, id: [u8; 32]) -> Pubkey { - let (transceiver_message, _) = Pubkey::find_program_address( - &[b"transceiver_message".as_ref(), &chain.to_be_bytes(), &id], - &self.program(), - ); - transceiver_message - } - fn custody(&self, mint: &Pubkey) -> Pubkey { - self.custody_with_token_program_id(mint, &spl_token::ID) + self.custody_with_token_program_id(mint, &anchor_spl::token::spl_token::ID) } fn custody_with_token_program_id(&self, mint: &Pubkey, token_program_id: &Pubkey) -> Pubkey { @@ -203,6 +122,10 @@ pub trait NTTAccounts { ) } + fn wormhole_sequence(&self, ntt_transceiver: &NTTTransceiver) -> Pubkey { + self.wormhole().sequence(&ntt_transceiver.emitter()) + } + fn program_data(&self) -> Pubkey { let (addr, _) = Pubkey::find_program_address(&[self.program().as_ref()], &bpf_loader_upgradeable::id()); diff --git a/solana/tests/cargo/src/sdk/accounts/wormhole.rs b/solana/tests/cargo/src/sdk/accounts/wormhole.rs new file mode 100644 index 000000000..b9980cdcc --- /dev/null +++ b/solana/tests/cargo/src/sdk/accounts/wormhole.rs @@ -0,0 +1,46 @@ +use anchor_lang::prelude::Pubkey; +use wormhole_anchor_sdk::wormhole; + +pub struct Wormhole { + pub program: Pubkey, +} + +impl Wormhole { + pub fn bridge(&self) -> Pubkey { + let (bridge, _) = + Pubkey::find_program_address(&[wormhole::BridgeData::SEED_PREFIX], &self.program); + bridge + } + + pub fn fee_collector(&self) -> Pubkey { + let (fee_collector, _) = + Pubkey::find_program_address(&[wormhole::FeeCollector::SEED_PREFIX], &self.program); + fee_collector + } + + pub fn sequence(&self, emitter: &Pubkey) -> Pubkey { + let (sequence, _) = Pubkey::find_program_address( + &[wormhole::SequenceTracker::SEED_PREFIX, emitter.as_ref()], + &self.program, + ); + sequence + } + + pub fn guardian_set_with_bump(&self, guardian_set_index: u32) -> (Pubkey, u8) { + let (guardian_set, guardian_set_bump) = Pubkey::find_program_address( + &[b"GuardianSet", &guardian_set_index.to_be_bytes()], + &self.program, + ); + (guardian_set, guardian_set_bump) + } + + pub fn guardian_set(&self, guardian_set_index: u32) -> Pubkey { + self.guardian_set_with_bump(guardian_set_index).0 + } + + pub fn posted_vaa(&self, vaa_hash: &[u8]) -> Pubkey { + let (posted_vaa, _) = + Pubkey::find_program_address(&[b"PostedVAA", vaa_hash], &self.program); + posted_vaa + } +} diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/admin.rs b/solana/tests/cargo/src/sdk/instructions/admin.rs similarity index 100% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/admin.rs rename to solana/tests/cargo/src/sdk/instructions/admin.rs diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/initialize.rs b/solana/tests/cargo/src/sdk/instructions/initialize.rs similarity index 96% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/initialize.rs rename to solana/tests/cargo/src/sdk/instructions/initialize.rs index a771690d0..28d446471 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/instructions/initialize.rs +++ b/solana/tests/cargo/src/sdk/instructions/initialize.rs @@ -44,7 +44,7 @@ pub fn initialize_with_token_program_id( // fetch account Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/mod.rs b/solana/tests/cargo/src/sdk/instructions/mod.rs similarity index 100% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/mod.rs rename to solana/tests/cargo/src/sdk/instructions/mod.rs diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/post_vaa.rs b/solana/tests/cargo/src/sdk/instructions/post_vaa.rs similarity index 58% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/post_vaa.rs rename to solana/tests/cargo/src/sdk/instructions/post_vaa.rs index fb807834e..5f6e837ed 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/instructions/post_vaa.rs +++ b/solana/tests/cargo/src/sdk/instructions/post_vaa.rs @@ -5,6 +5,7 @@ //! also, this whole module is a mess. this is way harder than it needs to be use anchor_lang::prelude::*; +use libsecp256k1::{sign, Message}; use serde_wormhole::RawMessage; use solana_program::{instruction::AccountMeta, sysvar}; use solana_program_test::ProgramTestContext; @@ -12,12 +13,13 @@ use solana_sdk::{ instruction::Instruction, secp256k1_instruction::new_secp256k1_instruction, signature::Keypair, signer::Signer, transaction::Transaction, }; +use wormhole_sdk::vaa::*; use crate::{common::submit::Submittable, sdk::accounts::Wormhole}; -use wormhole_sdk::vaa::*; - // NOTE: assuming guardian set index 0 which has a single guardian (who is always the signer) +pub const GUARDIAN_SET_INDEX: u32 = 0; +pub const GUARDIAN_INDEX: u8 = 0; pub const MAX_LEN_GUARDIAN_KEYS: usize = 19; @@ -55,7 +57,7 @@ pub async fn post_vaa( posted_vaa } -pub fn verify_signatures( +fn verify_signatures( wh: &Wormhole, accounts: VerifySignatures, vaa: Vaa, @@ -134,3 +136,89 @@ pub fn verify_signatures( posted_vaa, ) } + +pub fn get_guardian_signature(vaa: Vaa, index: u8) -> Signature { + let priv_key: libsecp256k1::SecretKey = libsecp256k1::SecretKey::parse( + &hex::decode(GUARDIAN_SECRET_KEY) + .unwrap() + .try_into() + .unwrap(), + ) + .unwrap(); + + let (_, body): (Header, Body) = vaa.into(); + let serialized_body: Body> = Body { + payload: Box::::from(body.payload.try_to_vec().unwrap()), + ..body + }; + let digest = serialized_body.digest().unwrap().secp256k_hash; + let msg = Message::parse(&digest); + + let (sig, recovery_id) = sign(&msg, &priv_key); + + let mut signature = [0u8; 65]; + signature[..64].copy_from_slice(&sig.serialize()); + signature[64] = recovery_id.serialize(); + + Signature { index, signature } +} + +cfg_if! { + if #[cfg(feature = "shim")] { + use wormhole_svm_shim::verify_vaa::{ + CloseSignatures, CloseSignaturesAccounts, PostSignatures, PostSignaturesAccounts, + PostSignaturesData, + }; + use crate::sdk::transceivers::accounts::NTTTransceiver; + + pub async fn post_signatures( + ntt_transceiver: &NTTTransceiver, + ctx: &mut ProgramTestContext, + guardian_signatures: &Keypair, + vaa: &Vaa, + ) { + PostSignatures { + program_id: &ntt_transceiver.verify_vaa_shim_shim(), + accounts: PostSignaturesAccounts { + payer: &ctx.payer.pubkey(), + guardian_signatures: &guardian_signatures.pubkey(), + }, + data: PostSignaturesData::new( + vaa.guardian_set_index, + u8::try_from(vaa.signatures.len()).unwrap(), + &vaa.signatures + .iter() + .map(|sig| { + let mut buf = [0u8; 66]; + buf[0] = sig.index; + buf[1..].copy_from_slice(&sig.signature); + buf + }) + .collect::>(), + ), + } + .instruction() + .submit_with_signers(&[guardian_signatures], ctx) + .await + .unwrap(); + } + + pub async fn close_signatures( + ntt_transceiver: &NTTTransceiver, + ctx: &mut ProgramTestContext, + guardian_signatures: &Pubkey, + ) { + CloseSignatures { + program_id: &ntt_transceiver.verify_vaa_shim_shim(), + accounts: CloseSignaturesAccounts { + guardian_signatures, + refund_recipient: &ctx.payer.pubkey(), + }, + } + .instruction() + .submit(ctx) + .await + .unwrap(); + } + } +} diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/redeem.rs b/solana/tests/cargo/src/sdk/instructions/redeem.rs similarity index 66% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/redeem.rs rename to solana/tests/cargo/src/sdk/instructions/redeem.rs index a25867942..30b61b57b 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/instructions/redeem.rs +++ b/solana/tests/cargo/src/sdk/instructions/redeem.rs @@ -15,24 +15,24 @@ pub struct Redeem { pub inbox_rate_limit: Pubkey, } -pub fn redeem(ntt: &NTT, accs: Redeem, args: RedeemArgs) -> Instruction { +pub fn redeem(ntt: &NTT, accounts: Redeem, args: RedeemArgs) -> Instruction { let data = example_native_token_transfers::instruction::Redeem { args }; let accounts = example_native_token_transfers::accounts::Redeem { - payer: accs.payer, + payer: accounts.payer, config: ntt.config(), - peer: accs.peer, - transceiver_message: accs.transceiver_message, - transceiver: ntt.registered_transceiver(&accs.transceiver), - mint: accs.mint, - inbox_item: accs.inbox_item, - inbox_rate_limit: accs.inbox_rate_limit, + peer: accounts.peer, + transceiver_message: accounts.transceiver_message, + transceiver: ntt.registered_transceiver(&accounts.transceiver), + mint: accounts.mint, + inbox_item: accounts.inbox_item, + inbox_rate_limit: accounts.inbox_rate_limit, outbox_rate_limit: ntt.outbox_rate_limit(), system_program: System::id(), }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/release_inbound.rs b/solana/tests/cargo/src/sdk/instructions/release_inbound.rs similarity index 75% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/release_inbound.rs rename to solana/tests/cargo/src/sdk/instructions/release_inbound.rs index 6c8be1c51..c25305818 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/instructions/release_inbound.rs +++ b/solana/tests/cargo/src/sdk/instructions/release_inbound.rs @@ -14,26 +14,26 @@ pub struct ReleaseInbound { pub fn release_inbound_unlock( ntt: &NTT, - release_inbound: ReleaseInbound, + accounts: ReleaseInbound, args: ReleaseInboundArgs, ) -> Instruction { let data = example_native_token_transfers::instruction::ReleaseInboundUnlock { args }; let accounts = example_native_token_transfers::accounts::ReleaseInboundUnlock { common: example_native_token_transfers::accounts::ReleaseInbound { - payer: release_inbound.payer, + payer: accounts.payer, config: NotPausedConfig { config: ntt.config(), }, - inbox_item: release_inbound.inbox_item, - recipient: release_inbound.recipient, + inbox_item: accounts.inbox_item, + recipient: accounts.recipient, token_authority: ntt.token_authority(), - mint: release_inbound.mint, + mint: accounts.mint, token_program: Token::id(), - custody: ntt.custody(&release_inbound.mint), + custody: ntt.custody(&accounts.mint), }, }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/transfer.rs b/solana/tests/cargo/src/sdk/instructions/transfer.rs similarity index 75% rename from solana/programs/example-native-token-transfers/tests/sdk/instructions/transfer.rs rename to solana/tests/cargo/src/sdk/instructions/transfer.rs index 531527d56..f13ab5049 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/instructions/transfer.rs +++ b/solana/tests/cargo/src/sdk/instructions/transfer.rs @@ -16,8 +16,8 @@ pub struct Transfer { pub outbox_item: Pubkey, } -pub fn transfer(ntt: &NTT, transfer: Transfer, args: TransferArgs, mode: Mode) -> Instruction { - transfer_with_token_program_id(ntt, transfer, args, mode, &Token::id()) +pub fn transfer(ntt: &NTT, accounts: Transfer, args: TransferArgs, mode: Mode) -> Instruction { + transfer_with_token_program_id(ntt, accounts, args, mode, &Token::id()) } pub fn transfer_with_token_program_id( @@ -33,57 +33,57 @@ pub fn transfer_with_token_program_id( } } -pub fn transfer_burn(ntt: &NTT, transfer: Transfer, args: TransferArgs) -> Instruction { - transfer_burn_with_token_program_id(ntt, transfer, args, &Token::id()) +pub fn transfer_burn(ntt: &NTT, accounts: Transfer, args: TransferArgs) -> Instruction { + transfer_burn_with_token_program_id(ntt, accounts, args, &Token::id()) } pub fn transfer_burn_with_token_program_id( ntt: &NTT, - transfer: Transfer, + accounts: Transfer, args: TransferArgs, token_program_id: &Pubkey, ) -> Instruction { let chain_id = args.recipient_chain.id; - let session_authority = ntt.session_authority(&transfer.from_authority, &args); + let session_authority = ntt.session_authority(&accounts.from_authority, &args); let data = example_native_token_transfers::instruction::TransferBurn { args }; let accounts = example_native_token_transfers::accounts::TransferBurn { - common: common_with_token_program_id(ntt, &transfer, token_program_id), + common: common_with_token_program_id(ntt, &accounts, token_program_id), inbox_rate_limit: ntt.inbox_rate_limit(chain_id), - peer: transfer.peer, + peer: accounts.peer, session_authority, token_authority: ntt.token_authority(), }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt.program(), accounts: accounts.to_account_metas(None), data: data.data(), } } -pub fn transfer_lock(ntt: &NTT, transfer: Transfer, args: TransferArgs) -> Instruction { - transfer_lock_with_token_program_id(ntt, transfer, args, &Token::id()) +pub fn transfer_lock(ntt: &NTT, accounts: Transfer, args: TransferArgs) -> Instruction { + transfer_lock_with_token_program_id(ntt, accounts, args, &Token::id()) } pub fn transfer_lock_with_token_program_id( ntt: &NTT, - transfer: Transfer, + accounts: Transfer, args: TransferArgs, token_program_id: &Pubkey, ) -> Instruction { let chain_id = args.recipient_chain.id; - let session_authority = ntt.session_authority(&transfer.from_authority, &args); + let session_authority = ntt.session_authority(&accounts.from_authority, &args); let data = example_native_token_transfers::instruction::TransferLock { args }; let accounts = example_native_token_transfers::accounts::TransferLock { - common: common_with_token_program_id(ntt, &transfer, token_program_id), + common: common_with_token_program_id(ntt, &accounts, token_program_id), inbox_rate_limit: ntt.inbox_rate_limit(chain_id), - peer: transfer.peer, + peer: accounts.peer, session_authority, }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt.program(), accounts: accounts.to_account_metas(None), data: data.data(), } @@ -118,20 +118,20 @@ pub fn approve_token_authority_with_token_program_id( fn common_with_token_program_id( ntt: &NTT, - transfer: &Transfer, + accounts: &Transfer, token_program_id: &Pubkey, ) -> example_native_token_transfers::accounts::Transfer { example_native_token_transfers::accounts::Transfer { - payer: transfer.payer, + payer: accounts.payer, config: NotPausedConfig { config: ntt.config(), }, - mint: transfer.mint, - from: transfer.from, + mint: accounts.mint, + from: accounts.from, token_program: *token_program_id, - outbox_item: transfer.outbox_item, + outbox_item: accounts.outbox_item, outbox_rate_limit: ntt.outbox_rate_limit(), system_program: System::id(), - custody: ntt.custody_with_token_program_id(&transfer.mint, token_program_id), + custody: ntt.custody_with_token_program_id(&accounts.mint, token_program_id), } } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/mod.rs b/solana/tests/cargo/src/sdk/mod.rs similarity index 100% rename from solana/programs/example-native-token-transfers/tests/sdk/mod.rs rename to solana/tests/cargo/src/sdk/mod.rs diff --git a/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/mod.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/mod.rs new file mode 100644 index 000000000..a7bf964e4 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/mod.rs @@ -0,0 +1,5 @@ +pub mod ntt_transceiver; +pub mod wormhole; + +pub use ntt_transceiver::*; +pub use wormhole::*; diff --git a/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/ntt_transceiver.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/ntt_transceiver.rs new file mode 100644 index 000000000..fc1637177 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/ntt_transceiver.rs @@ -0,0 +1,55 @@ +use anchor_lang::prelude::Pubkey; + +pub type NTTTransceiver = dyn NTTTransceiverAccounts; + +pub trait NTTTransceiverAccounts { + fn program(&self) -> Pubkey { + example_native_token_transfers::ID + } + + fn emitter(&self) -> Pubkey { + let (emitter, _) = Pubkey::find_program_address(&[b"emitter".as_ref()], &self.program()); + emitter + } + + fn wormhole_message(&self, outbox_item: &Pubkey) -> Pubkey { + let (wormhole_message, _) = Pubkey::find_program_address( + &[b"message".as_ref(), outbox_item.as_ref()], + &self.program(), + ); + wormhole_message + } + + fn transceiver_peer(&self, chain: u16) -> Pubkey { + let (peer, _) = Pubkey::find_program_address( + &[b"transceiver_peer".as_ref(), &chain.to_be_bytes()], + &self.program(), + ); + peer + } + + fn transceiver_message(&self, chain: u16, id: [u8; 32]) -> Pubkey { + let (transceiver_message, _) = Pubkey::find_program_address( + &[b"transceiver_message".as_ref(), &chain.to_be_bytes(), &id], + &self.program(), + ); + transceiver_message + } + + fn unverified_message_account(&self, payer: &Pubkey, seed: u64) -> Pubkey { + let (unverified_message_account, _) = Pubkey::find_program_address( + &[b"vaa_body".as_ref(), payer.as_ref(), &seed.to_be_bytes()], + &self.program(), + ); + unverified_message_account + } +} + +/// This implements the account derivations correctly. For negative tests, other +/// implementations will implement them incorrectly. +pub struct GoodNTTTransceiver {} + +#[allow(non_upper_case_globals)] +pub const good_ntt_transceiver: GoodNTTTransceiver = GoodNTTTransceiver {}; + +impl NTTTransceiverAccounts for GoodNTTTransceiver {} diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/accounts/wormhole.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/wormhole.rs similarity index 62% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/accounts/wormhole.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/accounts/wormhole.rs index 985786036..3fc1f5b37 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/accounts/wormhole.rs +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/accounts/wormhole.rs @@ -2,13 +2,15 @@ use anchor_lang::prelude::*; use example_native_token_transfers::accounts::WormholeAccounts; use solana_sdk::sysvar::SysvarId; -use crate::sdk::accounts::NTT; +use crate::sdk::accounts::ntt::NTT; -pub fn wormhole_accounts(ntt: &NTT) -> WormholeAccounts { +use super::ntt_transceiver::NTTTransceiver; + +pub fn wormhole_accounts(ntt: &NTT, ntt_transceiver: &NTTTransceiver) -> WormholeAccounts { WormholeAccounts { bridge: ntt.wormhole().bridge(), fee_collector: ntt.wormhole().fee_collector(), - sequence: ntt.wormhole_sequence(), + sequence: ntt.wormhole_sequence(ntt_transceiver), program: ntt.wormhole().program, system_program: System::id(), clock: Clock::id(), diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/admin.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/admin.rs similarity index 72% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/admin.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/instructions/admin.rs index 74d809440..6713bb63f 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/admin.rs +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/admin.rs @@ -1,8 +1,8 @@ use anchor_lang::{prelude::Pubkey, system_program::System, Id, InstructionData, ToAccountMetas}; -use example_native_token_transfers::transceivers::wormhole::SetTransceiverPeerArgs; +pub use example_native_token_transfers::transceivers::wormhole::SetTransceiverPeerArgs; use solana_sdk::instruction::Instruction; -use crate::sdk::accounts::NTT; +use crate::sdk::{accounts::NTT, transceivers::accounts::NTTTransceiver}; pub struct SetTransceiverPeer { pub payer: Pubkey, @@ -11,6 +11,7 @@ pub struct SetTransceiverPeer { pub fn set_transceiver_peer( ntt: &NTT, + ntt_transceiver: &NTTTransceiver, accounts: SetTransceiverPeer, args: SetTransceiverPeerArgs, ) -> Instruction { @@ -21,12 +22,12 @@ pub fn set_transceiver_peer( config: ntt.config(), owner: accounts.owner, payer: accounts.payer, - peer: ntt.transceiver_peer(chain_id), + peer: ntt_transceiver.transceiver_peer(chain_id), system_program: System::id(), }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt_transceiver.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/broadcast_id.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/broadcast_id.rs similarity index 62% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/broadcast_id.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/instructions/broadcast_id.rs index 27bfdd763..fc2b737d4 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/broadcast_id.rs +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/broadcast_id.rs @@ -1,7 +1,10 @@ use anchor_lang::{prelude::*, InstructionData}; use solana_program::instruction::Instruction; -use crate::sdk::{accounts::NTT, transceivers::wormhole::accounts::wormhole::wormhole_accounts}; +use crate::sdk::{ + accounts::NTT, + transceivers::accounts::{wormhole_accounts, NTTTransceiver}, +}; pub struct BroadcastId { pub payer: Pubkey, @@ -9,20 +12,20 @@ pub struct BroadcastId { pub mint: Pubkey, } -pub fn broadcast_id(ntt: &NTT, accs: BroadcastId) -> Instruction { +pub fn broadcast_id(ntt: &NTT, ntt_transceiver: &NTTTransceiver, accs: BroadcastId) -> Instruction { let data = example_native_token_transfers::instruction::BroadcastWormholeId {}; let accounts = example_native_token_transfers::accounts::BroadcastId { payer: accs.payer, config: ntt.config(), wormhole_message: accs.wormhole_message, - emitter: ntt.emitter(), - wormhole: wormhole_accounts(ntt), + emitter: ntt_transceiver.emitter(), + wormhole: wormhole_accounts(ntt, ntt_transceiver), mint: accs.mint, }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt_transceiver.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/broadcast_peer.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/broadcast_peer.rs similarity index 63% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/broadcast_peer.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/instructions/broadcast_peer.rs index ddcbd0907..074d29034 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/broadcast_peer.rs +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/broadcast_peer.rs @@ -2,7 +2,10 @@ use anchor_lang::{prelude::*, InstructionData}; use example_native_token_transfers::transceivers::wormhole::BroadcastPeerArgs; use solana_program::instruction::Instruction; -use crate::sdk::{accounts::NTT, transceivers::wormhole::accounts::wormhole::wormhole_accounts}; +use crate::sdk::{ + accounts::NTT, + transceivers::accounts::{wormhole_accounts, NTTTransceiver}, +}; pub struct BroadcastPeer { pub payer: Pubkey, @@ -10,7 +13,11 @@ pub struct BroadcastPeer { pub chain_id: u16, } -pub fn broadcast_peer(ntt: &NTT, accs: BroadcastPeer) -> Instruction { +pub fn broadcast_peer( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + accs: BroadcastPeer, +) -> Instruction { let data = example_native_token_transfers::instruction::BroadcastWormholePeer { args: BroadcastPeerArgs { chain_id: accs.chain_id, @@ -20,14 +27,14 @@ pub fn broadcast_peer(ntt: &NTT, accs: BroadcastPeer) -> Instruction { let accounts = example_native_token_transfers::accounts::BroadcastPeer { payer: accs.payer, config: ntt.config(), - peer: ntt.transceiver_peer(accs.chain_id), + peer: ntt_transceiver.transceiver_peer(accs.chain_id), wormhole_message: accs.wormhole_message, - emitter: ntt.emitter(), - wormhole: wormhole_accounts(ntt), + emitter: ntt_transceiver.emitter(), + wormhole: wormhole_accounts(ntt, ntt_transceiver), }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt_transceiver.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/mod.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/mod.rs similarity index 100% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/mod.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/instructions/mod.rs diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/receive_message.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/receive_message.rs similarity index 71% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/receive_message.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/instructions/receive_message.rs index 6e5ffa707..dcb848919 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/receive_message.rs +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/receive_message.rs @@ -1,7 +1,7 @@ use anchor_lang::{prelude::Pubkey, system_program::System, Id, InstructionData, ToAccountMetas}; use solana_sdk::instruction::Instruction; -use crate::sdk::accounts::NTT; +use crate::sdk::{accounts::NTT, transceivers::accounts::NTTTransceiver}; #[derive(Debug, Clone)] pub struct ReceiveMessage { @@ -12,7 +12,11 @@ pub struct ReceiveMessage { pub id: [u8; 32], } -pub fn receive_message(ntt: &NTT, accs: ReceiveMessage) -> Instruction { +pub fn receive_message( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + accs: ReceiveMessage, +) -> Instruction { let data = example_native_token_transfers::instruction::ReceiveWormholeMessage {}; let accounts = example_native_token_transfers::accounts::ReceiveMessage { @@ -22,12 +26,12 @@ pub fn receive_message(ntt: &NTT, accs: ReceiveMessage) -> Instruction { }, peer: accs.peer, vaa: accs.vaa, - transceiver_message: ntt.transceiver_message(accs.chain_id, accs.id), + transceiver_message: ntt_transceiver.transceiver_message(accs.chain_id, accs.id), system_program: System::id(), }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt_transceiver.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/release_outbound.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/release_outbound.rs similarity index 70% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/release_outbound.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/instructions/release_outbound.rs index 871b5655a..0b957e301 100644 --- a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/instructions/release_outbound.rs +++ b/solana/tests/cargo/src/sdk/transceivers/legacy/instructions/release_outbound.rs @@ -4,7 +4,10 @@ use example_native_token_transfers::{ }; use solana_sdk::instruction::Instruction; -use crate::sdk::{accounts::NTT, transceivers::wormhole::accounts::wormhole::wormhole_accounts}; +use crate::sdk::{ + accounts::NTT, + transceivers::accounts::{wormhole_accounts, NTTTransceiver}, +}; pub struct ReleaseOutbound { pub payer: Pubkey, @@ -13,6 +16,7 @@ pub struct ReleaseOutbound { pub fn release_outbound( ntt: &NTT, + ntt_transceiver: &NTTTransceiver, release_outbound: ReleaseOutbound, args: ReleaseOutboundArgs, ) -> Instruction { @@ -23,13 +27,13 @@ pub fn release_outbound( config: ntt.config(), }, outbox_item: release_outbound.outbox_item, - wormhole_message: ntt.wormhole_message(&release_outbound.outbox_item), - emitter: ntt.emitter(), + wormhole_message: ntt_transceiver.wormhole_message(&release_outbound.outbox_item), + emitter: ntt_transceiver.emitter(), transceiver: ntt.registered_transceiver(&ntt.program()), - wormhole: wormhole_accounts(ntt), + wormhole: wormhole_accounts(ntt, ntt_transceiver), }; Instruction { - program_id: example_native_token_transfers::ID, + program_id: ntt_transceiver.program(), accounts: accounts.to_account_metas(None), data: data.data(), } diff --git a/solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/mod.rs b/solana/tests/cargo/src/sdk/transceivers/legacy/mod.rs similarity index 100% rename from solana/programs/example-native-token-transfers/tests/sdk/transceivers/wormhole/mod.rs rename to solana/tests/cargo/src/sdk/transceivers/legacy/mod.rs diff --git a/solana/tests/cargo/src/sdk/transceivers/mod.rs b/solana/tests/cargo/src/sdk/transceivers/mod.rs new file mode 100644 index 000000000..b6fd253e4 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/mod.rs @@ -0,0 +1,9 @@ +cfg_if! { + if #[cfg(feature = "shim")] { + pub mod shim; + pub use shim::*; + } else { + pub mod legacy; + pub use legacy::*; + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/accounts/mod.rs b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/mod.rs new file mode 100644 index 000000000..4400f931a --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/mod.rs @@ -0,0 +1,7 @@ +pub mod ntt_transceiver; +pub mod post_message_shim; +pub mod wormhole; + +pub use ntt_transceiver::*; +pub use post_message_shim::*; +pub use wormhole::*; diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/accounts/ntt_transceiver.rs b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/ntt_transceiver.rs new file mode 100644 index 000000000..b240b6a70 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/ntt_transceiver.rs @@ -0,0 +1,74 @@ +use anchor_lang::prelude::Pubkey; +use wormhole_svm_definitions::solana::{POST_MESSAGE_SHIM_PROGRAM_ID, VERIFY_VAA_SHIM_PROGRAM_ID}; + +use super::post_message_shim::PostMessageShim; + +pub type NTTTransceiver = dyn NTTTransceiverAccounts; + +pub trait NTTTransceiverAccounts { + fn program(&self) -> Pubkey { + ntt_transceiver::ID + } + + fn post_message_shim(&self) -> PostMessageShim { + PostMessageShim { + program: POST_MESSAGE_SHIM_PROGRAM_ID, + } + } + + fn verify_vaa_shim_shim(&self) -> Pubkey { + VERIFY_VAA_SHIM_PROGRAM_ID + } + + fn emitter(&self) -> Pubkey { + let (emitter, _) = Pubkey::find_program_address(&[b"emitter".as_ref()], &self.program()); + emitter + } + + fn outbox_item_signer(&self) -> Pubkey { + let (outbox_item_signer, _) = + Pubkey::find_program_address(&[b"outbox_item_signer".as_ref()], &self.program()); + outbox_item_signer + } + + fn wormhole_message(&self) -> Pubkey { + let (wormhole_message, _) = Pubkey::find_program_address( + &[self.emitter().as_ref()], + &self.post_message_shim().program, + ); + wormhole_message + } + + fn transceiver_peer(&self, chain: u16) -> Pubkey { + let (peer, _) = Pubkey::find_program_address( + &[b"transceiver_peer".as_ref(), &chain.to_be_bytes()], + &self.program(), + ); + peer + } + + fn transceiver_message(&self, chain: u16, id: [u8; 32]) -> Pubkey { + let (transceiver_message, _) = Pubkey::find_program_address( + &[b"transceiver_message".as_ref(), &chain.to_be_bytes(), &id], + &self.program(), + ); + transceiver_message + } + + fn unverified_message_account(&self, payer: &Pubkey, seed: u64) -> Pubkey { + let (unverified_message_account, _) = Pubkey::find_program_address( + &[b"vaa_body".as_ref(), payer.as_ref(), &seed.to_be_bytes()], + &self.program(), + ); + unverified_message_account + } +} + +/// This implements the account derivations correctly. For negative tests, other +/// implementations will implement them incorrectly. +pub struct GoodNTTTransceiver {} + +#[allow(non_upper_case_globals)] +pub const good_ntt_transceiver: GoodNTTTransceiver = GoodNTTTransceiver {}; + +impl NTTTransceiverAccounts for GoodNTTTransceiver {} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/accounts/post_message_shim.rs b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/post_message_shim.rs new file mode 100644 index 000000000..d883dbb5c --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/post_message_shim.rs @@ -0,0 +1,14 @@ +use anchor_lang::prelude::Pubkey; +use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; + +pub struct PostMessageShim { + pub program: Pubkey, +} + +impl PostMessageShim { + pub fn event_authority(&self) -> Pubkey { + let (event_authority, _) = + Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &self.program); + event_authority + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/accounts/wormhole.rs b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/wormhole.rs new file mode 100644 index 000000000..0169e87bf --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/accounts/wormhole.rs @@ -0,0 +1,21 @@ +use anchor_lang::prelude::*; +use ntt_transceiver::accounts::WormholeAccounts; +use solana_sdk::sysvar::SysvarId; + +use crate::sdk::accounts::NTT; + +use super::ntt_transceiver::NTTTransceiver; + +pub fn wormhole_accounts(ntt: &NTT, ntt_transceiver: &NTTTransceiver) -> WormholeAccounts { + WormholeAccounts { + bridge: ntt.wormhole().bridge(), + fee_collector: ntt.wormhole().fee_collector(), + sequence: ntt.wormhole_sequence(ntt_transceiver), + program: ntt.wormhole().program, + system_program: System::id(), + post_message_shim: ntt_transceiver.post_message_shim().program, + wormhole_post_message_shim_ea: ntt_transceiver.post_message_shim().event_authority(), + clock: Clock::id(), + rent: Rent::id(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/admin.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/admin.rs new file mode 100644 index 000000000..4b27b1356 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/admin.rs @@ -0,0 +1,34 @@ +use anchor_lang::{prelude::Pubkey, system_program::System, Id, InstructionData, ToAccountMetas}; +pub use ntt_transceiver::wormhole::instructions::SetTransceiverPeerArgs; +use solana_sdk::instruction::Instruction; + +use crate::sdk::{accounts::NTT, transceivers::accounts::NTTTransceiver}; + +pub struct SetTransceiverPeer { + pub payer: Pubkey, + pub owner: Pubkey, +} + +pub fn set_transceiver_peer( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + accounts: SetTransceiverPeer, + args: SetTransceiverPeerArgs, +) -> Instruction { + let chain_id = args.chain_id.id; + let data = ntt_transceiver::instruction::SetWormholePeer { args }; + + let accounts = ntt_transceiver::accounts::SetTransceiverPeer { + config: ntt.config(), + owner: accounts.owner, + payer: accounts.payer, + peer: ntt_transceiver.transceiver_peer(chain_id), + system_program: System::id(), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/broadcast_id.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/broadcast_id.rs new file mode 100644 index 000000000..8610ed6dd --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/broadcast_id.rs @@ -0,0 +1,35 @@ +use anchor_lang::{prelude::*, InstructionData}; +use solana_program::instruction::Instruction; + +use crate::sdk::{ + accounts::NTT, + transceivers::accounts::{wormhole_accounts, NTTTransceiver}, +}; + +pub struct BroadcastId { + pub payer: Pubkey, + pub mint: Pubkey, +} + +pub fn broadcast_id( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + accounts: BroadcastId, +) -> Instruction { + let data = ntt_transceiver::instruction::BroadcastWormholeId {}; + + let accounts = ntt_transceiver::accounts::BroadcastId { + payer: accounts.payer, + config: ntt.config(), + mint: accounts.mint, + wormhole_message: ntt_transceiver.wormhole_message(), + emitter: ntt_transceiver.emitter(), + wormhole: wormhole_accounts(ntt, ntt_transceiver), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/broadcast_peer.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/broadcast_peer.rs new file mode 100644 index 000000000..259fc6543 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/broadcast_peer.rs @@ -0,0 +1,40 @@ +use anchor_lang::{prelude::*, InstructionData}; +use ntt_transceiver::wormhole::instructions::BroadcastPeerArgs; +use solana_program::instruction::Instruction; + +use crate::sdk::{ + accounts::NTT, + transceivers::accounts::{wormhole_accounts, NTTTransceiver}, +}; + +pub struct BroadcastPeer { + pub payer: Pubkey, + pub chain_id: u16, +} + +pub fn broadcast_peer( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + accounts: BroadcastPeer, +) -> Instruction { + let data = ntt_transceiver::instruction::BroadcastWormholePeer { + args: BroadcastPeerArgs { + chain_id: accounts.chain_id, + }, + }; + + let accounts = ntt_transceiver::accounts::BroadcastPeer { + payer: accounts.payer, + config: ntt.config(), + peer: ntt_transceiver.transceiver_peer(accounts.chain_id), + wormhole_message: ntt_transceiver.wormhole_message(), + emitter: ntt_transceiver.emitter(), + wormhole: wormhole_accounts(ntt, ntt_transceiver), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/mod.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/mod.rs new file mode 100644 index 000000000..f2a046aae --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/mod.rs @@ -0,0 +1,6 @@ +pub mod admin; +pub mod broadcast_id; +pub mod broadcast_peer; +pub mod receive_message; +pub mod release_outbound; +pub mod unverified_message_account; diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/receive_message.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/receive_message.rs new file mode 100644 index 000000000..3ba7902e3 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/receive_message.rs @@ -0,0 +1,80 @@ +use anchor_lang::{prelude::Pubkey, system_program::System, Id, InstructionData, ToAccountMetas}; +use ntt_transceiver::vaa_body::VaaBodyData; +use solana_sdk::instruction::Instruction; + +use crate::sdk::{accounts::NTT, transceivers::accounts::NTTTransceiver}; + +#[derive(Debug, Clone)] +pub struct ReceiveMessage { + pub payer: Pubkey, + pub peer: Pubkey, + pub chain_id: u16, + pub id: [u8; 32], + pub guardian_set: (Pubkey, u8), + pub guardian_signatures: Pubkey, +} + +pub fn receive_message_instruction_data( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + receive_message: ReceiveMessage, + vaa_body: VaaBodyData, +) -> Instruction { + let data = ntt_transceiver::instruction::ReceiveWormholeMessageInstructionData { + guardian_set_bump: receive_message.guardian_set.1, + vaa_body, + }; + + let accounts = ntt_transceiver::accounts::ReceiveMessageInstructionData { + payer: receive_message.payer, + config: ntt_transceiver::accounts::NotPausedConfig { + config: ntt.config(), + }, + peer: receive_message.peer, + transceiver_message: ntt_transceiver + .transceiver_message(receive_message.chain_id, receive_message.id), + guardian_set: receive_message.guardian_set.0, + guardian_signatures: receive_message.guardian_signatures, + verify_vaa_shim: ntt_transceiver.verify_vaa_shim_shim(), + system_program: System::id(), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} + +pub fn receive_message_account( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + receive_message: ReceiveMessage, + seed: u64, +) -> Instruction { + let data = ntt_transceiver::instruction::ReceiveWormholeMessageAccount { + guardian_set_bump: receive_message.guardian_set.1, + seed, + }; + + let accounts = ntt_transceiver::accounts::ReceiveMessageAccount { + payer: receive_message.payer, + config: ntt_transceiver::accounts::NotPausedConfig { + config: ntt.config(), + }, + peer: receive_message.peer, + transceiver_message: ntt_transceiver + .transceiver_message(receive_message.chain_id, receive_message.id), + guardian_set: receive_message.guardian_set.0, + guardian_signatures: receive_message.guardian_signatures, + verify_vaa_shim: ntt_transceiver.verify_vaa_shim_shim(), + system_program: System::id(), + message: ntt_transceiver.unverified_message_account(&receive_message.payer, seed), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/release_outbound.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/release_outbound.rs new file mode 100644 index 000000000..0332fc26c --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/release_outbound.rs @@ -0,0 +1,41 @@ +use anchor_lang::{prelude::*, InstructionData, ToAccountMetas}; +use example_native_token_transfers::accounts::NotPausedConfig; +use ntt_transceiver::wormhole::instructions::ReleaseOutboundArgs; +use solana_sdk::instruction::Instruction; + +use crate::sdk::{ + accounts::NTT, + transceivers::accounts::{wormhole_accounts, NTTTransceiver}, +}; + +pub struct ReleaseOutbound { + pub payer: Pubkey, + pub outbox_item: Pubkey, +} + +pub fn release_outbound( + ntt: &NTT, + ntt_transceiver: &NTTTransceiver, + accounts: ReleaseOutbound, + args: ReleaseOutboundArgs, +) -> Instruction { + let data = ntt_transceiver::instruction::ReleaseWormholeOutbound { args }; + let accounts = ntt_transceiver::accounts::ReleaseOutbound { + payer: accounts.payer, + config: NotPausedConfig { + config: ntt.config(), + }, + outbox_item: accounts.outbox_item, + transceiver: ntt.registered_transceiver(&ntt_transceiver.program()), + wormhole_message: ntt_transceiver.wormhole_message(), + emitter: ntt_transceiver.emitter(), + wormhole: wormhole_accounts(ntt, ntt_transceiver), + manager: ntt.program(), + outbox_item_signer: ntt_transceiver.outbox_item_signer(), + }; + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/instructions/unverified_message_account.rs b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/unverified_message_account.rs new file mode 100644 index 000000000..97ff936a3 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/instructions/unverified_message_account.rs @@ -0,0 +1,60 @@ +use anchor_lang::{prelude::Pubkey, system_program::System, Id, InstructionData, ToAccountMetas}; +use ntt_transceiver::wormhole::PostUnverifiedMessageAccountArgs; +use solana_sdk::instruction::Instruction; + +use crate::sdk::transceivers::accounts::NTTTransceiver; + +#[derive(Debug, Clone)] +pub struct UnverifiedMessageAccount { + pub payer: Pubkey, +} + +pub fn post_unverified_message_account( + ntt_transceiver: &NTTTransceiver, + accounts: UnverifiedMessageAccount, + seed: u64, + chunk: Vec, +) -> Instruction { + let message_size = u32::try_from(chunk.len()).unwrap(); + let data = ntt_transceiver::instruction::PostUnverifiedWormholeMessageAccount { + args: PostUnverifiedMessageAccountArgs { + seed, + offset: 0, + chunk, + message_size, + }, + }; + + let accounts = ntt_transceiver::accounts::PostUnverifiedMessageAccount { + payer: accounts.payer, + message: ntt_transceiver.unverified_message_account(&accounts.payer, seed), + system_program: System::id(), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} + +// NOTE: this is currently unused but left for posterity +pub fn close_unverified_message_account( + ntt_transceiver: &NTTTransceiver, + accounts: UnverifiedMessageAccount, + seed: u64, +) -> Instruction { + let data = ntt_transceiver::instruction::CloseUnverifiedWormholeMessageAccount { seed }; + + let accounts = ntt_transceiver::accounts::CloseUnverifiedMessageAccount { + payer: accounts.payer, + message: ntt_transceiver.unverified_message_account(&accounts.payer, seed), + system_program: System::id(), + }; + + Instruction { + program_id: ntt_transceiver.program(), + accounts: accounts.to_account_metas(None), + data: data.data(), + } +} diff --git a/solana/tests/cargo/src/sdk/transceivers/shim/mod.rs b/solana/tests/cargo/src/sdk/transceivers/shim/mod.rs new file mode 100644 index 000000000..5c4c40bc3 --- /dev/null +++ b/solana/tests/cargo/src/sdk/transceivers/shim/mod.rs @@ -0,0 +1,2 @@ +pub mod accounts; +pub mod instructions; diff --git a/solana/programs/example-native-token-transfers/tests/fixtures/mainnet_core_bridge.so b/solana/tests/fixtures/mainnet_core_bridge.so similarity index 100% rename from solana/programs/example-native-token-transfers/tests/fixtures/mainnet_core_bridge.so rename to solana/tests/fixtures/mainnet_core_bridge.so diff --git a/solana/programs/example-native-token-transfers/tests/fixtures/mainnet_wormhole_post_message_shim.so b/solana/tests/fixtures/mainnet_wormhole_post_message_shim.so similarity index 100% rename from solana/programs/example-native-token-transfers/tests/fixtures/mainnet_wormhole_post_message_shim.so rename to solana/tests/fixtures/mainnet_wormhole_post_message_shim.so diff --git a/solana/programs/example-native-token-transfers/tests/fixtures/mainnet_wormhole_verify_vaa_shim.so b/solana/tests/fixtures/mainnet_wormhole_verify_vaa_shim.so similarity index 100% rename from solana/programs/example-native-token-transfers/tests/fixtures/mainnet_wormhole_verify_vaa_shim.so rename to solana/tests/fixtures/mainnet_wormhole_verify_vaa_shim.so