wormhole-circle-integration/solana/modules/wormhole-cctp/src/messages/deposit.rs

187 lines
5.4 KiB
Rust

//! Messages relevant to the Token Bridge across all networks. These messages are serialized and
//! then published via the Core Bridge program.
use std::io;
use ruint::aliases::U256;
use wormhole_io::{Readable, TypePrefixedPayload, Writeable};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Deposit {
pub token_address: [u8; 32],
pub amount: U256,
pub source_cctp_domain: u32,
pub destination_cctp_domain: u32,
pub cctp_nonce: u64,
pub burn_source: [u8; 32],
pub mint_recipient: [u8; 32],
/// NOTE: This payload length is encoded as u16.
pub payload: Vec<u8>,
}
impl TypePrefixedPayload for Deposit {
const TYPE: Option<u8> = Some(1);
}
impl Readable for Deposit {
const SIZE: Option<usize> = None;
fn read<R>(reader: &mut R) -> io::Result<Self>
where
Self: Sized,
R: io::Read,
{
let token_address = Readable::read(reader)?;
let amount = <[u8; 32]>::read(reader).map(U256::from_be_bytes)?;
let source_cctp_domain = Readable::read(reader)?;
let destination_cctp_domain = Readable::read(reader)?;
let cctp_nonce = Readable::read(reader)?;
let burn_source = Readable::read(reader)?;
let mint_recipient = Readable::read(reader)?;
let payload_len = u16::read(reader).map(usize::from)?;
let mut payload = vec![0u8; payload_len];
reader.read_exact(&mut payload)?;
Ok(Self {
token_address,
amount,
source_cctp_domain,
destination_cctp_domain,
cctp_nonce,
burn_source,
mint_recipient,
payload,
})
}
}
impl Writeable for Deposit {
fn written_size(&self) -> usize {
32 + 32 + 4 + 4 + 8 + 32 + 32 + 2 + self.payload.len()
}
fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
where
Self: Sized,
W: std::io::Write,
{
self.token_address.write(writer)?;
self.amount.to_be_bytes::<32>().write(writer)?;
self.source_cctp_domain.write(writer)?;
self.destination_cctp_domain.write(writer)?;
self.cctp_nonce.write(writer)?;
self.burn_source.write(writer)?;
self.mint_recipient.write(writer)?;
u16::try_from(self.payload.len())
.map_err(|_| std::io::ErrorKind::InvalidData.into())
.and_then(|len| len.write(writer))?;
writer.write_all(&self.payload)?;
Ok(())
}
}
#[cfg(test)]
mod test {
use hex_literal::hex;
use wormhole_io::WriteableBytes;
use wormhole_raw_vaas::cctp;
use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
struct AllYourBase {
pub are: u16,
pub belong: u32,
pub to: u64,
pub us: WriteableBytes,
}
impl TypePrefixedPayload for AllYourBase {
const TYPE: Option<u8> = Some(69);
}
impl Readable for AllYourBase {
const SIZE: Option<usize> = None;
fn read<R>(reader: &mut R) -> io::Result<Self>
where
Self: Sized,
R: io::Read,
{
Ok(Self {
are: Readable::read(reader)?,
belong: Readable::read(reader)?,
to: Readable::read(reader)?,
us: Readable::read(reader)?,
})
}
}
impl Writeable for AllYourBase {
fn written_size(&self) -> usize {
2 + 4 + 8 + self.us.written_size()
}
fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
where
Self: Sized,
W: std::io::Write,
{
self.are.write(writer)?;
self.belong.write(writer)?;
self.to.write(writer)?;
self.us.write(writer)?;
Ok(())
}
}
#[test]
fn serde() {
let payload = AllYourBase {
are: 42,
belong: 1337,
to: 9001,
us: b"Beep boop".to_vec().into(),
};
let deposit = Deposit {
token_address: hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
amount: U256::from(69420u64),
source_cctp_domain: 5,
destination_cctp_domain: 1,
cctp_nonce: 69,
burn_source: hex!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
mint_recipient: hex!(
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
),
payload: payload.to_vec_payload(),
};
let encoded = deposit.to_vec_payload();
let msg = cctp::WormholeCctpMessage::parse(&encoded).unwrap();
let parsed = msg.deposit().unwrap();
let expected = Deposit {
token_address: parsed.token_address(),
amount: U256::from_be_bytes(parsed.amount()),
source_cctp_domain: parsed.source_cctp_domain(),
destination_cctp_domain: parsed.destination_cctp_domain(),
cctp_nonce: parsed.cctp_nonce(),
burn_source: parsed.burn_source(),
mint_recipient: parsed.mint_recipient(),
payload: payload.to_vec_payload(),
};
assert_eq!(deposit, expected);
// Check for other encoded parameters.
assert_eq!(
usize::from(parsed.payload_len()),
payload.payload_written_size()
);
// TODO: Recover by calling read_payload.
}
}