diff --git a/cosmwasm/Cargo.lock b/cosmwasm/Cargo.lock index 58f76835de..1799e32f53 100644 --- a/cosmwasm/Cargo.lock +++ b/cosmwasm/Cargo.lock @@ -487,15 +487,15 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.13.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e" +checksum = "ca153120cf5b91af88be106b0c6c0263423d959bc813b1592982c02c4691a4ae" dependencies = [ "anyhow", "cosmwasm-std", "cosmwasm-storage", - "cw-storage-plus 0.13.4", - "cw-utils 0.13.4", + "cw-storage-plus 0.14.0", + "cw-utils 0.14.0", "derivative", "itertools", "prost 0.9.0", @@ -515,6 +515,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c8b264257c4f44c49b7ce09377af63aa040768ecd3fd7bdd2d48a09323a1e90" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-storage-plus" version = "1.1.0" @@ -538,6 +549,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-utils" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414b91f3d7a619bb26c835119d7095804596a1382ddc1d184c33c1d2c17f6c5e" +dependencies = [ + "cosmwasm-std", + "cw2 0.14.0", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cw-utils" version = "1.0.1" @@ -565,6 +590,18 @@ dependencies = [ "serde", ] +[[package]] +name = "cw2" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa74c324af8e3506fd8d50759a265bead3f87402e413c840042af5d2808463d6" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 0.14.0", + "schemars", + "serde", +] + [[package]] name = "cw2" version = "1.1.0" @@ -1958,6 +1995,15 @@ dependencies = [ "syn 2.0.23", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2633,13 +2679,20 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", + "cw-multi-test", "generic-array", "hex", "k256", "schemars", "serde", + "serde-json-wasm 0.4.1", + "serde_wormhole", "sha3 0.9.1", "thiserror", + "tiny-keccak", + "wormchain-ibc-receiver", + "wormhole-bindings", + "wormhole-vaas-serde", ] [[package]] diff --git a/cosmwasm/contracts/global-accountant/Cargo.toml b/cosmwasm/contracts/global-accountant/Cargo.toml index 0db7df8fc3..6b0ae1b199 100644 --- a/cosmwasm/contracts/global-accountant/Cargo.toml +++ b/cosmwasm/contracts/global-accountant/Cargo.toml @@ -32,6 +32,6 @@ wormhole-sdk = { workspace = true, features = ["schemars"] } [dev-dependencies] anyhow = { version = "1", features = ["backtrace"] } -cw-multi-test = "0.13.2" +cw-multi-test = "0.14" serde-json-wasm = "0.4" wormhole-bindings = { version = "0.1", features = ["fake"] } diff --git a/cosmwasm/contracts/ntt-global-accountant/Cargo.toml b/cosmwasm/contracts/ntt-global-accountant/Cargo.toml index 17a3117a91..50b73ee0dc 100644 --- a/cosmwasm/contracts/ntt-global-accountant/Cargo.toml +++ b/cosmwasm/contracts/ntt-global-accountant/Cargo.toml @@ -35,6 +35,6 @@ wormhole-sdk = { workspace = true, features = ["schemars"] } [dev-dependencies] anyhow = { version = "1", features = ["backtrace"] } -cw-multi-test = "0.13.2" +cw-multi-test = "0.14" serde-json-wasm = "0.4" wormhole-bindings = { version = "0.1", features = ["fake"] } diff --git a/cosmwasm/contracts/wormchain-ibc-receiver/Cargo.toml b/cosmwasm/contracts/wormchain-ibc-receiver/Cargo.toml index 58c6be3054..74b7f04181 100644 --- a/cosmwasm/contracts/wormchain-ibc-receiver/Cargo.toml +++ b/cosmwasm/contracts/wormchain-ibc-receiver/Cargo.toml @@ -23,7 +23,7 @@ wormhole-sdk = { workspace = true, features = ["schemars"] } serde_wormhole.workspace = true [dev-dependencies] -cw-multi-test = "0.13.2" +cw-multi-test = "0.14" serde-json-wasm = "0.4" wormhole-bindings = { version = "0.1.0", features=["fake"] } serde = { version = "1.0.137", default-features = false, features = ["derive"] } \ No newline at end of file diff --git a/cosmwasm/contracts/wormhole/Cargo.toml b/cosmwasm/contracts/wormhole/Cargo.toml index 2d5fdd9b96..e1ad59868e 100644 --- a/cosmwasm/contracts/wormhole/Cargo.toml +++ b/cosmwasm/contracts/wormhole/Cargo.toml @@ -29,3 +29,12 @@ k256 = { version = "0.11", default-features = false, features = ["ecdsa"] } sha3 = { version = "0.9.1", default-features = false } generic-array = { version = "0.14.4" } hex = "0.4.2" + +[dev-dependencies] +cw-multi-test = "0.14" +serde_wormhole.workspace = true +wormhole-sdk.workspace = true +wormhole-bindings = { version = "0.1", features = ["fake"] } +tiny-keccak = { version = "2.0", features = ["keccak"] } +serde-json-wasm = "0.4" +wormchain-ibc-receiver = { path = "../wormchain-ibc-receiver" } diff --git a/cosmwasm/contracts/wormhole/src/testing/integration.rs b/cosmwasm/contracts/wormhole/src/testing/integration.rs new file mode 100644 index 0000000000..0e5e2f5800 --- /dev/null +++ b/cosmwasm/contracts/wormhole/src/testing/integration.rs @@ -0,0 +1,912 @@ +use crate::msg::{ExecuteMsg, GetStateResponse, GuardianSetInfoResponse}; +use crate::testing::utils::{ + create_transfer_vaa_body, instantiate_with_guardians, sign_vaa_body_version_2, + IntoGuardianAddress, WormholeApp, +}; +use crate::{ + contract::instantiate, + msg::QueryMsg, + state::{ConfigInfo, GuardianAddress, ParsedVAA, CONFIG_KEY}, +}; +use cosmwasm_std::{ + from_slice, + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + Coin, OwnedDeps, Response, StdResult, Storage, +}; +use cosmwasm_std::{Deps, DepsMut, Empty, QuerierWrapper, StdError, Uint128, Uint256}; +use cosmwasm_storage::to_length_prefixed; +use cw_multi_test::{ContractWrapper, Executor}; +use k256::ecdsa::SigningKey; +use serde_wormhole::RawMessage; +use std::ops::Deref; +use wormhole_bindings::fake::{create_gov_vaa_body, SignVaa, WormholeKeeper}; +use wormhole_sdk::core::{Action, GovernancePacket}; +use wormhole_sdk::token::Message; +use wormhole_sdk::{relayer, Address, Amount, Chain, GuardianSetInfo, GOVERNANCE_EMITTER}; + +static INITIALIZER: &str = "initializer"; + +fn get_config_info(storage: &S) -> ConfigInfo { + let key = to_length_prefixed(CONFIG_KEY); + let data = storage.get(&key).expect("data should exist"); + from_slice(&data).expect("invalid data") +} + +fn do_init(guardians: &[GuardianAddress]) -> OwnedDeps { + let mut deps = mock_dependencies(); + let init_msg = instantiate_with_guardians(guardians); + let env = mock_env(); + let info = mock_info(INITIALIZER, &[]); + let res: Response = instantiate(deps.as_mut(), env, info, init_msg).unwrap(); + assert_eq!(0, res.messages.len()); + + // query the store directly + assert_eq!( + get_config_info(&deps.storage), + ConfigInfo { + guardian_set_index: 0, + guardian_set_expirity: 50, + gov_chain: Chain::Solana.into(), + gov_address: GOVERNANCE_EMITTER.0.to_vec(), + fee: Coin::new(0, "uluna"), + chain_id: Chain::Terra2.into(), + fee_denom: "uluna".to_string(), + } + ); + deps +} + +#[test] +fn init_works() { + let guardians = [GuardianAddress { + bytes: hex::decode("beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe") + .expect("Decoding failed") + .into(), + }]; + let _deps = do_init(&guardians); +} + +#[test] +fn queries_test() -> StdResult<()> { + let WormholeApp { + app, + wormhole_contract, + wormhole_keeper, + .. + } = WormholeApp::new_with_faker_guardians(); + + let (_, signed_vaa) = create_gov_vaa_body(1, "test").sign_vaa(&wormhole_keeper); + + // Query verify VAA + let parsed_vaa: ParsedVAA = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + )?; + + let test_payload = serde_wormhole::from_slice::(parsed_vaa.payload.as_slice()); + assert!(test_payload.is_ok(), "failed to parse test payload"); + assert_eq!(test_payload.unwrap(), "test", "test payload does not match"); + + assert_eq!(parsed_vaa.version, 1, "version does not match"); + assert_eq!( + parsed_vaa.guardian_set_index, 0, + "guardian set index does not match" + ); + + // Query guardian set info + let guardian_set_response: GuardianSetInfoResponse = app + .wrap() + .query_wasm_smart(wormhole_contract.clone(), &QueryMsg::GuardianSetInfo {})?; + + assert_eq!( + guardian_set_response.guardian_set_index, 0u32, + "guardian set index does not match" + ); + assert_eq!( + guardian_set_response.addresses.len(), + 7, + "guardian set length does not match" + ); + + // Query get state + let get_state_resp: GetStateResponse = app + .wrap() + .query_wasm_smart(wormhole_contract.clone(), &QueryMsg::GetState {})?; + assert_eq!( + get_state_resp.fee.denom, "uluna", + "fee denom does not match" + ); + assert_eq!( + get_state_resp.fee.amount, + Uint128::from(0u128), + "fee amount does not match" + ); + + // TODO: set the appropriate MockedApi in the AppBuilder so that QueryAddressHex can be integration tested + // This should be simple once we're on cosmwasm 1.5+ https://docs.rs/cw-multi-test/1.2.0/cw_multi_test/struct.SimpleAddressGenerator.html + + Ok(()) +} + +#[test] +fn verify_vaas_query() -> StdResult<()> { + let WormholeApp { + app, + wormhole_contract, + wormhole_keeper, + .. + } = WormholeApp::new_with_faker_guardians(); + let (_, signed_vaa) = + create_transfer_vaa_body(1, GOVERNANCE_EMITTER).sign_vaa(&wormhole_keeper.clone()); + + let vaa_response: ParsedVAA = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + )?; + + assert_eq!(vaa_response.version, 1, "version does not match"); + assert_eq!( + vaa_response.guardian_set_index, 0, + "guardian set index does not match" + ); + assert_eq!(vaa_response.timestamp, 1, "timestamp does not match"); + assert_eq!(vaa_response.nonce, 1, "nonce does not match"); + assert_eq!(vaa_response.len_signers, 7, "len signers does not match"); + assert_eq!( + vaa_response.emitter_chain, 1, + "emitter chain does not match" + ); + assert_eq!(vaa_response.sequence, 1, "sequence does not match"); + assert_eq!( + vaa_response.consistency_level, 32, + "consistency level does not match" + ); + assert_eq!( + vaa_response.emitter_address.as_slice(), + GOVERNANCE_EMITTER.0.as_slice(), + "emitter address does not match" + ); + + let transfer_payload = + serde_wormhole::from_slice::>(vaa_response.payload.as_slice()); + + assert!(transfer_payload.is_ok(), "failed to parse transfer payload"); + assert!( + matches!( + transfer_payload.unwrap(), + Message::Transfer { + token_chain: Chain::Solana, + .. + } + ), + "unexpected payload" + ); + + // Verify a governance VAA + let (_, signed_vaa) = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Osmosis, + action: Action::SetFee { + amount: Amount(*b"00000000000000000000000000000012"), + }, + }, + ) + .sign_vaa(&wormhole_keeper); + + let vaa_response: ParsedVAA = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + )?; + + assert_eq!( + vaa_response.version, 1, + "governance vaa version does not match" + ); + assert_eq!( + vaa_response.guardian_set_index, 0, + "governance vaa guardian set index does not match" + ); + let governance_payload = + serde_wormhole::from_slice::(vaa_response.payload.as_slice()); + + assert!( + governance_payload.is_ok(), + "failed to parse governance payload" + ); + assert!( + matches!( + governance_payload.unwrap(), + GovernancePacket { + action: Action::SetFee { .. }, + chain: Chain::Osmosis, + } + ), + "unexpected payload" + ); + + Ok(()) +} + +#[test] +fn verify_vaa_failure_modes() -> StdResult<()> { + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + let vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::SetFee { + amount: Amount(Uint256::from(1u128).to_be_bytes()), + }, + }, + ); + + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa.clone(), + block_time: app.block_info().height, + }, + ); + assert!( + vaa_response.is_ok(), + "VAA signed by the proper guardianset should verify successfully" + ); + + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: u64::MAX, + }, + ); + assert!( + vaa_response.is_err(), + "VAA should fail if the guardian set is past it's expiry \"GuardianSetExpired\"" + ); + + // VAA signed with a nonstandard version listed in the header + let (_, signed_vaa) = sign_vaa_body_version_2(wormhole_keeper.clone(), vaa_body.clone()); + + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa.clone(), + block_time: app.block_info().height, + }, + ); + + assert!( + vaa_response.is_err(), + "VAA should fail \"InvalidVersion\" when signed with a nonstandard version" + ); + + // VAA signed with a non-matching guardianset + let (_, signed_vaa) = vaa_body.clone().sign_vaa( + // signing with 7 guardians + &WormholeKeeper::new(), + ); + + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + ); + assert!( + vaa_response.is_err(), + "VAA with more guardians than the established guardian set should fail \"TooManySignatures\"" + ); + + // VAA signed with a non-matching guardianset + let guardian_keys: Vec = vec![]; + let (_, signed_vaa) = vaa_body.clone().sign_vaa( + // signing with 0 guardians + &guardian_keys.into(), + ); + + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + ); + + assert!( + vaa_response.is_err(), + "VAA with fewer guardians than the established guardian set should fail \"NoQuorum\"" + ); + + // VAA signed with a different guardian + let guardian_keys: Vec = vec![SigningKey::from_bytes(&[ + 121, 51, 199, 93, 237, 227, 62, 220, 128, 129, 195, 4, 190, 163, 254, 12, 212, 224, 188, + 76, 141, 242, 229, 121, 192, 5, 161, 176, 136, 99, 83, 53, + ]) + .unwrap()]; + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&guardian_keys.into()); + + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + ); + assert!( + vaa_response.is_err(), + "VAA signed by a guardian not in the established guardian set should fail \"GuardianSignatureError\"" + ); + + // Verifying a VAA that's already been executed should fail + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + // Submit the VAA first + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!( + vaa_response.is_ok(), + "VAA submission should succeed when the VAA has not been executed" + ); + + // Attempt to verify the VAA again after it's been executed + let vaa_response: StdResult = app.wrap().query_wasm_smart( + wormhole_contract.clone(), + &QueryMsg::VerifyVAA { + vaa: signed_vaa, + block_time: app.block_info().height, + }, + ); + assert!( + vaa_response.is_err(), + "VAA that has already been executed should fail \"VAAAlreadyExecuted\"" + ); + + Ok(()) +} + +#[test] +#[ignore] +pub fn update_contract_gov_vaa() -> StdResult<()> { + /// TODO: This test is disabled because it requires cw_multi_test 0.16+ to update the contract admin + use wormchain_ibc_receiver::contract::{ + execute as receiver_execute, instantiate as receiver_instantiate, query as receiver_query, + }; + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + admin, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + // We have to give the wormhole contract admin rights over itself so that it can migrate itself + let update_admin_response = app.execute( + admin.clone(), + cosmwasm_std::CosmosMsg::Wasm(cosmwasm_std::WasmMsg::UpdateAdmin { + contract_addr: wormhole_contract.to_string(), + admin: wormhole_contract.to_string(), + }), + ); + + assert!( + update_admin_response.is_ok(), + "Update Contract Admin should succeed" + ); + + // store the wormchain_ibc_receiver contract so we can migrate to it + let new_code_id = app.store_code(Box::new(ContractWrapper::new( + |deps, env, info, msg| { + receiver_execute(deps, env, info, msg) + .map_err(|anyhow_err| StdError::generic_err(anyhow_err.to_string())) + }, + |deps, env, info, msg| { + receiver_instantiate( + DepsMut { + storage: deps.storage, + api: deps.api, + querier: QuerierWrapper::new(deps.querier.deref()), + }, + env, + info, + msg, + ) + .map_err(|anyhow_err| StdError::generic_err(anyhow_err.to_string())) + }, + |deps, env, msg| { + receiver_query( + Deps { + storage: deps.storage, + api: deps.api, + querier: QuerierWrapper::::new(deps.querier.deref()), + }, + env, + msg, + ) + .map_err(|anyhow_err| StdError::generic_err(anyhow_err.to_string())) + }, + ))); + + let vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::ContractUpgrade { + new_contract: Address(Uint256::from(new_code_id).to_be_bytes()), + }, + }, + ); + + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + // Submit the VAA first + let vaa_response = app.execute_contract( + admin.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + + assert!( + vaa_response.is_ok(), + "Update Contract VAA submission should succeed" + ); + + Ok(()) +} + +#[test] +#[ignore] +pub fn set_fee_gov_vaa() -> StdResult<()> { + // TODO: set the appropriate MockedApi in the AppBuilder so that PostMessage can be integration tested + // This should be simple once we're on cosmwasm 1.5+ https://docs.rs/cw-multi-test/1.2.0/cw_multi_test/struct.SimpleAddressGenerator.html + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + // At this point there is no fee and this should be a free action. + let post_message_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::PostMessage { + message: b"test".into(), + nonce: 1, + }, + &[], + ); + + assert!( + post_message_response.is_ok(), + "Post Message should succeed when there is no fee" + ); + + let vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::SetFee { + amount: Amount(Uint256::from(18u128).to_be_bytes()), + }, + }, + ); + + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + // Submit the VAA first + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!(vaa_response.is_ok(), "SetFee VAA submission should succeed"); + + // At this point there is a fee and this should fail since we aren't paying the fee. + let post_message_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::PostMessage { + message: b"test".into(), + nonce: 1, + }, + &[], + ); + + assert!( + post_message_response.is_err(), + "Post Message should fail \"FeeTooLow\"" + ); + + Ok(()) +} + +#[test] +pub fn set_fee_gov_vaa_2() -> StdResult<()> { + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + let vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::SetFee { + amount: Amount(Uint256::from(18u128).to_be_bytes()), + }, + }, + ); + + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + // Submit the VAA first + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!(vaa_response.is_ok(), "SetFee VAA submission should succeed"); + + // now query the state and see if the fee has been updated + let get_state_resp: GetStateResponse = app + .wrap() + .query_wasm_smart(wormhole_contract.clone(), &QueryMsg::GetState {})?; + assert_eq!( + get_state_resp.fee.denom, "uluna", + "fee denom does not match" + ); + assert_eq!( + get_state_resp.fee.amount, + Uint128::from(18u128), + "fee amount does not match" + ); + + Ok(()) +} + +#[test] +pub fn submit_vaa_replay_protection() { + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + let vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::SetFee { + amount: Amount(Uint256::from(18u128).to_be_bytes()), + }, + }, + ); + + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + // Submit the VAA first + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!(vaa_response.is_ok(), "SetFee VAA submission should succeed"); + + // Submit the VAA again + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!( + vaa_response.is_err(), + "Submitting the same VAA twice should fail" + ); +} + +#[test] +pub fn only_gov_vaas_allowed() { + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + let vaa_body = create_transfer_vaa_body(1, Address([100u8; 32])); + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + + assert!( + vaa_response.is_err(), + "VAA submission should fail \"InvalidVAA\" when not a governance VAA" + ); +} + +#[test] +pub fn only_core_module_vaas_allowed() { + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + let vaa_body = create_gov_vaa_body( + 1, + relayer::GovernancePacket { + chain: Chain::Terra2, + action: relayer::Action::RegisterChain { + chain: Chain::Solana, + emitter_address: Address([0u8; 32]), + }, + }, + ); + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!( + vaa_response.is_err(), + "VAA submission should fail \"this is not a valid module\" when not a core module VAA" + ); +} + +#[test] +pub fn update_guardian_set() -> StdResult<()> { + let WormholeApp { + mut app, + wormhole_contract, + wormhole_keeper, + user, + .. + } = WormholeApp::new_with_guardians(vec![SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, 206, + 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap()]); + + let vaa_body = create_gov_vaa_body( + 1, + GovernancePacket { + chain: Chain::Terra2, + action: Action::SetFee { + amount: Amount([0u8; 32]), + }, + }, + ); + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + assert!( + vaa_response.is_ok(), + "VAA submission with initial guardian set should succeed" + ); + + // Add a second guardian + let new_guardian_keys = vec![ + SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, + 206, 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 150, 48, 135, 223, 194, 186, 243, 139, 177, 8, 126, 32, 210, 57, 42, 28, 29, 102, 196, + 201, 106, 136, 40, 149, 218, 150, 240, 213, 192, 128, 161, 245, + ]) + .unwrap(), + ]; + + // Query the current guardian set so we know what the next index should be + let guardian_set_response: GuardianSetInfoResponse = app + .wrap() + .query_wasm_smart(wormhole_contract.clone(), &QueryMsg::GuardianSetInfo {})?; + + let invalid_guardian_set_vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::GuardianSetUpgrade { + // This should fail because the index should only increase by one + new_guardian_set_index: guardian_set_response.guardian_set_index + 2, + new_guardian_set: GuardianSetInfo { + addresses: new_guardian_keys + .iter() + .map(|key| -> wormhole_sdk::GuardianAddress { + key.clone().into_guardian_address() + }) + .collect(), + }, + }, + }, + ); + + let (_, signed_guardian_set_update_vaa) = invalid_guardian_set_vaa_body + .clone() + .sign_vaa(&wormhole_keeper); + + let guardian_set_update_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_guardian_set_update_vaa.clone(), + }, + &[], + ); + assert!( + guardian_set_update_response.is_err(), + "UpdateGuardianSet VAA submission should fail \"InvalidGuardianSetIndex\"" + ); + + let update_guardian_set_vaa_body = create_gov_vaa_body( + 2, + GovernancePacket { + chain: Chain::Terra2, + action: Action::GuardianSetUpgrade { + new_guardian_set_index: guardian_set_response.guardian_set_index + 1, + new_guardian_set: GuardianSetInfo { + addresses: new_guardian_keys + .iter() + .map(|key| -> wormhole_sdk::GuardianAddress { + key.clone().into_guardian_address() + }) + .collect(), + }, + }, + }, + ); + + // Sign with the current singular guardian + let (_, signed_guardian_set_update_vaa) = update_guardian_set_vaa_body + .clone() + .sign_vaa(&wormhole_keeper); + + let guardian_set_update_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_guardian_set_update_vaa.clone(), + }, + &[], + ); + assert!( + guardian_set_update_response.is_ok(), + "UpdateGuardianSet VAA submission should succeed" + ); + + let wormhole_keeper: WormholeKeeper = new_guardian_keys.into(); + wormhole_keeper.set_index(guardian_set_response.guardian_set_index + 1); + + let vaa_body = create_gov_vaa_body( + 1, + GovernancePacket { + chain: Chain::Terra2, + action: Action::SetFee { + amount: Amount(Uint256::from(1u128).to_be_bytes()), + }, + }, + ); + let (_, signed_vaa) = vaa_body.clone().sign_vaa(&wormhole_keeper); + + let vaa_response = app.execute_contract( + user.clone(), + wormhole_contract.clone(), + &ExecuteMsg::SubmitVAA { + vaa: signed_vaa.clone(), + }, + &[], + ); + + assert!( + vaa_response.is_ok(), + "VAA submission with updated guardian set should succeed" + ); + + let get_state_resp: GetStateResponse = app + .wrap() + .query_wasm_smart(wormhole_contract.clone(), &QueryMsg::GetState {})?; + assert_eq!( + get_state_resp.fee.amount, + Uint128::from(1u128), + "Fee should have been updated to 1uluna" + ); + + Ok(()) +} diff --git a/cosmwasm/contracts/wormhole/src/testing/mod.rs b/cosmwasm/contracts/wormhole/src/testing/mod.rs index 14f00389d0..e7791b3196 100644 --- a/cosmwasm/contracts/wormhole/src/testing/mod.rs +++ b/cosmwasm/contracts/wormhole/src/testing/mod.rs @@ -1 +1,3 @@ +pub mod integration; mod tests; +pub mod utils; diff --git a/cosmwasm/contracts/wormhole/src/testing/utils.rs b/cosmwasm/contracts/wormhole/src/testing/utils.rs new file mode 100644 index 0000000000..09c0f969af --- /dev/null +++ b/cosmwasm/contracts/wormhole/src/testing/utils.rs @@ -0,0 +1,196 @@ +use std::convert::TryInto; + +use crate::{ + contract::{execute, instantiate, query}, + msg::InstantiateMsg, + state::{GuardianAddress, GuardianSetInfo}, +}; +use cosmwasm_std::{Addr, Binary, Uint256}; +use cw_multi_test::{App, AppBuilder, ContractWrapper, Executor, WasmKeeper}; +use k256::ecdsa::SigningKey; +use k256::elliptic_curve::sec1::ToEncodedPoint; +use serde::Serialize; +use tiny_keccak::{Hasher, Keccak}; +use wormhole_bindings::fake::{create_vaa_body, default_guardian_keys, WormholeKeeper}; +use wormhole_sdk::{ + token::Message, + vaa::{Body, Header, Vaa}, + Address, Amount, Chain, GOVERNANCE_EMITTER, +}; + +/// Sign a VAA body with version 2 in the header. +pub fn sign_vaa_body_version_2( + wh: WormholeKeeper, + body: Body

, +) -> (Vaa

, Binary) { + let data = serde_wormhole::to_vec(&body).unwrap(); + let signatures = WormholeKeeper::new().sign(&data); + + let header = Header { + version: 2, + guardian_set_index: wh.guardian_set_index(), + signatures, + }; + + let v: Vaa

= (header, body).into(); + let data = serde_wormhole::to_vec(&v).map(From::from).unwrap(); + + (v, data) +} + +pub fn create_transfer_vaa_body(i: usize, emitter_address: Address) -> Body { + create_vaa_body( + i, + i as u16, + emitter_address, + Message::Transfer { + amount: Amount(Uint256::from(i as u128).to_be_bytes()), + token_address: Address([(i + 1) as u8; 32]), + token_chain: (i as u16).into(), + recipient: Address([i as u8; 32]), + recipient_chain: ((i + 2) as u16).into(), + fee: Amount([0u8; 32]), + }, + ) +} + +pub struct WormholeApp { + pub app: App< + cw_multi_test::BankKeeper, + cosmwasm_std::testing::MockApi, + cosmwasm_std::MemoryStorage, + WormholeKeeper, + WasmKeeper, + >, + pub admin: Addr, + pub user: Addr, + pub wormhole_contract: Addr, + pub wormhole_keeper: WormholeKeeper, +} + +impl WormholeApp { + pub fn new_with_guardians(guardians: Vec) -> Self { + create_wormhole_app(Some(( + instantiate_with_guardians( + guardians + .iter() + .map(|k| k.clone().into()) + .collect::>() + .as_slice(), + ), + guardians, + ))) + } + pub fn new_with_faker_guardians() -> Self { + create_wormhole_app(Some(( + instantiate_with_guardians( + default_guardian_keys() + .iter() + .map(|k| k.clone().into()) + .collect::>() + .as_slice(), + ), + default_guardian_keys().to_vec(), + ))) + } +} + +pub fn instantiate_with_guardians(guardians: &[GuardianAddress]) -> InstantiateMsg { + InstantiateMsg { + gov_chain: Chain::Solana.into(), + gov_address: GOVERNANCE_EMITTER.0.into(), + initial_guardian_set: GuardianSetInfo { + addresses: guardians.to_vec(), + expiration_time: 1571797500, + }, + guardian_set_expirity: 50, + chain_id: Chain::Terra2.into(), + fee_denom: "uluna".to_string(), + } +} + +pub fn create_wormhole_app( + instantiate_msg: Option<(InstantiateMsg, Vec)>, +) -> WormholeApp { + let (instantiate_msg, keys) = instantiate_msg.unwrap_or_else(|| { + let key_bytes = + hex::decode("beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe").expect("Decoding failed"); + ( + instantiate_with_guardians(&[GuardianAddress { + bytes: key_bytes.clone().into(), + }]), + vec![SigningKey::from_bytes(key_bytes.as_slice()).unwrap()], + ) + }); + + let wormhole_keeper: WormholeKeeper = keys.to_vec().into(); + + let mut app = AppBuilder::new_custom() + .with_custom(wormhole_keeper.clone()) + .build(|_, _, _| {}); + + let admin = Addr::unchecked("admin"); + let user = Addr::unchecked("user"); + + let cw_wormhole_wrapper = ContractWrapper::new_with_empty(execute, instantiate, query); + + let code_id = app.store_code(Box::new(cw_wormhole_wrapper)); + + let contract_addr = app + .instantiate_contract( + code_id, + admin.clone(), + &instantiate_msg, + &[], + "cw_wormhole", + Some(admin.to_string()), + ) + .unwrap(); + + WormholeApp { + app, + admin, + user, + wormhole_contract: contract_addr, + wormhole_keeper, + } +} + +impl From for GuardianAddress { + fn from(value: SigningKey) -> Self { + // Get the public key bytes + let public_key = value.verifying_key().to_encoded_point(false); + let public_key_bytes = public_key.as_bytes(); + + // Skip the first byte (0x04 prefix for uncompressed public keys) + let key_without_prefix = &public_key_bytes[1..]; + + // Hash with Keccak-256 + let mut hasher = Keccak::v256(); + let mut hash = [0u8; 32]; + hasher.update(key_without_prefix); + hasher.finalize(&mut hash); + + // Take last 20 bytes + let address = &hash[12..32]; + + GuardianAddress { + bytes: address.to_vec().into(), + } + } +} + +pub trait IntoGuardianAddress { + fn into_guardian_address(self) -> wormhole_sdk::GuardianAddress; +} + +impl IntoGuardianAddress for SigningKey { + fn into_guardian_address(self) -> wormhole_sdk::GuardianAddress { + let guardian: GuardianAddress = self.into(); + + // Take last 20 bytes + let address: [u8; 20] = guardian.bytes.0.try_into().unwrap(); + + wormhole_sdk::GuardianAddress(address) + } +} diff --git a/cosmwasm/contracts/wormhole/tests/integration.rs b/cosmwasm/contracts/wormhole/tests/integration.rs deleted file mode 100644 index b779444ee5..0000000000 --- a/cosmwasm/contracts/wormhole/tests/integration.rs +++ /dev/null @@ -1,65 +0,0 @@ -use cosmwasm_std::{ - from_slice, - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Coin, OwnedDeps, Response, Storage, -}; -use cosmwasm_storage::to_length_prefixed; - -use cw_wormhole::{ - contract::instantiate, - msg::InstantiateMsg, - state::{ConfigInfo, GuardianAddress, GuardianSetInfo, CONFIG_KEY}, -}; - -static INITIALIZER: &str = "initializer"; -static GOV_ADDR: &[u8] = b"GOVERNANCE_ADDRESS"; - -fn get_config_info(storage: &S) -> ConfigInfo { - let key = to_length_prefixed(CONFIG_KEY); - let data = storage.get(&key).expect("data should exist"); - from_slice(&data).expect("invalid data") -} - -fn do_init(guardians: &[GuardianAddress]) -> OwnedDeps { - let mut deps = mock_dependencies(); - let init_msg = InstantiateMsg { - gov_chain: 0, - gov_address: GOV_ADDR.into(), - initial_guardian_set: GuardianSetInfo { - addresses: guardians.to_vec(), - expiration_time: 100, - }, - guardian_set_expirity: 50, - chain_id: 18, - fee_denom: "uluna".to_string(), - }; - let env = mock_env(); - let info = mock_info(INITIALIZER, &[]); - let res: Response = instantiate(deps.as_mut(), env, info, init_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // query the store directly - assert_eq!( - get_config_info(&deps.storage), - ConfigInfo { - guardian_set_index: 0, - guardian_set_expirity: 50, - gov_chain: 0, - gov_address: GOV_ADDR.to_vec(), - fee: Coin::new(0, "uluna"), - chain_id: 18, - fee_denom: "uluna".to_string(), - } - ); - deps -} - -#[test] -fn init_works() { - let guardians = [GuardianAddress { - bytes: hex::decode("beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe") - .expect("Decoding failed") - .into(), - }]; - let _deps = do_init(&guardians); -} diff --git a/cosmwasm/packages/wormhole-bindings/Cargo.toml b/cosmwasm/packages/wormhole-bindings/Cargo.toml index e9a82a3f1c..ad9d55f046 100644 --- a/cosmwasm/packages/wormhole-bindings/Cargo.toml +++ b/cosmwasm/packages/wormhole-bindings/Cargo.toml @@ -14,6 +14,6 @@ cosmwasm-std = "1" schemars = "0.8.8" serde = { version = "1.0.137", default-features = false, features = ["derive"] } serde_wormhole = { workspace = true, optional = true } -cw-multi-test = { version = "0.13.2", optional = true } +cw-multi-test = { version = "0.14", optional = true } k256 = { version = "0.11", optional = true, features = ["ecdsa", "keccak256"] } wormhole-sdk.workspace = true diff --git a/cosmwasm/packages/wormhole-bindings/src/fake.rs b/cosmwasm/packages/wormhole-bindings/src/fake.rs index c97771addf..eb47a990ca 100644 --- a/cosmwasm/packages/wormhole-bindings/src/fake.rs +++ b/cosmwasm/packages/wormhole-bindings/src/fake.rs @@ -5,17 +5,61 @@ use cosmwasm_std::{to_binary, Addr, Api, Binary, BlockInfo, CustomQuery, Empty, use cw_multi_test::{AppResponse, CosmosRouter, Module}; use k256::ecdsa::{recoverable, signature::Signer, SigningKey}; use schemars::JsonSchema; -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Serialize}; use serde_wormhole::RawMessage; -use wormhole_sdk::vaa::{digest, Header, Signature}; +use wormhole_sdk::{ + token::Message, + vaa::{digest, Body, Header, Signature}, + Address, Chain, Vaa, GOVERNANCE_EMITTER, +}; use crate::WormholeQuery; +pub fn default_guardian_keys() -> [SigningKey; 7] { + [ + SigningKey::from_bytes(&[ + 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, 238, + 206, 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 150, 48, 135, 223, 194, 186, 243, 139, 177, 8, 126, 32, 210, 57, 42, 28, 29, 102, 196, + 201, 106, 136, 40, 149, 218, 150, 240, 213, 192, 128, 161, 245, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 121, 51, 199, 93, 237, 227, 62, 220, 128, 129, 195, 4, 190, 163, 254, 12, 212, 224, + 188, 76, 141, 242, 229, 121, 192, 5, 161, 176, 136, 99, 83, 53, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 224, 180, 4, 114, 215, 161, 184, 12, 218, 96, 20, 141, 154, 242, 46, 230, 167, 165, 54, + 141, 108, 64, 146, 27, 193, 89, 251, 139, 234, 132, 124, 30, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 69, 1, 17, 179, 19, 47, 56, 47, 255, 219, 143, 89, 115, 54, 242, 209, 163, 131, 225, + 30, 59, 195, 217, 141, 167, 253, 6, 95, 252, 52, 7, 223, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 181, 3, 165, 125, 15, 200, 155, 56, 157, 204, 105, 221, 203, 149, 215, 175, 220, 228, + 200, 37, 169, 39, 68, 127, 132, 196, 203, 232, 155, 55, 67, 253, + ]) + .unwrap(), + SigningKey::from_bytes(&[ + 72, 81, 175, 107, 23, 108, 178, 66, 32, 53, 14, 117, 233, 33, 114, 102, 68, 89, 83, + 201, 129, 57, 56, 130, 214, 212, 172, 16, 23, 22, 234, 160, + ]) + .unwrap(), + ] +} + #[derive(Debug)] struct Inner { index: u32, expiration: u64, - guardians: [SigningKey; 7], + guardians: Vec, } #[derive(Clone, Debug)] @@ -23,47 +67,10 @@ pub struct WormholeKeeper(Rc>); impl WormholeKeeper { pub fn new() -> WormholeKeeper { - let guardians = [ - SigningKey::from_bytes(&[ - 93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199, - 238, 206, 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226, - ]) - .unwrap(), - SigningKey::from_bytes(&[ - 150, 48, 135, 223, 194, 186, 243, 139, 177, 8, 126, 32, 210, 57, 42, 28, 29, 102, - 196, 201, 106, 136, 40, 149, 218, 150, 240, 213, 192, 128, 161, 245, - ]) - .unwrap(), - SigningKey::from_bytes(&[ - 121, 51, 199, 93, 237, 227, 62, 220, 128, 129, 195, 4, 190, 163, 254, 12, 212, 224, - 188, 76, 141, 242, 229, 121, 192, 5, 161, 176, 136, 99, 83, 53, - ]) - .unwrap(), - SigningKey::from_bytes(&[ - 224, 180, 4, 114, 215, 161, 184, 12, 218, 96, 20, 141, 154, 242, 46, 230, 167, 165, - 54, 141, 108, 64, 146, 27, 193, 89, 251, 139, 234, 132, 124, 30, - ]) - .unwrap(), - SigningKey::from_bytes(&[ - 69, 1, 17, 179, 19, 47, 56, 47, 255, 219, 143, 89, 115, 54, 242, 209, 163, 131, - 225, 30, 59, 195, 217, 141, 167, 253, 6, 95, 252, 52, 7, 223, - ]) - .unwrap(), - SigningKey::from_bytes(&[ - 181, 3, 165, 125, 15, 200, 155, 56, 157, 204, 105, 221, 203, 149, 215, 175, 220, - 228, 200, 37, 169, 39, 68, 127, 132, 196, 203, 232, 155, 55, 67, 253, - ]) - .unwrap(), - SigningKey::from_bytes(&[ - 72, 81, 175, 107, 23, 108, 178, 66, 32, 53, 14, 117, 233, 33, 114, 102, 68, 89, 83, - 201, 129, 57, 56, 130, 214, 212, 172, 16, 23, 22, 234, 160, - ]) - .unwrap(), - ]; WormholeKeeper(Rc::new(RefCell::new(Inner { index: 0, expiration: 0, - guardians, + guardians: default_guardian_keys().to_vec(), }))) } @@ -213,6 +220,16 @@ impl Default for WormholeKeeper { } } +impl From> for WormholeKeeper { + fn from(guardians: Vec) -> Self { + WormholeKeeper(Rc::new(RefCell::new(Inner { + index: 0, + expiration: 0, + guardians, + }))) + } +} + impl Module for WormholeKeeper { type ExecT = Empty; type QueryT = WormholeQuery; @@ -256,3 +273,54 @@ impl Module for WormholeKeeper { self.query(request, block) } } + +pub fn create_gov_vaa_body(i: usize, payload: Payload) -> Body { + Body { + timestamp: i as u32, + nonce: i as u32, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: i as u64, + consistency_level: 0, + payload, + } +} + +pub fn create_vaa_body( + i: usize, + emitter_chain: impl Into, + emitter_address: Address, + payload: Message, +) -> Body { + Body { + timestamp: i as u32, + nonce: i as u32, + emitter_chain: emitter_chain.into(), + emitter_address, + sequence: i as u64, + consistency_level: 32, + payload, + } +} + +pub trait SignVaa { + fn sign_vaa(self, wh: &WormholeKeeper) -> (Vaa, Binary); +} + +impl SignVaa for Body { + fn sign_vaa(self, wh: &WormholeKeeper) -> (Vaa, Binary) { + let data = serde_wormhole::to_vec(&self).unwrap(); + let signatures = wh.sign(&data); + + let header = Header { + version: 1, + guardian_set_index: wh.guardian_set_index(), + signatures, + }; + + let v: Vaa = (header, self).into(); + let data = serde_wormhole::to_vec(&v).map(From::from).unwrap(); + + (v, data) + } +} diff --git a/wormchain/interchaintest/setup.go b/wormchain/interchaintest/setup.go index 6cc5dbc5ed..0ae366dac7 100644 --- a/wormchain/interchaintest/setup.go +++ b/wormchain/interchaintest/setup.go @@ -132,11 +132,11 @@ func BuildInterchain(t *testing.T, chains []ibc.Chain) (context.Context, ibc.Rel }) err := ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ - TestName: t.Name(), - Client: client, - NetworkID: network, - SkipPathCreation: false, - BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + // BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), }) require.NoError(t, err) diff --git a/wormchain/interchaintest/upgrade_test.go b/wormchain/interchaintest/upgrade_test.go index 2ce7440f84..4e18b3b45e 100644 --- a/wormchain/interchaintest/upgrade_test.go +++ b/wormchain/interchaintest/upgrade_test.go @@ -40,7 +40,7 @@ import ( // - Verify asset 1 balance of gaia user 1, osmo user 1, osmo user 2, and cw20 contract total supply func TestUpgrade(t *testing.T) { // Base setup - numVals := 5 + numVals := 3 guardians := guardians.CreateValSet(t, numVals) chains := CreateChains(t, "v2.18.1", *guardians) ctx, r, eRep, client := BuildInterchain(t, chains)