Implement WASM client for token bridge

Change-Id: I592cc0ece0553d55c50d87010783fefb7f9b5f2f
This commit is contained in:
Hendrik Hofstadt 2021-07-27 22:18:33 +02:00
parent f9eb8a2c0a
commit dfdf31dcce
10 changed files with 409 additions and 11 deletions

View File

@ -10,6 +10,11 @@ set -euo pipefail
-v $(pwd)/../bridge_ui/rust_modules/core:/usr/src/bridge/bridge/program/pkg \ -v $(pwd)/../bridge_ui/rust_modules/core:/usr/src/bridge/bridge/program/pkg \
-e EMITTER_ADDRESS=11111111111111111111111111111115 \ -e EMITTER_ADDRESS=11111111111111111111111111111115 \
localhost/certusone/wormhole-wasmpack:latest \ 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 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
) )

View File

@ -12,6 +12,7 @@ name = "bridge"
client = ["solitaire/client", "solitaire-client", "no-entrypoint"] client = ["solitaire/client", "solitaire-client", "no-entrypoint"]
cpi = ["no-entrypoint"] cpi = ["no-entrypoint"]
default = [] default = []
wasm = ["no-entrypoint"]
no-entrypoint = ["solitaire/no-entrypoint"] no-entrypoint = ["solitaire/no-entrypoint"]
trace = ["solitaire/trace"] trace = ["solitaire/trace"]

View File

@ -42,6 +42,10 @@ use std::io::{
Cursor, Cursor,
Write, Write,
}; };
use serde::{
Deserialize,
Serialize
};
impl From<&PostVAAData> for GuardianSetDerivationData { impl From<&PostVAAData> for GuardianSetDerivationData {
fn from(data: &PostVAAData) -> Self { fn from(data: &PostVAAData) -> Self {
@ -88,7 +92,7 @@ pub struct Signature {
pub type ForeignAddress = [u8; 32]; pub type ForeignAddress = [u8; 32];
#[derive(Default, BorshSerialize, BorshDeserialize, Clone)] #[derive(Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize)]
pub struct PostVAAData { pub struct PostVAAData {
// Header part // Header part
pub version: u8, pub version: u8,

View File

@ -11,9 +11,11 @@ pub mod vaa;
#[cfg(feature = "no-entrypoint")] #[cfg(feature = "no-entrypoint")]
pub mod instructions; pub mod instructions;
#[cfg(feature = "wasm")]
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
extern crate wasm_bindgen; extern crate wasm_bindgen;
#[cfg(feature = "wasm")]
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub mod wasm; pub mod wasm;

View File

@ -1502,8 +1502,6 @@ checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]] [[package]]
name = "memmap2" name = "memmap2"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a"
dependencies = [ dependencies = [
"libc", "libc",
] ]

View File

@ -1,2 +1,5 @@
[workspace] [workspace]
members = ["program"] members = ["program"]
[patch.crates-io]
memmap2 = { path = "../../bridge/memmap2-rs" }

View File

@ -11,6 +11,7 @@ name = "token_bridge"
[features] [features]
no-entrypoint = ["solitaire/no-entrypoint", "rand"] no-entrypoint = ["solitaire/no-entrypoint", "rand"]
trace = ["solitaire/trace"] trace = ["solitaire/trace"]
wasm = ["no-entrypoint"]
client = ["solitaire-client", "solitaire/client", "no-entrypoint"] client = ["solitaire-client", "solitaire/client", "no-entrypoint"]
cpi = ["no-entrypoint"] cpi = ["no-entrypoint"]
default = [] default = []

View File

@ -58,15 +58,17 @@ use bridge::{
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
use primitive_types::U256; use primitive_types::U256;
use solana_program::instruction::Instruction; use solana_program::{
instruction::{
AccountMeta,
Instruction,
},
pubkey::Pubkey,
};
use solitaire::{ use solitaire::{
processors::seeded::Seeded, processors::seeded::Seeded,
AccountState, AccountState,
}; };
use solana_program::instruction::{
AccountMeta,
};
use solana_program::pubkey::Pubkey;
use spl_token::state::Mint; use spl_token::state::Mint;
use std::str::FromStr; use std::str::FromStr;

View File

@ -6,6 +6,14 @@
#[cfg(feature = "no-entrypoint")] #[cfg(feature = "no-entrypoint")]
pub mod instructions; 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 accounts;
pub mod api; pub mod api;
pub mod messages; pub mod messages;
@ -20,6 +28,7 @@ pub use api::{
register_chain, register_chain,
transfer_native, transfer_native,
transfer_wrapped, transfer_wrapped,
upgrade_contract,
AttestToken, AttestToken,
AttestTokenData, AttestTokenData,
CompleteNative, CompleteNative,
@ -38,7 +47,6 @@ pub use api::{
TransferWrappedData, TransferWrappedData,
UpgradeContract, UpgradeContract,
UpgradeContractData, UpgradeContractData,
upgrade_contract
}; };
use solitaire::*; use solitaire::*;

View File

@ -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<u8>,
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<u8>,
nonce: u32,
amount: u64,
fee: u64,
target_address: Vec<u8>,
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<u8>,
) -> 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<u8>,
) -> 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<u8>,
) -> 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<u8>,
) -> 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<u8>,
) -> 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();
}