sdk: add underlying sdk for wormhole programs

Change-Id: I858f3e43e6458af51131de9165a63078e4bb024c
This commit is contained in:
Reisen 2021-12-08 14:22:10 +00:00 committed by Reisen
parent 97566d878a
commit ee0fea0436
13 changed files with 2895 additions and 1 deletions

1412
sdk/rust/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
[workspace]
members = [
"core",
"sdk"
]

62
sdk/rust/sdk/Cargo.toml Normal file
View File

@ -0,0 +1,62 @@
[package]
name = "wormhole-sdk"
version = "0.1.0"
edition = "2018"
[features]
# Helper methods will target the Wormhole mainnet contract addresses.
mainnet = []
# Helper methosd will target the Wormhole devnet contract addresses.
devnet = []
# Enable Optional dependencies that are only required when targetting Terra.
terra = [
"cosmwasm-std",
"cosmwasm-storage",
"schemars",
"serde",
"wormhole-bridge-terra",
]
# Enable Optional dependencies that are only required when targetting Solana.
solana = [
"solana-program",
"wormhole-bridge-solana",
]
[profile.release]
opt-level = 3
lto = "thin"
[dependencies]
borsh = { version="0.8.1" }
nom = { version="7", default-features=false, features=["alloc"] }
primitive-types = { version = "0.9.0", default-features = false }
wormhole-core = { path="../core", version="0.1.0" }
# Solana Specific
solana-program = { version="1.7.0", optional=true }
# Terra Specific
cosmwasm-std = { version = "0.16.0", optional=true }
cosmwasm-storage = { version = "0.16.0", optional=true }
schemars = { version = "0.8.1", optional=true }
serde = { version = "1.0.103", default-features = false, features = ["derive"], optional=true }
[dependencies.wormhole-bridge-solana]
path = "../../../solana/bridge/program"
version = "0.1.0"
optional = true
features = [ "no-entrypoint" ]
[dependencies.wormhole-bridge-terra]
path = "../../../terra/contracts/wormhole"
version = "0.1.0"
optional = true
[dev-dependencies]
byteorder = "*"
hex = "*"

View File

@ -0,0 +1,7 @@
[build]
rustflags = [
"-Cpasses=sancov-module",
"-Cllvm-args=-sanitizer-coverage-level=3",
"-Cllvm-args=-sanitizer-coverage-inline-8bit-counters",
"-Zsanitizer=address",
]

1137
sdk/rust/sdk/fuzz/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
[package]
name = "wormhole-sdk-fuzz"
version = "0.0.0"
edition = "2021"
publish = false
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
[dependencies.wormhole-sdk]
path = ".."
features = ["solana", "vaa"]
# Create isolated workspace.
[workspace]
members = ["."]
[[bin]]
name = "vaa"
path = "fuzzers/vaa.rs"
[[bin]]
name = "governance"
path = "fuzzers/governance.rs"

View File

@ -0,0 +1,8 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use wormhole_sdk::VAA;
fuzz_target!(|data: &[u8]| {
VAA::from_bytes(data);
});

View File

@ -0,0 +1,8 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use wormhole_sdk::vaa::VAA;
fuzz_target!(|data: &[u8]| {
VAA::from_bytes(data);
});

View File

@ -0,0 +1,14 @@
//! Exposes an API implementation depending on which feature flags have been toggled for the
//! library. Check submodules for chain runtime specific documentation.
#[cfg(feature = "solana")]
pub mod solana;
#[cfg(feature = "solana")]
pub use solana::*;
#[cfg(feature = "terra")]
pub mod terra;
#[cfg(feature = "terra")]
pub use terra::*;

View File

@ -0,0 +1,134 @@
use borsh::BorshDeserialize;
use solana_program::pubkey::Pubkey;
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
use solana_program::program::invoke_signed;
use std::str::FromStr;
// Export Bridge API
pub use bridge::BridgeConfig;
pub use bridge::BridgeData;
pub use bridge::MessageData;
pub use bridge::PostVAAData;
pub use bridge::PostedVAAData;
pub use bridge::VerifySignaturesData;
pub use bridge::instructions;
pub use bridge::solitaire as bridge_entrypoint;
pub use bridge::types::ConsistencyLevel;
use wormhole_core::WormholeError;
use wormhole_core::VAA;
/// Export Core Mainnet Contract Address
#[cfg(feature = "mainnet")]
pub fn id() -> Pubkey {
Pubkey::from_str("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth").unwrap()
}
/// Export Core Devnet Contract Address
#[cfg(feature = "testnet")]
pub fn id() -> Pubkey {
Pubkey::from_str("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5").unwrap()
}
/// Export Local Tilt Devnet Contract Address
#[cfg(feature = "devnet")]
pub fn id() -> Pubkey {
Pubkey::from_str("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o").unwrap()
}
/// Derives the Wormhole configuration account address.
pub fn config(id: &Pubkey) -> Pubkey {
let (config, _) = Pubkey::find_program_address(&[b"Bridge"], &id);
config
}
/// Derives the Wormhole fee account address, users of the bridge must pay this address before
/// submitting messages to the bridge.
pub fn fee_collector(id: &Pubkey) -> Pubkey {
let (fee_collector, _) = Pubkey::find_program_address(&[b"fee_collector"], &id);
fee_collector
}
/// Derives the sequence address for an emitter, which is incremented after each message post.
pub fn sequence(id: &Pubkey, emitter: &Pubkey) -> Pubkey {
let (sequence, _) = Pubkey::find_program_address(&[b"Sequence", &emitter.to_bytes()], &id);
sequence
}
/// Derives the emitter address for a Solana contract, the emitter on Solana must be a signer, this
/// function helps generate a PDA and bump seed so users can emit using a PDA as the emitter.
pub fn emitter(id: &Pubkey) -> (Pubkey, Vec<&[u8]>, u8) {
let seeds = &["emitter".as_bytes()];
let (emitter, bump) = Pubkey::find_program_address(seeds, id);
(emitter, seeds.to_vec(), bump)
}
/// Deserialize helper the BridgeConfig from a Wormhole config account.
pub fn read_config(config: &AccountInfo) -> Result<BridgeConfig, WormholeError> {
let bridge_data = BridgeData::try_from_slice(&config.data.borrow())
.map_err(|_| WormholeError::DeserializeFailed)?;
Ok(bridge_data.config)
}
/// Deserialize helper for parsing from Borsh encoded VAA's from Solana accounts.
pub fn read_vaa(vaa: &AccountInfo) -> Result<PostedVAAData, WormholeError> {
Ok(PostedVAAData::try_from_slice(&vaa.data.borrow())
.map_err(|_| WormholeError::DeserializeFailed)?)
}
/// This helper method wraps the steps required to invoke Wormhole, it takes care of fee payment,
/// emitter derivation, and function invocation. This will be the right thing to use if you need to
/// simply emit a message in the most straight forward way possible.
pub fn post_message(
program_id: Pubkey,
payer: Pubkey,
message: Pubkey,
payload: impl AsRef<[u8]>,
consistency: ConsistencyLevel,
seeds: Option<&[&[u8]]>,
accounts: &[AccountInfo],
nonce: u32,
) -> ProgramResult {
// Derive any necessary Pubkeys, derivation makes sure that we match the accounts the are being
// provided by the user as well.
let id = id();
let fee_collector = fee_collector(&id);
let (emitter, mut emitter_seeds, bump) = emitter(&program_id);
let bump = &[bump];
emitter_seeds.push(bump);
// Filter for the Config AccountInfo so we can access its data.
let config = config(&id);
let config = accounts.iter().find(|item| *item.key == config).unwrap();
let config = read_config(config).unwrap();
// Pay Fee to the Wormhole
invoke_signed(
&solana_program::system_instruction::transfer(
&payer,
&fee_collector,
config.fee
),
accounts,
&[],
)?;
// Invoke the Wormhole post_message endpoint to create an on-chain message.
invoke_signed(
&instructions::post_message(
id,
payer,
emitter,
message,
nonce,
payload.as_ref().to_vec(),
consistency,
)
.unwrap(),
accounts,
&[&emitter_seeds, seeds.unwrap_or(&[])],
)?;
Ok(())
}

View File

@ -0,0 +1,63 @@
use cosmwasm_std::{
to_binary,
Addr,
Binary,
CosmosMsg,
DepsMut,
Env,
QueryRequest,
StdResult,
WasmMsg,
WasmQuery,
};
use serde::Serialize;
use bridge::msg::{
ExecuteMsg,
QueryMsg,
};
use bridge::state::ParsedVAA;
/// Export Core Mainnet Contract Address
#[cfg(feature = "mainnet")]
pub fn id() -> Addr {
Addr::unchecked("terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5")
}
/// Export Core Devnet Contract Address
#[cfg(feature = "devnet")]
pub fn id() -> Addr {
Addr::unchecked("terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v")
}
pub fn post_message<T>(wormhole: Addr, nonce: u32, message: &T) -> StdResult<CosmosMsg>
where
T: Serialize,
T: ?Sized,
{
Ok(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: wormhole.to_string(),
funds: vec![],
msg: to_binary(&ExecuteMsg::PostMessage {
message: to_binary(message)?,
nonce,
})?,
}))
}
/// Parse a VAA using the Wormhole contract Query interface.
pub fn parse_vaa(
wormhole: Addr,
deps: DepsMut,
env: Env,
data: &Binary,
) -> StdResult<ParsedVAA> {
let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: wormhole.to_string(),
msg: to_binary(&QueryMsg::VerifyVAA {
vaa: data.clone(),
block_time: env.block.time.seconds(),
})?,
}))?;
Ok(vaa)
}

21
sdk/rust/sdk/src/lib.rs Normal file
View File

@ -0,0 +1,21 @@
//! This SDK provides API's for implementing cross-chain message passing via the Wormhole protocol.
//! This package aims to provide a consistent API regardless of the underlying runtime, but some
//! types will differ depending on which implementation is being targeted.
//!
//! Each implementation can be toggled using feature flags, which will switch out the underlying
//! depenencies to pull in the depenendices for the corresponding runtimes.
//!
//! Implementations:
//!
//! Runtime | Feature Flag | Version
//! ----------|-------------------------|----------------------------------------------------
//! Solana | --feature=solana | solana-sdk 1.7.1
//! Terra | --feature=terra | cosmos-sdk 0.16.0
//!
//! Docs specific to each blockchain's runtime can be found in submodules within the chains module
//! at the root of this package.
pub mod chains;
pub use wormhole_core::*;
pub use chains::*;

View File

@ -28,7 +28,7 @@ pub type PostedMessage<'a, const State: AccountState> = Data<'a, PostedMessageDa
#[repr(transparent)]
pub struct PostedMessageData(pub MessageData);
#[derive(Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize)]
#[derive(Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize)]
pub struct MessageData {
/// Header of the posted VAA
pub vaa_version: u8,