diff --git a/sdk/rust/Cargo.lock b/sdk/rust/Cargo.lock index 901dc0d67..99265f37f 100644 --- a/sdk/rust/Cargo.lock +++ b/sdk/rust/Cargo.lock @@ -3,20 +3,19 @@ version = 3 [[package]] -name = "block-buffer" -version = "0.9.0" +name = "anyhow" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array", -] +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] -name = "block-padding" -version = "0.2.1" +name = "block-buffer" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] [[package]] name = "bstr" @@ -31,33 +30,23 @@ dependencies = [ ] [[package]] -name = "byteorder" -version = "1.4.3" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "typenum", ] [[package]] -name = "fixed-hash" -version = "0.7.0" +name = "digest" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "static_assertions", + "block-buffer", + "crypto-common", ] [[package]] @@ -70,12 +59,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "itoa" version = "1.0.4" @@ -94,44 +77,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "primitive-types" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06345ee39fbccfb06ab45f3a1a5798d9dafa04cb8921a76d227040003a234b0e" -dependencies = [ - "fixed-hash", - "uint", -] - [[package]] name = "proc-macro2" version = "1.0.47" @@ -156,6 +107,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + [[package]] name = "serde" version = "1.0.147" @@ -185,6 +142,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.9" @@ -209,22 +177,14 @@ dependencies = [ [[package]] name = "sha3" -version = "0.9.1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ - "block-buffer", "digest", "keccak", - "opaque-debug", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "syn" version = "1.0.103" @@ -262,18 +222,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-ident" version = "1.0.5" @@ -290,10 +238,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wormhole-core" version = "0.1.0" dependencies = [ + "anyhow", "bstr", - "byteorder", - "hex", - "nom", - "primitive-types", + "serde", + "serde_json", + "serde_wormhole", "sha3", ] diff --git a/sdk/rust/core/Cargo.toml b/sdk/rust/core/Cargo.toml index 26967fc21..4a9fcdb65 100644 --- a/sdk/rust/core/Cargo.toml +++ b/sdk/rust/core/Cargo.toml @@ -1,20 +1,17 @@ [package] name = "wormhole-core" version = "0.1.0" -edition = "2018" +edition = "2021" - -[features] +[lib] +name = "wormhole" [dependencies] -byteorder = "*" -hex = "*" -nom = { version="7", default-features=false, features=["alloc"] } -primitive-types = { version="0.9.0", default-features=false } -sha3 = "0.9.1" -bstr = "*" - +anyhow = "1" +bstr = { version = "1", features = ["serde"] } +serde = { version = "1", default-features = false, features = ["alloc", "derive"] } +serde_wormhole = "0.1.0" +sha3 = "0.10.4" [dev-dependencies] -byteorder = "*" -hex = "*" +serde_json = "1" diff --git a/sdk/rust/core/src/arraystring.rs b/sdk/rust/core/src/arraystring.rs new file mode 100644 index 000000000..350e6d5ef --- /dev/null +++ b/sdk/rust/core/src/arraystring.rs @@ -0,0 +1,112 @@ +//! A module for serializing/deserializing a `BString` as a fixed-width 32 byte array. + +use std::{convert::identity, fmt, iter::repeat}; + +use bstr::BString; +use serde::{ + de::{Error as DeError, SeqAccess, Visitor}, + ser::{Error as SerError, SerializeTuple}, + Deserializer, Serializer, +}; + +pub fn serialize(value: T, serializer: S) -> Result +where + T: AsRef<[u8]>, + S: Serializer, +{ + let v = value.as_ref(); + let l = v.len(); + if l > 32 { + return Err(S::Error::custom(format_args!( + "value is too large ({l} bytes); max 32", + ))); + } + + let mut tup = serializer.serialize_tuple(32)?; + for e in repeat(&0u8).take(32 - l).chain(v) { + tup.serialize_element(e)?; + } + + tup.end() +} + +struct ArrayStringVisitor; +impl<'de> Visitor<'de> for ArrayStringVisitor { + type Value = BString; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("an array of 32 bytes") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut buf = Vec::with_capacity(32); + + for i in 0..32 { + let e = seq + .next_element() + .map(|e| e.ok_or_else(|| A::Error::invalid_length(i, &self))) + .and_then(identity)?; // TODO: Replace with Result::flatten once stabilized. + + if e == 0 && buf.is_empty() { + // Skip all leading zeroes. + continue; + } + + buf.push(e); + } + + Ok(BString::from(buf)) + } +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + deserializer.deserialize_tuple(32, ArrayStringVisitor) +} + +#[cfg(test)] +mod test { + use bstr::BString; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] + #[repr(transparent)] + struct MyString(#[serde(with = "super")] BString); + + #[test] + fn end_to_end() { + let v = "45825ca36ef7628727b83f8a409a08ad"; + let zeroes = [0u8; 32]; + + for i in 0..=32 { + let expected = MyString(v[i..].into()); + let buf = serde_wormhole::to_vec(&expected).unwrap(); + + assert_eq!(&zeroes[..i], &buf[..i]); + assert_eq!(v.as_bytes()[i..], buf[i..]); + + let actual = serde_wormhole::from_slice(&buf).unwrap(); + assert_eq!(expected, actual); + } + } + + #[test] + fn value_too_large() { + let v = MyString("61ddb8ede2a3f0cec9b550ef150c08096280d0480493365f".into()); + let _ = serde_wormhole::to_vec(&v) + .expect_err("successfully serialized string longer than 32 bytes"); + } + + #[test] + fn buffer_too_small() { + let b = [0u8; 16]; + + let _ = serde_wormhole::from_slice::(&b) + .expect_err("successfully deserialized string from a buffer that's too small"); + } +} diff --git a/sdk/rust/core/src/chain.rs b/sdk/rust/core/src/chain.rs index 85cd4e519..a5b03b174 100644 --- a/sdk/rust/core/src/chain.rs +++ b/sdk/rust/core/src/chain.rs @@ -1,41 +1,224 @@ -//! Exposes an API implementation depending on which feature flags have been toggled for the -//! library. Check submodules for chain runtime specific documentation. -use std::convert::TryFrom; // Remove in 2021 +//! Provide Types and Data about Wormhole's supported chains. -/// Chain contains a mapping of Wormhole supported chains to their u16 representation. These are -/// universally defined among all Wormhole contracts. -#[repr(u16)] -#[derive(Clone, Debug, PartialEq, Eq)] +use std::{fmt, str::FromStr}; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Chain { - All = 0, - Solana = 1, - Ethereum = 2, - Terra = 3, - Binance = 4, - Polygon = 5, - AVAX = 6, - Oasis = 7, + /// In the wormhole wire format, 0 indicates that a message is for any destination chain, it is + /// represented here as `Any`. + Any, + + /// Chains + Solana, + Ethereum, + Terra, + Bsc, + Polygon, + Avalanche, + Oasis, + Algorand, + Aurora, + Fantom, + Karura, + Acala, + Klaytn, + Celo, + Near, + Moonbeam, + Neon, + Terra2, + Injective, + Osmosis, + Sui, + Aptos, + Arbitrum, + Optimism, + Gnosis, + Pythnet, + Xpla, + Ropsten, + Wormchain, + + // Allow arbitrary u16s to support future chains. + Unknown(u16), } -impl TryFrom for Chain { - type Error = (); - fn try_from(other: u16) -> Result { +impl From for Chain { + fn from(other: u16) -> Chain { match other { - 0 => Ok(Chain::All), - 1 => Ok(Chain::Solana), - 2 => Ok(Chain::Ethereum), - 3 => Ok(Chain::Terra), - 4 => Ok(Chain::Binance), - 5 => Ok(Chain::Polygon), - 6 => Ok(Chain::AVAX), - 7 => Ok(Chain::Oasis), - _ => Err(()), + 0 => Chain::Any, + 1 => Chain::Solana, + 2 => Chain::Ethereum, + 3 => Chain::Terra, + 4 => Chain::Bsc, + 5 => Chain::Polygon, + 6 => Chain::Avalanche, + 7 => Chain::Oasis, + 8 => Chain::Algorand, + 9 => Chain::Aurora, + 10 => Chain::Fantom, + 11 => Chain::Karura, + 12 => Chain::Acala, + 13 => Chain::Klaytn, + 14 => Chain::Celo, + 15 => Chain::Near, + 16 => Chain::Moonbeam, + 17 => Chain::Neon, + 18 => Chain::Terra2, + 19 => Chain::Injective, + 20 => Chain::Osmosis, + 21 => Chain::Sui, + 22 => Chain::Aptos, + 23 => Chain::Arbitrum, + 24 => Chain::Optimism, + 25 => Chain::Gnosis, + 26 => Chain::Pythnet, + 28 => Chain::Xpla, + 3104 => Chain::Wormchain, + 10001 => Chain::Ropsten, + c => Chain::Unknown(c), + } + } +} + +impl From for u16 { + fn from(other: Chain) -> u16 { + match other { + Chain::Any => 0, + Chain::Solana => 1, + Chain::Ethereum => 2, + Chain::Terra => 3, + Chain::Bsc => 4, + Chain::Polygon => 5, + Chain::Avalanche => 6, + Chain::Oasis => 7, + Chain::Algorand => 8, + Chain::Aurora => 9, + Chain::Fantom => 10, + Chain::Karura => 11, + Chain::Acala => 12, + Chain::Klaytn => 13, + Chain::Celo => 14, + Chain::Near => 15, + Chain::Moonbeam => 16, + Chain::Neon => 17, + Chain::Terra2 => 18, + Chain::Injective => 19, + Chain::Osmosis => 20, + Chain::Sui => 21, + Chain::Aptos => 22, + Chain::Arbitrum => 23, + Chain::Optimism => 24, + Chain::Gnosis => 25, + Chain::Pythnet => 26, + Chain::Xpla => 28, + Chain::Wormchain => 3104, + Chain::Ropsten => 10001, + Chain::Unknown(c) => c, + } + } +} + +impl fmt::Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Any => f.write_str("Any"), + Self::Solana => f.write_str("Solana"), + Self::Ethereum => f.write_str("Ethereum"), + Self::Terra => f.write_str("Terra"), + Self::Bsc => f.write_str("Bsc"), + Self::Polygon => f.write_str("Polygon"), + Self::Avalanche => f.write_str("Avalanche"), + Self::Oasis => f.write_str("Oasis"), + Self::Algorand => f.write_str("Algorand"), + Self::Aurora => f.write_str("Aurora"), + Self::Fantom => f.write_str("Fantom"), + Self::Karura => f.write_str("Karura"), + Self::Acala => f.write_str("Acala"), + Self::Klaytn => f.write_str("Klaytn"), + Self::Celo => f.write_str("Celo"), + Self::Near => f.write_str("Near"), + Self::Moonbeam => f.write_str("Moonbeam"), + Self::Neon => f.write_str("Neon"), + Self::Terra2 => f.write_str("Terra2"), + Self::Injective => f.write_str("Injective"), + Self::Osmosis => f.write_str("Osmosis"), + Self::Sui => f.write_str("Sui"), + Self::Aptos => f.write_str("Aptos"), + Self::Arbitrum => f.write_str("Arbitrum"), + Self::Optimism => f.write_str("Optimism"), + Self::Gnosis => f.write_str("Gnosis"), + Self::Pythnet => f.write_str("Pythnet"), + Self::Xpla => f.write_str("Xpla"), + Self::Ropsten => f.write_str("Ropsten"), + Self::Wormchain => f.write_str("Wormchain"), + Self::Unknown(v) => write!(f, "Unknown({v})"), + } + } +} + +impl FromStr for Chain { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "Any" | "any" | "ANY" => Ok(Chain::Any), + "Solana" | "solana" | "SOLANA" => Ok(Chain::Solana), + "Ethereum" | "ethereum" | "ETHEREUM" => Ok(Chain::Ethereum), + "Terra" | "terra" | "TERRA" => Ok(Chain::Terra), + "Bsc" | "bsc" | "BSC" => Ok(Chain::Bsc), + "Polygon" | "polygon" | "POLYGON" => Ok(Chain::Polygon), + "Avalanche" | "avalanche" | "AVALANCHE" => Ok(Chain::Avalanche), + "Oasis" | "oasis" | "OASIS" => Ok(Chain::Oasis), + "Algorand" | "algorand" | "ALGORAND" => Ok(Chain::Algorand), + "Aurora" | "aurora" | "AURORA" => Ok(Chain::Aurora), + "Fantom" | "fantom" | "FANTOM" => Ok(Chain::Fantom), + "Karura" | "karura" | "KARURA" => Ok(Chain::Karura), + "Acala" | "acala" | "ACALA" => Ok(Chain::Acala), + "Klaytn" | "klaytn" | "KLAYTN" => Ok(Chain::Klaytn), + "Celo" | "celo" | "CELO" => Ok(Chain::Celo), + "Near" | "near" | "NEAR" => Ok(Chain::Near), + "Moonbeam" | "moonbeam" | "MOONBEAM" => Ok(Chain::Moonbeam), + "Neon" | "neon" | "NEON" => Ok(Chain::Neon), + "Terra2" | "terra2" | "TERRA2" => Ok(Chain::Terra2), + "Injective" | "injective" | "INJECTIVE" => Ok(Chain::Injective), + "Osmosis" | "osmosis" | "OSMOSIS" => Ok(Chain::Osmosis), + "Sui" | "sui" | "SUI" => Ok(Chain::Sui), + "Aptos" | "aptos" | "APTOS" => Ok(Chain::Aptos), + "Arbitrum" | "arbitrum" | "ARBITRUM" => Ok(Chain::Arbitrum), + "Optimism" | "optimism" | "OPTIMISM" => Ok(Chain::Optimism), + "Gnosis" | "gnosis" | "GNOSIS" => Ok(Chain::Gnosis), + "Pythnet" | "pythnet" | "PYTHNET" => Ok(Chain::Pythnet), + "Xpla" | "xpla" | "XPLA" => Ok(Chain::Xpla), + "Ropsten" | "ropsten" | "ROPSTEN" => Ok(Chain::Ropsten), + "Wormchain" | "wormchain" | "WORMCHAIN" => Ok(Chain::Wormchain), + _ => Err(format!("invalid chain: {s}")), } } } impl Default for Chain { fn default() -> Self { - Self::All + Self::Any + } +} + +impl Serialize for Chain { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u16((*self).into()) + } +} + +impl<'de> Deserialize<'de> for Chain { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + ::deserialize(deserializer).map(Self::from) } } diff --git a/sdk/rust/core/src/core.rs b/sdk/rust/core/src/core.rs new file mode 100644 index 000000000..bc22c62ee --- /dev/null +++ b/sdk/rust/core/src/core.rs @@ -0,0 +1,556 @@ +//! Parsers for core bridge VAAs. +//! +//! The core bridge is responsible for emitting messages and VAAs. But it also uses VAA's to +//! update and manage itself. + +use serde::{Deserialize, Serialize}; + +use crate::{Address, Amount, Chain, GuardianSetInfo}; + +pub type Vaa = crate::Vaa; + +/// Represents a governance action targeted at the core bridge itself. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Action { + #[serde(rename = "1")] + ContractUpgrade { new_contract: Address }, + #[serde(rename = "2")] + GuardianSetUpgrade { + new_guardian_set_index: u32, + new_guardian_set: GuardianSetInfo, + }, + #[serde(rename = "3")] + SetFee { amount: Amount }, + #[serde(rename = "4")] + TransferFee { amount: Amount, recipient: Address }, +} + +/// Represents the payload for a governance VAA targeted at the core bridge. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GovernancePacket { + /// Describes the chain on which the governance action should be carried out. + pub chain: Chain, + + /// The actual governance action to be carried out. + pub action: Action, +} + +// The wire format for GovernancePackets is wonky and doesn't lend itself well to auto-deriving +// Serialize / Deserialize so we implement it manually here. +mod governance_packet_impl { + use std::fmt; + + use serde::{ + de::{Error, MapAccess, SeqAccess, Unexpected, Visitor}, + ser::SerializeStruct, + Deserialize, Deserializer, Serialize, Serializer, + }; + + use crate::{ + core::{Action, GovernancePacket}, + Address, Amount, GuardianSetInfo, + }; + + // MODULE = "Core" + const MODULE: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, + 0x72, 0x65, + ]; + + struct Module; + + impl Serialize for Module { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + MODULE.serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for Module { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let arr = <[u8; 32]>::deserialize(deserializer)?; + + if arr == MODULE { + Ok(Module) + } else { + let expected = format!("{MODULE:?}"); + Err(Error::invalid_value(Unexpected::Bytes(&arr), &&*expected)) + } + } + } + + #[derive(Serialize, Deserialize)] + struct ContractUpgrade { + new_contract: Address, + } + + #[derive(Serialize, Deserialize)] + struct GuardianSetUpgrade { + new_guardian_set_index: u32, + new_guardian_set: GuardianSetInfo, + } + + #[derive(Serialize, Deserialize)] + struct SetFee { + amount: Amount, + } + + #[derive(Serialize, Deserialize)] + struct TransferFee { + amount: Amount, + recipient: Address, + } + + impl Serialize for GovernancePacket { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_struct("GovernancePacket", 4)?; + seq.serialize_field("module", &Module)?; + + // The wire format encodes the action before the chain and then appends the actual + // action payload. + match self.action { + Action::ContractUpgrade { new_contract } => { + seq.serialize_field("action", &1u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field("payload", &ContractUpgrade { new_contract })?; + } + Action::GuardianSetUpgrade { + new_guardian_set_index, + ref new_guardian_set, + } => { + seq.serialize_field("action", &2u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field( + "payload", + &GuardianSetUpgrade { + new_guardian_set_index, + new_guardian_set: new_guardian_set.clone(), + }, + )?; + } + Action::SetFee { amount } => { + seq.serialize_field("action", &3u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field("payload", &SetFee { amount })?; + } + Action::TransferFee { amount, recipient } => { + seq.serialize_field("action", &4u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field("payload", &TransferFee { amount, recipient })?; + } + } + + seq.end() + } + } + + struct GovernancePacketVisitor; + + impl<'de> Visitor<'de> for GovernancePacketVisitor { + type Value = GovernancePacket; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("struct GovernancePacket") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + static EXPECTING: &str = "struct GovernancePacket with 4 elements"; + + let _: Module = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(0, &EXPECTING))?; + let act: u8 = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(1, &EXPECTING))?; + let chain = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(2, &EXPECTING))?; + + let action = match act { + 1 => { + let ContractUpgrade { new_contract } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::ContractUpgrade { new_contract } + } + 2 => { + let GuardianSetUpgrade { + new_guardian_set_index, + new_guardian_set, + } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::GuardianSetUpgrade { + new_guardian_set_index, + new_guardian_set, + } + } + 3 => { + let SetFee { amount } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::SetFee { amount } + } + 4 => { + let TransferFee { amount, recipient } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::TransferFee { amount, recipient } + } + v => { + return Err(Error::invalid_value( + Unexpected::Unsigned(v.into()), + &"one of 1, 2, 3, 4", + )) + } + }; + + Ok(GovernancePacket { chain, action }) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "snake_case")] + enum Field { + Module, + Action, + Chain, + Payload, + } + + let mut module = None; + let mut chain = None; + let mut action = None; + let mut payload = None; + + while let Some(key) = map.next_key::()? { + match key { + Field::Module => { + if module.is_some() { + return Err(Error::duplicate_field("module")); + } + + module = map.next_value::().map(Some)?; + } + Field::Action => { + if action.is_some() { + return Err(Error::duplicate_field("action")); + } + + action = map.next_value::().map(Some)?; + } + Field::Chain => { + if chain.is_some() { + return Err(Error::duplicate_field("chain")); + } + + chain = map.next_value().map(Some)?; + } + Field::Payload => { + if payload.is_some() { + return Err(Error::duplicate_field("payload")); + } + + let a = action.as_ref().copied().ok_or_else(|| { + Error::custom("`action` must be known before deserializing `payload`") + })?; + + let p = match a { + 1 => { + let ContractUpgrade { new_contract } = map.next_value()?; + + Action::ContractUpgrade { new_contract } + } + 2 => { + let GuardianSetUpgrade { + new_guardian_set_index, + new_guardian_set, + } = map.next_value()?; + + Action::GuardianSetUpgrade { + new_guardian_set_index, + new_guardian_set, + } + } + 3 => { + let SetFee { amount } = map.next_value()?; + + Action::SetFee { amount } + } + 4 => { + let TransferFee { amount, recipient } = map.next_value()?; + + Action::TransferFee { amount, recipient } + } + v => { + return Err(Error::custom(format_args!( + "invalid action: {v}, expected one of: 1, 2, 3, 4" + ))) + } + }; + + payload = Some(p); + } + } + } + + let chain = chain.ok_or_else(|| Error::missing_field("chain"))?; + let action = payload.ok_or_else(|| Error::missing_field("payload"))?; + + Ok(GovernancePacket { chain, action }) + } + } + + impl<'de> Deserialize<'de> for GovernancePacket { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + const FIELDS: &[&str] = &["module", "action", "chain", "payload"]; + deserializer.deserialize_struct("GovernancePacket", FIELDS, GovernancePacketVisitor) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate::{vaa::Signature, GuardianAddress, GOVERNANCE_EMITTER}; + + #[test] + fn contract_upgrade() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xe3, 0xdb, 0x30, 0x93, 0x03, 0xb7, 0x12, + 0xa5, 0x62, 0xe6, 0xaa, 0x2a, 0xdc, 0x68, 0xbc, 0x10, 0xff, 0x22, 0x32, 0x8a, 0xb3, + 0x1d, 0xdb, 0x6a, 0x83, 0x70, 0x69, 0x43, 0xa9, 0xda, 0x97, 0xbf, 0x11, 0xba, 0x6e, + 0x3b, 0x96, 0x39, 0x55, 0x15, 0x86, 0x87, 0x86, 0x89, 0x8d, 0xc1, 0x9e, 0xcd, 0x73, + 0x7d, 0x19, 0x7b, 0x0d, 0x1a, 0x1f, 0x3f, 0x3c, 0x6a, 0xea, 0xd5, 0xc1, 0xfe, 0x70, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0xc5, 0xd0, 0x5a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x72, + 0x65, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46, 0xda, 0x7a, 0x03, 0x20, 0xdd, 0x99, 0x94, 0x38, 0xb4, 0x43, + 0x5d, 0xac, 0x82, 0xbf, 0x1d, 0xac, 0x13, 0xd2, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![Signature { + index: 0, + signature: [ + 0xe3, 0xdb, 0x30, 0x93, 0x03, 0xb7, 0x12, 0xa5, 0x62, 0xe6, 0xaa, 0x2a, 0xdc, + 0x68, 0xbc, 0x10, 0xff, 0x22, 0x32, 0x8a, 0xb3, 0x1d, 0xdb, 0x6a, 0x83, 0x70, + 0x69, 0x43, 0xa9, 0xda, 0x97, 0xbf, 0x11, 0xba, 0x6e, 0x3b, 0x96, 0x39, 0x55, + 0x15, 0x86, 0x87, 0x86, 0x89, 0x8d, 0xc1, 0x9e, 0xcd, 0x73, 0x7d, 0x19, 0x7b, + 0x0d, 0x1a, 0x1f, 0x3f, 0x3c, 0x6a, 0xea, 0xd5, 0xc1, 0xfe, 0x70, 0x09, 0x00, + ], + }], + timestamp: 1, + nonce: 1, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 80_072_794, + consistency_level: 0, + payload: GovernancePacket { + chain: Chain::Fantom, + action: Action::ContractUpgrade { + new_contract: Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x46, 0xda, 0x7a, 0x03, 0x20, 0xdd, 0x99, 0x94, 0x38, 0xb4, 0x43, + 0x5d, 0xac, 0x82, 0xbf, 0x1d, 0xac, 0x13, 0xd2, + ]), + }, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } + + #[test] + fn guardian_set_upgrade() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x7a, 0xc3, 0x1b, 0x28, 0x2c, 0x2a, 0xee, + 0xeb, 0x37, 0xf3, 0x38, 0x5e, 0xe0, 0xde, 0x5f, 0x8e, 0x42, 0x1d, 0x30, 0xb9, 0xe5, + 0xae, 0x8b, 0xa3, 0xd4, 0x37, 0x5c, 0x1c, 0x77, 0xa8, 0x6e, 0x77, 0x15, 0x9b, 0xb6, + 0x97, 0xd9, 0xc4, 0x56, 0xd6, 0xf8, 0xc0, 0x2d, 0x22, 0xa9, 0x4b, 0x12, 0x79, 0xb6, + 0x5b, 0x0d, 0x6a, 0x99, 0x57, 0xe7, 0xd3, 0x85, 0x74, 0x23, 0x84, 0x5a, 0xc7, 0x58, + 0xe3, 0x00, 0x61, 0x0a, 0xc1, 0xd2, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x72, + 0x65, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x13, 0x58, 0xcc, 0x3a, 0xe5, 0xc0, + 0x97, 0xb2, 0x13, 0xce, 0x3c, 0x81, 0x97, 0x9e, 0x1b, 0x9f, 0x95, 0x70, 0x74, 0x6a, + 0xa5, 0xff, 0x6c, 0xb9, 0x52, 0x58, 0x9b, 0xde, 0x86, 0x2c, 0x25, 0xef, 0x43, 0x92, + 0x13, 0x2f, 0xb9, 0xd4, 0xa4, 0x21, 0x57, 0x11, 0x4d, 0xe8, 0x46, 0x01, 0x93, 0xbd, + 0xf3, 0xa2, 0xfc, 0xf8, 0x1f, 0x86, 0xa0, 0x97, 0x65, 0xf4, 0x76, 0x2f, 0xd1, 0x10, + 0x7a, 0x00, 0x86, 0xb3, 0x2d, 0x7a, 0x09, 0x77, 0x92, 0x6a, 0x20, 0x51, 0x31, 0xd8, + 0x73, 0x1d, 0x39, 0xcb, 0xeb, 0x8c, 0x82, 0xb2, 0xfd, 0x82, 0xfa, 0xed, 0x27, 0x11, + 0xd5, 0x9a, 0xf0, 0xf2, 0x49, 0x9d, 0x16, 0xe7, 0x26, 0xf6, 0xb2, 0x11, 0xb3, 0x97, + 0x56, 0xc0, 0x42, 0x44, 0x1b, 0xe6, 0xd8, 0x65, 0x0b, 0x69, 0xb5, 0x4e, 0xbe, 0x71, + 0x5e, 0x23, 0x43, 0x54, 0xce, 0x5b, 0x4d, 0x34, 0x8f, 0xb7, 0x4b, 0x95, 0x8e, 0x89, + 0x66, 0xe2, 0xec, 0x3d, 0xbd, 0x49, 0x58, 0xa7, 0xcd, 0xeb, 0x5f, 0x73, 0x89, 0xfa, + 0x26, 0x94, 0x15, 0x19, 0xf0, 0x86, 0x33, 0x49, 0xc2, 0x23, 0xb7, 0x3a, 0x6d, 0xde, + 0xe7, 0x74, 0xa3, 0xbf, 0x91, 0x39, 0x53, 0xd6, 0x95, 0x26, 0x0d, 0x88, 0xbc, 0x1a, + 0xa2, 0x5a, 0x4e, 0xee, 0x36, 0x3e, 0xf0, 0x00, 0x0a, 0xc0, 0x07, 0x67, 0x27, 0xb3, + 0x5f, 0xbe, 0xa2, 0xda, 0xc2, 0x8f, 0xee, 0x5c, 0xcb, 0x0f, 0xea, 0x76, 0x8e, 0xaf, + 0x45, 0xce, 0xd1, 0x36, 0xb9, 0xd9, 0xe2, 0x49, 0x03, 0x46, 0x4a, 0xe8, 0x89, 0xf5, + 0xc8, 0xa7, 0x23, 0xfc, 0x14, 0xf9, 0x31, 0x24, 0xb7, 0xc7, 0x38, 0x84, 0x3c, 0xbb, + 0x89, 0xe8, 0x64, 0xc8, 0x62, 0xc3, 0x8c, 0xdd, 0xcc, 0xcf, 0x95, 0xd2, 0xcc, 0x37, + 0xa4, 0xdc, 0x03, 0x6a, 0x8d, 0x23, 0x2b, 0x48, 0xf6, 0x2c, 0xdd, 0x47, 0x31, 0x41, + 0x2f, 0x48, 0x90, 0xda, 0x79, 0x8f, 0x68, 0x96, 0xa3, 0x33, 0x1f, 0x64, 0xb4, 0x8c, + 0x12, 0xd1, 0xd5, 0x7f, 0xd9, 0xcb, 0xe7, 0x08, 0x11, 0x71, 0xaa, 0x1b, 0xe1, 0xd3, + 0x6c, 0xaf, 0xe3, 0x86, 0x79, 0x10, 0xf9, 0x9c, 0x09, 0xe3, 0x47, 0x89, 0x9c, 0x19, + 0xc3, 0x81, 0x92, 0xb6, 0xe7, 0x38, 0x7c, 0xcd, 0x76, 0x82, 0x77, 0xc1, 0x7d, 0xab, + 0x1b, 0x7a, 0x50, 0x27, 0xc0, 0xb3, 0xcf, 0x17, 0x8e, 0x21, 0xad, 0x2e, 0x77, 0xae, + 0x06, 0x71, 0x15, 0x49, 0xcf, 0xbb, 0x1f, 0x9c, 0x7a, 0x9d, 0x80, 0x96, 0xe8, 0x5e, + 0x14, 0x87, 0xf3, 0x55, 0x15, 0xd0, 0x2a, 0x92, 0x75, 0x35, 0x04, 0xa8, 0xd7, 0x54, + 0x71, 0xb9, 0xf4, 0x9e, 0xdb, 0x6f, 0xbe, 0xbc, 0x89, 0x8f, 0x40, 0x3e, 0x47, 0x73, + 0xe9, 0x5f, 0xeb, 0x15, 0xe8, 0x0c, 0x9a, 0x99, 0xc8, 0x34, 0x8d, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![Signature { + index: 0, + signature: [ + 0x7a, 0xc3, 0x1b, 0x28, 0x2c, 0x2a, 0xee, 0xeb, 0x37, 0xf3, 0x38, 0x5e, 0xe0, + 0xde, 0x5f, 0x8e, 0x42, 0x1d, 0x30, 0xb9, 0xe5, 0xae, 0x8b, 0xa3, 0xd4, 0x37, + 0x5c, 0x1c, 0x77, 0xa8, 0x6e, 0x77, 0x15, 0x9b, 0xb6, 0x97, 0xd9, 0xc4, 0x56, + 0xd6, 0xf8, 0xc0, 0x2d, 0x22, 0xa9, 0x4b, 0x12, 0x79, 0xb6, 0x5b, 0x0d, 0x6a, + 0x99, 0x57, 0xe7, 0xd3, 0x85, 0x74, 0x23, 0x84, 0x5a, 0xc7, 0x58, 0xe3, 0x00, + ], + }], + timestamp: 1_628_094_930, + nonce: 3, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 1337, + consistency_level: 0, + payload: GovernancePacket { + chain: Chain::Aptos, + action: Action::GuardianSetUpgrade { + new_guardian_set_index: 1, + new_guardian_set: GuardianSetInfo { + addresses: vec![ + GuardianAddress([ + 0x58, 0xcc, 0x3a, 0xe5, 0xc0, 0x97, 0xb2, 0x13, 0xce, 0x3c, 0x81, + 0x97, 0x9e, 0x1b, 0x9f, 0x95, 0x70, 0x74, 0x6a, 0xa5, + ]), + GuardianAddress([ + 0xff, 0x6c, 0xb9, 0x52, 0x58, 0x9b, 0xde, 0x86, 0x2c, 0x25, 0xef, + 0x43, 0x92, 0x13, 0x2f, 0xb9, 0xd4, 0xa4, 0x21, 0x57, + ]), + GuardianAddress([ + 0x11, 0x4d, 0xe8, 0x46, 0x01, 0x93, 0xbd, 0xf3, 0xa2, 0xfc, 0xf8, + 0x1f, 0x86, 0xa0, 0x97, 0x65, 0xf4, 0x76, 0x2f, 0xd1, + ]), + GuardianAddress([ + 0x10, 0x7a, 0x00, 0x86, 0xb3, 0x2d, 0x7a, 0x09, 0x77, 0x92, 0x6a, + 0x20, 0x51, 0x31, 0xd8, 0x73, 0x1d, 0x39, 0xcb, 0xeb, + ]), + GuardianAddress([ + 0x8c, 0x82, 0xb2, 0xfd, 0x82, 0xfa, 0xed, 0x27, 0x11, 0xd5, 0x9a, + 0xf0, 0xf2, 0x49, 0x9d, 0x16, 0xe7, 0x26, 0xf6, 0xb2, + ]), + GuardianAddress([ + 0x11, 0xb3, 0x97, 0x56, 0xc0, 0x42, 0x44, 0x1b, 0xe6, 0xd8, 0x65, + 0x0b, 0x69, 0xb5, 0x4e, 0xbe, 0x71, 0x5e, 0x23, 0x43, + ]), + GuardianAddress([ + 0x54, 0xce, 0x5b, 0x4d, 0x34, 0x8f, 0xb7, 0x4b, 0x95, 0x8e, 0x89, + 0x66, 0xe2, 0xec, 0x3d, 0xbd, 0x49, 0x58, 0xa7, 0xcd, + ]), + GuardianAddress([ + 0xeb, 0x5f, 0x73, 0x89, 0xfa, 0x26, 0x94, 0x15, 0x19, 0xf0, 0x86, + 0x33, 0x49, 0xc2, 0x23, 0xb7, 0x3a, 0x6d, 0xde, 0xe7, + ]), + GuardianAddress([ + 0x74, 0xa3, 0xbf, 0x91, 0x39, 0x53, 0xd6, 0x95, 0x26, 0x0d, 0x88, + 0xbc, 0x1a, 0xa2, 0x5a, 0x4e, 0xee, 0x36, 0x3e, 0xf0, + ]), + GuardianAddress([ + 0x00, 0x0a, 0xc0, 0x07, 0x67, 0x27, 0xb3, 0x5f, 0xbe, 0xa2, 0xda, + 0xc2, 0x8f, 0xee, 0x5c, 0xcb, 0x0f, 0xea, 0x76, 0x8e, + ]), + GuardianAddress([ + 0xaf, 0x45, 0xce, 0xd1, 0x36, 0xb9, 0xd9, 0xe2, 0x49, 0x03, 0x46, + 0x4a, 0xe8, 0x89, 0xf5, 0xc8, 0xa7, 0x23, 0xfc, 0x14, + ]), + GuardianAddress([ + 0xf9, 0x31, 0x24, 0xb7, 0xc7, 0x38, 0x84, 0x3c, 0xbb, 0x89, 0xe8, + 0x64, 0xc8, 0x62, 0xc3, 0x8c, 0xdd, 0xcc, 0xcf, 0x95, + ]), + GuardianAddress([ + 0xd2, 0xcc, 0x37, 0xa4, 0xdc, 0x03, 0x6a, 0x8d, 0x23, 0x2b, 0x48, + 0xf6, 0x2c, 0xdd, 0x47, 0x31, 0x41, 0x2f, 0x48, 0x90, + ]), + GuardianAddress([ + 0xda, 0x79, 0x8f, 0x68, 0x96, 0xa3, 0x33, 0x1f, 0x64, 0xb4, 0x8c, + 0x12, 0xd1, 0xd5, 0x7f, 0xd9, 0xcb, 0xe7, 0x08, 0x11, + ]), + GuardianAddress([ + 0x71, 0xaa, 0x1b, 0xe1, 0xd3, 0x6c, 0xaf, 0xe3, 0x86, 0x79, 0x10, + 0xf9, 0x9c, 0x09, 0xe3, 0x47, 0x89, 0x9c, 0x19, 0xc3, + ]), + GuardianAddress([ + 0x81, 0x92, 0xb6, 0xe7, 0x38, 0x7c, 0xcd, 0x76, 0x82, 0x77, 0xc1, + 0x7d, 0xab, 0x1b, 0x7a, 0x50, 0x27, 0xc0, 0xb3, 0xcf, + ]), + GuardianAddress([ + 0x17, 0x8e, 0x21, 0xad, 0x2e, 0x77, 0xae, 0x06, 0x71, 0x15, 0x49, + 0xcf, 0xbb, 0x1f, 0x9c, 0x7a, 0x9d, 0x80, 0x96, 0xe8, + ]), + GuardianAddress([ + 0x5e, 0x14, 0x87, 0xf3, 0x55, 0x15, 0xd0, 0x2a, 0x92, 0x75, 0x35, + 0x04, 0xa8, 0xd7, 0x54, 0x71, 0xb9, 0xf4, 0x9e, 0xdb, + ]), + GuardianAddress([ + 0x6f, 0xbe, 0xbc, 0x89, 0x8f, 0x40, 0x3e, 0x47, 0x73, 0xe9, 0x5f, + 0xeb, 0x15, 0xe8, 0x0c, 0x9a, 0x99, 0xc8, 0x34, 0x8d, + ]), + ], + expiration_time: 0, + }, + }, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } +} diff --git a/sdk/rust/core/src/error.rs b/sdk/rust/core/src/error.rs deleted file mode 100644 index 949965fa7..000000000 --- a/sdk/rust/core/src/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -/// Ergonomic error handler for use within the Wormhole core/SDK libraries. -#[macro_export] -macro_rules! require { - ($expr:expr, $name:ident) => { - if !$expr { - return Err($name.into()); - } - }; -} - -/// This ErrorCode maps to the nom ParseError, we use an integer because the library is deprecating -/// the current error type, so we should avoid depending on it for now. -type ErrorCode = usize; - -#[derive(Debug)] -pub enum WormholeError { - InvalidGovernanceAction, - InvalidGovernanceChain, - InvalidGovernanceModule, - DeserializeFailed, - ParseError(ErrorCode), -} diff --git a/sdk/rust/core/src/lib.rs b/sdk/rust/core/src/lib.rs index b06de21cf..067d22503 100644 --- a/sdk/rust/core/src/lib.rs +++ b/sdk/rust/core/src/lib.rs @@ -1,34 +1,71 @@ +//! The `core` module provides all the pure Rust Wormhole primitives. +//! +//! This crate provides chain-agnostic types from Wormhole for consumption in on-chain contracts +//! and within other chain-specific Wormhole Rust SDK's. It includes: +//! +//! - Constants containing known network data/addresses. +//! - Parsers for VAA's and Payloads. +//! - Data types for Wormhole primitives such as GuardianSets and signatures. +//! - Verification Primitives for securely checking payloads. + +#![deny(warnings)] #![deny(unused_results)] -pub use chain::*; -pub use error::*; -pub use vaa::*; +use serde::{Deserialize, Serialize}; -pub mod chain; +mod arraystring; +mod chain; +pub mod core; +pub mod nft; +mod serde_array; +pub mod token; pub mod vaa; -#[macro_use] -pub mod error; +pub use {chain::Chain, vaa::Vaa}; -/// Helper method that attempts to parse and truncate UTF-8 from a byte stream. This is useful when -/// the wire data is expected to contain UTF-8 that is either already truncated, or needs to be, -/// while still maintaining the ability to render. -/// -/// This should be used to parse any Text-over-Wormhole fields that are meant to be human readable. -pub(crate) fn parse_fixed_utf8, const N: usize>(s: T) -> Option { - use bstr::ByteSlice; - use std::io::Cursor; - use std::io::Read; +/// The `GOVERNANCE_EMITTER` is a special address Wormhole guardians trust to observe governance +/// actions from. The value is "0000000000000000000000000000000000000000000000000000000000000004". +pub const GOVERNANCE_EMITTER: Address = Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, +]); - // Read Bytes. - let mut cursor = Cursor::new(s.as_ref()); - let mut buffer = vec![0u8; N]; - cursor.read_exact(&mut buffer).ok()?; - buffer.retain(|&c| c != 0); +#[derive( + Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, +)] +pub struct GuardianAddress(pub [u8; 20]); - // Attempt UTF-8 Decoding. Stripping invalid Unicode characters (0xFFFD). - let mut buffer: Vec = buffer.chars().collect(); - buffer.retain(|&c| c != '\u{FFFD}'); +/// Wormhole specifies addresses as 32 bytes. Addresses that are shorter, for example 20 byte +/// Ethereum addresses, are left zero padded to 32. +#[derive( + Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, +)] +pub struct Address(pub [u8; 32]); - Some(buffer.iter().collect()) +/// Wormhole specifies an amount as a uint256 encoded in big-endian order. +#[derive( + Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, +)] +pub struct Amount(pub [u8; 32]); + +/// A `GuardianSet` is a versioned set of keys that can sign Wormhole messages. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GuardianSetInfo { + /// The set of guardians public keys, in Ethereum's compressed format. + pub addresses: Vec, + + /// How long after a GuardianSet change before this set is expired. + #[serde(skip)] + pub expiration_time: u64, +} + +impl GuardianSetInfo { + pub fn quorum(&self) -> usize { + // allow quorum of 0 for testing purposes... + if self.addresses.is_empty() { + 0 + } else { + ((self.addresses.len() * 10 / 3) * 2) / 10 + 1 + } + } } diff --git a/sdk/rust/core/src/nft.rs b/sdk/rust/core/src/nft.rs new file mode 100644 index 000000000..16fa585d9 --- /dev/null +++ b/sdk/rust/core/src/nft.rs @@ -0,0 +1,701 @@ +//! Parsers for NFT bridge VAA Actions.. +//! +//! NFT bridging relies on VAA's that indicate custody/lockup/burn events in order to maintain +//! token parity between multiple chains. Parsers are provided here that can be used to read and +//! verify these events. + +use bstr::BString; +use serde::{Deserialize, Serialize}; + +use crate::{Address, Chain}; + +/// The ID of the token, encoded as a big-endian uint256. +#[derive( + Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, +)] +pub struct TokenId(pub [u8; 32]); + +/// Represents a non-governance action targeted at the NFT bridge. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Message { + /// The Transfer message contains specifics detailing a token lock up on a sending chain. Chains + /// that are attempting to initiate a transfer must lock up tokens in some manner, such as in a + /// custody account or via burning, before emitting this message. + #[serde(rename = "1")] + Transfer { + /// Address of the token. Left-zero-padded if shorter than 32 bytes + nft_address: Address, + + /// Chain ID of the token + nft_chain: Chain, + + /// Symbol of the token + #[serde(with = "crate::arraystring")] + symbol: BString, + + /// Name of the token + #[serde(with = "crate::arraystring")] + name: BString, + + /// TokenID of the token + token_id: TokenId, + + /// URI of the token metadata + uri: BString, + + /// The address of the recipient. + to: Address, + + /// The chain ID of the recipient. + to_chain: Chain, + }, +} + +/// Represents a governance action targeted at the NFT bridge. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Action { + /// Registers an emitter address for a particular chain on a different chain. An emitter + /// address must be registered for a chain and must match the emitter address in the VAA before + /// the NFT bridge will accept VAAs from that chain. + #[serde(rename = "1")] + RegisterChain { + chain: Chain, + emitter_address: Address, + }, + + /// Upgrades the NFT bridge contract to a new address. + #[serde(rename = "2")] + ContractUpgrade { new_contract: Address }, +} + +/// Represents the payload for a governance VAA targeted at the NFT bridge. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GovernancePacket { + /// The chain on which the governance action should be carried out. + pub chain: Chain, + + /// The actual governance action to be carried out. + pub action: Action, +} + +// The wire format for GovernancePackets is wonky and doesn't lend itself well to auto-deriving +// Serialize / Deserialize so we implement it manually here. +mod governance_packet_impl { + use std::fmt; + + use serde::{ + de::{Error, MapAccess, SeqAccess, Unexpected, Visitor}, + ser::SerializeStruct, + Deserialize, Deserializer, Serialize, Serializer, + }; + + use crate::{ + nft::{Action, GovernancePacket}, + Address, Chain, + }; + + // MODULE = "NFTBridge" + const MODULE: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x46, 0x54, 0x42, 0x72, 0x69, 0x64, + 0x67, 0x65, + ]; + + struct Module; + + impl Serialize for Module { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + MODULE.serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for Module { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let arr = <[u8; 32]>::deserialize(deserializer)?; + + if arr == MODULE { + Ok(Module) + } else { + let expected = format!("{MODULE:?}"); + Err(Error::invalid_value(Unexpected::Bytes(&arr), &&*expected)) + } + } + } + + #[derive(Serialize, Deserialize)] + struct ContractUpgrade { + new_contract: Address, + } + + #[derive(Serialize, Deserialize)] + struct RegisterChain { + chain: Chain, + emitter_address: Address, + } + + impl Serialize for GovernancePacket { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_struct("GovernancePacket", 4)?; + seq.serialize_field("module", &Module)?; + + // The wire format encodes the action before the chain and then appends the actual + // action payload. + match self.action { + Action::RegisterChain { + chain, + emitter_address, + } => { + seq.serialize_field("action", &1u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field( + "payload", + &RegisterChain { + chain, + emitter_address, + }, + )?; + } + Action::ContractUpgrade { new_contract } => { + seq.serialize_field("action", &2u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field("payload", &ContractUpgrade { new_contract })?; + } + } + + seq.end() + } + } + + struct GovernancePacketVisitor; + + impl<'de> Visitor<'de> for GovernancePacketVisitor { + type Value = GovernancePacket; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("struct GovernancePacket") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + static EXPECTING: &str = "struct GovernancePacket with 4 elements"; + + let _: Module = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(0, &EXPECTING))?; + let act: u8 = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(1, &EXPECTING))?; + let chain = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(2, &EXPECTING))?; + + let action = match act { + 1 => { + let RegisterChain { + chain, + emitter_address, + } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::RegisterChain { + chain, + emitter_address, + } + } + 2 => { + let ContractUpgrade { new_contract } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::ContractUpgrade { new_contract } + } + v => { + return Err(Error::invalid_value( + Unexpected::Unsigned(v.into()), + &"one of 1, 2", + )) + } + }; + + Ok(GovernancePacket { chain, action }) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "snake_case")] + enum Field { + Module, + Action, + Chain, + Payload, + } + + let mut module = None; + let mut chain = None; + let mut action = None; + let mut payload = None; + + while let Some(key) = map.next_key::()? { + match key { + Field::Module => { + if module.is_some() { + return Err(Error::duplicate_field("module")); + } + + module = map.next_value::().map(Some)?; + } + Field::Action => { + if action.is_some() { + return Err(Error::duplicate_field("action")); + } + + action = map.next_value::().map(Some)?; + } + Field::Chain => { + if chain.is_some() { + return Err(Error::duplicate_field("chain")); + } + + chain = map.next_value().map(Some)?; + } + Field::Payload => { + if payload.is_some() { + return Err(Error::duplicate_field("payload")); + } + + let a = action.as_ref().copied().ok_or_else(|| { + Error::custom("`action` must be known before deserializing `payload`") + })?; + + let p = match a { + 1 => { + let RegisterChain { + chain, + emitter_address, + } = map.next_value()?; + + Action::RegisterChain { + chain, + emitter_address, + } + } + 2 => { + let ContractUpgrade { new_contract } = map.next_value()?; + + Action::ContractUpgrade { new_contract } + } + v => { + return Err(Error::custom(format_args!( + "invalid action: {v}, expected one of: 1, 2" + ))) + } + }; + + payload = Some(p); + } + } + } + + let _ = module.ok_or_else(|| Error::missing_field("module"))?; + let chain = chain.ok_or_else(|| Error::missing_field("chain"))?; + let action = payload.ok_or_else(|| Error::missing_field("payload"))?; + + Ok(GovernancePacket { chain, action }) + } + } + + impl<'de> Deserialize<'de> for GovernancePacket { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + const FIELDS: &[&str] = &["module", "action", "chain", "payload"]; + deserializer.deserialize_struct("GovernancePacket", FIELDS, GovernancePacketVisitor) + } + } +} + +#[cfg(test)] +mod test { + use crate::{vaa::Signature, Vaa, GOVERNANCE_EMITTER}; + + use super::*; + + #[test] + fn transfer() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x77, 0xbb, 0x0b, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4f, 0x4f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, + 0x41, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0a, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![], + timestamp: 1, + nonce: 1, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 41_401_099, + consistency_level: 0, + payload: Message::Transfer { + nft_address: GOVERNANCE_EMITTER, + nft_chain: Chain::Solana, + symbol: "FOO".into(), + name: "BAR".into(), + token_id: TokenId([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + ]), + uri: "google.com".into(), + to: GOVERNANCE_EMITTER, + to_chain: Chain::Fantom, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } + + #[test] + fn register_chain() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, + 0x1d, 0x08, 0x90, 0x5c, 0x02, 0xe2, 0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, + 0xc8, 0xdb, 0x4f, 0x77, 0xfe, 0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, + 0x92, 0xa9, 0x19, 0x4d, 0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, + 0xf0, 0xc2, 0x1c, 0xe9, 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, + 0x98, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x1b, 0xfa, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x46, 0x54, 0x42, 0x72, 0x69, 0x64, 0x67, + 0x65, 0x01, 0x00, 0x00, 0x00, 0x01, 0x3b, 0x26, 0x40, 0x9f, 0x8a, 0xad, 0xed, 0x3f, + 0x5d, 0xdc, 0xa1, 0x84, 0x69, 0x5a, 0xa6, 0xa0, 0xfa, 0x82, 0x9b, 0x0c, 0x85, 0xca, + 0xf8, 0x48, 0x56, 0x32, 0x48, 0x96, 0xd2, 0x14, 0xca, 0x98, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![Signature { + index: 0, + signature: [ + 0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, 0x1d, 0x08, 0x90, 0x5c, 0x02, 0xe2, + 0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, 0xc8, 0xdb, 0x4f, 0x77, 0xfe, + 0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, 0x92, 0xa9, 0x19, 0x4d, + 0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, 0xf0, 0xc2, 0x1c, + 0xe9, 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, 0x98, 0x01, + ], + }], + timestamp: 1, + nonce: 1, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 20_716_538, + consistency_level: 0, + payload: GovernancePacket { + chain: Chain::Any, + action: Action::RegisterChain { + chain: Chain::Solana, + emitter_address: Address([ + 0x3b, 0x26, 0x40, 0x9f, 0x8a, 0xad, 0xed, 0x3f, 0x5d, 0xdc, 0xa1, 0x84, + 0x69, 0x5a, 0xa6, 0xa0, 0xfa, 0x82, 0x9b, 0x0c, 0x85, 0xca, 0xf8, 0x48, + 0x56, 0x32, 0x48, 0x96, 0xd2, 0x14, 0xca, 0x98, + ]), + }, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } + + #[test] + fn contract_upgrade() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x73, 0xaf, 0x3c, 0x2f, 0x55, 0x98, 0x90, + 0x41, 0xb0, 0x06, 0x8a, 0x4e, 0xae, 0xba, 0x8f, 0x88, 0x25, 0x10, 0x60, 0x21, 0x99, + 0x6d, 0xe6, 0x14, 0x39, 0x54, 0xc0, 0x6f, 0xc4, 0xcf, 0xad, 0x5c, 0x3f, 0x1a, 0xba, + 0x6c, 0xa9, 0x51, 0x18, 0x8e, 0x96, 0x2f, 0x11, 0x25, 0x1c, 0x25, 0xca, 0x98, 0x62, + 0x86, 0x85, 0xe2, 0xfc, 0x2b, 0x65, 0xec, 0x60, 0x94, 0x50, 0xcf, 0xc4, 0xe6, 0x51, + 0x12, 0x00, 0x03, 0x00, 0xc6, 0x2e, 0xb0, 0xde, 0x7a, 0xb3, 0xd0, 0x28, 0x92, 0x0f, + 0x18, 0xe1, 0x0d, 0xf7, 0xf2, 0xa0, 0x4d, 0x00, 0xeb, 0x88, 0xb8, 0x94, 0x0a, 0x27, + 0xbb, 0xce, 0x8c, 0xe6, 0x6c, 0x67, 0x52, 0x68, 0x82, 0xc9, 0x6c, 0xc5, 0x0d, 0xd3, + 0x81, 0xbd, 0xbf, 0xf3, 0x0b, 0xb6, 0x5b, 0x93, 0x47, 0xca, 0xdf, 0xf0, 0x96, 0x76, + 0x72, 0x90, 0x43, 0xaf, 0x60, 0xd6, 0x52, 0xa0, 0xb9, 0x06, 0x30, 0x00, 0x04, 0xf0, + 0x7b, 0x0f, 0xe6, 0xca, 0xb1, 0x8b, 0xe7, 0xae, 0x82, 0x7f, 0xe1, 0x1c, 0xa8, 0x99, + 0x2f, 0xf0, 0xb5, 0xa2, 0xa3, 0x2d, 0x65, 0xbe, 0x95, 0x82, 0x31, 0xbf, 0x84, 0xcf, + 0x6b, 0x14, 0xfb, 0x5e, 0xeb, 0x8a, 0x51, 0xad, 0xe1, 0xc7, 0x0a, 0xa5, 0xb8, 0xb8, + 0xcf, 0x83, 0xaf, 0xe3, 0xc2, 0x2d, 0x34, 0x71, 0x48, 0x1b, 0xda, 0x38, 0x96, 0xea, + 0x2a, 0x70, 0x46, 0x9f, 0x96, 0x92, 0xb8, 0x00, 0x05, 0x74, 0xd4, 0x96, 0x72, 0x9d, + 0xde, 0x6e, 0x19, 0x09, 0x59, 0xa1, 0x79, 0x45, 0xe8, 0x7c, 0xd9, 0x5e, 0x23, 0xc1, + 0x00, 0x47, 0xab, 0x2e, 0xef, 0x5c, 0x7d, 0x0e, 0xc6, 0xf9, 0x12, 0x08, 0xc7, 0x27, + 0x9d, 0x8a, 0x1b, 0x1f, 0x4a, 0x60, 0x06, 0x9d, 0x25, 0x93, 0x2a, 0x67, 0xd6, 0x55, + 0xf2, 0xd0, 0xb4, 0x20, 0x59, 0x00, 0x3d, 0xe7, 0x5f, 0xd3, 0xca, 0x79, 0x92, 0xfd, + 0xd1, 0xb0, 0xd4, 0x01, 0x06, 0x51, 0xf0, 0x6a, 0xb6, 0xfd, 0xfd, 0x42, 0xf3, 0x23, + 0x84, 0x0a, 0x81, 0x8b, 0x38, 0xcf, 0xd1, 0xc9, 0x9d, 0x22, 0x36, 0x6c, 0x87, 0x79, + 0x03, 0xb9, 0x8d, 0xf4, 0x0b, 0xda, 0xf2, 0xa2, 0x04, 0x2e, 0xb8, 0xcd, 0x0e, 0x59, + 0xc4, 0x63, 0x7e, 0x6a, 0x80, 0xa1, 0x99, 0xb1, 0x10, 0x8c, 0xd7, 0x65, 0x9b, 0xa8, + 0x37, 0x0f, 0x6f, 0x94, 0x76, 0xb4, 0x79, 0x83, 0x98, 0x54, 0xd8, 0xc1, 0xa6, 0x00, + 0x07, 0x78, 0x4b, 0x37, 0xc6, 0x10, 0x3c, 0x75, 0x2c, 0xd9, 0x7a, 0x58, 0x6b, 0xe8, + 0xe1, 0xce, 0xff, 0x22, 0xa6, 0xe4, 0x88, 0x43, 0x32, 0x84, 0xff, 0x31, 0xcd, 0x90, + 0x8b, 0x5c, 0x7d, 0x87, 0xe2, 0x44, 0xda, 0x75, 0x16, 0xd0, 0x7a, 0x0f, 0xa6, 0x92, + 0x68, 0x4f, 0x82, 0x6e, 0x5e, 0x6c, 0x8b, 0x45, 0x20, 0x04, 0x20, 0x6d, 0x6f, 0xe5, + 0xba, 0xe4, 0x6a, 0xfb, 0x1c, 0x21, 0x8c, 0xf5, 0x0d, 0x00, 0x09, 0x0b, 0x6c, 0xcc, + 0xe6, 0x7e, 0xd5, 0x01, 0xcb, 0x20, 0xfb, 0x06, 0xde, 0x76, 0xb9, 0x08, 0x3f, 0x13, + 0x29, 0xad, 0x78, 0xd3, 0x84, 0x51, 0x7a, 0x94, 0xab, 0x8e, 0x9a, 0xa6, 0x01, 0xbe, + 0x7a, 0x0f, 0x00, 0xbb, 0x60, 0x3b, 0x36, 0x50, 0x4a, 0x74, 0xdd, 0xc3, 0x67, 0x35, + 0x9c, 0x8e, 0xa0, 0x7f, 0x5c, 0xcd, 0x50, 0x22, 0x77, 0x2c, 0xc1, 0x57, 0xe0, 0x0f, + 0x54, 0x15, 0x63, 0x71, 0xe4, 0x01, 0x0b, 0xe1, 0xe6, 0xb7, 0x12, 0xa2, 0x35, 0x3e, + 0x49, 0x07, 0x92, 0x9c, 0x40, 0x57, 0xd5, 0xba, 0xbe, 0xf8, 0x0d, 0xc0, 0x9f, 0xeb, + 0xb3, 0xdc, 0x29, 0x34, 0xf4, 0x40, 0x48, 0x79, 0x5e, 0xef, 0x95, 0x4e, 0x99, 0x10, + 0x6c, 0xb2, 0x9b, 0x1a, 0x8d, 0x93, 0x53, 0x69, 0xda, 0xec, 0xbb, 0x8b, 0xae, 0x45, + 0x19, 0x2e, 0xfc, 0x3c, 0xed, 0xbc, 0x57, 0x31, 0x5d, 0x67, 0xec, 0x9e, 0xa8, 0x00, + 0x0a, 0x00, 0x0d, 0xdc, 0xc0, 0x4c, 0xd7, 0x55, 0x6a, 0x94, 0xe8, 0xb4, 0x18, 0xdc, + 0x3a, 0x8a, 0x00, 0xef, 0xb1, 0x28, 0xd4, 0xf7, 0x6f, 0x71, 0x43, 0x12, 0x47, 0xa9, + 0x2e, 0x7a, 0x19, 0xa8, 0x33, 0xfc, 0x02, 0x21, 0x5d, 0x1f, 0xd6, 0x9e, 0x0e, 0x59, + 0x6d, 0xa5, 0x31, 0xea, 0x58, 0x7a, 0xe2, 0xac, 0x2f, 0x36, 0xd2, 0x0f, 0x3b, 0x19, + 0x33, 0x06, 0x0a, 0xd6, 0xef, 0x3e, 0x9d, 0x7d, 0x84, 0x75, 0xba, 0x01, 0x0e, 0xc7, + 0x62, 0x16, 0x49, 0x49, 0x06, 0xae, 0xd8, 0xd8, 0x31, 0x0c, 0x31, 0xa5, 0xe4, 0xa5, + 0x45, 0xea, 0x2a, 0xf8, 0xdb, 0xe3, 0x8e, 0xeb, 0x74, 0xa3, 0xd1, 0x4f, 0x07, 0x34, + 0xdd, 0x2b, 0x1a, 0x21, 0xb9, 0x85, 0x0e, 0xa2, 0x0e, 0x6c, 0x2c, 0xa4, 0x22, 0x48, + 0x78, 0x8b, 0xfb, 0x17, 0x43, 0x9d, 0x37, 0xab, 0xda, 0x25, 0xd7, 0x05, 0xb5, 0x68, + 0xb2, 0x65, 0xb1, 0x13, 0xb9, 0x76, 0xc5, 0x00, 0x10, 0xc6, 0x1f, 0xe6, 0xeb, 0xea, + 0x61, 0xf2, 0xca, 0xe8, 0x95, 0xc3, 0x34, 0x96, 0x50, 0x70, 0x48, 0x7d, 0x39, 0xab, + 0x6b, 0xf0, 0x44, 0x28, 0x34, 0x0a, 0xf0, 0xf7, 0x69, 0x9f, 0xdd, 0xd3, 0xc0, 0x1e, + 0x0c, 0x86, 0xda, 0xea, 0x9e, 0xb4, 0x49, 0x70, 0x12, 0x48, 0xa2, 0x4f, 0xa4, 0xc0, + 0xff, 0x75, 0xfb, 0x60, 0x0b, 0x2c, 0x01, 0x34, 0xbd, 0x72, 0x17, 0x91, 0xf6, 0x67, + 0x11, 0x6e, 0x42, 0x00, 0x11, 0x19, 0xb7, 0x9c, 0xaa, 0x25, 0x0e, 0x75, 0xda, 0x14, + 0x82, 0xc7, 0xf4, 0x12, 0x68, 0x73, 0x08, 0xd1, 0x3e, 0xf7, 0x00, 0xf7, 0x26, 0xb9, + 0x94, 0x17, 0xbb, 0x75, 0xae, 0x6d, 0xd1, 0x43, 0xfc, 0x61, 0x9f, 0x8c, 0x9c, 0x29, + 0xc7, 0x3f, 0x99, 0x49, 0xaf, 0xfd, 0x16, 0x29, 0xcc, 0x28, 0x6a, 0x61, 0x91, 0x7e, + 0xd7, 0x85, 0x37, 0x54, 0xa4, 0x67, 0x02, 0x92, 0x0b, 0x76, 0xcf, 0x90, 0xeb, 0x00, + 0x12, 0xb0, 0xba, 0xb5, 0xe1, 0xa8, 0xb3, 0x21, 0xe9, 0x2f, 0x9d, 0x3d, 0xf9, 0x24, + 0x30, 0x18, 0x3e, 0x48, 0x43, 0xe5, 0x7c, 0x6f, 0x8c, 0x15, 0xc2, 0x2d, 0x62, 0xb2, + 0xf8, 0xe4, 0xb8, 0xcd, 0xc2, 0x75, 0x9f, 0x28, 0x54, 0xb8, 0xd1, 0x22, 0xac, 0xdb, + 0x4b, 0x4d, 0x89, 0x28, 0xb8, 0x7d, 0xf4, 0x19, 0x9c, 0xc6, 0x83, 0x01, 0x1d, 0x2e, + 0x9b, 0x7d, 0x41, 0xa9, 0x6d, 0x8b, 0x48, 0x0c, 0xcc, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x39, 0xc4, 0xba, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x25, 0x64, 0xd2, 0xef, + 0xd6, 0x08, 0x4d, 0xf0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4e, 0x46, 0x54, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x02, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x83, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 1, + signatures: vec![ + Signature { + index: 2, + signature: [ + 0x73, 0xaf, 0x3c, 0x2f, 0x55, 0x98, 0x90, 0x41, 0xb0, 0x06, 0x8a, 0x4e, + 0xae, 0xba, 0x8f, 0x88, 0x25, 0x10, 0x60, 0x21, 0x99, 0x6d, 0xe6, 0x14, + 0x39, 0x54, 0xc0, 0x6f, 0xc4, 0xcf, 0xad, 0x5c, 0x3f, 0x1a, 0xba, 0x6c, + 0xa9, 0x51, 0x18, 0x8e, 0x96, 0x2f, 0x11, 0x25, 0x1c, 0x25, 0xca, 0x98, + 0x62, 0x86, 0x85, 0xe2, 0xfc, 0x2b, 0x65, 0xec, 0x60, 0x94, 0x50, 0xcf, + 0xc4, 0xe6, 0x51, 0x12, 0x00, + ], + }, + Signature { + index: 3, + signature: [ + 0x00, 0xc6, 0x2e, 0xb0, 0xde, 0x7a, 0xb3, 0xd0, 0x28, 0x92, 0x0f, 0x18, + 0xe1, 0x0d, 0xf7, 0xf2, 0xa0, 0x4d, 0x00, 0xeb, 0x88, 0xb8, 0x94, 0x0a, + 0x27, 0xbb, 0xce, 0x8c, 0xe6, 0x6c, 0x67, 0x52, 0x68, 0x82, 0xc9, 0x6c, + 0xc5, 0x0d, 0xd3, 0x81, 0xbd, 0xbf, 0xf3, 0x0b, 0xb6, 0x5b, 0x93, 0x47, + 0xca, 0xdf, 0xf0, 0x96, 0x76, 0x72, 0x90, 0x43, 0xaf, 0x60, 0xd6, 0x52, + 0xa0, 0xb9, 0x06, 0x30, 0x00, + ], + }, + Signature { + index: 4, + signature: [ + 0xf0, 0x7b, 0x0f, 0xe6, 0xca, 0xb1, 0x8b, 0xe7, 0xae, 0x82, 0x7f, 0xe1, + 0x1c, 0xa8, 0x99, 0x2f, 0xf0, 0xb5, 0xa2, 0xa3, 0x2d, 0x65, 0xbe, 0x95, + 0x82, 0x31, 0xbf, 0x84, 0xcf, 0x6b, 0x14, 0xfb, 0x5e, 0xeb, 0x8a, 0x51, + 0xad, 0xe1, 0xc7, 0x0a, 0xa5, 0xb8, 0xb8, 0xcf, 0x83, 0xaf, 0xe3, 0xc2, + 0x2d, 0x34, 0x71, 0x48, 0x1b, 0xda, 0x38, 0x96, 0xea, 0x2a, 0x70, 0x46, + 0x9f, 0x96, 0x92, 0xb8, 0x00, + ], + }, + Signature { + index: 5, + signature: [ + 0x74, 0xd4, 0x96, 0x72, 0x9d, 0xde, 0x6e, 0x19, 0x09, 0x59, 0xa1, 0x79, + 0x45, 0xe8, 0x7c, 0xd9, 0x5e, 0x23, 0xc1, 0x00, 0x47, 0xab, 0x2e, 0xef, + 0x5c, 0x7d, 0x0e, 0xc6, 0xf9, 0x12, 0x08, 0xc7, 0x27, 0x9d, 0x8a, 0x1b, + 0x1f, 0x4a, 0x60, 0x06, 0x9d, 0x25, 0x93, 0x2a, 0x67, 0xd6, 0x55, 0xf2, + 0xd0, 0xb4, 0x20, 0x59, 0x00, 0x3d, 0xe7, 0x5f, 0xd3, 0xca, 0x79, 0x92, + 0xfd, 0xd1, 0xb0, 0xd4, 0x01, + ], + }, + Signature { + index: 6, + signature: [ + 0x51, 0xf0, 0x6a, 0xb6, 0xfd, 0xfd, 0x42, 0xf3, 0x23, 0x84, 0x0a, 0x81, + 0x8b, 0x38, 0xcf, 0xd1, 0xc9, 0x9d, 0x22, 0x36, 0x6c, 0x87, 0x79, 0x03, + 0xb9, 0x8d, 0xf4, 0x0b, 0xda, 0xf2, 0xa2, 0x04, 0x2e, 0xb8, 0xcd, 0x0e, + 0x59, 0xc4, 0x63, 0x7e, 0x6a, 0x80, 0xa1, 0x99, 0xb1, 0x10, 0x8c, 0xd7, + 0x65, 0x9b, 0xa8, 0x37, 0x0f, 0x6f, 0x94, 0x76, 0xb4, 0x79, 0x83, 0x98, + 0x54, 0xd8, 0xc1, 0xa6, 0x00, + ], + }, + Signature { + index: 7, + signature: [ + 0x78, 0x4b, 0x37, 0xc6, 0x10, 0x3c, 0x75, 0x2c, 0xd9, 0x7a, 0x58, 0x6b, + 0xe8, 0xe1, 0xce, 0xff, 0x22, 0xa6, 0xe4, 0x88, 0x43, 0x32, 0x84, 0xff, + 0x31, 0xcd, 0x90, 0x8b, 0x5c, 0x7d, 0x87, 0xe2, 0x44, 0xda, 0x75, 0x16, + 0xd0, 0x7a, 0x0f, 0xa6, 0x92, 0x68, 0x4f, 0x82, 0x6e, 0x5e, 0x6c, 0x8b, + 0x45, 0x20, 0x04, 0x20, 0x6d, 0x6f, 0xe5, 0xba, 0xe4, 0x6a, 0xfb, 0x1c, + 0x21, 0x8c, 0xf5, 0x0d, 0x00, + ], + }, + Signature { + index: 9, + signature: [ + 0x0b, 0x6c, 0xcc, 0xe6, 0x7e, 0xd5, 0x01, 0xcb, 0x20, 0xfb, 0x06, 0xde, + 0x76, 0xb9, 0x08, 0x3f, 0x13, 0x29, 0xad, 0x78, 0xd3, 0x84, 0x51, 0x7a, + 0x94, 0xab, 0x8e, 0x9a, 0xa6, 0x01, 0xbe, 0x7a, 0x0f, 0x00, 0xbb, 0x60, + 0x3b, 0x36, 0x50, 0x4a, 0x74, 0xdd, 0xc3, 0x67, 0x35, 0x9c, 0x8e, 0xa0, + 0x7f, 0x5c, 0xcd, 0x50, 0x22, 0x77, 0x2c, 0xc1, 0x57, 0xe0, 0x0f, 0x54, + 0x15, 0x63, 0x71, 0xe4, 0x01, + ], + }, + Signature { + index: 11, + signature: [ + 0xe1, 0xe6, 0xb7, 0x12, 0xa2, 0x35, 0x3e, 0x49, 0x07, 0x92, 0x9c, 0x40, + 0x57, 0xd5, 0xba, 0xbe, 0xf8, 0x0d, 0xc0, 0x9f, 0xeb, 0xb3, 0xdc, 0x29, + 0x34, 0xf4, 0x40, 0x48, 0x79, 0x5e, 0xef, 0x95, 0x4e, 0x99, 0x10, 0x6c, + 0xb2, 0x9b, 0x1a, 0x8d, 0x93, 0x53, 0x69, 0xda, 0xec, 0xbb, 0x8b, 0xae, + 0x45, 0x19, 0x2e, 0xfc, 0x3c, 0xed, 0xbc, 0x57, 0x31, 0x5d, 0x67, 0xec, + 0x9e, 0xa8, 0x00, 0x0a, 0x00, + ], + }, + Signature { + index: 13, + signature: [ + 0xdc, 0xc0, 0x4c, 0xd7, 0x55, 0x6a, 0x94, 0xe8, 0xb4, 0x18, 0xdc, 0x3a, + 0x8a, 0x00, 0xef, 0xb1, 0x28, 0xd4, 0xf7, 0x6f, 0x71, 0x43, 0x12, 0x47, + 0xa9, 0x2e, 0x7a, 0x19, 0xa8, 0x33, 0xfc, 0x02, 0x21, 0x5d, 0x1f, 0xd6, + 0x9e, 0x0e, 0x59, 0x6d, 0xa5, 0x31, 0xea, 0x58, 0x7a, 0xe2, 0xac, 0x2f, + 0x36, 0xd2, 0x0f, 0x3b, 0x19, 0x33, 0x06, 0x0a, 0xd6, 0xef, 0x3e, 0x9d, + 0x7d, 0x84, 0x75, 0xba, 0x01, + ], + }, + Signature { + index: 14, + signature: [ + 0xc7, 0x62, 0x16, 0x49, 0x49, 0x06, 0xae, 0xd8, 0xd8, 0x31, 0x0c, 0x31, + 0xa5, 0xe4, 0xa5, 0x45, 0xea, 0x2a, 0xf8, 0xdb, 0xe3, 0x8e, 0xeb, 0x74, + 0xa3, 0xd1, 0x4f, 0x07, 0x34, 0xdd, 0x2b, 0x1a, 0x21, 0xb9, 0x85, 0x0e, + 0xa2, 0x0e, 0x6c, 0x2c, 0xa4, 0x22, 0x48, 0x78, 0x8b, 0xfb, 0x17, 0x43, + 0x9d, 0x37, 0xab, 0xda, 0x25, 0xd7, 0x05, 0xb5, 0x68, 0xb2, 0x65, 0xb1, + 0x13, 0xb9, 0x76, 0xc5, 0x00, + ], + }, + Signature { + index: 16, + signature: [ + 0xc6, 0x1f, 0xe6, 0xeb, 0xea, 0x61, 0xf2, 0xca, 0xe8, 0x95, 0xc3, 0x34, + 0x96, 0x50, 0x70, 0x48, 0x7d, 0x39, 0xab, 0x6b, 0xf0, 0x44, 0x28, 0x34, + 0x0a, 0xf0, 0xf7, 0x69, 0x9f, 0xdd, 0xd3, 0xc0, 0x1e, 0x0c, 0x86, 0xda, + 0xea, 0x9e, 0xb4, 0x49, 0x70, 0x12, 0x48, 0xa2, 0x4f, 0xa4, 0xc0, 0xff, + 0x75, 0xfb, 0x60, 0x0b, 0x2c, 0x01, 0x34, 0xbd, 0x72, 0x17, 0x91, 0xf6, + 0x67, 0x11, 0x6e, 0x42, 0x00, + ], + }, + Signature { + index: 17, + signature: [ + 0x19, 0xb7, 0x9c, 0xaa, 0x25, 0x0e, 0x75, 0xda, 0x14, 0x82, 0xc7, 0xf4, + 0x12, 0x68, 0x73, 0x08, 0xd1, 0x3e, 0xf7, 0x00, 0xf7, 0x26, 0xb9, 0x94, + 0x17, 0xbb, 0x75, 0xae, 0x6d, 0xd1, 0x43, 0xfc, 0x61, 0x9f, 0x8c, 0x9c, + 0x29, 0xc7, 0x3f, 0x99, 0x49, 0xaf, 0xfd, 0x16, 0x29, 0xcc, 0x28, 0x6a, + 0x61, 0x91, 0x7e, 0xd7, 0x85, 0x37, 0x54, 0xa4, 0x67, 0x02, 0x92, 0x0b, + 0x76, 0xcf, 0x90, 0xeb, 0x00, + ], + }, + Signature { + index: 18, + signature: [ + 0xb0, 0xba, 0xb5, 0xe1, 0xa8, 0xb3, 0x21, 0xe9, 0x2f, 0x9d, 0x3d, 0xf9, + 0x24, 0x30, 0x18, 0x3e, 0x48, 0x43, 0xe5, 0x7c, 0x6f, 0x8c, 0x15, 0xc2, + 0x2d, 0x62, 0xb2, 0xf8, 0xe4, 0xb8, 0xcd, 0xc2, 0x75, 0x9f, 0x28, 0x54, + 0xb8, 0xd1, 0x22, 0xac, 0xdb, 0x4b, 0x4d, 0x89, 0x28, 0xb8, 0x7d, 0xf4, + 0x19, 0x9c, 0xc6, 0x83, 0x01, 0x1d, 0x2e, 0x9b, 0x7d, 0x41, 0xa9, 0x6d, + 0x8b, 0x48, 0x0c, 0xcc, 0x01, + ], + }, + ], + timestamp: 0, + nonce: 969_194_102, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 2_694_510_404_604_284_400, + consistency_level: 32, + payload: GovernancePacket { + chain: Chain::Terra, + action: Action::ContractUpgrade { + new_contract: Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x83, + ]), + }, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } +} diff --git a/sdk/rust/core/src/serde_array.rs b/sdk/rust/core/src/serde_array.rs new file mode 100644 index 000000000..27eb7fbf5 --- /dev/null +++ b/sdk/rust/core/src/serde_array.rs @@ -0,0 +1,64 @@ +use std::{fmt, mem::MaybeUninit}; + +use serde::{ + de::{Error, SeqAccess, Visitor}, + ser::SerializeTuple, + Deserializer, Serializer, +}; + +pub fn serialize(value: &[u8; N], serializer: S) -> Result +where + S: Serializer, +{ + let mut seq = serializer.serialize_tuple(N)?; + for v in value { + seq.serialize_element(v)?; + } + + seq.end() +} + +struct ArrayVisitor; +impl<'de, const N: usize> Visitor<'de> for ArrayVisitor { + type Value = [u8; N]; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "an array of length {}", N) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + // TODO: Replace with `MaybeUninit::uninit_array()` once that's stabilized. + let mut buf = MaybeUninit::<[u8; N]>::uninit(); + let ptr = buf.as_mut_ptr() as *mut u8; + let mut pos = 0; + + while pos < N { + let v = seq + .next_element() + .and_then(|v| v.ok_or_else(|| Error::invalid_length(pos, &self)))?; + + // Safety: The resulting pointer is within the bounds of the allocation because + // we know that `pos < N`. + unsafe { ptr.add(pos).write(v) }; + + pos += 1; + } + + if pos == N { + // Safety: We've initialized all the bytes in `buf`. + Ok(unsafe { buf.assume_init() }) + } else { + Err(Error::invalid_length(pos, &self)) + } + } +} + +pub fn deserialize<'de, const N: usize, D>(deserializer: D) -> Result<[u8; N], D::Error> +where + D: Deserializer<'de>, +{ + deserializer.deserialize_tuple(N, ArrayVisitor) +} diff --git a/sdk/rust/core/src/token.rs b/sdk/rust/core/src/token.rs new file mode 100644 index 000000000..a6e9eb0ba --- /dev/null +++ b/sdk/rust/core/src/token.rs @@ -0,0 +1,906 @@ +//! Parsers for Token bridge VAAs. +//! +//! Token bridging relies on VAA's that indicate custody/lockup/burn events in order to maintain +//! token parity between multiple chains. These parsers can be used to read these VAAs. It also +//! defines the Governance actions that this module supports, namely contract upgrades and chain +//! registrations. + +use bstr::BString; +use serde::{Deserialize, Serialize}; + +use crate::{Address, Amount, Chain}; + +/// Represents a non-governance action targeted at the token bridge. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Message { + /// The Transfer message contains specifics detailing a token lock up on a sending chain. Chains + /// that are attempting to initiate a transfer must lock up tokens in some manner, such as in a + /// custody account or via burning, before emitting this message. + #[serde(rename = "1")] + Transfer { + /// The amount being transferred. + amount: Amount, + + /// Address of the token. Left-zero-padded if shorter than 32 bytes. + token_address: Address, + + /// Chain ID of the token. + token_chain: Chain, + + /// Address of the recipient. Left-zero-padded if shorter than 32 bytes. + recipient: Address, + + /// Chain ID of the recipient. + recipient_chain: Chain, + + /// Amount that the user is willing to pay as the relayer fee. Must be <= `amount`. + fee: Amount, + }, + + /// Contains information about a token and its origin chain. + #[serde(rename = "2")] + AssetMeta { + /// Address of the token. Left-zero-padded if shorter than 32 bytes. + token_address: Address, + + /// Chain ID of the token. + token_chain: Chain, + + /// Number of decimals in the token. + decimals: u8, + + /// Symbol of the token. + #[serde(with = "crate::arraystring")] + symbol: BString, + + /// Name of the token. + #[serde(with = "crate::arraystring")] + name: BString, + }, + + /// Similar to `Transfer` but also includes an arbitrary payload that is appended to the end of + /// the message. + /// + /// # Examples + /// + /// Deserialize a `TransferWithPayload` message using the `serde_wormhole` crate: + /// + /// ``` + /// # fn example() -> anyhow::Result<()> { + /// # use wormhole::{Address, Amount, Chain, vaa::Signature, GOVERNANCE_EMITTER}; + /// use wormhole::{token::Message, Vaa}; + /// # + /// # let data = [ + /// # 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, 0x1d, + /// # 0x08, 0x90, 0x5c, 0x02, 0xe2, 0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, 0xc8, 0xdb, + /// # 0x4f, 0x77, 0xfe, 0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, 0x92, 0xa9, 0x19, + /// # 0x4d, 0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, 0xf0, 0xc2, 0x1c, 0xe9, + /// # 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, 0x98, 0x01, 0x00, 0x00, 0x00, + /// # 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// # 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// # 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, + /// # 0x1b, 0xfa, 0x00, 0x03, 0x99, 0x30, 0x29, 0x01, 0x67, 0xf3, 0x48, 0x6a, 0xf6, 0x4c, 0xdd, + /// # 0x07, 0x64, 0xa1, 0x6a, 0xca, 0xcc, 0xc5, 0x64, 0x2a, 0x66, 0x71, 0xaa, 0x87, 0x98, 0x64, + /// # 0x0c, 0x25, 0x1f, 0x39, 0x73, 0x25, 0x12, 0x98, 0x15, 0xe5, 0x79, 0xdb, 0xd8, 0x25, 0xfd, + /// # 0x72, 0x58, 0x99, 0x97, 0xd1, 0x78, 0xb6, 0x2a, 0x57, 0x47, 0xab, 0xcf, 0x51, 0x62, 0xa5, + /// # 0xb7, 0xec, 0x19, 0x4e, 0x03, 0x51, 0x41, 0x18, 0x00, 0x08, 0xa4, 0x7e, 0xa2, 0x49, 0xd4, + /// # 0xf0, 0x56, 0x6f, 0xbd, 0x12, 0x77, 0xfe, 0xa8, 0x6e, 0x92, 0x81, 0x3a, 0x35, 0x90, 0x3b, + /// # 0x29, 0x73, 0x4d, 0x75, 0x56, 0x0a, 0xfe, 0x70, 0xb0, 0xb9, 0x10, 0x33, 0x00, 0x14, 0x47, + /// # 0x3e, 0x51, 0x74, 0x32, 0xbc, 0x3f, 0xb3, 0xf9, 0x82, 0xbb, 0xf3, 0xc9, 0x43, 0x41, 0xcf, + /// # 0x74, 0x24, 0xff, 0xa4, 0x02, 0x35, 0xbe, 0xb1, 0x7c, 0x47, 0x16, 0xba, 0xbc, 0xaa, 0xbe, + /// # 0x99, 0x54, 0xa2, 0x97, 0xbe, 0x2a, 0xed, 0xae, 0x91, 0x95, 0xb9, 0x6a, 0x8a, 0xba, 0xbd, + /// # 0x3a, 0xc1, 0xa4, 0xd1, 0x96, 0x0a, 0xf9, 0xab, 0x92, 0x42, 0x0d, 0x41, 0x33, 0xe5, 0xa8, + /// # 0x37, 0x32, 0xd3, 0xc3, 0xb8, 0xf3, 0x93, 0xd7, 0xc0, 0x9e, 0xe8, 0x87, 0xae, 0x16, 0xbf, + /// # 0xfa, 0x5e, 0x70, 0xea, 0x36, 0xa2, 0x82, 0x37, 0x1d, 0x46, 0x81, 0x94, 0x10, 0x34, 0xb1, + /// # 0xad, 0x0f, 0x4b, 0xc9, 0x17, 0x1e, 0x91, 0x25, 0x11, + /// # ]; + /// + /// let (msg, payload) = serde_wormhole::from_slice_with_payload::>(&data)?; + /// assert!(matches!(msg.payload, Message::TransferWithPayload { .. })); + /// assert_eq!(&data[256..], payload); + /// # + /// # Ok(()) + /// # } + /// # + /// # example().unwrap(); + /// ``` + /// + /// Deserialize a `TransferWithPayload` message using `serde_json`: + /// + /// ``` + /// # fn example() -> anyhow::Result<()> { + /// # use wormhole::{Address, Amount, Chain, vaa::Signature, GOVERNANCE_EMITTER}; + /// use anyhow::anyhow; + /// use wormhole::{token::Message, Vaa}; + /// # let vaa = Vaa { + /// # version: 1, + /// # guardian_set_index: 0, + /// # signatures: Vec::new(), + /// # timestamp: 1, + /// # nonce: 1, + /// # emitter_chain: Chain::Solana, + /// # emitter_address: GOVERNANCE_EMITTER, + /// # sequence: 20_716_538, + /// # consistency_level: 0, + /// # payload: Message::TransferWithPayload { + /// # amount: Amount([0xcc; 32]), + /// # token_address: Address([0x22; 32]), + /// # token_chain: Chain::Algorand, + /// # recipient: Address([0x19; 32]), + /// # recipient_chain: Chain::Osmosis, + /// # sender_address: Address([0xfe; 32]), + /// # }, + /// # }; + /// # + /// # let payload = [ + /// # 0x93, 0xd7, 0xc0, 0x9e, 0xe8, 0x87, 0xae, 0x16, 0xbf, 0xfa, 0x5e, 0x70, 0xea, 0x36, 0xa2, + /// # 0x82, 0x37, 0x1d, 0x46, 0x81, 0x94, 0x10, 0x34, 0xb1, 0xad, 0x0f, 0x4b, 0xc9, + /// # ]; + /// # + /// # let mut data = serde_json::to_vec(&vaa)?; + /// # data.extend_from_slice(&payload); + /// + /// let mut stream = serde_json::Deserializer::from_slice(&data).into_iter(); + /// let msg: Vaa = stream + /// .next() + /// .ok_or_else(|| anyhow!("missing token message"))??; + /// assert!(matches!(msg.payload, Message::TransferWithPayload { .. })); + /// assert_eq!(&data[stream.byte_offset()..], payload); + /// # + /// # assert_eq!(vaa, msg); + /// # Ok(()) + /// # } + /// # + /// # example().unwrap(); + /// ``` + #[serde(rename = "3")] + TransferWithPayload { + /// The amount being transferred. + amount: Amount, + + /// Address of the token. Left-zero-padded if shorter than 32 bytes. + token_address: Address, + + /// Chain ID of the token. + token_chain: Chain, + + /// Address of the recipient. Left-zero-padded if shorter than 32 bytes. + recipient: Address, + + /// Chain ID of the recipient. + recipient_chain: Chain, + + /// The identity of the sender sending the payload. + sender_address: Address, + // The actual payload is appended to the end of the message. + }, +} + +/// Represents a governance action targeted at the token bridge. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Action { + /// Registers an emitter address for a particular chain on a different chain. An emitter + /// address must be registered for a chain and must match the emitter address in the VAA before + /// the token bridge will accept VAAs from that chain. + #[serde(rename = "1")] + RegisterChain { + chain: Chain, + emitter_address: Address, + }, + + /// Upgrades the token bridge contract to a new address. + #[serde(rename = "2")] + ContractUpgrade { new_contract: Address }, +} + +/// Represents the payload for a governance VAA targeted at the token bridge. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GovernancePacket { + /// The chain on which the governance action should be carried out. + pub chain: Chain, + + /// The actual governance action to be carried out. + pub action: Action, +} + +// The wire format for GovernancePackets is wonky and doesn't lend itself well to auto-deriving +// Serialize / Deserialize so we implement it manually here. +mod governance_packet_impl { + use std::fmt; + + use serde::{ + de::{Error, MapAccess, SeqAccess, Unexpected, Visitor}, + ser::SerializeStruct, + Deserialize, Deserializer, Serialize, Serializer, + }; + + use crate::{ + token::{Action, GovernancePacket}, + Address, Chain, + }; + + // MODULE = "TokenBridge" + const MODULE: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x72, 0x69, 0x64, + 0x67, 0x65, + ]; + + struct Module; + + impl Serialize for Module { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + MODULE.serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for Module { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let arr = <[u8; 32]>::deserialize(deserializer)?; + + if arr == MODULE { + Ok(Module) + } else { + let expected = format!("{MODULE:?}"); + Err(Error::invalid_value(Unexpected::Bytes(&arr), &&*expected)) + } + } + } + + #[derive(Serialize, Deserialize)] + struct ContractUpgrade { + new_contract: Address, + } + + #[derive(Serialize, Deserialize)] + struct RegisterChain { + chain: Chain, + emitter_address: Address, + } + + impl Serialize for GovernancePacket { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_struct("GovernancePacket", 4)?; + seq.serialize_field("module", &Module)?; + + // The wire format encodes the action before the chain and then appends the actual + // action payload. + match self.action { + Action::RegisterChain { + chain, + emitter_address, + } => { + seq.serialize_field("action", &1u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field( + "payload", + &RegisterChain { + chain, + emitter_address, + }, + )?; + } + Action::ContractUpgrade { new_contract } => { + seq.serialize_field("action", &2u8)?; + seq.serialize_field("chain", &self.chain)?; + seq.serialize_field("payload", &ContractUpgrade { new_contract })?; + } + } + + seq.end() + } + } + + struct GovernancePacketVisitor; + + impl<'de> Visitor<'de> for GovernancePacketVisitor { + type Value = GovernancePacket; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("struct GovernancePacket") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + static EXPECTING: &str = "struct GovernancePacket with 4 elements"; + + let _: Module = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(0, &EXPECTING))?; + let act: u8 = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(1, &EXPECTING))?; + let chain = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(2, &EXPECTING))?; + + let action = match act { + 1 => { + let RegisterChain { + chain, + emitter_address, + } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::RegisterChain { + chain, + emitter_address, + } + } + 2 => { + let ContractUpgrade { new_contract } = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(3, &EXPECTING))?; + + Action::ContractUpgrade { new_contract } + } + v => { + return Err(Error::invalid_value( + Unexpected::Unsigned(v.into()), + &"one of 1, 2", + )) + } + }; + + Ok(GovernancePacket { chain, action }) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "snake_case")] + enum Field { + Module, + Action, + Chain, + Payload, + } + + let mut module = None; + let mut chain = None; + let mut action = None; + let mut payload = None; + + while let Some(key) = map.next_key::()? { + match key { + Field::Module => { + if module.is_some() { + return Err(Error::duplicate_field("module")); + } + + module = map.next_value::().map(Some)?; + } + Field::Action => { + if action.is_some() { + return Err(Error::duplicate_field("action")); + } + + action = map.next_value::().map(Some)?; + } + Field::Chain => { + if chain.is_some() { + return Err(Error::duplicate_field("chain")); + } + + chain = map.next_value().map(Some)?; + } + Field::Payload => { + if payload.is_some() { + return Err(Error::duplicate_field("payload")); + } + + let a = action.as_ref().copied().ok_or_else(|| { + Error::custom("`action` must be known before deserializing `payload`") + })?; + + let p = match a { + 1 => { + let RegisterChain { + chain, + emitter_address, + } = map.next_value()?; + + Action::RegisterChain { + chain, + emitter_address, + } + } + 2 => { + let ContractUpgrade { new_contract } = map.next_value()?; + + Action::ContractUpgrade { new_contract } + } + v => { + return Err(Error::custom(format_args!( + "invalid action: {v}, expected one of: 1, 2" + ))) + } + }; + + payload = Some(p); + } + } + } + + let _ = module.ok_or_else(|| Error::missing_field("module"))?; + let chain = chain.ok_or_else(|| Error::missing_field("chain"))?; + let action = payload.ok_or_else(|| Error::missing_field("payload"))?; + + Ok(GovernancePacket { chain, action }) + } + } + + impl<'de> Deserialize<'de> for GovernancePacket { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + const FIELDS: &[&str] = &["module", "action", "chain", "payload"]; + deserializer.deserialize_struct("GovernancePacket", FIELDS, GovernancePacketVisitor) + } + } +} + +#[cfg(test)] +mod test { + use crate::{vaa::Signature, Vaa, GOVERNANCE_EMITTER}; + + use super::*; + + #[test] + pub fn transfer() { + let payload = [ + 0x01, 0xfa, 0x22, 0x8b, 0x3d, 0xf3, 0x59, 0x21, 0xa1, 0xc9, 0x39, 0xad, 0x9c, 0x54, + 0xe1, 0x2f, 0x87, 0xc6, 0x27, 0x25, 0xd1, 0xaf, 0x7c, 0x7b, 0x6a, 0xea, 0xbf, 0x48, + 0x14, 0xe7, 0x26, 0x56, 0xfa, 0xe8, 0xf2, 0xd2, 0x53, 0xd1, 0x08, 0x52, 0xa2, 0x6f, + 0x55, 0xae, 0x93, 0xb5, 0x9b, 0x46, 0x91, 0x3d, 0xdf, 0x89, 0x1c, 0xb5, 0x38, 0x75, + 0x5a, 0x45, 0x47, 0xde, 0x51, 0x12, 0x01, 0x5d, 0xc3, 0x00, 0x15, 0x7a, 0x1f, 0x37, + 0xe7, 0x14, 0x28, 0xbc, 0x04, 0x75, 0xd9, 0x26, 0x8e, 0x4b, 0x53, 0x06, 0xd3, 0xa1, + 0x22, 0x40, 0x13, 0xc4, 0x36, 0xbd, 0x1c, 0x23, 0xd7, 0x2a, 0x1c, 0x16, 0x46, 0x43, + 0x50, 0x00, 0x07, 0x48, 0xc7, 0x7c, 0x80, 0xbd, 0x11, 0xee, 0x41, 0x20, 0xb3, 0x52, + 0x3b, 0xd0, 0x0a, 0x2f, 0xdd, 0x02, 0xe8, 0xef, 0xfc, 0x7a, 0xfe, 0x3d, 0xd6, 0x73, + 0x2c, 0x5d, 0xa0, 0x6b, 0x08, 0x98, 0x6c, + ]; + let msg = Message::Transfer { + amount: Amount([ + 0xfa, 0x22, 0x8b, 0x3d, 0xf3, 0x59, 0x21, 0xa1, 0xc9, 0x39, 0xad, 0x9c, 0x54, 0xe1, + 0x2f, 0x87, 0xc6, 0x27, 0x25, 0xd1, 0xaf, 0x7c, 0x7b, 0x6a, 0xea, 0xbf, 0x48, 0x14, + 0xe7, 0x26, 0x56, 0xfa, + ]), + token_address: Address([ + 0xe8, 0xf2, 0xd2, 0x53, 0xd1, 0x08, 0x52, 0xa2, 0x6f, 0x55, 0xae, 0x93, 0xb5, 0x9b, + 0x46, 0x91, 0x3d, 0xdf, 0x89, 0x1c, 0xb5, 0x38, 0x75, 0x5a, 0x45, 0x47, 0xde, 0x51, + 0x12, 0x01, 0x5d, 0xc3, + ]), + token_chain: Chain::Sui, + recipient: Address([ + 0x7a, 0x1f, 0x37, 0xe7, 0x14, 0x28, 0xbc, 0x04, 0x75, 0xd9, 0x26, 0x8e, 0x4b, 0x53, + 0x06, 0xd3, 0xa1, 0x22, 0x40, 0x13, 0xc4, 0x36, 0xbd, 0x1c, 0x23, 0xd7, 0x2a, 0x1c, + 0x16, 0x46, 0x43, 0x50, + ]), + recipient_chain: Chain::Oasis, + fee: Amount([ + 0x48, 0xc7, 0x7c, 0x80, 0xbd, 0x11, 0xee, 0x41, 0x20, 0xb3, 0x52, 0x3b, 0xd0, 0x0a, + 0x2f, 0xdd, 0x02, 0xe8, 0xef, 0xfc, 0x7a, 0xfe, 0x3d, 0xd6, 0x73, 0x2c, 0x5d, 0xa0, + 0x6b, 0x08, 0x98, 0x6c, + ]), + }; + + assert_eq!(payload.as_ref(), &serde_wormhole::to_vec(&msg).unwrap()); + assert_eq!(msg, serde_wormhole::from_slice(&payload).unwrap()); + + let encoded = serde_json::to_string(&msg).unwrap(); + assert_eq!(msg, serde_json::from_str(&encoded).unwrap()); + } + + #[test] + pub fn asset_meta() { + let payload = [ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0xef, 0xfa, 0xce, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x45, 0x45, 0x46, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x42, 0x65, 0x65, 0x66, 0x20, 0x66, 0x61, 0x63, 0x65, 0x20, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, + ]; + + let msg = Message::AssetMeta { + token_address: Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0xef, 0xfa, 0xce, + ]), + token_chain: Chain::Ethereum, + decimals: 12, + symbol: "BEEF".into(), + name: "Beef face Token".into(), + }; + + assert_eq!(payload.as_ref(), &serde_wormhole::to_vec(&msg).unwrap()); + assert_eq!(msg, serde_wormhole::from_slice(&payload).unwrap()); + + let encoded = serde_json::to_string(&msg).unwrap(); + assert_eq!(msg, serde_json::from_str(&encoded).unwrap()); + } + + #[test] + pub fn transfer_with_payload() { + let payload = [ + 0x03, 0x99, 0x30, 0x29, 0x01, 0x67, 0xf3, 0x48, 0x6a, 0xf6, 0x4c, 0xdd, 0x07, 0x64, + 0xa1, 0x6a, 0xca, 0xcc, 0xc5, 0x64, 0x2a, 0x66, 0x71, 0xaa, 0x87, 0x98, 0x64, 0x0c, + 0x25, 0x1f, 0x39, 0x73, 0x25, 0x12, 0x98, 0x15, 0xe5, 0x79, 0xdb, 0xd8, 0x25, 0xfd, + 0x72, 0x58, 0x99, 0x97, 0xd1, 0x78, 0xb6, 0x2a, 0x57, 0x47, 0xab, 0xcf, 0x51, 0x62, + 0xa5, 0xb7, 0xec, 0x19, 0x4e, 0x03, 0x51, 0x41, 0x18, 0x00, 0x08, 0xa4, 0x7e, 0xa2, + 0x49, 0xd4, 0xf0, 0x56, 0x6f, 0xbd, 0x12, 0x77, 0xfe, 0xa8, 0x6e, 0x92, 0x81, 0x3a, + 0x35, 0x90, 0x3b, 0x29, 0x73, 0x4d, 0x75, 0x56, 0x0a, 0xfe, 0x70, 0xb0, 0xb9, 0x10, + 0x33, 0x00, 0x14, 0x47, 0x3e, 0x51, 0x74, 0x32, 0xbc, 0x3f, 0xb3, 0xf9, 0x82, 0xbb, + 0xf3, 0xc9, 0x43, 0x41, 0xcf, 0x74, 0x24, 0xff, 0xa4, 0x02, 0x35, 0xbe, 0xb1, 0x7c, + 0x47, 0x16, 0xba, 0xbc, 0xaa, 0xbe, 0x99, 0x54, 0xa2, 0x97, 0xbe, 0x2a, 0xed, 0xae, + 0x91, 0x95, 0xb9, 0x6a, 0x8a, 0xba, 0xbd, 0x3a, 0xc1, 0xa4, 0xd1, 0x96, 0x0a, 0xf9, + 0xab, 0x92, 0x42, 0x0d, 0x41, 0x33, 0xe5, 0xa8, 0x37, 0x32, 0xd3, 0xc3, 0xb8, 0xf3, + 0x93, 0xd7, 0xc0, 0x9e, 0xe8, 0x87, 0xae, 0x16, 0xbf, 0xfa, 0x5e, 0x70, 0xea, 0x36, + 0xa2, 0x82, 0x37, 0x1d, 0x46, 0x81, 0x94, 0x10, 0x34, 0xb1, 0xad, 0x0f, 0x4b, 0xc9, + 0x17, 0x1e, 0x91, 0x25, 0x11, + ]; + let msg = Message::TransferWithPayload { + amount: Amount([ + 0x99, 0x30, 0x29, 0x01, 0x67, 0xf3, 0x48, 0x6a, 0xf6, 0x4c, 0xdd, 0x07, 0x64, 0xa1, + 0x6a, 0xca, 0xcc, 0xc5, 0x64, 0x2a, 0x66, 0x71, 0xaa, 0x87, 0x98, 0x64, 0x0c, 0x25, + 0x1f, 0x39, 0x73, 0x25, + ]), + token_address: Address([ + 0x12, 0x98, 0x15, 0xe5, 0x79, 0xdb, 0xd8, 0x25, 0xfd, 0x72, 0x58, 0x99, 0x97, 0xd1, + 0x78, 0xb6, 0x2a, 0x57, 0x47, 0xab, 0xcf, 0x51, 0x62, 0xa5, 0xb7, 0xec, 0x19, 0x4e, + 0x03, 0x51, 0x41, 0x18, + ]), + token_chain: Chain::Algorand, + recipient: Address([ + 0xa4, 0x7e, 0xa2, 0x49, 0xd4, 0xf0, 0x56, 0x6f, 0xbd, 0x12, 0x77, 0xfe, 0xa8, 0x6e, + 0x92, 0x81, 0x3a, 0x35, 0x90, 0x3b, 0x29, 0x73, 0x4d, 0x75, 0x56, 0x0a, 0xfe, 0x70, + 0xb0, 0xb9, 0x10, 0x33, + ]), + recipient_chain: Chain::Osmosis, + sender_address: Address([ + 0x47, 0x3e, 0x51, 0x74, 0x32, 0xbc, 0x3f, 0xb3, 0xf9, 0x82, 0xbb, 0xf3, 0xc9, 0x43, + 0x41, 0xcf, 0x74, 0x24, 0xff, 0xa4, 0x02, 0x35, 0xbe, 0xb1, 0x7c, 0x47, 0x16, 0xba, + 0xbc, 0xaa, 0xbe, 0x99, + ]), + }; + + assert_eq!(&payload[..133], &serde_wormhole::to_vec(&msg).unwrap()); + let (actual, data) = serde_wormhole::from_slice_with_payload(&payload).unwrap(); + assert_eq!(msg, actual); + assert_eq!(&payload[133..], data); + + let mut encoded = serde_json::to_vec(&msg).unwrap(); + encoded.extend_from_slice(&payload[133..]); + + let mut stream = serde_json::Deserializer::from_slice(&encoded).into_iter(); + assert_eq!(msg, stream.next().unwrap().unwrap()); + assert_eq!(payload[133..], encoded[stream.byte_offset()..]); + } + + #[test] + fn register_chain() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, + 0x1d, 0x08, 0x90, 0x5c, 0x02, 0xe2, 0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, + 0xc8, 0xdb, 0x4f, 0x77, 0xfe, 0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, + 0x92, 0xa9, 0x19, 0x4d, 0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, + 0xf0, 0xc2, 0x1c, 0xe9, 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, + 0x98, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x1b, 0xfa, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x72, 0x69, 0x64, 0x67, + 0x65, 0x01, 0x00, 0x00, 0x00, 0x01, 0x3b, 0x26, 0x40, 0x9f, 0x8a, 0xad, 0xed, 0x3f, + 0x5d, 0xdc, 0xa1, 0x84, 0x69, 0x5a, 0xa6, 0xa0, 0xfa, 0x82, 0x9b, 0x0c, 0x85, 0xca, + 0xf8, 0x48, 0x56, 0x32, 0x48, 0x96, 0xd2, 0x14, 0xca, 0x98, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![Signature { + index: 0, + signature: [ + 0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, 0x1d, 0x08, 0x90, 0x5c, 0x02, 0xe2, + 0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, 0xc8, 0xdb, 0x4f, 0x77, 0xfe, + 0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, 0x92, 0xa9, 0x19, 0x4d, + 0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, 0xf0, 0xc2, 0x1c, + 0xe9, 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, 0x98, 0x01, + ], + }], + timestamp: 1, + nonce: 1, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 20_716_538, + consistency_level: 0, + payload: GovernancePacket { + chain: Chain::Any, + action: Action::RegisterChain { + chain: Chain::Solana, + emitter_address: Address([ + 0x3b, 0x26, 0x40, 0x9f, 0x8a, 0xad, 0xed, 0x3f, 0x5d, 0xdc, 0xa1, 0x84, + 0x69, 0x5a, 0xa6, 0xa0, 0xfa, 0x82, 0x9b, 0x0c, 0x85, 0xca, 0xf8, 0x48, + 0x56, 0x32, 0x48, 0x96, 0xd2, 0x14, 0xca, 0x98, + ]), + }, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } + + #[test] + fn contract_upgrade() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x73, 0xaf, 0x3c, 0x2f, 0x55, 0x98, 0x90, + 0x41, 0xb0, 0x06, 0x8a, 0x4e, 0xae, 0xba, 0x8f, 0x88, 0x25, 0x10, 0x60, 0x21, 0x99, + 0x6d, 0xe6, 0x14, 0x39, 0x54, 0xc0, 0x6f, 0xc4, 0xcf, 0xad, 0x5c, 0x3f, 0x1a, 0xba, + 0x6c, 0xa9, 0x51, 0x18, 0x8e, 0x96, 0x2f, 0x11, 0x25, 0x1c, 0x25, 0xca, 0x98, 0x62, + 0x86, 0x85, 0xe2, 0xfc, 0x2b, 0x65, 0xec, 0x60, 0x94, 0x50, 0xcf, 0xc4, 0xe6, 0x51, + 0x12, 0x00, 0x03, 0x00, 0xc6, 0x2e, 0xb0, 0xde, 0x7a, 0xb3, 0xd0, 0x28, 0x92, 0x0f, + 0x18, 0xe1, 0x0d, 0xf7, 0xf2, 0xa0, 0x4d, 0x00, 0xeb, 0x88, 0xb8, 0x94, 0x0a, 0x27, + 0xbb, 0xce, 0x8c, 0xe6, 0x6c, 0x67, 0x52, 0x68, 0x82, 0xc9, 0x6c, 0xc5, 0x0d, 0xd3, + 0x81, 0xbd, 0xbf, 0xf3, 0x0b, 0xb6, 0x5b, 0x93, 0x47, 0xca, 0xdf, 0xf0, 0x96, 0x76, + 0x72, 0x90, 0x43, 0xaf, 0x60, 0xd6, 0x52, 0xa0, 0xb9, 0x06, 0x30, 0x00, 0x04, 0xf0, + 0x7b, 0x0f, 0xe6, 0xca, 0xb1, 0x8b, 0xe7, 0xae, 0x82, 0x7f, 0xe1, 0x1c, 0xa8, 0x99, + 0x2f, 0xf0, 0xb5, 0xa2, 0xa3, 0x2d, 0x65, 0xbe, 0x95, 0x82, 0x31, 0xbf, 0x84, 0xcf, + 0x6b, 0x14, 0xfb, 0x5e, 0xeb, 0x8a, 0x51, 0xad, 0xe1, 0xc7, 0x0a, 0xa5, 0xb8, 0xb8, + 0xcf, 0x83, 0xaf, 0xe3, 0xc2, 0x2d, 0x34, 0x71, 0x48, 0x1b, 0xda, 0x38, 0x96, 0xea, + 0x2a, 0x70, 0x46, 0x9f, 0x96, 0x92, 0xb8, 0x00, 0x05, 0x74, 0xd4, 0x96, 0x72, 0x9d, + 0xde, 0x6e, 0x19, 0x09, 0x59, 0xa1, 0x79, 0x45, 0xe8, 0x7c, 0xd9, 0x5e, 0x23, 0xc1, + 0x00, 0x47, 0xab, 0x2e, 0xef, 0x5c, 0x7d, 0x0e, 0xc6, 0xf9, 0x12, 0x08, 0xc7, 0x27, + 0x9d, 0x8a, 0x1b, 0x1f, 0x4a, 0x60, 0x06, 0x9d, 0x25, 0x93, 0x2a, 0x67, 0xd6, 0x55, + 0xf2, 0xd0, 0xb4, 0x20, 0x59, 0x00, 0x3d, 0xe7, 0x5f, 0xd3, 0xca, 0x79, 0x92, 0xfd, + 0xd1, 0xb0, 0xd4, 0x01, 0x06, 0x51, 0xf0, 0x6a, 0xb6, 0xfd, 0xfd, 0x42, 0xf3, 0x23, + 0x84, 0x0a, 0x81, 0x8b, 0x38, 0xcf, 0xd1, 0xc9, 0x9d, 0x22, 0x36, 0x6c, 0x87, 0x79, + 0x03, 0xb9, 0x8d, 0xf4, 0x0b, 0xda, 0xf2, 0xa2, 0x04, 0x2e, 0xb8, 0xcd, 0x0e, 0x59, + 0xc4, 0x63, 0x7e, 0x6a, 0x80, 0xa1, 0x99, 0xb1, 0x10, 0x8c, 0xd7, 0x65, 0x9b, 0xa8, + 0x37, 0x0f, 0x6f, 0x94, 0x76, 0xb4, 0x79, 0x83, 0x98, 0x54, 0xd8, 0xc1, 0xa6, 0x00, + 0x07, 0x78, 0x4b, 0x37, 0xc6, 0x10, 0x3c, 0x75, 0x2c, 0xd9, 0x7a, 0x58, 0x6b, 0xe8, + 0xe1, 0xce, 0xff, 0x22, 0xa6, 0xe4, 0x88, 0x43, 0x32, 0x84, 0xff, 0x31, 0xcd, 0x90, + 0x8b, 0x5c, 0x7d, 0x87, 0xe2, 0x44, 0xda, 0x75, 0x16, 0xd0, 0x7a, 0x0f, 0xa6, 0x92, + 0x68, 0x4f, 0x82, 0x6e, 0x5e, 0x6c, 0x8b, 0x45, 0x20, 0x04, 0x20, 0x6d, 0x6f, 0xe5, + 0xba, 0xe4, 0x6a, 0xfb, 0x1c, 0x21, 0x8c, 0xf5, 0x0d, 0x00, 0x09, 0x0b, 0x6c, 0xcc, + 0xe6, 0x7e, 0xd5, 0x01, 0xcb, 0x20, 0xfb, 0x06, 0xde, 0x76, 0xb9, 0x08, 0x3f, 0x13, + 0x29, 0xad, 0x78, 0xd3, 0x84, 0x51, 0x7a, 0x94, 0xab, 0x8e, 0x9a, 0xa6, 0x01, 0xbe, + 0x7a, 0x0f, 0x00, 0xbb, 0x60, 0x3b, 0x36, 0x50, 0x4a, 0x74, 0xdd, 0xc3, 0x67, 0x35, + 0x9c, 0x8e, 0xa0, 0x7f, 0x5c, 0xcd, 0x50, 0x22, 0x77, 0x2c, 0xc1, 0x57, 0xe0, 0x0f, + 0x54, 0x15, 0x63, 0x71, 0xe4, 0x01, 0x0b, 0xe1, 0xe6, 0xb7, 0x12, 0xa2, 0x35, 0x3e, + 0x49, 0x07, 0x92, 0x9c, 0x40, 0x57, 0xd5, 0xba, 0xbe, 0xf8, 0x0d, 0xc0, 0x9f, 0xeb, + 0xb3, 0xdc, 0x29, 0x34, 0xf4, 0x40, 0x48, 0x79, 0x5e, 0xef, 0x95, 0x4e, 0x99, 0x10, + 0x6c, 0xb2, 0x9b, 0x1a, 0x8d, 0x93, 0x53, 0x69, 0xda, 0xec, 0xbb, 0x8b, 0xae, 0x45, + 0x19, 0x2e, 0xfc, 0x3c, 0xed, 0xbc, 0x57, 0x31, 0x5d, 0x67, 0xec, 0x9e, 0xa8, 0x00, + 0x0a, 0x00, 0x0d, 0xdc, 0xc0, 0x4c, 0xd7, 0x55, 0x6a, 0x94, 0xe8, 0xb4, 0x18, 0xdc, + 0x3a, 0x8a, 0x00, 0xef, 0xb1, 0x28, 0xd4, 0xf7, 0x6f, 0x71, 0x43, 0x12, 0x47, 0xa9, + 0x2e, 0x7a, 0x19, 0xa8, 0x33, 0xfc, 0x02, 0x21, 0x5d, 0x1f, 0xd6, 0x9e, 0x0e, 0x59, + 0x6d, 0xa5, 0x31, 0xea, 0x58, 0x7a, 0xe2, 0xac, 0x2f, 0x36, 0xd2, 0x0f, 0x3b, 0x19, + 0x33, 0x06, 0x0a, 0xd6, 0xef, 0x3e, 0x9d, 0x7d, 0x84, 0x75, 0xba, 0x01, 0x0e, 0xc7, + 0x62, 0x16, 0x49, 0x49, 0x06, 0xae, 0xd8, 0xd8, 0x31, 0x0c, 0x31, 0xa5, 0xe4, 0xa5, + 0x45, 0xea, 0x2a, 0xf8, 0xdb, 0xe3, 0x8e, 0xeb, 0x74, 0xa3, 0xd1, 0x4f, 0x07, 0x34, + 0xdd, 0x2b, 0x1a, 0x21, 0xb9, 0x85, 0x0e, 0xa2, 0x0e, 0x6c, 0x2c, 0xa4, 0x22, 0x48, + 0x78, 0x8b, 0xfb, 0x17, 0x43, 0x9d, 0x37, 0xab, 0xda, 0x25, 0xd7, 0x05, 0xb5, 0x68, + 0xb2, 0x65, 0xb1, 0x13, 0xb9, 0x76, 0xc5, 0x00, 0x10, 0xc6, 0x1f, 0xe6, 0xeb, 0xea, + 0x61, 0xf2, 0xca, 0xe8, 0x95, 0xc3, 0x34, 0x96, 0x50, 0x70, 0x48, 0x7d, 0x39, 0xab, + 0x6b, 0xf0, 0x44, 0x28, 0x34, 0x0a, 0xf0, 0xf7, 0x69, 0x9f, 0xdd, 0xd3, 0xc0, 0x1e, + 0x0c, 0x86, 0xda, 0xea, 0x9e, 0xb4, 0x49, 0x70, 0x12, 0x48, 0xa2, 0x4f, 0xa4, 0xc0, + 0xff, 0x75, 0xfb, 0x60, 0x0b, 0x2c, 0x01, 0x34, 0xbd, 0x72, 0x17, 0x91, 0xf6, 0x67, + 0x11, 0x6e, 0x42, 0x00, 0x11, 0x19, 0xb7, 0x9c, 0xaa, 0x25, 0x0e, 0x75, 0xda, 0x14, + 0x82, 0xc7, 0xf4, 0x12, 0x68, 0x73, 0x08, 0xd1, 0x3e, 0xf7, 0x00, 0xf7, 0x26, 0xb9, + 0x94, 0x17, 0xbb, 0x75, 0xae, 0x6d, 0xd1, 0x43, 0xfc, 0x61, 0x9f, 0x8c, 0x9c, 0x29, + 0xc7, 0x3f, 0x99, 0x49, 0xaf, 0xfd, 0x16, 0x29, 0xcc, 0x28, 0x6a, 0x61, 0x91, 0x7e, + 0xd7, 0x85, 0x37, 0x54, 0xa4, 0x67, 0x02, 0x92, 0x0b, 0x76, 0xcf, 0x90, 0xeb, 0x00, + 0x12, 0xb0, 0xba, 0xb5, 0xe1, 0xa8, 0xb3, 0x21, 0xe9, 0x2f, 0x9d, 0x3d, 0xf9, 0x24, + 0x30, 0x18, 0x3e, 0x48, 0x43, 0xe5, 0x7c, 0x6f, 0x8c, 0x15, 0xc2, 0x2d, 0x62, 0xb2, + 0xf8, 0xe4, 0xb8, 0xcd, 0xc2, 0x75, 0x9f, 0x28, 0x54, 0xb8, 0xd1, 0x22, 0xac, 0xdb, + 0x4b, 0x4d, 0x89, 0x28, 0xb8, 0x7d, 0xf4, 0x19, 0x9c, 0xc6, 0x83, 0x01, 0x1d, 0x2e, + 0x9b, 0x7d, 0x41, 0xa9, 0x6d, 0x8b, 0x48, 0x0c, 0xcc, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x39, 0xc4, 0xba, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x25, 0x64, 0xd2, 0xef, + 0xd6, 0x08, 0x4d, 0xf0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x02, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x83, + ]; + + let vaa = Vaa { + version: 1, + guardian_set_index: 1, + signatures: vec![ + Signature { + index: 2, + signature: [ + 0x73, 0xaf, 0x3c, 0x2f, 0x55, 0x98, 0x90, 0x41, 0xb0, 0x06, 0x8a, 0x4e, + 0xae, 0xba, 0x8f, 0x88, 0x25, 0x10, 0x60, 0x21, 0x99, 0x6d, 0xe6, 0x14, + 0x39, 0x54, 0xc0, 0x6f, 0xc4, 0xcf, 0xad, 0x5c, 0x3f, 0x1a, 0xba, 0x6c, + 0xa9, 0x51, 0x18, 0x8e, 0x96, 0x2f, 0x11, 0x25, 0x1c, 0x25, 0xca, 0x98, + 0x62, 0x86, 0x85, 0xe2, 0xfc, 0x2b, 0x65, 0xec, 0x60, 0x94, 0x50, 0xcf, + 0xc4, 0xe6, 0x51, 0x12, 0x00, + ], + }, + Signature { + index: 3, + signature: [ + 0x00, 0xc6, 0x2e, 0xb0, 0xde, 0x7a, 0xb3, 0xd0, 0x28, 0x92, 0x0f, 0x18, + 0xe1, 0x0d, 0xf7, 0xf2, 0xa0, 0x4d, 0x00, 0xeb, 0x88, 0xb8, 0x94, 0x0a, + 0x27, 0xbb, 0xce, 0x8c, 0xe6, 0x6c, 0x67, 0x52, 0x68, 0x82, 0xc9, 0x6c, + 0xc5, 0x0d, 0xd3, 0x81, 0xbd, 0xbf, 0xf3, 0x0b, 0xb6, 0x5b, 0x93, 0x47, + 0xca, 0xdf, 0xf0, 0x96, 0x76, 0x72, 0x90, 0x43, 0xaf, 0x60, 0xd6, 0x52, + 0xa0, 0xb9, 0x06, 0x30, 0x00, + ], + }, + Signature { + index: 4, + signature: [ + 0xf0, 0x7b, 0x0f, 0xe6, 0xca, 0xb1, 0x8b, 0xe7, 0xae, 0x82, 0x7f, 0xe1, + 0x1c, 0xa8, 0x99, 0x2f, 0xf0, 0xb5, 0xa2, 0xa3, 0x2d, 0x65, 0xbe, 0x95, + 0x82, 0x31, 0xbf, 0x84, 0xcf, 0x6b, 0x14, 0xfb, 0x5e, 0xeb, 0x8a, 0x51, + 0xad, 0xe1, 0xc7, 0x0a, 0xa5, 0xb8, 0xb8, 0xcf, 0x83, 0xaf, 0xe3, 0xc2, + 0x2d, 0x34, 0x71, 0x48, 0x1b, 0xda, 0x38, 0x96, 0xea, 0x2a, 0x70, 0x46, + 0x9f, 0x96, 0x92, 0xb8, 0x00, + ], + }, + Signature { + index: 5, + signature: [ + 0x74, 0xd4, 0x96, 0x72, 0x9d, 0xde, 0x6e, 0x19, 0x09, 0x59, 0xa1, 0x79, + 0x45, 0xe8, 0x7c, 0xd9, 0x5e, 0x23, 0xc1, 0x00, 0x47, 0xab, 0x2e, 0xef, + 0x5c, 0x7d, 0x0e, 0xc6, 0xf9, 0x12, 0x08, 0xc7, 0x27, 0x9d, 0x8a, 0x1b, + 0x1f, 0x4a, 0x60, 0x06, 0x9d, 0x25, 0x93, 0x2a, 0x67, 0xd6, 0x55, 0xf2, + 0xd0, 0xb4, 0x20, 0x59, 0x00, 0x3d, 0xe7, 0x5f, 0xd3, 0xca, 0x79, 0x92, + 0xfd, 0xd1, 0xb0, 0xd4, 0x01, + ], + }, + Signature { + index: 6, + signature: [ + 0x51, 0xf0, 0x6a, 0xb6, 0xfd, 0xfd, 0x42, 0xf3, 0x23, 0x84, 0x0a, 0x81, + 0x8b, 0x38, 0xcf, 0xd1, 0xc9, 0x9d, 0x22, 0x36, 0x6c, 0x87, 0x79, 0x03, + 0xb9, 0x8d, 0xf4, 0x0b, 0xda, 0xf2, 0xa2, 0x04, 0x2e, 0xb8, 0xcd, 0x0e, + 0x59, 0xc4, 0x63, 0x7e, 0x6a, 0x80, 0xa1, 0x99, 0xb1, 0x10, 0x8c, 0xd7, + 0x65, 0x9b, 0xa8, 0x37, 0x0f, 0x6f, 0x94, 0x76, 0xb4, 0x79, 0x83, 0x98, + 0x54, 0xd8, 0xc1, 0xa6, 0x00, + ], + }, + Signature { + index: 7, + signature: [ + 0x78, 0x4b, 0x37, 0xc6, 0x10, 0x3c, 0x75, 0x2c, 0xd9, 0x7a, 0x58, 0x6b, + 0xe8, 0xe1, 0xce, 0xff, 0x22, 0xa6, 0xe4, 0x88, 0x43, 0x32, 0x84, 0xff, + 0x31, 0xcd, 0x90, 0x8b, 0x5c, 0x7d, 0x87, 0xe2, 0x44, 0xda, 0x75, 0x16, + 0xd0, 0x7a, 0x0f, 0xa6, 0x92, 0x68, 0x4f, 0x82, 0x6e, 0x5e, 0x6c, 0x8b, + 0x45, 0x20, 0x04, 0x20, 0x6d, 0x6f, 0xe5, 0xba, 0xe4, 0x6a, 0xfb, 0x1c, + 0x21, 0x8c, 0xf5, 0x0d, 0x00, + ], + }, + Signature { + index: 9, + signature: [ + 0x0b, 0x6c, 0xcc, 0xe6, 0x7e, 0xd5, 0x01, 0xcb, 0x20, 0xfb, 0x06, 0xde, + 0x76, 0xb9, 0x08, 0x3f, 0x13, 0x29, 0xad, 0x78, 0xd3, 0x84, 0x51, 0x7a, + 0x94, 0xab, 0x8e, 0x9a, 0xa6, 0x01, 0xbe, 0x7a, 0x0f, 0x00, 0xbb, 0x60, + 0x3b, 0x36, 0x50, 0x4a, 0x74, 0xdd, 0xc3, 0x67, 0x35, 0x9c, 0x8e, 0xa0, + 0x7f, 0x5c, 0xcd, 0x50, 0x22, 0x77, 0x2c, 0xc1, 0x57, 0xe0, 0x0f, 0x54, + 0x15, 0x63, 0x71, 0xe4, 0x01, + ], + }, + Signature { + index: 11, + signature: [ + 0xe1, 0xe6, 0xb7, 0x12, 0xa2, 0x35, 0x3e, 0x49, 0x07, 0x92, 0x9c, 0x40, + 0x57, 0xd5, 0xba, 0xbe, 0xf8, 0x0d, 0xc0, 0x9f, 0xeb, 0xb3, 0xdc, 0x29, + 0x34, 0xf4, 0x40, 0x48, 0x79, 0x5e, 0xef, 0x95, 0x4e, 0x99, 0x10, 0x6c, + 0xb2, 0x9b, 0x1a, 0x8d, 0x93, 0x53, 0x69, 0xda, 0xec, 0xbb, 0x8b, 0xae, + 0x45, 0x19, 0x2e, 0xfc, 0x3c, 0xed, 0xbc, 0x57, 0x31, 0x5d, 0x67, 0xec, + 0x9e, 0xa8, 0x00, 0x0a, 0x00, + ], + }, + Signature { + index: 13, + signature: [ + 0xdc, 0xc0, 0x4c, 0xd7, 0x55, 0x6a, 0x94, 0xe8, 0xb4, 0x18, 0xdc, 0x3a, + 0x8a, 0x00, 0xef, 0xb1, 0x28, 0xd4, 0xf7, 0x6f, 0x71, 0x43, 0x12, 0x47, + 0xa9, 0x2e, 0x7a, 0x19, 0xa8, 0x33, 0xfc, 0x02, 0x21, 0x5d, 0x1f, 0xd6, + 0x9e, 0x0e, 0x59, 0x6d, 0xa5, 0x31, 0xea, 0x58, 0x7a, 0xe2, 0xac, 0x2f, + 0x36, 0xd2, 0x0f, 0x3b, 0x19, 0x33, 0x06, 0x0a, 0xd6, 0xef, 0x3e, 0x9d, + 0x7d, 0x84, 0x75, 0xba, 0x01, + ], + }, + Signature { + index: 14, + signature: [ + 0xc7, 0x62, 0x16, 0x49, 0x49, 0x06, 0xae, 0xd8, 0xd8, 0x31, 0x0c, 0x31, + 0xa5, 0xe4, 0xa5, 0x45, 0xea, 0x2a, 0xf8, 0xdb, 0xe3, 0x8e, 0xeb, 0x74, + 0xa3, 0xd1, 0x4f, 0x07, 0x34, 0xdd, 0x2b, 0x1a, 0x21, 0xb9, 0x85, 0x0e, + 0xa2, 0x0e, 0x6c, 0x2c, 0xa4, 0x22, 0x48, 0x78, 0x8b, 0xfb, 0x17, 0x43, + 0x9d, 0x37, 0xab, 0xda, 0x25, 0xd7, 0x05, 0xb5, 0x68, 0xb2, 0x65, 0xb1, + 0x13, 0xb9, 0x76, 0xc5, 0x00, + ], + }, + Signature { + index: 16, + signature: [ + 0xc6, 0x1f, 0xe6, 0xeb, 0xea, 0x61, 0xf2, 0xca, 0xe8, 0x95, 0xc3, 0x34, + 0x96, 0x50, 0x70, 0x48, 0x7d, 0x39, 0xab, 0x6b, 0xf0, 0x44, 0x28, 0x34, + 0x0a, 0xf0, 0xf7, 0x69, 0x9f, 0xdd, 0xd3, 0xc0, 0x1e, 0x0c, 0x86, 0xda, + 0xea, 0x9e, 0xb4, 0x49, 0x70, 0x12, 0x48, 0xa2, 0x4f, 0xa4, 0xc0, 0xff, + 0x75, 0xfb, 0x60, 0x0b, 0x2c, 0x01, 0x34, 0xbd, 0x72, 0x17, 0x91, 0xf6, + 0x67, 0x11, 0x6e, 0x42, 0x00, + ], + }, + Signature { + index: 17, + signature: [ + 0x19, 0xb7, 0x9c, 0xaa, 0x25, 0x0e, 0x75, 0xda, 0x14, 0x82, 0xc7, 0xf4, + 0x12, 0x68, 0x73, 0x08, 0xd1, 0x3e, 0xf7, 0x00, 0xf7, 0x26, 0xb9, 0x94, + 0x17, 0xbb, 0x75, 0xae, 0x6d, 0xd1, 0x43, 0xfc, 0x61, 0x9f, 0x8c, 0x9c, + 0x29, 0xc7, 0x3f, 0x99, 0x49, 0xaf, 0xfd, 0x16, 0x29, 0xcc, 0x28, 0x6a, + 0x61, 0x91, 0x7e, 0xd7, 0x85, 0x37, 0x54, 0xa4, 0x67, 0x02, 0x92, 0x0b, + 0x76, 0xcf, 0x90, 0xeb, 0x00, + ], + }, + Signature { + index: 18, + signature: [ + 0xb0, 0xba, 0xb5, 0xe1, 0xa8, 0xb3, 0x21, 0xe9, 0x2f, 0x9d, 0x3d, 0xf9, + 0x24, 0x30, 0x18, 0x3e, 0x48, 0x43, 0xe5, 0x7c, 0x6f, 0x8c, 0x15, 0xc2, + 0x2d, 0x62, 0xb2, 0xf8, 0xe4, 0xb8, 0xcd, 0xc2, 0x75, 0x9f, 0x28, 0x54, + 0xb8, 0xd1, 0x22, 0xac, 0xdb, 0x4b, 0x4d, 0x89, 0x28, 0xb8, 0x7d, 0xf4, + 0x19, 0x9c, 0xc6, 0x83, 0x01, 0x1d, 0x2e, 0x9b, 0x7d, 0x41, 0xa9, 0x6d, + 0x8b, 0x48, 0x0c, 0xcc, 0x01, + ], + }, + ], + timestamp: 0, + nonce: 969_194_102, + emitter_chain: Chain::Solana, + emitter_address: GOVERNANCE_EMITTER, + sequence: 2_694_510_404_604_284_400, + consistency_level: 32, + payload: GovernancePacket { + chain: Chain::Terra, + action: Action::ContractUpgrade { + new_contract: Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x83, + ]), + }, + }, + }; + + assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap()); + assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap()); + + let encoded = serde_json::to_string(&vaa).unwrap(); + assert_eq!(vaa, serde_json::from_str(&encoded).unwrap()); + } +} diff --git a/sdk/rust/core/src/vaa.rs b/sdk/rust/core/src/vaa.rs index fb9c5efd4..c03568d5f 100644 --- a/sdk/rust/core/src/vaa.rs +++ b/sdk/rust/core/src/vaa.rs @@ -9,335 +9,269 @@ //! includes parsers for the core VAA type. Programs targetting wormhole can use this module to //! parse and verify incoming VAA's securely. -use nom::combinator::rest; -use nom::error::{Error, ErrorKind}; -use nom::multi::{count, fill}; -use nom::number::complete::{u16, u32, u64, u8}; -use nom::number::Endianness; -use nom::{Err, Finish, IResult}; -use std::convert::TryFrom; +use std::io::{self, Write}; -use crate::{ - require, Chain, - WormholeError::{ - self, InvalidGovernanceAction, InvalidGovernanceChain, InvalidGovernanceModule, - }, -}; +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use sha3::Digest as Sha3Digest; -// Import Module Specific VAAs. - -pub mod core; -pub mod nft; -pub mod token; +use crate::{Address, Chain, GuardianAddress}; /// Signatures are typical ECDSA signatures prefixed with a Guardian position. These have the /// following byte layout: /// ```markdown -/// 0 .. 1: Guardian No. -/// 1 .. 65: Signature (ECDSA) -/// 65 .. 66: Recovery ID (ECDSA) +/// 0 .. 64: Signature (ECDSA) +/// 64 .. 65: Recovery ID (ECDSA) /// ``` -pub type Signature = [u8; 66]; +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Signature { + pub index: u8, + #[serde(with = "crate::serde_array")] + pub signature: [u8; 65], +} -/// Wormhole specifies token addresses as 32 bytes. Addresses that are shorter, for example 20 byte -/// Ethereum addresses, are left zero padded to 32. -pub type ForeignAddress = [u8; 32]; - -/// Fields on VAA's are all usually fixed bytestrings, however they often contain UTF-8. When -/// parsed these result in `String` with the additional constraint that they are always equal or -/// less to the underlying byte field. -type ShortUTFString = String; +impl Default for Signature { + fn default() -> Self { + Self { + index: 0, + signature: [0; 65], + } + } +} /// The core VAA itself. This structure is what is received by a contract on the receiving side of -/// a wormhole message passing flow. The payload of the message must be parsed separately to the -/// VAA itself as it is completely user defined. -#[derive(Debug, Default, PartialEq, Eq)] -pub struct VAA { - // Header +/// a wormhole message passing flow. The generic parameter `P` represents the user-defined payload +/// for the VAA. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Vaa

{ + // Implementation note: it would be nice if we could use `#[serde(flatten)]` and directly embed the + // `Header` and `Body` structs. Unfortunately using flatten causes serde to serialize/deserialize + // the struct as a map, which requires the underlying data format to encode field names on the + // wire, which the wormhole data format does not do. So instead we have to duplicate the fields + // and provide a conversion function to/from `Vaa` to `(Header, Body)`. pub version: u8, pub guardian_set_index: u32, pub signatures: Vec, - - // Body pub timestamp: u32, pub nonce: u32, pub emitter_chain: Chain, - pub emitter_address: ForeignAddress, + pub emitter_address: Address, pub sequence: u64, pub consistency_level: u8, - pub payload: Vec, + pub payload: P, } -/// Contains the hash, secp256k1 payload, and serialized digest of the VAA. These are used in -/// various places in Wormhole codebases. -pub struct VAADigest { - pub digest: Vec, +/// The header for a VAA. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Header { + pub version: u8, + pub guardian_set_index: u32, + pub signatures: Vec, +} + +/// The body for a VAA. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Body

{ + /// Seconds since UNIX epoch. + pub timestamp: u32, + pub nonce: u32, + pub emitter_chain: Chain, + pub emitter_address: Address, + pub sequence: u64, + pub consistency_level: u8, + pub payload: P, +} + +/// Digest data for the Body. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Digest { + /// Guardians don't hash the VAA body directly, instead they hash the VAA and sign the hash. The + /// purpose of this is it means when submitting a VAA on-chain we only have to submit the hash + /// which reduces gas costs. pub hash: [u8; 32], + + /// The secp256k_hash is the hash of the hash of the VAA. The reason we provide this is because + /// of how secp256k works internally. It hashes its payload before signing. This means that + /// when verifying secp256k signatures, we're actually checking if a guardian has signed the + /// hash of the hash of the VAA. Functions such as `ecrecover` expect the secp256k hash rather + /// than the original payload. + pub secp256k_hash: [u8; 32], } -impl VAA { - /// Given any argument treatable as a series of bytes, attempt to deserialize into a valid VAA. - pub fn from_bytes>(input: T) -> Result { - match parse_vaa(input.as_ref()).finish() { - Ok(input) => Ok(input.1), - Err(e) => Err(WormholeError::ParseError(e.code as usize)), +/// Calculates and returns the digest for `body` to be used in VAA operations. +/// +/// A VAA is distinguished by the unique 256bit Keccak256 hash of its body. This hash is +/// utilised in all Wormhole components for identifying unique VAA's, including the bridge, +/// modules, and core guardian software. The `Digest` is documented with reasoning for +/// each field. +/// +/// NOTE: This function uses a library to do Keccak256 hashing, but on-chain this may not be +/// efficient. If efficiency is needed, consider calling `body()` instead and hashing the +/// result using on-chain primitives. +pub fn digest(body: &[u8]) -> io::Result { + // The `body` of the VAA is hashed to produce a `digest` of the VAA. + let hash: [u8; 32] = { + let mut h = sha3::Keccak256::default(); + h.write_all(body)?; + h.finalize().into() + }; + + // Hash `hash` again to get the secp256k internal hash, see `Digest` for more details. + let secp256k_hash: [u8; 32] = { + let mut h = sha3::Keccak256::default(); + h.write_all(&hash)?; + h.finalize().into() + }; + + Ok(Digest { + hash, + secp256k_hash, + }) +} + +impl

Vaa

{ + /// Check if the VAA is a Governance VAA. + pub fn is_governance(&self) -> bool { + self.emitter_address == crate::GOVERNANCE_EMITTER && self.emitter_chain == Chain::Solana + } +} + +impl

From> for (Header, Body

) { + fn from(v: Vaa

) -> Self { + ( + Header { + version: v.version, + guardian_set_index: v.guardian_set_index, + signatures: v.signatures, + }, + Body { + timestamp: v.timestamp, + nonce: v.nonce, + emitter_chain: v.emitter_chain, + emitter_address: v.emitter_address, + sequence: v.sequence, + consistency_level: v.consistency_level, + payload: v.payload, + }, + ) + } +} + +impl

From<(Header, Body

)> for Vaa

{ + fn from((hdr, body): (Header, Body

)) -> Self { + Vaa { + version: hdr.version, + guardian_set_index: hdr.guardian_set_index, + signatures: hdr.signatures, + timestamp: body.timestamp, + nonce: body.nonce, + emitter_chain: body.emitter_chain, + emitter_address: body.emitter_address, + sequence: body.sequence, + consistency_level: body.consistency_level, + payload: body.payload, } } +} - /// A VAA is distinguished by the unique hash of its deterministic components. This method - /// returns a 256 bit Keccak hash of these components. This hash is utilised in all Wormhole - /// components for identifying unique VAA's, including the bridge, modules, and core guardian - /// software. - pub fn digest(&self) -> Option { - use byteorder::{BigEndian, WriteBytesExt}; - use sha3::Digest; - use std::io::{Cursor, Write}; +impl Header { + pub fn verify(&self, _body: &[u8], _addrs: &[GuardianAddress]) -> anyhow::Result { + todo!("VAA body verification") + } +} - // Hash Deterministic Pieces - let body = { - let mut v = Cursor::new(Vec::new()); - v.write_u32::(self.timestamp).ok()?; - v.write_u32::(self.nonce).ok()?; - v.write_u16::(self.emitter_chain.clone() as u16) - .ok()?; - let _ = v.write(&self.emitter_address).ok()?; - v.write_u64::(self.sequence).ok()?; - v.write_u8(self.consistency_level).ok()?; - let _ = v.write(&self.payload).ok()?; - v.into_inner() - }; +impl Body

{ + /// Body Digest Components. + /// + /// A VAA is distinguished by the unique 256bit Keccak256 hash of its body. This hash is + /// utilised in all Wormhole components for identifying unique VAA's, including the bridge, + /// modules, and core guardian software. The `Digest` is documented with reasoning for + /// each field. + /// + /// NOTE: This function uses a library to do Keccak256 hashing, but on-chain this may not be + /// efficient. If efficiency is needed, consider calling `serde_wormhole::to_writer` instead + /// and hashing the result using on-chain primitives. + #[inline] + pub fn digest(&self) -> anyhow::Result { + self.digest_with_payload(&[]) + } - // We hash the body so that secp256k1 signatures are signing the hash instead of the body - // within our contracts. We do this so we don't have to submit the entire VAA for signature - // verification, only the hash. + /// Like `digest` but allows specifying an additional payload to include in the body hash. + pub fn digest_with_payload(&self, payload: &[u8]) -> anyhow::Result { + // The `body` of the VAA is hashed to produce a `digest` of the VAA. let hash: [u8; 32] = { let mut h = sha3::Keccak256::default(); - let _ = h.write(body.as_slice()).unwrap(); + serde_wormhole::to_writer(&mut h, self).context("failed to serialize body")?; + h.write_all(payload) + .context("failed to hash extra payload")?; h.finalize().into() }; - Some(VAADigest { digest: body, hash }) + // Hash `hash` again to get the secp256k internal hash, see `Digest` for detail. + let secp256k_hash: [u8; 32] = { + let mut h = sha3::Keccak256::default(); + h.write_all(&hash) + .context("failed to compute second hash")?; + h.finalize().into() + }; + + Ok(Digest { + hash, + secp256k_hash, + }) } } -/// Using nom, parse a fixed array of bytes without any allocation. Useful for parsing addresses, -/// signatures, identifiers, etc. -#[inline] -pub fn parse_fixed(input: &[u8]) -> IResult<&[u8], [u8; S]> { - let mut buffer = [0u8; S]; - let (i, _) = fill(u8, &mut buffer)(input)?; - Ok((i, buffer)) -} - -/// Parse a Chain ID, which is a 16 bit numeric ID. The mapping of network to ID is defined by the -/// Wormhole standard. -#[inline] -pub fn parse_chain(input: &[u8]) -> IResult<&[u8], Chain> { - let (i, chain) = u16(Endianness::Big)(input)?; - let chain = Chain::try_from(chain).map_err(|_| Err::Error(Error::new(i, ErrorKind::NoneOf)))?; - Ok((i, chain)) -} - -/// Parse a VAA from a vector of raw bytes. Nom handles situations where the data is either too -/// short or too long. -#[inline] -fn parse_vaa(input: &[u8]) -> IResult<&[u8], VAA> { - let (i, version) = u8(input)?; - let (i, guardian_set_index) = u32(Endianness::Big)(i)?; - let (i, signature_count) = u8(i)?; - let (i, signatures) = count(parse_fixed, signature_count.into())(i)?; - let (i, timestamp) = u32(Endianness::Big)(i)?; - let (i, nonce) = u32(Endianness::Big)(i)?; - let (i, emitter_chain) = parse_chain(i)?; - let (i, emitter_address) = parse_fixed(i)?; - let (i, sequence) = u64(Endianness::Big)(i)?; - let (i, consistency_level) = u8(i)?; - let (i, payload) = rest(i)?; - Ok(( - i, - VAA { - version, - guardian_set_index, - signatures, - timestamp, - nonce, - emitter_chain, - emitter_address, - sequence, - consistency_level, - payload: payload.to_vec(), - }, - )) -} - -/// All current Wormhole programs using Governance are prefixed with a Governance header with a -/// consistent format. -pub struct GovHeader { - pub module: [u8; 32], - pub action: u8, - pub chains: Chain, -} - -pub trait GovernanceAction: Sized { - const ACTION: u8; - const MODULE: &'static [u8]; - - /// Implement a nom parser for the Action. - fn parse(input: &[u8]) -> IResult<&[u8], Self>; - - /// Serialize to Wormhole wire format. - /// fn serialize(&self) -> Result, WormholeError>; - - /// Parses an Action from a governance payload securely. - fn from_bytes>( - input: T, - chain: Option, - ) -> Result<(GovHeader, Self), WormholeError> { - match parse_action(input.as_ref()).finish() { - Ok((_, (header, action))) => { - // If no Chain is given, we assume All, which implies always valid. - let chain = chain.unwrap_or(Chain::All); - - // Left 0-pad the MODULE in case it is unpadded. - let mut module = [0u8; 32]; - let modlen = Self::MODULE.len(); - module[32 - modlen..].copy_from_slice(Self::MODULE); - - // Verify Governance Data. - let valid_chain = chain == header.chains || chain == Chain::All; - let valid_action = header.action == Self::ACTION; - let valid_module = module == header.module; - require!(valid_action, InvalidGovernanceAction); - require!(valid_chain, InvalidGovernanceChain); - require!(valid_module, InvalidGovernanceModule); - - Ok((header, action)) - } - Err(e) => Err(WormholeError::ParseError(e.code as usize)), - } - } -} - -#[inline] -pub fn parse_action(input: &[u8]) -> IResult<&[u8], (GovHeader, A)> { - let (i, header) = parse_governance_header(input)?; - let (i, action) = A::parse(i)?; - Ok((i, (header, action))) -} - -#[inline] -pub fn parse_governance_header(input: &[u8]) -> IResult<&[u8], GovHeader> { - let (i, module) = parse_fixed(input)?; - let (i, action) = u8(i)?; - let (i, chains) = u16(Endianness::Big)(i)?; - Ok(( - i, - GovHeader { - module, - action, - chains: Chain::try_from(chains).unwrap(), - }, - )) -} - #[cfg(test)] -mod testing { - use super::{parse_governance_header, Chain, VAA}; +mod test { + use super::*; #[test] - fn test_valid_gov_header() { - let module = - hex::decode("000000000000000000000000000000000000000000546f6b656e427269646765") - .unwrap(); + fn arbitrary_payload() { + let buf = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xe2, 0x9d, 0x3a, 0xd1, 0x80, 0xb1, 0x53, + 0xd6, 0x8c, 0x3f, 0x44, 0x5d, 0x75, 0xea, 0xa6, 0x2f, 0xcc, 0x99, 0x69, 0x09, 0x45, + 0xba, 0xaf, 0x4a, 0xd0, 0x46, 0x3e, 0x9c, 0xe4, 0x4f, 0x27, 0xf7, 0x5d, 0xa3, 0xd4, + 0x9f, 0x79, 0x72, 0x29, 0x20, 0xaa, 0xc8, 0x1b, 0xa2, 0xbe, 0x80, 0xf6, 0x88, 0x89, + 0x5f, 0x17, 0x49, 0x42, 0xfe, 0xdc, 0x40, 0x3b, 0xc4, 0xe5, 0xce, 0x35, 0x55, 0xb7, + 0x7b, 0x00, 0x62, 0xb9, 0xf7, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x9a, 0x2a, 0x01, + 0xb7, 0x05, 0x19, 0xf6, 0x7a, 0xdb, 0x30, 0x9a, 0x99, 0x4e, 0xc8, 0xc6, 0x9a, 0x96, + 0x7e, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x46, 0x72, 0x6f, + 0x6d, 0x3a, 0x20, 0x65, 0x76, 0x6d, 0x30, 0x5c, 0x6e, 0x4d, 0x73, 0x67, 0x3a, 0x20, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, + ]; - // Decode VAA. - let vaa = hex::decode("01000000000100b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e980100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000013c1bfa00000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); - let vaa = VAA::from_bytes(vaa).unwrap(); - - // Decode Payload - let (_, header) = parse_governance_header(&vaa.payload).unwrap(); - - // Confirm Parsed matches Required. - assert_eq!(&header.module, &module[..]); - assert_eq!(header.action, 1); - assert_eq!(header.chains, Chain::All); - } - - // Legacy VAA Signature Struct. - #[derive(Default, Clone)] - pub struct VAASignature { - pub signature: Vec, - pub guardian_index: u8, - } - - // Original VAA Parsing Code. Used to compare current code to old for parity. - pub fn legacy_deserialize(data: &[u8]) -> std::result::Result { - use byteorder::{BigEndian, ReadBytesExt}; - use std::convert::TryFrom; - use std::io::Read; - - let mut rdr = std::io::Cursor::new(data); - let mut v = VAA { - ..Default::default() + let vaa = Vaa { + version: 1, + guardian_set_index: 0, + signatures: vec![Signature { + index: 0, + signature: [ + 0xe2, 0x9d, 0x3a, 0xd1, 0x80, 0xb1, 0x53, 0xd6, 0x8c, 0x3f, 0x44, 0x5d, 0x75, + 0xea, 0xa6, 0x2f, 0xcc, 0x99, 0x69, 0x09, 0x45, 0xba, 0xaf, 0x4a, 0xd0, 0x46, + 0x3e, 0x9c, 0xe4, 0x4f, 0x27, 0xf7, 0x5d, 0xa3, 0xd4, 0x9f, 0x79, 0x72, 0x29, + 0x20, 0xaa, 0xc8, 0x1b, 0xa2, 0xbe, 0x80, 0xf6, 0x88, 0x89, 0x5f, 0x17, 0x49, + 0x42, 0xfe, 0xdc, 0x40, 0x3b, 0xc4, 0xe5, 0xce, 0x35, 0x55, 0xb7, 0x7b, 0x00, + ], + }], + timestamp: 1_656_354_705, + nonce: 0, + emitter_chain: Chain::Ethereum, + emitter_address: Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x9a, + 0x2a, 0x01, 0xb7, 0x05, 0x19, 0xf6, 0x7a, 0xdb, 0x30, 0x9a, 0x99, 0x4e, 0xc8, 0xc6, + 0x9a, 0x96, 0x7e, 0x8b, + ]), + sequence: 0, + consistency_level: 1, + payload: (), }; - v.version = rdr.read_u8()?; - v.guardian_set_index = rdr.read_u32::()?; - let len_sig = rdr.read_u8()?; - let mut sigs: Vec<_> = Vec::with_capacity(len_sig as usize); - for _i in 0..len_sig { - let mut sig = [0u8; 66]; - sig[0] = rdr.read_u8()?; - rdr.read_exact(&mut sig[1..66])?; - sigs.push(sig); - } - v.signatures = sigs; - v.timestamp = rdr.read_u32::()?; - v.nonce = rdr.read_u32::()?; - v.emitter_chain = Chain::try_from(rdr.read_u16::()?).unwrap(); - let mut emitter_address = [0u8; 32]; - rdr.read_exact(&mut emitter_address)?; - v.emitter_address = emitter_address; - v.sequence = rdr.read_u64::()?; - v.consistency_level = rdr.read_u8()?; - let _ = rdr.read_to_end(&mut v.payload)?; - Ok(v) + + let (actual, payload) = serde_wormhole::from_slice_with_payload(&buf).unwrap(); + assert_eq!(vaa, actual); + assert_eq!(bstr::B("From: evm0\\nMsg: Hello World!"), payload); + + assert_eq!(&buf[..123], &serde_wormhole::to_vec(&vaa).unwrap()); } - - #[test] - fn test_parse_vaa_parity() { - // Decode VAA with old and new parsers, and compare result. - let vaa = hex::decode("01000000000100b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e980100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000013c1bfa00000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); - let new = VAA::from_bytes(&vaa).unwrap(); - let old = legacy_deserialize(&vaa).unwrap(); - assert_eq!(new, old); - } - - #[test] - fn test_valid_parse_vaa() { - let signers = hex::decode("00b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e9801").unwrap(); - let payload = hex::decode("000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); - let emitter = - hex::decode("0000000000000000000000000000000000000000000000000000000000000004") - .unwrap(); - - // Decode VAA. - let vaa = hex::decode("01000000000100b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e980100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000013c1bfa00000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); - let vaa = VAA::from_bytes(vaa).unwrap(); - - // Verify Decoded VAA. - assert_eq!(vaa.version, 1); - assert_eq!(vaa.guardian_set_index, 0); - assert_eq!(vaa.signatures.len(), 1); - assert_eq!(vaa.signatures[0][..], signers); - assert_eq!(vaa.timestamp, 1); - assert_eq!(vaa.nonce, 1); - assert_eq!(vaa.emitter_chain, Chain::Solana); - assert_eq!(vaa.emitter_address, emitter[..]); - assert_eq!(vaa.sequence, 20_716_538); - assert_eq!(vaa.consistency_level, 0); - assert_eq!(vaa.payload, payload); - } - - #[test] - fn test_invalid_vaa() {} } diff --git a/sdk/rust/core/src/vaa/core.rs b/sdk/rust/core/src/vaa/core.rs deleted file mode 100644 index 2664544f9..000000000 --- a/sdk/rust/core/src/vaa/core.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! This module exposes parsers for core bridge VAAs. The main job of the bridge is to forward -//! VAA's to other chains, however governance actions are themselves VAAs and as such the bridge -//! requires parsing Bridge specific VAAs. -//! -//! The core bridge does not define any general VAA's, thus all the payloads in this file are -//! expected to require governance to be executed. - -use nom::multi::{count, fill}; -use nom::number::complete::{u32, u8}; -use nom::number::Endianness; -use nom::IResult; -use primitive_types::U256; - -use crate::vaa::{parse_fixed, GovernanceAction}; - -pub struct GovernanceContractUpgrade { - pub new_contract: [u8; 32], -} - -impl GovernanceAction for GovernanceContractUpgrade { - const MODULE: &'static [u8] = b"Core"; - const ACTION: u8 = 1; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let (i, new_contract) = parse_fixed(input)?; - Ok((i, Self { new_contract })) - } -} - -pub struct GovernanceGuardianSetChange { - pub new_guardian_set_index: u32, - pub new_guardian_set: Vec<[u8; 20]>, -} - -impl GovernanceAction for GovernanceGuardianSetChange { - const MODULE: &'static [u8] = b"Core"; - const ACTION: u8 = 2; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let (i, new_guardian_set_index) = u32(Endianness::Big)(input)?; - let (i, guardian_count) = u8(i)?; - let (i, new_guardian_set) = count(parse_fixed, guardian_count.into())(i)?; - Ok(( - i, - Self { - new_guardian_set_index, - new_guardian_set, - }, - )) - } -} - -pub struct GovernanceSetMessageFee { - pub fee: U256, -} - -impl GovernanceAction for GovernanceSetMessageFee { - const MODULE: &'static [u8] = b"Core"; - const ACTION: u8 = 3; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let mut fee = [0u8; 32]; - let (i, _) = fill(u8, &mut fee)(input)?; - Ok(( - i, - Self { - fee: U256::from_big_endian(&fee), - }, - )) - } -} - -pub struct GovernanceTransferFees { - pub amount: U256, - pub to: [u8; 32], -} - -impl GovernanceAction for GovernanceTransferFees { - const MODULE: &'static [u8] = b"Core"; - const ACTION: u8 = 4; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let mut amount = [0u8; 32]; - let (i, _) = fill(u8, &mut amount)(input)?; - let (i, to) = parse_fixed(i)?; - Ok(( - i, - Self { - amount: U256::from_big_endian(&amount), - to, - }, - )) - } -} diff --git a/sdk/rust/core/src/vaa/nft.rs b/sdk/rust/core/src/vaa/nft.rs deleted file mode 100644 index aa20e9145..000000000 --- a/sdk/rust/core/src/vaa/nft.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! This module exposes parsers for NFT bridge VAAs. Token bridging relies on VAA's that indicate -//! custody/lockup/burn events in order to maintain token parity between multiple chains. These -//! parsers can be used to read these VAAs. It also defines the Governance actions that this module -//! supports, namely contract upgrades and chain registrations. - -use nom::bytes::complete::take; -use nom::combinator::verify; -use nom::number::complete::u8; -use nom::{Finish, IResult}; -use primitive_types::U256; -use std::str::from_utf8; - -use crate::vaa::ShortUTFString; -use crate::vaa::{parse_chain, parse_fixed, GovernanceAction}; -use crate::{parse_fixed_utf8, Chain, WormholeError}; - -/// Transfer is a message containing specifics detailing a token lock up on a sending chain. Chains -/// that are attempting to initiate a transfer must lock up tokens in some manner, such as in a -/// custody account or via burning, before emitting this message. -#[derive(PartialEq, Eq, Debug, Clone)] -pub struct Transfer { - /// Address of the token. Left-zero-padded if shorter than 32 bytes - pub nft_address: [u8; 32], - - /// Chain ID of the token - pub nft_chain: Chain, - - /// Symbol of the token - pub symbol: ShortUTFString, - - /// Name of the token - pub name: ShortUTFString, - - /// TokenID of the token (big-endian uint256) - pub token_id: U256, - - /// URI of the token metadata - pub uri: ShortUTFString, - - /// Address of the recipient. Left-zero-padded if shorter than 32 bytes - pub to: [u8; 32], - - /// Chain ID of the recipient - pub to_chain: Chain, -} - -impl Transfer { - pub fn from_bytes>(input: T) -> Result { - match parse_payload_transfer(input.as_ref()).finish() { - Ok(input) => Ok(input.1), - Err(e) => Err(WormholeError::ParseError(e.code as usize)), - } - } -} - -fn parse_payload_transfer(input: &[u8]) -> IResult<&[u8], Transfer> { - // Parse Payload - let (i, _) = verify(u8, |&s| s == 0x1)(input)?; - let (i, nft_address) = parse_fixed(i)?; - let (i, nft_chain) = parse_chain(i)?; - let (i, symbol): (_, [u8; 32]) = parse_fixed(i)?; - let (i, name): (_, [u8; 32]) = parse_fixed(i)?; - let (i, token_id): (_, [u8; 32]) = parse_fixed(i)?; - let (i, uri_len) = u8(i)?; - let (i, uri) = take(uri_len)(i)?; - let (i, to) = parse_fixed(i)?; - let (i, to_chain) = parse_chain(i)?; - - // Name/Symbol and URI should be UTF-8 strings, attempt to parse the first two by removing - // invalid bytes -- for the latter, assume UTF-8 and fail if unparseable. - let name = parse_fixed_utf8::<_, 32>(name).unwrap(); - let symbol = parse_fixed_utf8::<_, 32>(symbol).unwrap(); - let uri = from_utf8(uri).unwrap().to_string(); - - Ok(( - i, - Transfer { - nft_address, - nft_chain, - symbol, - name, - token_id: U256::from_big_endian(&token_id), - uri, - to, - to_chain, - }, - )) -} - -#[derive(PartialEq, Eq, Debug)] -pub struct GovernanceRegisterChain { - pub emitter: Chain, - pub endpoint_address: [u8; 32], -} - -impl GovernanceAction for GovernanceRegisterChain { - const MODULE: &'static [u8] = b"NFTBridge"; - const ACTION: u8 = 1; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let (i, emitter) = parse_chain(input)?; - let (i, endpoint_address) = parse_fixed(i)?; - Ok(( - i, - Self { - emitter, - endpoint_address, - }, - )) - } -} - -#[derive(PartialEq, Eq, Debug)] -pub struct GovernanceContractUpgrade { - pub new_contract: [u8; 32], -} - -impl GovernanceAction for GovernanceContractUpgrade { - const MODULE: &'static [u8] = b"NFTBridge"; - const ACTION: u8 = 2; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let (i, new_contract) = parse_fixed(input)?; - Ok((i, Self { new_contract })) - } -} diff --git a/sdk/rust/core/src/vaa/token.rs b/sdk/rust/core/src/vaa/token.rs deleted file mode 100644 index 0d9df4691..000000000 --- a/sdk/rust/core/src/vaa/token.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! This module exposes parsers for token bridge VAAs. Token bridging relies on VAA's that indicate -//! custody/lockup/burn events in order to maintain token parity between multiple chains. These -//! parsers can be used to read these VAAs. It also defines the Governance actions that this module -//! supports, namely contract upgrades and chain registrations. - -use nom::combinator::verify; -use nom::multi::fill; -use nom::number::complete::u8; -use nom::{Finish, IResult}; -use primitive_types::U256; - -use crate::vaa::{parse_chain, parse_fixed, GovernanceAction, ShortUTFString}; -use crate::{parse_fixed_utf8, Chain, WormholeError}; - -/// Transfer is a message containing specifics detailing a token lock up on a sending chain. Chains -/// that are attempting to initiate a transfer must lock up tokens in some manner, such as in a -/// custody account or via burning, before emitting this message. -#[derive(PartialEq, Eq, Debug, Clone)] -pub struct Transfer { - /// Amount being transferred (big-endian uint256) - pub amount: U256, - - /// Address of the token. Left-zero-padded if shorter than 32 bytes - pub token_address: [u8; 32], - - /// Chain ID of the token - pub token_chain: Chain, - - /// Address of the recipient. Left-zero-padded if shorter than 32 bytes - pub to: [u8; 32], - - /// Chain ID of the recipient - pub to_chain: Chain, - - /// Amount of tokens (big-endian uint256) that the user is willing to pay as relayer fee. Must be <= Amount. - pub fee: U256, -} - -impl Transfer { - pub fn from_bytes>(input: T) -> Result { - match parse_payload_transfer(input.as_ref()).finish() { - Ok(input) => Ok(input.1), - Err(e) => Err(WormholeError::ParseError(e.code as usize)), - } - } -} - -fn parse_payload_transfer(input: &[u8]) -> IResult<&[u8], Transfer> { - // Parser Buffers. - let mut amount = [0u8; 32]; - let mut fee = [0u8; 32]; - - // Parse Payload. - let (i, _) = verify(u8, |&s| s == 0x1)(input)?; - let (i, _) = fill(u8, &mut amount)(i)?; - let (i, token_address) = parse_fixed(i)?; - let (i, token_chain) = parse_chain(i)?; - let (i, to) = parse_fixed(i)?; - let (i, to_chain) = parse_chain(i)?; - let (i, _) = fill(u8, &mut fee)(i)?; - - Ok(( - i, - Transfer { - amount: U256::from_big_endian(&amount), - token_address, - token_chain, - to, - to_chain, - fee: U256::from_big_endian(&fee), - }, - )) -} - -#[derive(PartialEq, Eq, Debug)] -pub struct AssetMeta { - /// Address of the original token on the source chain. - pub token_address: [u8; 32], - - /// Source Chain ID. - pub token_chain: Chain, - - /// Number of decimals the source token has on its origin chain. - pub decimals: u8, - - /// Ticker Symbol for the token on its origin chain. - pub symbol: ShortUTFString, - - /// Full Token name for the token on its origin chain. - pub name: ShortUTFString, -} - -impl AssetMeta { - pub fn from_bytes>(input: T) -> Result { - match parse_payload_asset_meta(input.as_ref()).finish() { - Ok(input) => Ok(input.1), - Err(e) => Err(WormholeError::ParseError(e.code as usize)), - } - } -} - -fn parse_payload_asset_meta(input: &[u8]) -> IResult<&[u8], AssetMeta> { - // Parse Payload. - let (i, _) = verify(u8, |&s| s == 0x2)(input)?; - let (i, token_address) = parse_fixed(i)?; - let (i, token_chain) = parse_chain(i)?; - let (i, decimals) = u8(i)?; - let (i, symbol): (_, [u8; 32]) = parse_fixed(i)?; - let (i, name): (_, [u8; 32]) = parse_fixed(i)?; - - // Name/Symbol should be UTF-8 strings, attempt to parse them by removing invalid bytes. - let symbol = parse_fixed_utf8::<_, 32>(symbol).unwrap(); - let name = parse_fixed_utf8::<_, 32>(name).unwrap(); - - Ok(( - i, - AssetMeta { - token_address, - token_chain, - decimals, - symbol, - name, - }, - )) -} - -#[derive(PartialEq, Eq, Debug)] -pub struct GovernanceRegisterChain { - pub emitter: Chain, - pub endpoint_address: [u8; 32], -} - -impl GovernanceAction for GovernanceRegisterChain { - const MODULE: &'static [u8] = b"TokenBridge"; - const ACTION: u8 = 1; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let (i, emitter) = parse_chain(input)?; - let (i, endpoint_address) = parse_fixed(i)?; - Ok(( - i, - Self { - emitter, - endpoint_address, - }, - )) - } -} - -#[derive(PartialEq, Eq, Debug)] -pub struct GovernanceContractUpgrade { - pub new_contract: [u8; 32], -} - -impl GovernanceAction for GovernanceContractUpgrade { - const MODULE: &'static [u8] = b"TokenBridge"; - const ACTION: u8 = 2; - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - let (i, new_contract) = parse_fixed(input)?; - Ok((i, Self { new_contract })) - } -} diff --git a/sdk/rust/sdk/Cargo.toml b/sdk/rust/sdk/Cargo.toml deleted file mode 100644 index 6799e6de9..000000000 --- a/sdk/rust/sdk/Cargo.toml +++ /dev/null @@ -1,65 +0,0 @@ -[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 testnet contract addresses. -testnet = [] - -# 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.9.3" } -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.10.31", 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 -features = [ "library" ] - -[dev-dependencies] -byteorder = "*" -hex = "*" diff --git a/sdk/rust/sdk/fuzz/.cargo/config b/sdk/rust/sdk/fuzz/.cargo/config deleted file mode 100644 index e187ed4a3..000000000 --- a/sdk/rust/sdk/fuzz/.cargo/config +++ /dev/null @@ -1,7 +0,0 @@ -[build] -rustflags = [ - "-Cpasses=sancov-module", - "-Cllvm-args=-sanitizer-coverage-level=3", - "-Cllvm-args=-sanitizer-coverage-inline-8bit-counters", - "-Zsanitizer=address", -] diff --git a/sdk/rust/sdk/fuzz/Cargo.lock b/sdk/rust/sdk/fuzz/Cargo.lock deleted file mode 100644 index c4d536445..000000000 --- a/sdk/rust/sdk/fuzz/Cargo.lock +++ /dev/null @@ -1,1137 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "arbitrary" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitvec" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake3" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "cc", - "cfg-if 0.1.10", - "constant_time_eq", - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array 0.14.4", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "borsh" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dda7dc709193c0d86a1a51050a926dc3df1cf262ec46a23a25dba421ea1924" -dependencies = [ - "borsh-derive", - "hashbrown", -] - -[[package]] -name = "borsh-derive" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684155372435f578c0fa1acd13ebbb182cc19d6b38b64ae7901da4393217d264" -dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", - "proc-macro-crate 0.1.5", - "proc-macro2", - "syn", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2102f62f8b6d3edeab871830782285b64cc1830168094db05c8e458f209bc5c3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196c978c4c9b0b142d446ef3240690bf5a8a33497074a113ff9a337ccb750483" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bs58" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" - -[[package]] -name = "bv" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" -dependencies = [ - "feature-probe", - "serde", -] - -[[package]] -name = "byte-slice-cast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698" - -[[package]] -name = "bytemuck" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "cpufeatures" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array 0.14.4", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.4", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "feature-probe" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" - -[[package]] -name = "fixed-hash" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand 0.8.4", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "serde", - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.10.2+wasi-snapshot-preview1", -] - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array 0.14.4", - "hmac", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "impl-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - -[[package]] -name = "libsecp256k1" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd1137239ab33b41aa9637a88a28249e5e70c40a42ccc92db7f12cc356c1fcd7" -dependencies = [ - "arrayref", - "base64 0.12.3", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.7.3", - "serde", - "sha2", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "memmap2" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" -dependencies = [ - "libc", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" -dependencies = [ - "memchr", - "minimal-lexical", - "version_check", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "parity-scale-codec" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" -dependencies = [ - "arrayvec 0.7.2", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" -dependencies = [ - "proc-macro-crate 1.1.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - -[[package]] -name = "primitive-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" -dependencies = [ - "fixed-hash", - "impl-codec", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-crate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" -dependencies = [ - "block-buffer", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer", - "digest 0.9.0", - "keccak", - "opaque-debug", -] - -[[package]] -name = "solana-frozen-abi" -version = "1.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03105023ee2e50eef15d4d43fa6d3acbbc5265f570ebb021d0af9c62e6f5189" -dependencies = [ - "bs58", - "bv", - "generic-array 0.14.4", - "log", - "memmap2", - "rustc_version", - "serde", - "serde_derive", - "sha2", - "solana-frozen-abi-macro", - "solana-logger", - "thiserror", -] - -[[package]] -name = "solana-frozen-abi-macro" -version = "1.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f372aa625bbde216f9f769a348585a41ecd8dcea8b9fe7b3f89565028fb670" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "syn", -] - -[[package]] -name = "solana-logger" -version = "1.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82ae204d12a5b885edb857684d9f6770be1ce4031d6cc90bbe272a1631ec671" -dependencies = [ - "env_logger", - "lazy_static", - "log", -] - -[[package]] -name = "solana-program" -version = "1.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c119435aa5b6d5f1dd9af1057f5311ce9e5a1ed6a2d19e2830c3d6cec25c74" -dependencies = [ - "base64 0.13.0", - "bincode", - "blake3", - "borsh", - "borsh-derive", - "bs58", - "bv", - "bytemuck", - "curve25519-dalek", - "hex", - "itertools", - "lazy_static", - "libsecp256k1", - "log", - "num-derive", - "num-traits", - "rand 0.7.3", - "rustc_version", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "sha2", - "sha3", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-sdk-macro", - "thiserror", -] - -[[package]] -name = "solana-sdk-macro" -version = "1.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "868cdefdbb43424b4915b8aaa767a112d62537d8d2c6d1d4d01a647e4842ed7b" -dependencies = [ - "bs58", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "typenum" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" - -[[package]] -name = "uint" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "wormhole-sdk" -version = "0.1.0" -dependencies = [ - "nom", - "primitive-types", - "solana-program", -] - -[[package]] -name = "wormhole-sdk-fuzz" -version = "0.0.0" -dependencies = [ - "libfuzzer-sys", - "wormhole-sdk", -] - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "zeroize" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" diff --git a/sdk/rust/sdk/fuzz/Cargo.toml b/sdk/rust/sdk/fuzz/Cargo.toml deleted file mode 100644 index d070a6f6d..000000000 --- a/sdk/rust/sdk/fuzz/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[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" diff --git a/sdk/rust/sdk/fuzz/fuzzers/governance.rs b/sdk/rust/sdk/fuzz/fuzzers/governance.rs deleted file mode 100644 index ee36732a1..000000000 --- a/sdk/rust/sdk/fuzz/fuzzers/governance.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] -use libfuzzer_sys::fuzz_target; -use wormhole_sdk::VAA; - -fuzz_target!(|data: &[u8]| { - VAA::from_bytes(data); -}); diff --git a/sdk/rust/sdk/fuzz/fuzzers/vaa.rs b/sdk/rust/sdk/fuzz/fuzzers/vaa.rs deleted file mode 100644 index 6855afd41..000000000 --- a/sdk/rust/sdk/fuzz/fuzzers/vaa.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] -use libfuzzer_sys::fuzz_target; -use wormhole_sdk::vaa::VAA; - -fuzz_target!(|data: &[u8]| { - VAA::from_bytes(data); -}); diff --git a/sdk/rust/sdk/src/chains.rs b/sdk/rust/sdk/src/chains.rs deleted file mode 100644 index 88039ae49..000000000 --- a/sdk/rust/sdk/src/chains.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! 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::*; diff --git a/sdk/rust/sdk/src/chains/solana.rs b/sdk/rust/sdk/src/chains/solana.rs deleted file mode 100644 index ce05bbebf..000000000 --- a/sdk/rust/sdk/src/chains/solana.rs +++ /dev/null @@ -1,135 +0,0 @@ -use borsh::BorshDeserialize; -use solana_program::account_info::AccountInfo; -use solana_program::entrypoint::ProgramResult; -use solana_program::program::invoke_signed; -use solana_program::pubkey::Pubkey; -use std::str::FromStr; - -// Export Bridge API -pub use bridge::instructions; -pub use bridge::solitaire as bridge_entrypoint; -pub use bridge::types::ConsistencyLevel; -pub use bridge::BridgeConfig; -pub use bridge::BridgeData; -pub use bridge::MessageData; -pub use bridge::PostVAAData; -pub use bridge::PostedVAAData; -pub use bridge::VerifySignaturesData; - -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 { - 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 { - 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, - pda_seeds: Option<&[&[&[u8]]]>, - accounts: &[AccountInfo], - nonce: u32, -) -> ProgramResult { - // Derive any necessary Pubkeys, derivation makes sure that we match the accounts that 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(); - - let mut seeds = vec![&*emitter_seeds]; - if let Some(v) = pda_seeds { - seeds.extend(v); - } - - // 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, - &seeds, - )?; - - Ok(()) -} diff --git a/sdk/rust/sdk/src/chains/terra.rs b/sdk/rust/sdk/src/chains/terra.rs deleted file mode 100644 index 4a95e8785..000000000 --- a/sdk/rust/sdk/src/chains/terra.rs +++ /dev/null @@ -1,48 +0,0 @@ -use cosmwasm_std::{ - to_binary, Addr, Binary, CosmosMsg, DepsMut, Env, QueryRequest, StdResult, WasmMsg, WasmQuery, -}; -use serde::Serialize; - -use wormhole::msg::{ExecuteMsg, QueryMsg}; -use wormhole::state::ParsedVAA; - -/// Export Core Mainnet Contract Address -#[cfg(feature = "mainnet")] -pub fn id() -> Addr { - Addr::unchecked("terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5") -} - -/// Export Core Devnet Contract Address -#[cfg(feature = "testnet")] -pub fn id() -> Addr { - Addr::unchecked("terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v") -} - -/// Export Core Devnet Contract Address -#[cfg(feature = "devnet")] -pub fn id() -> Addr { - Addr::unchecked("terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5") -} - -pub fn post_message(nonce: u32, message: impl AsRef<[u8]>) -> StdResult { - Ok(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: id().to_string(), - funds: vec![], - msg: to_binary(&ExecuteMsg::PostMessage { - message: Binary::from(message.as_ref()), - nonce, - })?, - })) -} - -/// Parse a VAA using the Wormhole contract Query interface. -pub fn parse_vaa(deps: DepsMut, env: Env, data: &Binary) -> StdResult { - let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: id().to_string(), - msg: to_binary(&QueryMsg::VerifyVAA { - vaa: data.clone(), - block_time: env.block.time.seconds(), - })?, - }))?; - Ok(vaa) -} diff --git a/sdk/rust/sdk/src/lib.rs b/sdk/rust/sdk/src/lib.rs deleted file mode 100644 index b7a2cdecd..000000000 --- a/sdk/rust/sdk/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! 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 chains::*; -pub use wormhole_core::*;