sdk/rust: Add serde-based struct defintions
Refactor the core crate to add serde-based struct definitions for the various messages used by the different wormhole smart contracts. This will also make it easier to use alternate data formats (like json) for client-side tooling. Co-authored-by: Reisen <reisen@morphism.org>
This commit is contained in:
parent
92cb3d28a3
commit
d157c7bf67
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
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<BString, D::Error>
|
||||
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::<MyString>(&b)
|
||||
.expect_err("successfully deserialized string from a buffer that's too small");
|
||||
}
|
||||
}
|
|
@ -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<u16> for Chain {
|
||||
type Error = ();
|
||||
fn try_from(other: u16) -> Result<Chain, Self::Error> {
|
||||
impl From<u16> 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<Chain> 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<Self, Self::Err> {
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u16((*self).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Chain {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
<u16 as Deserialize>::deserialize(deserializer).map(Self::from)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<GovernancePacket>;
|
||||
|
||||
/// 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
MODULE.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Module {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
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<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
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::<Field>()? {
|
||||
match key {
|
||||
Field::Module => {
|
||||
if module.is_some() {
|
||||
return Err(Error::duplicate_field("module"));
|
||||
}
|
||||
|
||||
module = map.next_value::<Module>().map(Some)?;
|
||||
}
|
||||
Field::Action => {
|
||||
if action.is_some() {
|
||||
return Err(Error::duplicate_field("action"));
|
||||
}
|
||||
|
||||
action = map.next_value::<u8>().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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
|
@ -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<T: AsRef<[u8]>, const N: usize>(s: T) -> Option<String> {
|
||||
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<char> = 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<GuardianAddress>,
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
MODULE.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Module {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
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<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
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::<Field>()? {
|
||||
match key {
|
||||
Field::Module => {
|
||||
if module.is_some() {
|
||||
return Err(Error::duplicate_field("module"));
|
||||
}
|
||||
|
||||
module = map.next_value::<Module>().map(Some)?;
|
||||
}
|
||||
Field::Action => {
|
||||
if action.is_some() {
|
||||
return Err(Error::duplicate_field("action"));
|
||||
}
|
||||
|
||||
action = map.next_value::<u8>().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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
use std::{fmt, mem::MaybeUninit};
|
||||
|
||||
use serde::{
|
||||
de::{Error, SeqAccess, Visitor},
|
||||
ser::SerializeTuple,
|
||||
Deserializer, Serializer,
|
||||
};
|
||||
|
||||
pub fn serialize<const N: usize, S>(value: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_tuple(N)?;
|
||||
for v in value {
|
||||
seq.serialize_element(v)?;
|
||||
}
|
||||
|
||||
seq.end()
|
||||
}
|
||||
|
||||
struct ArrayVisitor<const N: usize>;
|
||||
impl<'de, const N: usize> Visitor<'de> for ArrayVisitor<N> {
|
||||
type Value = [u8; N];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "an array of length {}", N)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
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)
|
||||
}
|
|
@ -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::<Vaa<Message>>(&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<Message> = 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
MODULE.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Module {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
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<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
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::<Field>()? {
|
||||
match key {
|
||||
Field::Module => {
|
||||
if module.is_some() {
|
||||
return Err(Error::duplicate_field("module"));
|
||||
}
|
||||
|
||||
module = map.next_value::<Module>().map(Some)?;
|
||||
}
|
||||
Field::Action => {
|
||||
if action.is_some() {
|
||||
return Err(Error::duplicate_field("action"));
|
||||
}
|
||||
|
||||
action = map.next_value::<u8>().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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -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<P> {
|
||||
// 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<Signature>,
|
||||
|
||||
// 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<u8>,
|
||||
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<u8>,
|
||||
/// 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<Signature>,
|
||||
}
|
||||
|
||||
/// The body for a VAA.
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Body<P> {
|
||||
/// 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<T: AsRef<[u8]>>(input: T) -> Result<Self, WormholeError> {
|
||||
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<Digest> {
|
||||
// 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<P> Vaa<P> {
|
||||
/// 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<P> From<Vaa<P>> for (Header, Body<P>) {
|
||||
fn from(v: Vaa<P>) -> 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<P> From<(Header, Body<P>)> for Vaa<P> {
|
||||
fn from((hdr, body): (Header, Body<P>)) -> 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<VAADigest> {
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use sha3::Digest;
|
||||
use std::io::{Cursor, Write};
|
||||
impl Header {
|
||||
pub fn verify(&self, _body: &[u8], _addrs: &[GuardianAddress]) -> anyhow::Result<Digest> {
|
||||
todo!("VAA body verification")
|
||||
}
|
||||
}
|
||||
|
||||
// Hash Deterministic Pieces
|
||||
let body = {
|
||||
let mut v = Cursor::new(Vec::new());
|
||||
v.write_u32::<BigEndian>(self.timestamp).ok()?;
|
||||
v.write_u32::<BigEndian>(self.nonce).ok()?;
|
||||
v.write_u16::<BigEndian>(self.emitter_chain.clone() as u16)
|
||||
.ok()?;
|
||||
let _ = v.write(&self.emitter_address).ok()?;
|
||||
v.write_u64::<BigEndian>(self.sequence).ok()?;
|
||||
v.write_u8(self.consistency_level).ok()?;
|
||||
let _ = v.write(&self.payload).ok()?;
|
||||
v.into_inner()
|
||||
};
|
||||
impl<P: Serialize> Body<P> {
|
||||
/// 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<Digest> {
|
||||
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<Digest> {
|
||||
// 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<const S: usize>(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<Vec<u8>, WormholeError>;
|
||||
|
||||
/// Parses an Action from a governance payload securely.
|
||||
fn from_bytes<T: AsRef<[u8]>>(
|
||||
input: T,
|
||||
chain: Option<Chain>,
|
||||
) -> 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<A: GovernanceAction>(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<u8>,
|
||||
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<VAA, std::io::Error> {
|
||||
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::<BigEndian>()?;
|
||||
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::<BigEndian>()?;
|
||||
v.nonce = rdr.read_u32::<BigEndian>()?;
|
||||
v.emitter_chain = Chain::try_from(rdr.read_u16::<BigEndian>()?).unwrap();
|
||||
let mut emitter_address = [0u8; 32];
|
||||
rdr.read_exact(&mut emitter_address)?;
|
||||
v.emitter_address = emitter_address;
|
||||
v.sequence = rdr.read_u64::<BigEndian>()?;
|
||||
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() {}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
|
@ -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<T: AsRef<[u8]>>(input: T) -> Result<Self, WormholeError> {
|
||||
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 }))
|
||||
}
|
||||
}
|
|
@ -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<T: AsRef<[u8]>>(input: T) -> Result<Self, WormholeError> {
|
||||
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<T: AsRef<[u8]>>(input: T) -> Result<Self, WormholeError> {
|
||||
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 }))
|
||||
}
|
||||
}
|
|
@ -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 = "*"
|
|
@ -1,7 +0,0 @@
|
|||
[build]
|
||||
rustflags = [
|
||||
"-Cpasses=sancov-module",
|
||||
"-Cllvm-args=-sanitizer-coverage-level=3",
|
||||
"-Cllvm-args=-sanitizer-coverage-inline-8bit-counters",
|
||||
"-Zsanitizer=address",
|
||||
]
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
|
@ -1,7 +0,0 @@
|
|||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use wormhole_sdk::VAA;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
VAA::from_bytes(data);
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use wormhole_sdk::vaa::VAA;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
VAA::from_bytes(data);
|
||||
});
|
|
@ -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::*;
|
|
@ -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<BridgeConfig, WormholeError> {
|
||||
let bridge_data = BridgeData::try_from_slice(&config.data.borrow())
|
||||
.map_err(|_| WormholeError::DeserializeFailed)?;
|
||||
Ok(bridge_data.config)
|
||||
}
|
||||
|
||||
/// Deserialize helper for parsing from Borsh encoded VAA's from Solana accounts.
|
||||
pub fn read_vaa(vaa: &AccountInfo) -> Result<PostedVAAData, WormholeError> {
|
||||
Ok(PostedVAAData::try_from_slice(&vaa.data.borrow())
|
||||
.map_err(|_| WormholeError::DeserializeFailed)?)
|
||||
}
|
||||
|
||||
/// This helper method wraps the steps required to invoke Wormhole, it takes care of fee payment,
|
||||
/// emitter derivation, and function invocation. This will be the right thing to use if you need to
|
||||
/// simply emit a message in the most straight forward way possible.
|
||||
pub fn post_message(
|
||||
program_id: Pubkey,
|
||||
payer: Pubkey,
|
||||
message: Pubkey,
|
||||
payload: impl AsRef<[u8]>,
|
||||
consistency: ConsistencyLevel,
|
||||
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(())
|
||||
}
|
|
@ -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<CosmosMsg> {
|
||||
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<ParsedVAA> {
|
||||
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)
|
||||
}
|
|
@ -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::*;
|
Loading…
Reference in New Issue