From dfdf31dcce4c629f5cd1556f3ba1f548d3333862 Mon Sep 17 00:00:00 2001 From: Hendrik Hofstadt Date: Tue, 27 Jul 2021 22:18:33 +0200 Subject: [PATCH] Implement WASM client for token bridge Change-Id: I592cc0ece0553d55c50d87010783fefb7f9b5f2f --- generate-wasm.sh | 7 +- solana/bridge/program/Cargo.toml | 1 + solana/bridge/program/src/api/post_vaa.rs | 6 +- solana/bridge/program/src/lib.rs | 2 + solana/modules/token_bridge/Cargo.lock | 2 - solana/modules/token_bridge/Cargo.toml | 5 +- .../modules/token_bridge/program/Cargo.toml | 1 + .../token_bridge/program/src/instructions.rs | 12 +- .../modules/token_bridge/program/src/lib.rs | 10 +- .../modules/token_bridge/program/src/wasm.rs | 374 ++++++++++++++++++ 10 files changed, 409 insertions(+), 11 deletions(-) create mode 100644 solana/modules/token_bridge/program/src/wasm.rs diff --git a/generate-wasm.sh b/generate-wasm.sh index 34c4a2a0..df9e66d1 100755 --- a/generate-wasm.sh +++ b/generate-wasm.sh @@ -10,6 +10,11 @@ set -euo pipefail -v $(pwd)/../bridge_ui/rust_modules/core:/usr/src/bridge/bridge/program/pkg \ -e EMITTER_ADDRESS=11111111111111111111111111111115 \ localhost/certusone/wormhole-wasmpack:latest \ - /usr/local/cargo/bin/wasm-pack build --target nodejs -- --features no-entrypoint + /usr/local/cargo/bin/wasm-pack build --target nodejs -- --features wasm cp $(pwd)/../bridge_ui/rust_modules/core/. $(pwd)/../clients/solana/pkg/ -R + docker run --rm -it --workdir /usr/src/bridge/modules/token_bridge/program \ + -v $(pwd)/../bridge_ui/rust_modules/token:/usr/src/bridge/modules/token_bridge/program/pkg \ + -e EMITTER_ADDRESS=11111111111111111111111111111115 \ + localhost/certusone/wormhole-wasmpack:latest \ + /usr/local/cargo/bin/wasm-pack build --target nodejs -- --features wasm ) diff --git a/solana/bridge/program/Cargo.toml b/solana/bridge/program/Cargo.toml index cdec6e77..44e5c424 100644 --- a/solana/bridge/program/Cargo.toml +++ b/solana/bridge/program/Cargo.toml @@ -12,6 +12,7 @@ name = "bridge" client = ["solitaire/client", "solitaire-client", "no-entrypoint"] cpi = ["no-entrypoint"] default = [] +wasm = ["no-entrypoint"] no-entrypoint = ["solitaire/no-entrypoint"] trace = ["solitaire/trace"] diff --git a/solana/bridge/program/src/api/post_vaa.rs b/solana/bridge/program/src/api/post_vaa.rs index 993651ed..108ef8e5 100644 --- a/solana/bridge/program/src/api/post_vaa.rs +++ b/solana/bridge/program/src/api/post_vaa.rs @@ -42,6 +42,10 @@ use std::io::{ Cursor, Write, }; +use serde::{ + Deserialize, + Serialize +}; impl From<&PostVAAData> for GuardianSetDerivationData { fn from(data: &PostVAAData) -> Self { @@ -88,7 +92,7 @@ pub struct Signature { pub type ForeignAddress = [u8; 32]; -#[derive(Default, BorshSerialize, BorshDeserialize, Clone)] +#[derive(Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize)] pub struct PostVAAData { // Header part pub version: u8, diff --git a/solana/bridge/program/src/lib.rs b/solana/bridge/program/src/lib.rs index 5d04d8cb..7f129e47 100644 --- a/solana/bridge/program/src/lib.rs +++ b/solana/bridge/program/src/lib.rs @@ -11,9 +11,11 @@ pub mod vaa; #[cfg(feature = "no-entrypoint")] pub mod instructions; +#[cfg(feature = "wasm")] #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] extern crate wasm_bindgen; +#[cfg(feature = "wasm")] #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] pub mod wasm; diff --git a/solana/modules/token_bridge/Cargo.lock b/solana/modules/token_bridge/Cargo.lock index 248b56c3..41ba2b0d 100644 --- a/solana/modules/token_bridge/Cargo.lock +++ b/solana/modules/token_bridge/Cargo.lock @@ -1502,8 +1502,6 @@ checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "memmap2" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" dependencies = [ "libc", ] diff --git a/solana/modules/token_bridge/Cargo.toml b/solana/modules/token_bridge/Cargo.toml index 4ea365e8..c79096a1 100644 --- a/solana/modules/token_bridge/Cargo.toml +++ b/solana/modules/token_bridge/Cargo.toml @@ -1,2 +1,5 @@ [workspace] -members = ["program"] \ No newline at end of file +members = ["program"] + +[patch.crates-io] +memmap2 = { path = "../../bridge/memmap2-rs" } \ No newline at end of file diff --git a/solana/modules/token_bridge/program/Cargo.toml b/solana/modules/token_bridge/program/Cargo.toml index 1fdfd3eb..d1bf293a 100644 --- a/solana/modules/token_bridge/program/Cargo.toml +++ b/solana/modules/token_bridge/program/Cargo.toml @@ -11,6 +11,7 @@ name = "token_bridge" [features] no-entrypoint = ["solitaire/no-entrypoint", "rand"] trace = ["solitaire/trace"] +wasm = ["no-entrypoint"] client = ["solitaire-client", "solitaire/client", "no-entrypoint"] cpi = ["no-entrypoint"] default = [] diff --git a/solana/modules/token_bridge/program/src/instructions.rs b/solana/modules/token_bridge/program/src/instructions.rs index 502cc1f7..6bbd083b 100644 --- a/solana/modules/token_bridge/program/src/instructions.rs +++ b/solana/modules/token_bridge/program/src/instructions.rs @@ -58,15 +58,17 @@ use bridge::{ CHAIN_ID_SOLANA, }; use primitive_types::U256; -use solana_program::instruction::Instruction; +use solana_program::{ + instruction::{ + AccountMeta, + Instruction, + }, + pubkey::Pubkey, +}; use solitaire::{ processors::seeded::Seeded, AccountState, }; -use solana_program::instruction::{ - AccountMeta, -}; -use solana_program::pubkey::Pubkey; use spl_token::state::Mint; use std::str::FromStr; diff --git a/solana/modules/token_bridge/program/src/lib.rs b/solana/modules/token_bridge/program/src/lib.rs index 52e117c8..002e31ee 100644 --- a/solana/modules/token_bridge/program/src/lib.rs +++ b/solana/modules/token_bridge/program/src/lib.rs @@ -6,6 +6,14 @@ #[cfg(feature = "no-entrypoint")] pub mod instructions; +#[cfg(feature = "wasm")] +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +extern crate wasm_bindgen; + +#[cfg(feature = "wasm")] +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +pub mod wasm; + pub mod accounts; pub mod api; pub mod messages; @@ -20,6 +28,7 @@ pub use api::{ register_chain, transfer_native, transfer_wrapped, + upgrade_contract, AttestToken, AttestTokenData, CompleteNative, @@ -38,7 +47,6 @@ pub use api::{ TransferWrappedData, UpgradeContract, UpgradeContractData, - upgrade_contract }; use solitaire::*; diff --git a/solana/modules/token_bridge/program/src/wasm.rs b/solana/modules/token_bridge/program/src/wasm.rs new file mode 100644 index 00000000..915d3b07 --- /dev/null +++ b/solana/modules/token_bridge/program/src/wasm.rs @@ -0,0 +1,374 @@ +use crate::{ + instructions::{ + attest, + complete_native, + complete_wrapped, + create_wrapped, + register_chain, + transfer_native, + transfer_wrapped, + upgrade_contract, + }, + messages::{ + GovernancePayloadUpgrade, + PayloadAssetMeta, + PayloadGovernanceRegisterChain, + PayloadTransfer, + }, + CompleteNativeData, + CompleteWrappedData, + CreateWrappedData, + RegisterChainData, + TransferNativeData, + TransferWrappedData, +}; +use bridge::{ + accounts::MessageDerivationData, + vaa::VAA, + DeserializePayload, + PostVAAData, +}; +use solana_program::pubkey::Pubkey; +use solitaire::{ + processors::seeded::Seeded, + AccountState, +}; +use std::str::FromStr; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn attest_ix( + program_id: String, + bridge_id: String, + payer: String, + mint: String, + decimals: u8, + mint_meta: String, + nonce: u32, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let mint = Pubkey::from_str(mint.as_str()).unwrap(); + let mint_meta = Pubkey::from_str(mint_meta.as_str()).unwrap(); + + let ix = attest( + program_id, bridge_id, payer, mint, decimals, mint_meta, nonce, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + +#[wasm_bindgen] +pub fn transfer_native_ix( + program_id: String, + bridge_id: String, + payer: String, + from: String, + mint: String, + nonce: u32, + amount: u64, + fee: u64, + target_address: Vec, + target_chain: u16, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let from = Pubkey::from_str(from.as_str()).unwrap(); + let mint = Pubkey::from_str(mint.as_str()).unwrap(); + + let mut target_addr = [0u8; 32]; + target_addr.copy_from_slice(target_address.as_slice()); + + let ix = transfer_native( + program_id, + bridge_id, + payer, + from, + mint, + TransferNativeData { + nonce, + amount, + fee, + target_address: target_addr, + target_chain, + }, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + +#[wasm_bindgen] +pub fn transfer_wrapped_ix( + program_id: String, + bridge_id: String, + payer: String, + from: String, + from_owner: String, + token_chain: u16, + token_address: Vec, + nonce: u32, + amount: u64, + fee: u64, + target_address: Vec, + target_chain: u16, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let from = Pubkey::from_str(from.as_str()).unwrap(); + let from_owner = Pubkey::from_str(from_owner.as_str()).unwrap(); + + let mut target_addr = [0u8; 32]; + target_addr.copy_from_slice(target_address.as_slice()); + let mut token_addr = [0u8; 32]; + token_addr.copy_from_slice(token_address.as_slice()); + + let ix = transfer_wrapped( + program_id, + bridge_id, + payer, + from, + from_owner, + token_chain, + token_addr, + TransferWrappedData { + nonce, + amount, + fee, + target_address: target_addr, + target_chain, + }, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + +#[wasm_bindgen] +pub fn complete_transfer_native_ix( + program_id: String, + bridge_id: String, + payer: String, + vaa: Vec, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let vaa = VAA::deserialize(vaa.as_slice()).unwrap(); + let payload = PayloadTransfer::deserialize(&mut vaa.payload.as_slice()).unwrap(); + let message_key = bridge::accounts::Message::<'_, { AccountState::Uninitialized }>::key( + &MessageDerivationData { + emitter_key: vaa.emitter_address, + emitter_chain: vaa.emitter_chain, + nonce: vaa.nonce, + payload: vaa.payload.clone(), + sequence: None, + }, + &program_id, + ); + let post_vaa_data = PostVAAData { + version: vaa.version, + guardian_set_index: vaa.guardian_set_index, + timestamp: vaa.timestamp, + nonce: vaa.nonce, + emitter_chain: vaa.emitter_chain, + emitter_address: vaa.emitter_address, + sequence: vaa.sequence, + consistency_level: vaa.consistency_level, + payload: vaa.payload, + }; + + let ix = complete_native( + program_id, + bridge_id, + payer, + message_key, + post_vaa_data, + Pubkey::new(&payload.to[..]), + Pubkey::new(&payload.token_address), + CompleteNativeData {}, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + +#[wasm_bindgen] +pub fn complete_transfer_wrapped_ix( + program_id: String, + bridge_id: String, + payer: String, + vaa: Vec, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let vaa = VAA::deserialize(vaa.as_slice()).unwrap(); + let payload = PayloadTransfer::deserialize(&mut vaa.payload.as_slice()).unwrap(); + let message_key = bridge::accounts::Message::<'_, { AccountState::Uninitialized }>::key( + &MessageDerivationData { + emitter_key: vaa.emitter_address, + emitter_chain: vaa.emitter_chain, + nonce: vaa.nonce, + payload: vaa.payload.clone(), + sequence: None, + }, + &program_id, + ); + let post_vaa_data = PostVAAData { + version: vaa.version, + guardian_set_index: vaa.guardian_set_index, + timestamp: vaa.timestamp, + nonce: vaa.nonce, + emitter_chain: vaa.emitter_chain, + emitter_address: vaa.emitter_address, + sequence: vaa.sequence, + consistency_level: vaa.consistency_level, + payload: vaa.payload, + }; + + let ix = complete_wrapped( + program_id, + bridge_id, + payer, + message_key, + post_vaa_data, + payload.clone(), + Pubkey::new(&payload.to), + CompleteWrappedData {}, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + +#[wasm_bindgen] +pub fn create_wrapped_ix( + program_id: String, + bridge_id: String, + payer: String, + vaa: Vec, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let vaa = VAA::deserialize(vaa.as_slice()).unwrap(); + let payload = PayloadAssetMeta::deserialize(&mut vaa.payload.as_slice()).unwrap(); + let message_key = bridge::accounts::Message::<'_, { AccountState::Uninitialized }>::key( + &MessageDerivationData { + emitter_key: vaa.emitter_address, + emitter_chain: vaa.emitter_chain, + nonce: vaa.nonce, + payload: vaa.payload.clone(), + sequence: None, + }, + &program_id, + ); + let post_vaa_data = PostVAAData { + version: vaa.version, + guardian_set_index: vaa.guardian_set_index, + timestamp: vaa.timestamp, + nonce: vaa.nonce, + emitter_chain: vaa.emitter_chain, + emitter_address: vaa.emitter_address, + sequence: vaa.sequence, + consistency_level: vaa.consistency_level, + payload: vaa.payload, + }; + + let ix = create_wrapped( + program_id, + bridge_id, + payer, + message_key, + post_vaa_data, + payload, + CreateWrappedData {}, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + +#[wasm_bindgen] +pub fn upgrade_contract_ix( + program_id: String, + payer: String, + spill: String, + vaa: Vec, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let spill = Pubkey::from_str(spill.as_str()).unwrap(); + let vaa = VAA::deserialize(vaa.as_slice()).unwrap(); + let payload = GovernancePayloadUpgrade::deserialize(&mut vaa.payload.as_slice()).unwrap(); + let message_key = bridge::accounts::Message::<'_, { AccountState::Uninitialized }>::key( + &MessageDerivationData { + emitter_key: vaa.emitter_address, + emitter_chain: vaa.emitter_chain, + nonce: vaa.nonce, + payload: vaa.payload, + sequence: None, + }, + &program_id, + ); + let ix = upgrade_contract( + program_id, + Pubkey::from_str(payer.as_str()).unwrap(), + message_key, + Pubkey::new(&vaa.emitter_address), + payload.new_contract, + spill, + vaa.sequence, + ); + return JsValue::from_serde(&ix).unwrap(); +} + +#[wasm_bindgen] +pub fn register_chain_ix( + program_id: String, + bridge_id: String, + payer: String, + vaa: Vec, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let bridge_id = Pubkey::from_str(bridge_id.as_str()).unwrap(); + let payer = Pubkey::from_str(payer.as_str()).unwrap(); + let vaa = VAA::deserialize(vaa.as_slice()).unwrap(); + let payload = PayloadGovernanceRegisterChain::deserialize(&mut vaa.payload.as_slice()).unwrap(); + let message_key = bridge::accounts::Message::<'_, { AccountState::Uninitialized }>::key( + &MessageDerivationData { + emitter_key: vaa.emitter_address, + emitter_chain: vaa.emitter_chain, + nonce: vaa.nonce, + payload: vaa.payload.clone(), + sequence: None, + }, + &program_id, + ); + let post_vaa_data = PostVAAData { + version: vaa.version, + guardian_set_index: vaa.guardian_set_index, + timestamp: vaa.timestamp, + nonce: vaa.nonce, + emitter_chain: vaa.emitter_chain, + emitter_address: vaa.emitter_address, + sequence: vaa.sequence, + consistency_level: vaa.consistency_level, + payload: vaa.payload, + }; + let ix = register_chain( + program_id, + bridge_id, + payer, + message_key, + post_vaa_data, + payload, + RegisterChainData {}, + ) + .unwrap(); + return JsValue::from_serde(&ix).unwrap(); +}