sdk/rust: Add serde_wormhole crate

The serde_wormhole crate implements the wormhole wire format as a serde
data format.  This will let us replace all the hand-rolled
serialization with auto-generated code, which is less error-prone and
easier to review.
This commit is contained in:
Chirantan Ekbote 2022-11-10 16:20:00 +09:00 committed by Chirantan Ekbote
parent 6e25b6c801
commit 92cb3d28a3
7 changed files with 2153 additions and 1447 deletions

1492
sdk/rust/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,12 @@
[workspace]
members = [
"core",
"sdk"
"core/",
"serde_wormhole",
]
[profile.release]
opt-level = 3
lto = "thin"
[patch.crates-io]
serde_wormhole = { path = "serde_wormhole" }

View File

@ -0,0 +1,15 @@
[package]
name = "serde_wormhole"
version = "0.1.0"
edition = "2021"
description = "Serde data format for VAA payloads"
[dependencies]
itoa = "1.0.1"
serde = { version = "1.0.103", default-features = false }
thiserror = "1.0"
[dev-dependencies]
serde = { version = "1.0.103", default-features = false, features = ["alloc", "derive"] }
serde_bytes = "0.11.5"
serde_repr = "0.1.7"

View File

@ -0,0 +1,910 @@
use std::{convert::TryFrom, mem::size_of};
use serde::de::{
self, DeserializeSeed, EnumAccess, Error as DeError, IntoDeserializer, MapAccess, SeqAccess,
Unexpected, VariantAccess, Visitor,
};
use crate::error::Error;
/// A struct that deserializes the VAA payload wire format into rust values.
pub struct Deserializer<'de> {
input: &'de [u8],
}
impl<'de> Deserializer<'de> {
/// Construct a new instance of `Deserializer` from `input`.
pub fn new(input: &'de [u8]) -> Self {
Self { input }
}
/// Should be called once the value has been fully deserialized. Returns any data left in the
/// input buffer after deserialization.
pub fn end(self) -> &'de [u8] {
self.input
}
}
// This has to be a macro because `<type>::from_be_bytes` is not a trait function so there is no
// trait bound that we can use in a generic function.
macro_rules! deserialize_be_number {
($self:ident, $ty:ty) => {{
const LEN: usize = size_of::<$ty>();
if $self.input.len() < LEN {
return Err(Error::Eof);
}
let (data, rem) = $self.input.split_at(LEN);
let mut buf = [0u8; LEN];
buf.copy_from_slice(data);
$self.input = rem;
<$ty>::from_be_bytes(buf)
}};
}
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::DeserializeAnyNotSupported)
}
fn deserialize_ignored_any<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::DeserializeAnyNotSupported)
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let v = deserialize_be_number!(self, u8);
match v {
0 => visitor.visit_bool(false),
1 => visitor.visit_bool(true),
v => Err(Error::invalid_value(
Unexpected::Unsigned(v.into()),
&"a 0 or 1",
)),
}
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_i8(deserialize_be_number!(self, i8))
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_i16(deserialize_be_number!(self, i16))
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_i32(deserialize_be_number!(self, i32))
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_i64(deserialize_be_number!(self, i64))
}
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_i128(deserialize_be_number!(self, i128))
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_u8(deserialize_be_number!(self, u8))
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_u16(deserialize_be_number!(self, u16))
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_u32(deserialize_be_number!(self, u32))
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_u64(deserialize_be_number!(self, u64))
}
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_u128(deserialize_be_number!(self, u128))
}
fn deserialize_f32<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::Unsupported)
}
fn deserialize_f64<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::Unsupported)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let v = deserialize_be_number!(self, u32);
char::try_from(v)
.map_err(|_| Error::invalid_value(Unexpected::Unsigned(v.into()), &"a `char`"))
.and_then(|v| visitor.visit_char(v))
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let len = usize::from(deserialize_be_number!(self, u8));
if self.input.len() < len {
return Err(Error::Eof);
}
let (data, rem) = self.input.split_at(len);
self.input = rem;
std::str::from_utf8(data)
.map_err(|_| Error::invalid_value(Unexpected::Bytes(data), &"a UTF-8 string"))
.and_then(|s| visitor.visit_borrowed_str(s))
}
#[inline]
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_str(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let len = usize::from(deserialize_be_number!(self, u8));
if self.input.len() < len {
return Err(Error::Eof);
}
let (data, rem) = self.input.split_at(len);
self.input = rem;
visitor.visit_borrowed_bytes(data)
}
#[inline]
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_bytes(visitor)
}
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
// There are no optional values in this data format.
visitor.visit_some(self)
}
#[inline]
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
#[inline]
fn deserialize_unit_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
#[inline]
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let len = usize::from(deserialize_be_number!(self, u8));
visitor.visit_seq(BoundedSequence::new(self, len))
}
#[inline]
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(BoundedSequence::new(self, len))
}
#[inline]
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(BoundedSequence::new(self, len))
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let len = usize::from(deserialize_be_number!(self, u8));
visitor.visit_map(BoundedSequence::new(self, len))
}
#[inline]
fn deserialize_struct<V>(
self,
_name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(BoundedSequence::new(self, fields.len()))
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let variant = deserialize_be_number!(self, u8);
visitor.visit_enum(Enum { de: self, variant })
}
fn deserialize_identifier<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::Unsupported)
}
}
impl<'de, 'a> VariantAccess<'de> for &'a mut Deserializer<'de> {
type Error = Error;
#[inline]
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
#[inline]
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'de>,
{
seed.deserialize(self)
}
#[inline]
fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(BoundedSequence::new(self, len))
}
#[inline]
fn struct_variant<V>(
self,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(BoundedSequence::new(self, fields.len()))
}
}
struct BoundedSequence<'de, 'a> {
de: &'a mut Deserializer<'de>,
count: usize,
}
impl<'de, 'a> BoundedSequence<'de, 'a> {
fn new(de: &'a mut Deserializer<'de>, count: usize) -> Self {
Self { de, count }
}
}
impl<'de, 'a> SeqAccess<'de> for BoundedSequence<'de, 'a> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>,
{
if self.count == 0 {
return Ok(None);
}
self.count -= 1;
seed.deserialize(&mut *self.de).map(Some)
}
#[inline]
fn size_hint(&self) -> Option<usize> {
Some(self.count)
}
}
impl<'de, 'a> MapAccess<'de> for BoundedSequence<'de, 'a> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: DeserializeSeed<'de>,
{
if self.count == 0 {
return Ok(None);
}
self.count -= 1;
seed.deserialize(&mut *self.de).map(Some)
}
#[inline]
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'de>,
{
seed.deserialize(&mut *self.de)
}
#[inline]
fn size_hint(&self) -> Option<usize> {
Some(self.count)
}
}
/// Tells serde which enum variant it should deserialize. Enums are encoded in the byte stream as a
/// `u8` followed by the data for the variant but unfortunately, serde doesn't currently support
/// integer tags (see https://github.com/serde-rs/serde/issues/745). Instead we format the integer
/// into its string representation and have serde use that to determine the enum variant. This
/// requires using `#[serde(rename = "<integer tag>")]` on all enum variants.
///
/// # Examples
///
/// ```
/// use std::borrow::Cow;
///
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// enum TestEnum<'a> {
/// #[serde(rename = "19")]
/// Unit,
/// #[serde(rename = "235")]
/// NewType(u64),
/// #[serde(rename = "179")]
/// Tuple(u32, u64, Vec<u16>),
/// #[serde(rename = "97")]
/// Struct {
/// #[serde(borrow, with = "serde_bytes")]
/// data: Cow<'a, [u8]>,
/// footer: u32,
/// },
/// }
/// #
/// # assert!(matches!(serde_wormhole::from_slice(&[19]).unwrap(), TestEnum::Unit));
/// ```
struct Enum<'de, 'a> {
de: &'a mut Deserializer<'de>,
variant: u8,
}
impl<'de, 'a> EnumAccess<'de> for Enum<'de, 'a> {
type Error = Error;
type Variant = &'a mut Deserializer<'de>;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'de>,
{
let mut buf = itoa::Buffer::new();
seed.deserialize(buf.format(self.variant).into_deserializer())
.map(|v| (v, self.de))
}
}
#[cfg(test)]
mod tests {
use std::{
borrow::Cow,
collections::BTreeMap,
io::{Cursor, Write},
iter::FromIterator,
mem::size_of,
};
use serde::{Deserialize, Serialize};
use crate::{from_slice, Error};
#[test]
fn empty_input() {
let e = from_slice::<u8>(&[]).expect_err("empty buffer deserialized");
assert!(matches!(e, Error::Eof))
}
#[test]
fn trailing_data() {
macro_rules! check {
($buf:ident, $ty:ty) => {
let e = from_slice::<$ty>(&$buf)
.expect_err("deserialized with trailing data");
assert!(matches!(e, Error::TrailingData));
};
($buf:ident, $($ty:ty),*) => {
$(
check!($buf, $ty);
)*
};
}
let buf = 0x9ab0_8c9f_8462_2f63u64.to_be_bytes();
check!(buf, i8, i16, i32, u8, u16, u32);
}
#[test]
fn bool() {
let v: bool = from_slice(&[0u8]).unwrap();
assert!(!v);
let v: bool = from_slice(&[1u8]).unwrap();
assert!(v);
for v in 2..=u8::MAX {
from_slice::<bool>(&[v]).unwrap_err();
}
}
#[test]
fn integers() {
macro_rules! check {
($v:ident, $ty:ty) => {
// Casting an integer from a larger width to a smaller width will truncate the
// upper bits.
let expected = $v as $ty;
let buf = expected.to_be_bytes();
let actual: $ty = from_slice(&buf).expect("failed to deserialize integer");
assert_eq!(expected, actual);
};
($v:ident, $($ty:ty),*) => {
$(
check!($v, $ty);
)*
};
}
// Value randomly generated from `dd if=/dev/urandom | xxd -p -l 16`.
let v = 0x84f2_e24f_2e8a_734e_5a5f_def6_c597_f232u128;
check!(v, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
}
#[test]
fn char() {
let chars = ['\u{0065}', '\u{0301}'];
let mut buf = [0u8; size_of::<u32>() * 2];
let mut cursor = Cursor::new(&mut buf[..]);
for c in chars {
cursor.write_all(&u32::from(c).to_be_bytes()).unwrap();
}
let (c1, c2): (char, char) = from_slice(&buf).unwrap();
let mut actual = String::new();
actual.push(c1);
actual.push(c2);
assert_eq!("", actual);
}
#[test]
fn invalid_chars() {
for c in 0xd800u32..0xe000 {
from_slice::<char>(&c.to_be_bytes()).expect_err("deserialized invalid char");
}
// Spot check a few values in the higher range.
let invalid = [
0xa17c509eu32,
0xb4ee4bc8u32,
0x46055273u32,
0x3d3bb5fbu32,
0xeb82fddcu32,
0xe2777604u32,
0xe597554fu32,
0x12aa069fu32,
];
for c in invalid {
from_slice::<char>(&c.to_be_bytes()).expect_err("deserialized invalid char");
}
}
#[test]
fn char_array() {
let expected = "Löwe 老虎 Léopard";
let mut buf = [0u8; size_of::<u32>() * 15];
let mut cursor = Cursor::new(&mut buf[..]);
for c in expected.chars().map(u32::from) {
cursor.write_all(&c.to_be_bytes()).unwrap();
}
let actual = from_slice::<[char; 15]>(&buf)
.map(String::from_iter)
.unwrap();
assert_eq!(expected, actual);
}
#[test]
fn strings() {
let expected = "Löwe 老虎 Léopard";
let buf = IntoIterator::into_iter([expected.len() as u8])
.chain(expected.as_bytes().iter().copied())
.collect::<Vec<u8>>();
let actual = from_slice::<String>(&buf).unwrap();
assert_eq!(expected, actual);
let actual = from_slice::<&str>(&buf).unwrap();
assert_eq!(expected, actual);
let actual = from_slice::<&str>(&[0]).unwrap();
assert!(actual.is_empty());
}
#[test]
fn maps() {
let buf = [
0x04, 0x78, 0x26, 0x20, 0x90, 0x48, 0x96, 0xd4, 0x18, 0x8b, 0xce, 0x62, 0xcd, 0x87,
0x7a, 0x36, 0x1a, 0x4c, 0x5e, 0x4f, 0x65, 0x84, 0x76, 0xb3, 0x9c, 0x7e, 0xb3, 0xfa,
0x99, 0x29, 0xf2, 0x8b, 0x7f,
];
let expected = BTreeMap::from([
(0x78262090u32, 0x4896d418u32),
(0x8bce62cd, 0x877a361a),
(0x4c5e4f65, 0x8476b39c),
(0x7eb3fa99, 0x29f28b7f),
]);
let actual = from_slice(&buf).unwrap();
assert_eq!(expected, actual);
let actual = from_slice::<BTreeMap<u32, u32>>(&[0]).unwrap();
assert!(actual.is_empty());
}
#[test]
fn empty_bytes() {
let buf = [0x0];
let actual: Vec<u8> = from_slice(&buf).unwrap();
assert!(actual.is_empty());
let actual: &[u8] = from_slice(&buf).unwrap();
assert!(actual.is_empty());
}
#[test]
fn bytes() {
let buf = [
0x40, 0xc7, 0xe7, 0x8f, 0x91, 0x47, 0x32, 0xe0, 0x54, 0x4e, 0xde, 0x94, 0x27, 0xf4,
0xa9, 0x95, 0xd5, 0x96, 0xbe, 0x38, 0xd4, 0xa8, 0xca, 0xdd, 0x2e, 0xec, 0x95, 0x8d,
0xb3, 0x1a, 0xa3, 0x8a, 0x3b, 0xc2, 0xdb, 0x54, 0xac, 0x23, 0x85, 0xa7, 0xe8, 0x88,
0x39, 0xcb, 0xa4, 0x83, 0xde, 0xc4, 0x33, 0x83, 0x10, 0xba, 0x39, 0x55, 0x63, 0x67,
0xd9, 0x08, 0x19, 0xe2, 0x42, 0xf6, 0xc9, 0x5c, 0xe2,
];
let expected = &buf[1..];
let actual: Vec<u8> = from_slice(&buf).unwrap();
assert_eq!(expected, actual);
let actual: &[u8] = from_slice(&buf).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn max_bytes() {
let buf = [
0xff, 0x8a, 0xa0, 0x62, 0x4b, 0xf8, 0x5c, 0x2f, 0x71, 0x8b, 0xa2, 0xe4, 0x80, 0xbf,
0xb0, 0x15, 0xe0, 0xa3, 0x7c, 0xd3, 0x81, 0x56, 0x0d, 0x25, 0x13, 0x63, 0x23, 0xa1,
0x0f, 0x84, 0x7f, 0x3e, 0xed, 0x3a, 0xe1, 0xe2, 0x8e, 0x20, 0x33, 0x42, 0x83, 0x89,
0xa9, 0x0d, 0xe6, 0x58, 0xa5, 0xb4, 0x64, 0x60, 0x0f, 0x8f, 0xdf, 0x51, 0xd1, 0x00,
0x9d, 0x4b, 0x6e, 0x42, 0x04, 0x8b, 0xa2, 0xc8, 0x14, 0xed, 0x4f, 0x46, 0x64, 0xf5,
0xfd, 0xa6, 0xb2, 0x85, 0x63, 0x60, 0xa6, 0xb7, 0xd8, 0xed, 0x1a, 0xfd, 0x3f, 0x99,
0x6b, 0x3c, 0x85, 0xfe, 0x09, 0x04, 0xab, 0x9f, 0x56, 0xfa, 0x9f, 0x80, 0xd5, 0x93,
0x94, 0xa3, 0xc6, 0x62, 0xa8, 0x0e, 0x2d, 0xaa, 0x82, 0x94, 0xf9, 0x38, 0xf1, 0x58,
0x9e, 0x4c, 0x3e, 0x00, 0x64, 0x67, 0xda, 0x9e, 0x8b, 0x5c, 0xb1, 0xaa, 0xa8, 0x85,
0x43, 0xfd, 0x1a, 0xf2, 0xd8, 0xa7, 0xa7, 0x31, 0x55, 0x73, 0x91, 0x19, 0x5e, 0x43,
0xe3, 0xc0, 0xfb, 0xd0, 0xc6, 0xa8, 0x72, 0x43, 0x33, 0x2f, 0x69, 0x5c, 0x64, 0x92,
0xc7, 0x17, 0xb2, 0x30, 0x7a, 0xc1, 0x0a, 0x0d, 0x30, 0xbb, 0x94, 0xcb, 0x5c, 0x49,
0x88, 0xe0, 0xb4, 0x0b, 0x4e, 0xab, 0xd7, 0x8e, 0x2d, 0x82, 0x55, 0x33, 0xb1, 0x00,
0xa6, 0x89, 0x32, 0x59, 0x86, 0xde, 0xd7, 0x13, 0xea, 0x35, 0x0a, 0xa0, 0x50, 0x89,
0x95, 0xe7, 0xaf, 0xaa, 0x6a, 0x4e, 0x22, 0xb4, 0x7f, 0x2e, 0x49, 0x9d, 0x67, 0x3a,
0x95, 0x99, 0x75, 0x0a, 0x6b, 0x4d, 0x3e, 0x9d, 0x03, 0x1e, 0xfd, 0x82, 0xda, 0x02,
0x3e, 0x18, 0xe4, 0x26, 0xdf, 0xb0, 0x1d, 0x49, 0xce, 0x6c, 0xf8, 0xbc, 0xbe, 0x82,
0x27, 0x0e, 0x66, 0xa1, 0xc1, 0x85, 0xe2, 0xe1, 0x03, 0x83, 0xa4, 0x82, 0xf7, 0xd0,
0x66, 0x12, 0x8b, 0xc4,
];
let expected = &buf[1..];
let actual: Vec<u8> = from_slice(&buf).unwrap();
assert_eq!(expected, actual);
let actual: &[u8] = from_slice(&buf).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn tagged_enums() {
#[derive(Serialize, Deserialize)]
enum TestEnum<'a> {
#[serde(rename = "19")]
Unit,
#[serde(rename = "235")]
NewType(u64),
#[serde(rename = "179")]
Tuple(u32, u64, Vec<u16>),
#[serde(rename = "97")]
Struct {
#[serde(borrow, with = "serde_bytes")]
data: Cow<'a, [u8]>,
footer: u32,
},
}
assert!(matches!(from_slice(&[19]).unwrap(), TestEnum::Unit));
let buf = [235, 0xa7, 0xc5, 0x31, 0x9c, 0x8d, 0x87, 0x48, 0xd2];
if let TestEnum::NewType(v) = from_slice(&buf).unwrap() {
assert_eq!(v, 0xa7c5319c8d8748d2);
} else {
panic!();
}
let buf = [
179, 0x60, 0xfb, 0x4d, 0x0d, 0xc4, 0x98, 0x40, 0x65, 0xf5, 0xdb, 0xbf, 0x3c, 0x05,
0xa9, 0xca, 0xb9, 0xe7, 0x96, 0x3b, 0x74, 0xfa, 0x82, 0xb2,
];
if let TestEnum::Tuple(a, b, c) = from_slice(&buf).unwrap() {
assert_eq!(a, 0x60fb4d0d);
assert_eq!(b, 0xc4984065f5dbbf3c);
assert_eq!(c, &[0xa9ca, 0xb9e7, 0x963b, 0x74fa, 0x82b2]);
} else {
panic!();
}
let buf = [
97, 0x0b, 0xc2, 0xfd, 0xd6, 0xa1, 0xed, 0x8a, 0x12, 0x46, 0xd4, 0x20, 0xaf, 0xcc, 0x88,
0x8c, 0xd2,
];
if let TestEnum::Struct { data, footer } = from_slice(&buf).unwrap() {
assert_eq!(
&*data,
&[0xc2, 0xfd, 0xd6, 0xa1, 0xed, 0x8a, 0x12, 0x46, 0xd4, 0x20, 0xaf]
);
assert_eq!(footer, 0xcc888cd2);
} else {
panic!();
}
}
#[test]
fn unknown_enum_variant() {
#[derive(Debug, Serialize, Deserialize)]
enum Unknown {
#[serde(rename = "7")]
Foo,
}
from_slice::<Unknown>(&[1]).expect_err("Deserialized unknown enum variant");
}
#[test]
fn complex_struct() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Address<'a> {
#[serde(borrow, with = "serde_bytes")]
bytes: Cow<'a, [u8]>,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Info<'a> {
#[serde(borrow)]
addrs: Vec<Address<'a>>,
expiration: u64,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Upgrade<'a> {
index: u32,
#[serde(borrow)]
info: Info<'a>,
}
let expected = Upgrade {
index: 7,
info: Info {
addrs: vec![
Address {
bytes: Cow::Owned(vec![
0x4f, 0x58, 0x50, 0x9e, 0xb6, 0x8b, 0x9d, 0x19, 0x9e, 0x00, 0x92, 0x5e,
0xcb, 0x0f, 0xfd, 0x53, 0x80, 0x06, 0xfe, 0xc3,
]),
},
Address {
bytes: Cow::Owned(vec![
0xb6, 0x7c, 0xd5, 0xef, 0x88, 0x00, 0xa7, 0xbc, 0xba, 0x2e, 0xfb, 0x91,
0x09, 0x33, 0xee, 0x51, 0xdd, 0x02, 0x24, 0x35,
]),
},
Address {
bytes: Cow::Owned(vec![
0x2b, 0x05, 0x87, 0x83, 0x8a, 0x2a, 0xe9, 0xc4, 0x0e, 0x54, 0x28, 0x11,
0xc2, 0x99, 0x33, 0xa8, 0x65, 0xd4, 0x6c, 0x3d,
]),
},
Address {
bytes: Cow::Owned(vec![
0x08, 0xd2, 0xb5, 0x03, 0x64, 0xb5, 0x27, 0x7f, 0xf0, 0xaf, 0x90, 0x6d,
0x03, 0x10, 0x21, 0xb3, 0x20, 0xdd, 0xfb, 0xda,
]),
},
Address {
bytes: Cow::Owned(vec![
0xec, 0xc9, 0x7d, 0x9d, 0x6c, 0x68, 0x4e, 0x43, 0x6e, 0x39, 0x51, 0xe0,
0xa8, 0x6f, 0x49, 0xf1, 0xf4, 0xd3, 0xdb, 0x2a,
]),
},
Address {
bytes: Cow::Owned(vec![
0x11, 0xed, 0x25, 0xe6, 0x6b, 0xed, 0x56, 0x25, 0x87, 0xb4, 0x1c, 0x94,
0x9c, 0x81, 0xcf, 0x2c, 0x34, 0xb8, 0x5e, 0xc3,
]),
},
Address {
bytes: Cow::Owned(vec![
0x3d, 0x82, 0xcb, 0x29, 0xe8, 0xa7, 0x34, 0x37, 0x3a, 0x46, 0x07, 0xa4,
0xf2, 0xb3, 0x94, 0xb0, 0x73, 0xed, 0x86, 0x3b,
]),
},
Address {
bytes: Cow::Owned(vec![
0x99, 0xa4, 0xb5, 0x89, 0x01, 0x59, 0x18, 0x01, 0x08, 0x53, 0xcf, 0x17,
0x21, 0x14, 0x65, 0xcf, 0x05, 0x7f, 0xaa, 0x5d,
]),
},
Address {
bytes: Cow::Owned(vec![
0xcc, 0x38, 0x3b, 0x85, 0xde, 0xc2, 0x59, 0xe6, 0x22, 0xee, 0xa4, 0xea,
0x83, 0x72, 0x08, 0x7e, 0xdf, 0xea, 0xe1, 0xc3,
]),
},
Address {
bytes: Cow::Owned(vec![
0x7a, 0xd9, 0x4d, 0x53, 0x9c, 0xc2, 0xff, 0xe3, 0x1d, 0xd6, 0x60, 0x78,
0x31, 0xb3, 0x2f, 0x76, 0x12, 0xb7, 0xc7, 0xaf,
]),
},
Address {
bytes: Cow::Owned(vec![
0x10, 0x88, 0xf6, 0x6f, 0x1d, 0x27, 0x2d, 0xad, 0x5b, 0x48, 0xca, 0xaf,
0xba, 0x63, 0x99, 0xbe, 0x23, 0x3b, 0xd5, 0xca,
]),
},
Address {
bytes: Cow::Owned(vec![
0x49, 0x91, 0xa9, 0x0f, 0x47, 0xcd, 0xfe, 0xdb, 0xd6, 0xfb, 0xb3, 0xe9,
0xa4, 0xc2, 0xc2, 0x15, 0xb3, 0xe7, 0xe5, 0xb6,
]),
},
Address {
bytes: Cow::Owned(vec![
0xdd, 0xe3, 0x77, 0xb0, 0xc3, 0x1b, 0x56, 0x2c, 0x90, 0x67, 0x88, 0xc6,
0xc5, 0xa5, 0xd8, 0xb8, 0xee, 0xc3, 0xa0, 0x87,
]),
},
],
expiration: 0x90e4_9c5d_cb20_0792,
},
};
let buf = [
0x00, 0x00, 0x00, 0x07, 0x0d, 0x14, 0x4f, 0x58, 0x50, 0x9e, 0xb6, 0x8b, 0x9d, 0x19,
0x9e, 0x00, 0x92, 0x5e, 0xcb, 0x0f, 0xfd, 0x53, 0x80, 0x06, 0xfe, 0xc3, 0x14, 0xb6,
0x7c, 0xd5, 0xef, 0x88, 0x00, 0xa7, 0xbc, 0xba, 0x2e, 0xfb, 0x91, 0x09, 0x33, 0xee,
0x51, 0xdd, 0x02, 0x24, 0x35, 0x14, 0x2b, 0x05, 0x87, 0x83, 0x8a, 0x2a, 0xe9, 0xc4,
0x0e, 0x54, 0x28, 0x11, 0xc2, 0x99, 0x33, 0xa8, 0x65, 0xd4, 0x6c, 0x3d, 0x14, 0x08,
0xd2, 0xb5, 0x03, 0x64, 0xb5, 0x27, 0x7f, 0xf0, 0xaf, 0x90, 0x6d, 0x03, 0x10, 0x21,
0xb3, 0x20, 0xdd, 0xfb, 0xda, 0x14, 0xec, 0xc9, 0x7d, 0x9d, 0x6c, 0x68, 0x4e, 0x43,
0x6e, 0x39, 0x51, 0xe0, 0xa8, 0x6f, 0x49, 0xf1, 0xf4, 0xd3, 0xdb, 0x2a, 0x14, 0x11,
0xed, 0x25, 0xe6, 0x6b, 0xed, 0x56, 0x25, 0x87, 0xb4, 0x1c, 0x94, 0x9c, 0x81, 0xcf,
0x2c, 0x34, 0xb8, 0x5e, 0xc3, 0x14, 0x3d, 0x82, 0xcb, 0x29, 0xe8, 0xa7, 0x34, 0x37,
0x3a, 0x46, 0x07, 0xa4, 0xf2, 0xb3, 0x94, 0xb0, 0x73, 0xed, 0x86, 0x3b, 0x14, 0x99,
0xa4, 0xb5, 0x89, 0x01, 0x59, 0x18, 0x01, 0x08, 0x53, 0xcf, 0x17, 0x21, 0x14, 0x65,
0xcf, 0x05, 0x7f, 0xaa, 0x5d, 0x14, 0xcc, 0x38, 0x3b, 0x85, 0xde, 0xc2, 0x59, 0xe6,
0x22, 0xee, 0xa4, 0xea, 0x83, 0x72, 0x08, 0x7e, 0xdf, 0xea, 0xe1, 0xc3, 0x14, 0x7a,
0xd9, 0x4d, 0x53, 0x9c, 0xc2, 0xff, 0xe3, 0x1d, 0xd6, 0x60, 0x78, 0x31, 0xb3, 0x2f,
0x76, 0x12, 0xb7, 0xc7, 0xaf, 0x14, 0x10, 0x88, 0xf6, 0x6f, 0x1d, 0x27, 0x2d, 0xad,
0x5b, 0x48, 0xca, 0xaf, 0xba, 0x63, 0x99, 0xbe, 0x23, 0x3b, 0xd5, 0xca, 0x14, 0x49,
0x91, 0xa9, 0x0f, 0x47, 0xcd, 0xfe, 0xdb, 0xd6, 0xfb, 0xb3, 0xe9, 0xa4, 0xc2, 0xc2,
0x15, 0xb3, 0xe7, 0xe5, 0xb6, 0x14, 0xdd, 0xe3, 0x77, 0xb0, 0xc3, 0x1b, 0x56, 0x2c,
0x90, 0x67, 0x88, 0xc6, 0xc5, 0xa5, 0xd8, 0xb8, 0xee, 0xc3, 0xa0, 0x87, 0x90, 0xe4,
0x9c, 0x5d, 0xcb, 0x20, 0x07, 0x92,
];
let actual = from_slice(&buf).unwrap();
assert_eq!(expected, actual);
}
}

View File

@ -0,0 +1,38 @@
use std::{fmt::Display, io, num::ParseIntError};
use serde::{de, ser};
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum Error {
#[error("{0}")]
Message(Box<str>),
#[error("{0}")]
Io(#[from] io::Error),
#[error("unexpected end of input")]
Eof,
#[error("`deserialize_any` is not supported")]
DeserializeAnyNotSupported,
#[error("trailing data in input buffer")]
TrailingData,
#[error("this type is not supported")]
Unsupported,
#[error("sequence is too large ({0} elements), max supported length = 255")]
SequenceTooLarge(usize),
#[error("enum variant {0}::{1} cannot be parsed as a `u8`: {2}")]
EnumVariant(&'static str, &'static str, ParseIntError),
#[error("sequence length must be known before serialization")]
UnknownSequenceLength,
}
impl de::Error for Error {
fn custom<T: Display>(msg: T) -> Error {
Error::Message(msg.to_string().into_boxed_str())
}
}
impl ser::Error for Error {
fn custom<T: Display>(msg: T) -> Error {
Error::Message(msg.to_string().into_boxed_str())
}
}

View File

@ -0,0 +1,426 @@
//! Serialize and deserialize rust values from the VAA payload wire format.
//!
//! As of this writing (June, 2022) there is no proper specification for the VAA payload wire
//! format so this implementation has mostly been reverse engineered from the existing messages.
//! While the rest of this document talks about how various types are represented on the wire this
//! should be seen as an explanation of how things are implemented *in this crate* and not as
//! official documentation. In cases where the serialization of a payload produced by this crate
//! differs from the one use by the wormhole contracts, the serialization used by the actual
//! contract is considered the canonical serialization.
//!
//! Unless you want to interact with existing wormhole VAA payloads, this crate is probably not what
//! you are looking for. If you are simply using the wormhole bridge to send your own payloads then
//! using a schema with auto-generated code (like protobufs or flatbuffers) is probably a better
//! choice.
//!
//! ## Wire format
//!
//! The VAA payload wire format is not a self-describing format (unlike json and toml). Therefore it
//! is necessary to know the type that needs to be produced before deserializing a byte stream.
//!
//! The wire format currently supports the following primitive types:
//!
//! ### `bool`
//!
//! Encoded as a single byte where a value of 0 indicates false and 1 indicates true. All other
//! values are invalid.
//!
//! ### Integers
//!
//! `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, and `u128` are all supported and
//! encoded as full-width big-endian integers, i.e., `i16` is 2 bytes, `u64` is 8 bytes, etc.
//!
//! ### `char`
//!
//! Encoded as a big-endian `u32`, with the additional restriction that it must be a valid [`Unicode
//! Scalar Value`](https://www.unicode.org/glossary/#unicode_scalar_value).
//!
//! ### Sequences
//!
//! Variable length heterogeneous sequences are encoded as a single byte length followed by the
//! concatenation of the serialized form of each element in the sequence. Note that this means that
//! sequences cannot have more than 255 elements. Additionally, during serialization the length must
//! be known ahead of time.
//!
//! ### Byte arrays - `&[u8]`, `Vec<u8>`, and `Cow<'a, [u8]>`
//!
//! Byte arrays are treated as a subset of variable-length sequences and are encoded as a single
//! byte length followed by that many bytes of data. Again, since the length of the byte array has
//! to fit in a single byte it cannot be longer than 255 bytes.
//!
//! ### `&str`, `String`
//!
//! String types are encoded the same way as `&[u8]`, with the additional restriction that the byte
//! array must be valid UTF-8.
//!
//! ### Tuples
//!
//! Tuples are heterogenous sequences where the length is fixed and known ahead of time. In this
//! case the length is not encoded on the wire and the serialization of each element in the tuple is
//! concatenated to produce the final value.
//!
//! ### `Option<T>`
//!
//! The wire format does not support optional values. Options are always deserialized as `Some(T)`
//! while trying to serialize an `Option::None` will result in an error.
//!
//! ### Structs
//!
//! Structs are represented the same way as tuples and the wire format for a struct is identical to
//! the wire format for a tuple with the same fields in the same order. The only exception is unit
//! structs (structs with no fields), which are not represented in the wire format at all.
//!
//! ### `[T; N]`
//!
//! Arrays are treated as tuples with homogenous fields and have the same wire format.
//!
//! ### Enums
//!
//! Enums are encoded as a single byte identifying the variant followed by the serialization of the
//! variant.
//!
//! * Unit variants - No additional data is encoded.
//! * Newtype variants - Encoded using the serialization of the inner type.
//! * Tuple variants - Encoded as a regular tuple.
//! * Struct variants - Encoded as a regular struct.
//!
//! Since the enum variant is encoded as a single byte rather than the name of the variant itself,
//! it is necessary to use `#[serde(rename = "<value>")]` on each enum variant to ensure
//! that they can be serialized and deserialized properly.
//!
//! #### Examples
//!
//! ```
//! use std::borrow::Cow;
//!
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Serialize, Deserialize)]
//! enum TestEnum<'a> {
//! #[serde(rename = "19")]
//! Unit,
//! #[serde(rename = "235")]
//! NewType(u64),
//! #[serde(rename = "179")]
//! Tuple(u32, u64, Vec<u16>),
//! #[serde(rename = "97")]
//! Struct {
//! #[serde(borrow, with = "serde_bytes")]
//! data: Cow<'a, [u8]>,
//! footer: u32,
//! },
//! }
//!
//! assert!(matches!(serde_wormhole::from_slice(&[19]).unwrap(), TestEnum::Unit));
//! ```
//!
//! ### Map types
//!
//! Map types are encoded as a sequence of `(key, value)` tuples. The encoding for a `Vec<(K, V)>`
//! is identical to that of a `BTreeMap<K, V>`. During serialiazation, the number of elements in the
//! map must be known ahead of time. Like other sequences, the maximum number of elements in the map
//! is 255.
use std::io::{Read, Write};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
mod de;
mod error;
mod ser;
pub use error::Error;
/// Deserialize an instance of type `T` from the provided reader.
pub fn from_reader<R: Read, T: DeserializeOwned>(mut r: R) -> Result<T, Error> {
// We can do something smarter here by making the deserializer generic over the reader (see
// serde_json::Deserializer) but for now this is probably good enough.
let mut buf = Vec::with_capacity(128);
r.read_to_end(&mut buf)?;
from_slice(&buf)
}
/// Like `from_reader` but also returns any trailing data in the input buffer after
/// deserialization.
pub fn from_reader_with_payload<R: Read, T: DeserializeOwned>(
mut r: R,
) -> Result<(T, Vec<u8>), Error> {
// We can do something smarter here by making the deserializer generic over the reader (see
// serde_json::Deserializer) but for now this is probably good enough.
let mut buf = Vec::with_capacity(128);
r.read_to_end(&mut buf)?;
from_slice_with_payload(&buf).map(|(v, p)| (v, p.to_vec()))
}
/// Deserialize an instance of type `T` from a byte slice.
pub fn from_slice<'a, T: Deserialize<'a>>(buf: &'a [u8]) -> Result<T, Error> {
let mut deserializer = de::Deserializer::new(buf);
let v = T::deserialize(&mut deserializer)?;
if deserializer.end().is_empty() {
Ok(v)
} else {
Err(Error::TrailingData)
}
}
/// Like `from_slice` but also returns any trailing data in the input buffer after deserialization.
pub fn from_slice_with_payload<'a, T: Deserialize<'a>>(
buf: &'a [u8],
) -> Result<(T, &'a [u8]), Error> {
let mut deserializer = de::Deserializer::new(buf);
T::deserialize(&mut deserializer).map(|v| (v, deserializer.end()))
}
/// Serialize `T` into a byte vector.
pub fn to_vec<T: ?Sized + Serialize>(val: &T) -> Result<Vec<u8>, Error> {
let mut buf = Vec::with_capacity(128);
to_writer(&mut buf, val)?;
Ok(buf)
}
/// Serialize `T` into the provided writer.
pub fn to_writer<W: Write, T: ?Sized + Serialize>(w: W, val: &T) -> Result<(), Error> {
let mut serializer = ser::Serializer::new(w);
val.serialize(&mut serializer)
}
#[cfg(test)]
mod tests {
use core::panic;
use std::{borrow::Cow, collections::BTreeMap};
use super::*;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
mod serde_array {
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)
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct Header {
version: u8,
guardian_set_index: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
struct Signature {
index: u8,
#[serde(with = "serde_array")]
signature: [u8; 65],
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct Vaa<'s> {
header: Header,
#[serde(borrow)]
signatures: Cow<'s, [Signature]>,
timestamp: u32, // Seconds since UNIX epoch
nonce: u32,
emitter_chain: u16,
emitter_address: [u8; 32],
sequence: u64,
consistency_level: u8,
map: BTreeMap<u32, u32>,
payload: GovernancePacket,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
struct GuardianAddress<'a> {
#[serde(borrow, with = "serde_bytes")]
bytes: Cow<'a, [u8]>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct GuardianSetInfo<'a> {
#[serde(borrow)]
addresses: Cow<'a, [GuardianAddress<'a>]>,
expiration_time: u64,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct ContractUpgrade {
new_contract: u64,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct GuardianSetUpgrade<'a> {
new_guardian_set_index: u32,
#[serde(borrow)]
new_guardian_set: GuardianSetInfo<'a>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct SetFee {
high: u128,
low: u128,
}
#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
#[repr(u8)]
enum Action {
ContractUpgrade = 1,
GuardianSetUpgrade = 2,
SetFee = 3,
}
#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
#[repr(u16)]
enum Chain {
Unset = 0,
Solana = 1,
Ethereum = 2,
Terra = 3,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct GovernancePacket {
module: [u8; 32],
action: Action,
chain: Chain,
}
#[test]
fn end_to_end() {
let vaa = Vaa {
header: Header {
version: 3,
guardian_set_index: 0x97a5_6966,
},
signatures: Cow::Borrowed(&[
Signature {
index: 0x13,
signature: [
0x23, 0x35, 0xf3, 0xc2, 0x2c, 0xd2, 0x43, 0xf4, 0xcd, 0xe4, 0x7a, 0xa9,
0xdd, 0x99, 0x35, 0xbc, 0x20, 0x8f, 0x9c, 0x2d, 0x2e, 0xa4, 0x8e, 0xe0,
0x85, 0x89, 0x33, 0x65, 0x0b, 0x8c, 0x6c, 0x14, 0xd9, 0x6b, 0x41, 0xe8,
0x4b, 0xc7, 0xef, 0xae, 0x75, 0x3d, 0x9f, 0x1a, 0x36, 0x4c, 0x09, 0x62,
0x59, 0x92, 0xca, 0x29, 0xcc, 0x2c, 0xb1, 0x9b, 0xc6, 0x8e, 0xff, 0xf1,
0x29, 0xae, 0x21, 0xe9, 0x17,
],
},
Signature {
index: 0xb2,
signature: [
0xa1, 0x45, 0x54, 0x14, 0xd5, 0x3a, 0x4f, 0xb0, 0xf1, 0xf4, 0xf6, 0xf5,
0x6b, 0x17, 0xc2, 0x52, 0x19, 0xe8, 0x68, 0x54, 0x73, 0x39, 0xde, 0xd2,
0xef, 0x5c, 0xca, 0xca, 0x0f, 0x42, 0x0d, 0x3c, 0x71, 0x64, 0x50, 0xc0,
0x2f, 0xf3, 0xf8, 0x70, 0xee, 0x52, 0xa8, 0x4a, 0xfb, 0x2a, 0x62, 0x4d,
0xeb, 0xc8, 0x1e, 0xa3, 0x38, 0x07, 0x78, 0x67, 0x7f, 0x4b, 0x96, 0xa0,
0x54, 0xc0, 0x66, 0x7d, 0xe7,
],
},
]),
timestamp: 0x2db5_98b3,
nonce: 0x0861_20c4,
emitter_chain: 0x247b,
emitter_address: [
0x8b, 0xc0, 0x03, 0x0d, 0xe2, 0x50, 0x96, 0xcc, 0x48, 0xa8, 0xe7, 0xd7, 0x17, 0x05,
0x6f, 0x9c, 0xe8, 0xe8, 0x0c, 0x12, 0x0d, 0x05, 0x02, 0xed, 0x4c, 0xc9, 0x51, 0xb4,
0x9c, 0xe3, 0xc7, 0x94,
],
sequence: 0xcc2b_6c34_eda9_89c1,
consistency_level: 0x0d,
map: BTreeMap::from([(0x35845d1a, 0x25ff53af), (0x543596f3, 0x58373435)]),
payload: GovernancePacket {
module: [
0x50, 0x06, 0x58, 0xff, 0xff, 0xae, 0x1a, 0xdd, 0x07, 0xbc, 0xcf, 0x34, 0x10,
0x6c, 0xa3, 0xbb, 0x14, 0x40, 0x25, 0xe1, 0x8f, 0x1a, 0xa0, 0x39, 0x7b, 0x12,
0x5a, 0x03, 0x58, 0x6f, 0xe1, 0x88,
],
action: Action::ContractUpgrade,
chain: Chain::Solana,
},
};
let payload = &[0x3d, 0xab, 0x45, 0xaf, 0x7a, 0x6e, 0x9f, 0x7b];
let mut buf = to_vec(&vaa).unwrap();
buf.extend_from_slice(payload);
let (actual, governance_payload) = from_slice_with_payload(&buf).unwrap();
assert_eq!(vaa, actual);
match actual.payload.action {
Action::ContractUpgrade => {
let expected = 0x3dab_45af_7a6e_9f7b;
let msg: ContractUpgrade = from_slice(governance_payload).unwrap();
assert_eq!(expected, msg.new_contract);
}
_ => panic!("Unexpected action: {:?}", actual.payload.action),
}
}
}

View File

@ -0,0 +1,712 @@
use std::{convert::TryFrom, fmt::Display, io::Write};
use serde::{ser, Serialize};
use crate::Error;
/// A struct that serializes a rust value into the VAA payload wire format.
pub struct Serializer<W> {
writer: W,
}
impl<W: Write> Serializer<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
}
impl<'a, W: Write> ser::Serializer for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
#[inline]
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
if v {
self.writer.write_all(&[1]).map_err(Error::from)
} else {
self.writer.write_all(&[0]).map_err(Error::from)
}
}
#[inline]
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
self.writer.write_all(&v.to_be_bytes()).map_err(Error::from)
}
#[inline]
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
Err(Error::Unsupported)
}
#[inline]
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
Err(Error::Unsupported)
}
#[inline]
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
self.serialize_u32(v.into())
}
#[inline]
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
self.serialize_bytes(v.as_bytes())
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
let len = u8::try_from(v.len()).map_err(|_| Error::SequenceTooLarge(v.len()))?;
self.writer.write_all(&[len])?;
self.writer.write_all(v).map_err(Error::from)
}
#[inline]
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::Unsupported)
}
#[inline]
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
let v: u8 = variant
.parse()
.map_err(|e| Error::EnumVariant(name, variant, e))?;
self.writer.write_all(&[v]).map_err(Error::from)
}
#[inline]
fn serialize_newtype_struct<T: ?Sized>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized>(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
let v: u8 = variant
.parse()
.map_err(|e| Error::EnumVariant(name, variant, e))?;
self.writer.write_all(&[v])?;
value.serialize(self)
}
fn serialize_tuple_variant(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
let v: u8 = variant
.parse()
.map_err(|e| Error::EnumVariant(name, variant, e))?;
self.writer.write_all(&[v])?;
Ok(self)
}
fn serialize_struct_variant(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
let v: u8 = variant
.parse()
.map_err(|e| Error::EnumVariant(name, variant, e))?;
self.writer.write_all(&[v])?;
Ok(self)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let len = len
.ok_or(Error::UnknownSequenceLength)
.and_then(|v| u8::try_from(v).map_err(|_| Error::SequenceTooLarge(v)))?;
self.writer.write_all(&[len])?;
Ok(self)
}
#[inline]
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(self)
}
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Ok(self)
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let len = len
.ok_or(Error::UnknownSequenceLength)
.and_then(|v| u8::try_from(v).map_err(|_| Error::SequenceTooLarge(v)))?;
self.writer.write_all(&[len])?;
Ok(self)
}
#[inline]
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(self)
}
#[inline]
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Display,
{
self.serialize_str(&value.to_string())
}
#[inline]
fn is_human_readable(&self) -> bool {
false
}
}
impl<'a, W: Write> ser::SerializeSeq for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, W: Write> ser::SerializeTuple for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, W: Write> ser::SerializeTupleStruct for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, W: Write> ser::SerializeTupleVariant for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, W: Write> ser::SerializeStruct for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T: ?Sized>(
&mut self,
_key: &'static str,
value: &T,
) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, W: Write> ser::SerializeStructVariant for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T: ?Sized>(
&mut self,
_key: &'static str,
value: &T,
) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, W: Write> ser::SerializeMap for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
key.serialize(&mut **self)
}
#[inline]
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(&mut **self)
}
fn serialize_entry<K: ?Sized, V: ?Sized>(
&mut self,
key: &K,
value: &V,
) -> Result<(), Self::Error>
where
K: Serialize,
V: Serialize,
{
self.serialize_key(key)?;
self.serialize_value(value)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::{borrow::Cow, collections::BTreeMap};
use serde::{Deserialize, Serialize};
use crate::{to_vec, to_writer, Error};
#[test]
fn empty_buffer() {
to_writer(&mut [][..], &0xcc1a_7e0e_31f4_eae4u64)
.expect_err("serialized data to empty buffer");
}
#[test]
fn bool() {
assert_eq!(to_vec(&true).unwrap(), &[1]);
assert_eq!(to_vec(&false).unwrap(), &[0]);
}
#[test]
fn integers() {
macro_rules! check {
($v:ident, $ty:ty) => {
// Casting an integer from a larger width to a smaller width will truncate the
// upper bits.
let v = $v as $ty;
let expected = v.to_be_bytes();
let actual = to_vec(&v).expect("failed to serialize integer");
assert_eq!(actual, &expected);
};
($v:ident, $($ty:ty),*) => {
$(
check!($v, $ty);
)*
};
}
// Value randomly generated from `dd if=/dev/urandom | xxd -p -l 16`.
let v = 0x46b1_265e_2f09_2e98_15c5_4c28_5c53_986cu128;
check!(v, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
}
#[test]
fn strings() {
let buf = "Löwe 老虎 Léopard";
let expected = IntoIterator::into_iter([buf.len() as u8])
.chain(buf.as_bytes().iter().copied())
.collect::<Vec<u8>>();
let actual = to_vec(buf).unwrap();
assert_eq!(expected, actual);
let actual = to_vec(&buf.to_string()).unwrap();
assert_eq!(expected, actual);
let actual = to_vec(&"").unwrap();
assert_eq!(&[0], &*actual);
}
#[test]
fn maps() {
let m = BTreeMap::from([
(0xb74909e6u32, 0xe3a9db9cu32),
(0x383c5309u32, 0x5c6b2d54u32),
]);
let expected = [
0x02, 0x38, 0x3c, 0x53, 0x09, 0x5c, 0x6b, 0x2d, 0x54, 0xb7, 0x49, 0x09, 0xe6, 0xe3,
0xa9, 0xdb, 0x9c,
];
let actual = to_vec(&m).unwrap();
assert_eq!(actual, expected);
let actual = to_vec(&BTreeMap::<u32, u32>::new()).unwrap();
assert_eq!(actual, [0]);
}
#[test]
fn empty_bytes() {
let expected = [0x0];
let actual = to_vec::<Vec<u16>>(&vec![]).unwrap();
assert_eq!(actual, &expected);
let actual = to_vec::<[u16]>(&[]).unwrap();
assert_eq!(actual, &expected);
}
#[test]
fn bytes() {
let expected = [
0x40, 0xff, 0x17, 0xc6, 0x15, 0xa4, 0x7c, 0x3c, 0x8c, 0xf3, 0x8b, 0x9f, 0x88, 0x31,
0xc4, 0x46, 0x07, 0xcb, 0xe9, 0x2d, 0xc4, 0x59, 0xaf, 0x34, 0x5a, 0x32, 0x66, 0x8c,
0x05, 0xc9, 0x3d, 0xab, 0x4f, 0xd2, 0x8a, 0xb6, 0x2e, 0x68, 0x58, 0x45, 0xef, 0x56,
0x27, 0x1a, 0xb6, 0xe0, 0x17, 0x47, 0xf3, 0xb8, 0x5d, 0xbd, 0x1b, 0x92, 0xd9, 0xdd,
0xe2, 0x99, 0x04, 0xbb, 0x67, 0xf7, 0x9f, 0xe1, 0xe1,
];
let actual = to_vec(&expected[1..].to_vec()).unwrap();
assert_eq!(actual, expected);
let actual = to_vec(&expected[1..]).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn max_bytes() {
let expected = [
0xff, 0x79, 0xce, 0xa2, 0x42, 0xd5, 0x8d, 0x0a, 0xaf, 0xa1, 0x72, 0x01, 0x92, 0xfc,
0x23, 0x4f, 0x80, 0x56, 0x9d, 0xff, 0x9d, 0x44, 0x30, 0xe3, 0x22, 0xb6, 0xd5, 0x11,
0xdd, 0xbe, 0x68, 0x6b, 0x34, 0x53, 0x5a, 0x97, 0x1b, 0x22, 0x96, 0x6c, 0xd5, 0xc6,
0x08, 0x2a, 0xf0, 0x1b, 0x74, 0x22, 0xe8, 0xdf, 0xcd, 0xad, 0xa0, 0x75, 0x43, 0x84,
0xf5, 0x43, 0x66, 0x38, 0x42, 0x66, 0xbb, 0xa1, 0x10, 0x54, 0x6a, 0x00, 0x4c, 0x9c,
0x0d, 0x53, 0xed, 0x72, 0xc7, 0x6c, 0x9c, 0x86, 0x75, 0xbe, 0x7d, 0xf3, 0x54, 0x70,
0x25, 0xda, 0x96, 0x9b, 0xc8, 0x6e, 0xc5, 0xc1, 0x56, 0xcf, 0x5a, 0x8d, 0xe1, 0x12,
0x8d, 0xd7, 0x06, 0x33, 0xc5, 0x25, 0xf2, 0x31, 0xa2, 0x42, 0x3b, 0xc8, 0x30, 0xc9,
0x1e, 0x51, 0xa5, 0x6a, 0x52, 0x0d, 0x6c, 0xbb, 0xc7, 0xde, 0x44, 0x8e, 0xe0, 0x80,
0x00, 0xcf, 0x4b, 0xf1, 0x5e, 0xff, 0x68, 0x9d, 0xb5, 0x13, 0xad, 0x71, 0x6a, 0x94,
0x0d, 0x68, 0x37, 0x7f, 0x68, 0x47, 0xf6, 0x03, 0xc5, 0x08, 0xf2, 0x47, 0x90, 0x7d,
0x29, 0xd8, 0xeb, 0x7d, 0xc2, 0xbb, 0xaa, 0xea, 0x0b, 0x1a, 0x73, 0x44, 0xd1, 0x35,
0x42, 0x79, 0xd8, 0x2b, 0x99, 0xbb, 0x75, 0xb7, 0xad, 0x54, 0xd3, 0xbb, 0x7b, 0xa3,
0x4d, 0x3a, 0xea, 0x74, 0xbe, 0x82, 0x40, 0xac, 0x63, 0x6e, 0x03, 0x38, 0x3c, 0x57,
0xa2, 0x02, 0x8b, 0x6c, 0xc9, 0x32, 0x9f, 0x6a, 0x35, 0x8f, 0x2d, 0x4e, 0x4d, 0xc6,
0x2b, 0x51, 0x08, 0x02, 0x35, 0x03, 0x45, 0xa1, 0x13, 0x0a, 0xad, 0x3c, 0x53, 0x90,
0x18, 0xe1, 0x89, 0xf2, 0xeb, 0xf1, 0x57, 0x2d, 0x32, 0xc1, 0x1a, 0x46, 0x8d, 0x72,
0xe4, 0x39, 0xbb, 0x75, 0xda, 0x85, 0xec, 0x8d, 0x98, 0x31, 0xf2, 0xfb, 0x20, 0x9a,
0x4e, 0x9c, 0xe6, 0x8c,
];
let actual = to_vec(&expected[1..].to_vec()).unwrap();
assert_eq!(actual, expected);
let actual = to_vec(&expected[1..]).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn data_too_large() {
let e =
to_vec(&vec![0u16; 300]).expect_err("serialized sequence with more than 255 entries");
assert!(matches!(e, Error::SequenceTooLarge(300)));
}
#[test]
fn tagged_enums() {
#[derive(Serialize, Deserialize)]
enum TestEnum<'a> {
#[serde(rename = "19")]
Unit,
#[serde(rename = "235")]
NewType(u64),
#[serde(rename = "179")]
Tuple(u32, u64, Vec<u16>),
#[serde(rename = "97")]
Struct {
#[serde(borrow, with = "serde_bytes")]
data: Cow<'a, [u8]>,
footer: u32,
},
}
assert_eq!(to_vec(&TestEnum::Unit).unwrap(), &[19]);
let expected = [235, 0xa7, 0xc5, 0x31, 0x9c, 0x8d, 0x87, 0x48, 0xd2];
assert_eq!(
to_vec(&TestEnum::NewType(0xa7c5319c8d8748d2)).unwrap(),
&expected
);
let expected = [
179, 0x60, 0xfb, 0x4d, 0x0d, 0xc4, 0x98, 0x40, 0x65, 0xf5, 0xdb, 0xbf, 0x3c, 0x05,
0xa9, 0xca, 0xb9, 0xe7, 0x96, 0x3b, 0x74, 0xfa, 0x82, 0xb2,
];
let value = TestEnum::Tuple(
0x60fb4d0d,
0xc4984065f5dbbf3c,
vec![0xa9ca, 0xb9e7, 0x963b, 0x74fa, 0x82b2],
);
assert_eq!(to_vec(&value).unwrap(), &expected);
let expected = [
97, 0x0b, 0xc2, 0xfd, 0xd6, 0xa1, 0xed, 0x8a, 0x12, 0x46, 0xd4, 0x20, 0xaf, 0xcc, 0x88,
0x8c, 0xd2,
];
let value = TestEnum::Struct {
data: Cow::Owned(vec![
0xc2, 0xfd, 0xd6, 0xa1, 0xed, 0x8a, 0x12, 0x46, 0xd4, 0x20, 0xaf,
]),
footer: 0xcc888cd2,
};
assert_eq!(to_vec(&value).unwrap(), &expected);
}
#[test]
fn complex_struct() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Address<'a> {
#[serde(borrow, with = "serde_bytes")]
bytes: Cow<'a, [u8]>,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Info<'a> {
#[serde(borrow)]
addrs: Vec<Address<'a>>,
expiration: u64,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Upgrade<'a> {
index: u32,
#[serde(borrow)]
info: Info<'a>,
}
let expected = [
0x00, 0x00, 0x00, 0x07, 0x0d, 0x14, 0x4f, 0x58, 0x50, 0x9e, 0xb6, 0x8b, 0x9d, 0x19,
0x9e, 0x00, 0x92, 0x5e, 0xcb, 0x0f, 0xfd, 0x53, 0x80, 0x06, 0xfe, 0xc3, 0x14, 0xb6,
0x7c, 0xd5, 0xef, 0x88, 0x00, 0xa7, 0xbc, 0xba, 0x2e, 0xfb, 0x91, 0x09, 0x33, 0xee,
0x51, 0xdd, 0x02, 0x24, 0x35, 0x14, 0x2b, 0x05, 0x87, 0x83, 0x8a, 0x2a, 0xe9, 0xc4,
0x0e, 0x54, 0x28, 0x11, 0xc2, 0x99, 0x33, 0xa8, 0x65, 0xd4, 0x6c, 0x3d, 0x14, 0x08,
0xd2, 0xb5, 0x03, 0x64, 0xb5, 0x27, 0x7f, 0xf0, 0xaf, 0x90, 0x6d, 0x03, 0x10, 0x21,
0xb3, 0x20, 0xdd, 0xfb, 0xda, 0x14, 0xec, 0xc9, 0x7d, 0x9d, 0x6c, 0x68, 0x4e, 0x43,
0x6e, 0x39, 0x51, 0xe0, 0xa8, 0x6f, 0x49, 0xf1, 0xf4, 0xd3, 0xdb, 0x2a, 0x14, 0x11,
0xed, 0x25, 0xe6, 0x6b, 0xed, 0x56, 0x25, 0x87, 0xb4, 0x1c, 0x94, 0x9c, 0x81, 0xcf,
0x2c, 0x34, 0xb8, 0x5e, 0xc3, 0x14, 0x3d, 0x82, 0xcb, 0x29, 0xe8, 0xa7, 0x34, 0x37,
0x3a, 0x46, 0x07, 0xa4, 0xf2, 0xb3, 0x94, 0xb0, 0x73, 0xed, 0x86, 0x3b, 0x14, 0x99,
0xa4, 0xb5, 0x89, 0x01, 0x59, 0x18, 0x01, 0x08, 0x53, 0xcf, 0x17, 0x21, 0x14, 0x65,
0xcf, 0x05, 0x7f, 0xaa, 0x5d, 0x14, 0xcc, 0x38, 0x3b, 0x85, 0xde, 0xc2, 0x59, 0xe6,
0x22, 0xee, 0xa4, 0xea, 0x83, 0x72, 0x08, 0x7e, 0xdf, 0xea, 0xe1, 0xc3, 0x14, 0x7a,
0xd9, 0x4d, 0x53, 0x9c, 0xc2, 0xff, 0xe3, 0x1d, 0xd6, 0x60, 0x78, 0x31, 0xb3, 0x2f,
0x76, 0x12, 0xb7, 0xc7, 0xaf, 0x14, 0x10, 0x88, 0xf6, 0x6f, 0x1d, 0x27, 0x2d, 0xad,
0x5b, 0x48, 0xca, 0xaf, 0xba, 0x63, 0x99, 0xbe, 0x23, 0x3b, 0xd5, 0xca, 0x14, 0x49,
0x91, 0xa9, 0x0f, 0x47, 0xcd, 0xfe, 0xdb, 0xd6, 0xfb, 0xb3, 0xe9, 0xa4, 0xc2, 0xc2,
0x15, 0xb3, 0xe7, 0xe5, 0xb6, 0x14, 0xdd, 0xe3, 0x77, 0xb0, 0xc3, 0x1b, 0x56, 0x2c,
0x90, 0x67, 0x88, 0xc6, 0xc5, 0xa5, 0xd8, 0xb8, 0xee, 0xc3, 0xa0, 0x87, 0x90, 0xe4,
0x9c, 0x5d, 0xcb, 0x20, 0x07, 0x92,
];
let value = Upgrade {
index: 7,
info: Info {
addrs: vec![
Address {
bytes: Cow::Borrowed(&expected[6..26]),
},
Address {
bytes: Cow::Borrowed(&expected[27..47]),
},
Address {
bytes: Cow::Borrowed(&expected[48..68]),
},
Address {
bytes: Cow::Borrowed(&expected[69..89]),
},
Address {
bytes: Cow::Borrowed(&expected[90..110]),
},
Address {
bytes: Cow::Borrowed(&expected[111..131]),
},
Address {
bytes: Cow::Borrowed(&expected[132..152]),
},
Address {
bytes: Cow::Borrowed(&expected[153..173]),
},
Address {
bytes: Cow::Borrowed(&expected[174..194]),
},
Address {
bytes: Cow::Borrowed(&expected[195..215]),
},
Address {
bytes: Cow::Borrowed(&expected[216..236]),
},
Address {
bytes: Cow::Borrowed(&expected[237..257]),
},
Address {
bytes: Cow::Borrowed(&expected[258..278]),
},
],
expiration: 0x90e4_9c5d_cb20_0792,
},
};
let actual = to_vec(&value).unwrap();
assert_eq!(actual, expected);
}
}