Skip to content

Commit

Permalink
solana: broadcast transceiver info
Browse files Browse the repository at this point in the history
  • Loading branch information
kcsongor committed Feb 27, 2024
1 parent 4dbdfd7 commit 9762257
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 14 deletions.
143 changes: 142 additions & 1 deletion solana/modules/ntt-messages/src/transceivers/wormhole.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,149 @@
use crate::transceiver::Transceiver;
use std::io;

use wormhole_io::{Readable, TypePrefixedPayload, Writeable};

#[cfg(feature = "anchor")]
use anchor_lang::prelude::*;

use crate::{chain_id::ChainId, mode::Mode, transceiver::Transceiver};

#[derive(PartialEq, Eq, Clone, Debug)]
pub struct WormholeTransceiver {}

impl Transceiver for WormholeTransceiver {
const PREFIX: [u8; 4] = [0x99, 0x45, 0xFF, 0x10];
}

impl WormholeTransceiver {
pub const INFO_PREFIX: [u8; 4] = [0x9c, 0x23, 0xbd, 0x3b];

pub const PEER_INFO_PREFIX: [u8; 4] = [0x18, 0xfc, 0x67, 0xc2];
}

// * Transceiver info

#[derive(Debug, Clone)]
pub struct WormholeTransceiverInfo {
pub manager_address: [u8; 32],
pub manager_mode: Mode,
pub token_address: [u8; 32],
pub token_decimals: u8,
}

impl Readable for WormholeTransceiverInfo {
const SIZE: Option<usize> = Some(32 + 1 + 32 + 1);

fn read<R>(reader: &mut R) -> std::io::Result<Self>
where
Self: Sized,
R: std::io::Read,
{
let prefix = <[u8; 4]>::read(reader)?;
if prefix != WormholeTransceiver::INFO_PREFIX {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid prefix",
));
}

let manager_address = <[u8; 32]>::read(reader)?;
let manager_mode = Mode::read(reader)?;
let token_address = <[u8; 32]>::read(reader)?;
let token_decimals = u8::read(reader)?;

Ok(WormholeTransceiverInfo {
manager_address,
manager_mode,
token_address,
token_decimals,
})
}
}

impl Writeable for WormholeTransceiverInfo {
fn written_size(&self) -> usize {
WormholeTransceiverInfo::SIZE.unwrap()
}

fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
where
W: std::io::Write,
{
WormholeTransceiver::INFO_PREFIX.write(writer)?;
self.manager_address.write(writer)?;
self.manager_mode.write(writer)?;
self.token_address.write(writer)?;
self.token_decimals.write(writer)
}
}

impl TypePrefixedPayload for WormholeTransceiverInfo {
const TYPE: Option<u8> = None;
}

// * Transceiver registration

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WormholeTransceiverRegistration {
pub chain_id: ChainId,
pub transceiver_address: [u8; 32],
}

#[cfg(feature = "anchor")]
impl AnchorDeserialize for WormholeTransceiverRegistration {
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
Readable::read(reader)
}
}

#[cfg(feature = "anchor")]
impl AnchorSerialize for WormholeTransceiverRegistration {
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
Writeable::write(self, writer)
}
}

impl Readable for WormholeTransceiverRegistration {
const SIZE: Option<usize> = Some(2 + 32);

fn read<R>(reader: &mut R) -> std::io::Result<Self>
where
Self: Sized,
R: std::io::Read,
{
let prefix = <[u8; 4]>::read(reader)?;
if prefix != WormholeTransceiver::PEER_INFO_PREFIX {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid prefix",
));
}

let chain_id = ChainId::read(reader)?;
let transceiver_address = <[u8; 32]>::read(reader)?;

Ok(WormholeTransceiverRegistration {
chain_id,
transceiver_address,
})
}
}

impl Writeable for WormholeTransceiverRegistration {
fn written_size(&self) -> usize {
WormholeTransceiverRegistration::SIZE.unwrap()
}

fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
where
W: std::io::Write,
{
WormholeTransceiver::PEER_INFO_PREFIX.write(writer)?;
self.chain_id.write(writer)?;
self.transceiver_address.write(writer)
}
}

impl TypePrefixedPayload for WormholeTransceiverRegistration {
const TYPE: Option<u8> = None;
}
11 changes: 11 additions & 0 deletions solana/programs/example-native-token-transfers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,15 @@ pub mod example_native_token_transfers {
) -> Result<()> {
transceivers::wormhole::instructions::release_outbound(ctx, args)
}

pub fn broadcast_wormhole_id(ctx: Context<BroadcastId>) -> Result<()> {
transceivers::wormhole::instructions::broadcast_id(ctx)
}

pub fn broadcast_wormhole_peer(
ctx: Context<BroadcastPeer>,
args: BroadcastPeerArgs,
) -> Result<()> {
transceivers::wormhole::instructions::broadcast_peer(ctx, args)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use anchor_lang::prelude::*;
use anchor_spl::token_interface;
use ntt_messages::transceivers::wormhole::WormholeTransceiverInfo;

use crate::{config::*, transceivers::wormhole::accounts::*};

#[derive(Accounts)]
pub struct BroadcastId<'info> {
#[account(mut)]
pub payer: Signer<'info>,

pub config: Account<'info, Config>,

#[account(
address = config.mint,
)]
pub mint: InterfaceAccount<'info, token_interface::Mint>,

/// CHECK: initialized and written to by wormhole core bridge
#[account(mut)]
pub wormhole_message: Signer<'info>,

#[account(
mut,
seeds = [b"emitter"],
bump
)]
pub emitter: UncheckedAccount<'info>,

pub wormhole: WormholeAccounts<'info>,
}

pub fn broadcast_id(ctx: Context<BroadcastId>) -> Result<()> {
let accs = ctx.accounts;
let message = WormholeTransceiverInfo {
manager_address: accs.config.to_account_info().owner.to_bytes(),
manager_mode: accs.config.mode,
token_address: accs.mint.to_account_info().key.to_bytes(),
token_decimals: accs.mint.decimals,
};

// TODO: should we send this as an unreliable message into a PDA?
post_message(
&accs.wormhole,
accs.payer.to_account_info(),
accs.wormhole_message.to_account_info(),
accs.emitter.to_account_info(),
ctx.bumps.emitter,
&message,
&[],
)?;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use anchor_lang::prelude::*;
use ntt_messages::{chain_id::ChainId, transceivers::wormhole::WormholeTransceiverRegistration};

use crate::{
config::*,
transceivers::{accounts::peer::TransceiverPeer, wormhole::accounts::*},
};

#[derive(Accounts)]
#[instruction(args: BroadcastPeerArgs)]
pub struct BroadcastPeer<'info> {
#[account(mut)]
pub payer: Signer<'info>,

pub config: Account<'info, Config>,

#[account(
seeds = [TransceiverPeer::SEED_PREFIX, args.chain_id.to_be_bytes().as_ref()],
bump
)]
pub peer: Account<'info, TransceiverPeer>,

/// CHECK: initialized and written to by wormhole core bridge
#[account(mut)]
pub wormhole_message: Signer<'info>,

#[account(
mut,
seeds = [b"emitter"],
bump
)]
pub emitter: UncheckedAccount<'info>,

pub wormhole: WormholeAccounts<'info>,
}

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct BroadcastPeerArgs {
pub chain_id: u16,
}

pub fn broadcast_peer(ctx: Context<BroadcastPeer>, args: BroadcastPeerArgs) -> Result<()> {
let accs = ctx.accounts;

let message = WormholeTransceiverRegistration {
chain_id: ChainId { id: args.chain_id },
transceiver_address: accs.peer.address
};

// TODO: should we send this as an unreliable message into a PDA?
post_message(
&accs.wormhole,
accs.payer.to_account_info(),
accs.wormhole_message.to_account_info(),
accs.emitter.to_account_info(),
ctx.bumps.emitter,
&message,
&[],
)?;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
pub mod admin;
pub mod broadcast_id;
pub mod broadcast_peer;
pub mod receive_message;
pub mod release_outbound;

pub use admin::*;
pub use broadcast_id::*;
pub use broadcast_peer::*;
pub use receive_message::*;
pub use release_outbound::*;
51 changes: 51 additions & 0 deletions solana/programs/example-native-token-transfers/tests/broadcast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#![cfg(feature = "test-sbf")]
#![feature(type_changing_struct_update)]

use common::setup::OTHER_CHAIN;
use ntt_messages::chain_id::ChainId;
use ntt_messages::mode::Mode;
use ntt_messages::transceivers::wormhole::WormholeTransceiverRegistration;
use solana_program_test::*;
use solana_sdk::{signature::Keypair, signer::Signer};
use wormhole_anchor_sdk::wormhole::PostedVaa;

use crate::common::query::GetAccountDataAnchor;
use crate::common::setup::{setup, OTHER_TRANSCEIVER};
use crate::{
common::submit::Submittable,
sdk::transceivers::wormhole::instructions::broadcast_peer::{broadcast_peer, BroadcastPeer},
};

pub mod common;
pub mod sdk;

#[tokio::test]
async fn test_broadcast_peer() {
let (mut ctx, test_data) = setup(Mode::Locking).await;

let wh_message = Keypair::new();

broadcast_peer(
&test_data.ntt,
BroadcastPeer {
payer: ctx.payer.pubkey(),
wormhole_message: wh_message.pubkey(),
chain_id: OTHER_CHAIN,
},
)
.submit_with_signers(&[&wh_message], &mut ctx)
.await
.unwrap();

let msg: PostedVaa<WormholeTransceiverRegistration> = ctx
.get_account_data_anchor_unchecked(wh_message.pubkey())
.await;

assert_eq!(
*msg.data(),
WormholeTransceiverRegistration {
chain_id: ChainId { id: OTHER_CHAIN },
transceiver_address: OTHER_TRANSCEIVER
}
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod wormhole;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use anchor_lang::prelude::*;
use example_native_token_transfers::accounts::WormholeAccounts;
use solana_sdk::sysvar::SysvarId;

use crate::sdk::accounts::NTT;

pub fn wormhole_accounts(ntt: &NTT) -> WormholeAccounts {
WormholeAccounts {
bridge: ntt.wormhole.bridge(),
fee_collector: ntt.wormhole.fee_collector(),
sequence: ntt.wormhole_sequence(),
program: ntt.wormhole.program,
system_program: System::id(),
clock: Clock::id(),
rent: Rent::id(),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading

0 comments on commit 9762257

Please sign in to comment.