Compare commits

...

2 Commits

Author SHA1 Message Date
A5 Pickle 0fdd3f0ecf
solana: fix read; uptick 0.0.1-alpha.9 2024-01-30 19:40:07 -06:00
A5 Pickle edaafbb5f5
solana: payload is generic; uptick 0.0.1-alpha.8 2024-01-30 16:54:51 -06:00
5 changed files with 129 additions and 30 deletions

8
solana/Cargo.lock generated
View File

@ -2377,7 +2377,7 @@ dependencies = [
[[package]]
name = "wormhole-cctp-solana"
version = "0.0.1-alpha.7"
version = "0.0.1-alpha.9"
dependencies = [
"anchor-lang",
"anchor-spl",
@ -2393,7 +2393,7 @@ dependencies = [
[[package]]
name = "wormhole-circle-integration-solana"
version = "0.0.1-alpha.7"
version = "0.0.1-alpha.9"
dependencies = [
"ahash 0.8.6",
"anchor-lang",
@ -2424,9 +2424,9 @@ dependencies = [
[[package]]
name = "wormhole-io"
version = "0.1.1"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4965f46f7a99debe3c2cf9337c6e3eb7068da348aecf074a3e35686937f25c65"
checksum = "b021a14ea7bcef9517ed9f81d4466c4a663dd90e726c5724707a976fa83ad8f3"
[[package]]
name = "wormhole-raw-vaas"

View File

@ -7,7 +7,7 @@ resolver = "2"
[workspace.package]
edition = "2021"
version = "0.0.1-alpha.7"
version = "0.0.1-alpha.9"
authors = ["Wormhole Contributors"]
license = "Apache-2.0"
homepage = "https://wormhole.com"
@ -31,7 +31,7 @@ version = "0.28.0"
features = ["derive", "init-if-needed"]
[workspace.dependencies]
wormhole-io = "0.1.1"
wormhole-io = "0.1.3"
anchor-spl = "0.28.0"
solana-program = "1.16.16"
hex = "0.4.3"

View File

@ -1,9 +1,10 @@
use crate::{cctp, messages::Deposit, wormhole::core_bridge_program};
use anchor_lang::prelude::*;
use wormhole_io::TypePrefixedPayload;
/// Arguments used to burn Circle-supported tokens and publish a Wormhole Core Bridge message.
#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone)]
pub struct BurnAndPublishArgs {
pub struct BurnAndPublishArgs<P: TypePrefixedPayload> {
/// Token account where assets originated from. This pubkey is encoded in the [Deposit] message.
/// If this will be useful to an integrator, he should encode where the assets have been burned
/// from if it was not burned directly when calling [burn_and_publish].
@ -29,7 +30,7 @@ pub struct BurnAndPublishArgs {
/// Arbitrary payload, which can be used to encode instructions or data for another network's
/// smart contract.
pub payload: Vec<u8>,
pub payload: P,
}
/// Method to publish a Wormhole Core Bridge message alongside a CCTP message that burns a
@ -39,7 +40,7 @@ pub struct BurnAndPublishArgs {
/// assets originated from. A program calling this method will not necessarily be burning assets
/// from this token account directly. So this field is used to indicate the origin of the burned
/// assets.
pub fn burn_and_publish<'info>(
pub fn burn_and_publish<'info, P>(
cctp_ctx: CpiContext<
'_,
'_,
@ -48,8 +49,11 @@ pub fn burn_and_publish<'info>(
cctp::token_messenger_minter_program::cpi::DepositForBurnWithCaller<'info>,
>,
wormhole_ctx: CpiContext<'_, '_, '_, 'info, core_bridge_program::cpi::PostMessage<'info>>,
args: BurnAndPublishArgs,
) -> Result<u64> {
args: BurnAndPublishArgs<P>,
) -> Result<u64>
where
P: TypePrefixedPayload,
{
let BurnAndPublishArgs {
burn_source,
destination_caller,

View File

@ -7,7 +7,7 @@ use ruint::aliases::U256;
use wormhole_io::{Readable, TypePrefixedPayload, Writeable};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Deposit {
pub struct Deposit<P: TypePrefixedPayload> {
pub token_address: [u8; 32],
pub amount: U256,
pub source_cctp_domain: u32,
@ -16,14 +16,14 @@ pub struct Deposit {
pub burn_source: [u8; 32],
pub mint_recipient: [u8; 32],
/// NOTE: This payload length is encoded as u16.
pub payload: Vec<u8>,
pub payload: P,
}
impl TypePrefixedPayload for Deposit {
impl<P: TypePrefixedPayload> TypePrefixedPayload for Deposit<P> {
const TYPE: Option<u8> = Some(1);
}
impl Readable for Deposit {
impl<P: TypePrefixedPayload> Readable for Deposit<P> {
const SIZE: Option<usize> = None;
fn read<R>(reader: &mut R) -> io::Result<Self>
@ -40,10 +40,13 @@ impl Readable for Deposit {
let mint_recipient = Readable::read(reader)?;
let payload_len = u16::read(reader).map(usize::from)?;
let mut payload = Vec::with_capacity(payload_len);
reader.read_to_end(&mut payload)?;
if payload.len() == payload_len {
let payload = P::read_payload(reader)?;
if payload.payload_written_size() != payload_len {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"payload length mismatch",
));
} else {
Ok(Self {
token_address,
amount,
@ -54,15 +57,13 @@ impl Readable for Deposit {
mint_recipient,
payload,
})
} else {
Err(io::ErrorKind::InvalidData.into())
}
}
}
impl Writeable for Deposit {
impl<P: TypePrefixedPayload> Writeable for Deposit<P> {
fn written_size(&self) -> usize {
32 + 32 + 4 + 4 + 8 + 32 + 32 + 2 + self.payload.len()
32 + 32 + 4 + 4 + 8 + 32 + 32 + 2 + self.payload.payload_written_size()
}
fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
@ -77,10 +78,10 @@ impl Writeable for Deposit {
self.cctp_nonce.write(writer)?;
self.burn_source.write(writer)?;
self.mint_recipient.write(writer)?;
u16::try_from(self.payload.len())
u16::try_from(self.payload.payload_written_size())
.map_err(|_| std::io::ErrorKind::InvalidData.into())
.and_then(|len| len.write(writer))?;
writer.write_all(&self.payload)?;
self.payload.write_payload(writer)?;
Ok(())
}
}
@ -88,12 +89,67 @@ impl Writeable for Deposit {
#[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),
@ -104,7 +160,7 @@ mod test {
mint_recipient: hex!(
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
),
payload: b"All your base are belong to us.".to_vec(),
payload: payload.clone(),
};
let encoded = deposit.to_vec_payload();
@ -120,12 +176,15 @@ mod test {
cctp_nonce: parsed.cctp_nonce(),
burn_source: parsed.burn_source(),
mint_recipient: parsed.mint_recipient(),
payload: parsed.payload().to_vec(),
payload,
};
assert_eq!(deposit, expected);
// Check for other encoded parameters.
assert_eq!(parsed.payload_len(), deposit.payload.len() as u16);
assert_eq!(
usize::from(parsed.payload_len()),
deposit.payload.payload_written_size()
);
// TODO: Recover by calling read_payload.
}

View File

@ -4,7 +4,10 @@ use anchor_spl::token;
use wormhole_cctp_solana::{
cctp::{message_transmitter_program, token_messenger_minter_program},
utils::ExternalAccount,
wormhole::core_bridge_program,
wormhole::core_bridge_program::{
self,
sdk::io::{Readable, TypePrefixedPayload, Writeable},
},
};
/// Account context to invoke [transfer_tokens_with_payload].
@ -144,6 +147,39 @@ pub struct TransferTokensWithPayloadArgs {
pub payload: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct WrappedVec(Vec<u8>);
impl Readable for WrappedVec {
const SIZE: Option<usize> = None;
fn read<R>(reader: &mut R) -> std::io::Result<Self>
where
R: std::io::Read,
{
let mut out = vec![];
reader.read_to_end(&mut out)?;
Ok(Self(out))
}
}
impl Writeable for WrappedVec {
fn written_size(&self) -> usize {
self.0.len()
}
fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
where
W: std::io::Write,
{
writer.write_all(&self.0)
}
}
impl TypePrefixedPayload for WrappedVec {
const TYPE: Option<u8> = None;
}
/// This instruction invokes both Wormhole Core Bridge and CCTP Token Messenger Minter programs to
/// emit a Wormhole message associated with a CCTP message.
///
@ -231,7 +267,7 @@ pub fn transfer_tokens_with_payload(
amount,
mint_recipient,
wormhole_message_nonce,
payload,
payload: WrappedVec(payload),
},
)?;