Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solana: Multi Transceiver Support #528

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c0931e4
solana: Add transceivers as standalone program
nvsriram Sep 11, 2024
1f4c671
solana: re-export anchor client mods in config
kcsongor Sep 11, 2024
a714e4f
solana: enable idl-build for ntt-transceiver
kcsongor Sep 12, 2024
cce05bd
solana: Update Makefile to remove generics across all projects
nvsriram Sep 24, 2024
037d401
solana: Refactor messages to fix receive_message error
nvsriram Sep 24, 2024
d79a270
solana: Remove temp.json if jq command fails
nvsriram Oct 2, 2024
c94be9b
solana: Add zero-copy deserialization structs and helpers for `Transc…
nvsriram Oct 3, 2024
0786dc3
solana: Remove arbitrary bytes offset and use u8 slice instead
nvsriram Oct 3, 2024
beb913b
solana: Pass data slice for message deserialization
nvsriram Oct 3, 2024
ec3d1b7
solana: Remove unused lifetime
nvsriram Oct 3, 2024
b9b0644
solana: Complete AditionalPayload merge
nvsriram Oct 5, 2024
01e8b2d
solana: Add `mark_outbox_item_as_released` ix
nvsriram Oct 17, 2024
c79deec
solana: Update `release_outbound`to CPI into manager
nvsriram Oct 17, 2024
19e5202
solana: Fix lint warnings due to rebase
nvsriram Nov 8, 2024
f60f181
solana: Add `transceiver_type` ix to `ntt-transceiver` program
nvsriram Nov 12, 2024
b563f3a
solana: Bump idl version to 3_0_0
nvsriram Nov 11, 2024
c9f9590
solana: Add transceivers as standalone program
nvsriram Sep 11, 2024
f44b53e
solana: re-export anchor client mods in config
kcsongor Sep 11, 2024
16ee73e
solana: enable idl-build for ntt-transceiver
kcsongor Sep 12, 2024
adac7cf
solana: Update Makefile to remove generics across all projects
nvsriram Sep 24, 2024
e3c2d16
solana: Refactor messages to fix receive_message error
nvsriram Sep 24, 2024
fad5d0c
solana: Remove temp.json if jq command fails
nvsriram Oct 2, 2024
4ab766e
solana: Add zero-copy deserialization structs and helpers for `Transc…
nvsriram Oct 3, 2024
39b4a2f
solana: Remove arbitrary bytes offset and use u8 slice instead
nvsriram Oct 3, 2024
b7ff6b7
solana: Pass data slice for message deserialization
nvsriram Oct 3, 2024
b67a902
solana: Remove unused lifetime
nvsriram Oct 3, 2024
011c706
solana: Complete AditionalPayload merge
nvsriram Oct 5, 2024
984e2c0
solana: Add `mark_outbox_item_as_released` ix
nvsriram Oct 17, 2024
1e37f2a
solana: Update `release_outbound`to CPI into manager
nvsriram Oct 17, 2024
128249b
solana: Fix lint warnings due to rebase
nvsriram Nov 8, 2024
9778fad
solana: Add `transceiver_type` ix to `ntt-transceiver` program
nvsriram Nov 12, 2024
a3517b1
solana: Bump idl version to 3_0_0
nvsriram Nov 11, 2024
350f989
Merge branch 'solana/multi-transceiver-support' of https://github.com…
nvsriram Nov 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions solana/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ skip-lint = false
[programs.localnet]
dummy_transfer_hook = "BgabMDLaxsyB7eGMBt9L22MSk9KMrL4zY2iNe14kyFP5"
example_native_token_transfers = "nttiK1SepaQt6sZ4WGW5whvc9tEnGXGxuKeptcQPCcS"
ntt-transceiver = "Ee6jpX9oq2EsGuqGb6iZZxvtcpmMGZk8SAUbnQy4jcHR"
ntt_quoter = "9jFBLvMZZERVmeY4tbq5MejbXRE18paGEuoB6xVJZgGe"
wormhole_governance = "wgvEiKVzX9yyEoh41jZAdC6JqGUTS4CFXbFGBV5TKdZ"

Expand Down
25 changes: 20 additions & 5 deletions solana/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions solana/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ cargo-build:
# because the javascript library does not support generics yet, and just panics
anchor-build:
anchor build --arch sbf
@echo "Removing generics from target/idl/example_native_token_transfers.json"
./scripts/patch-idl target/idl/example_native_token_transfers.json
for jsonfile in target/idl/*.json; do \
echo "Removing generics from" $$jsonfile; \
./scripts/patch-idl $$jsonfile; \
done

prod-build:
anchor build --verifiable
Expand Down
2 changes: 1 addition & 1 deletion solana/modules/ntt-messages/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ntt-messages"
version = "2.0.0"
version = "3.0.0"
edition = "2021"

[features]
Expand Down
37 changes: 37 additions & 0 deletions solana/modules/ntt-messages/src/transceiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,43 @@ pub struct TransceiverMessageData<A: MaybeSpace> {
pub ntt_manager_payload: NttManagerMessage<A>,
}

/// This struct is for zero-copy deserialization of
/// `ValidatedTransceiverMessage::message()` in the redeem ix
pub struct TransceiverMessageDataBytes<'a, A: MaybeSpace> {
_phantom: PhantomData<A>,
span: &'a [u8],
}

impl<A: MaybeSpace> AsRef<[u8]> for TransceiverMessageDataBytes<'_, A> {
fn as_ref(&self) -> &[u8] {
self.span
}
}

impl<'a, A: MaybeSpace> TransceiverMessageDataBytes<'a, A> {
pub fn source_ntt_manager(&self) -> [u8; 32] {
self.span[..32].try_into().unwrap()
}

pub fn recipient_ntt_manager(&self) -> [u8; 32] {
self.span[32..64].try_into().unwrap()
}

pub fn ntt_manager_payload(&self) -> NttManagerMessage<A>
where
A: AnchorDeserialize,
{
NttManagerMessage::deserialize(&mut &self.span[64..]).unwrap()
}

pub fn parse(span: &'a [u8]) -> TransceiverMessageDataBytes<'a, A> {
TransceiverMessageDataBytes {
_phantom: PhantomData,
span,
}
}
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct TransceiverMessage<E: Transceiver, A: MaybeSpace> {
_phantom: PhantomData<E>,
Expand Down
2 changes: 1 addition & 1 deletion solana/programs/dummy-transfer-hook/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "dummy-transfer-hook"
version = "2.0.0"
version = "3.0.0"
description = "Created with Anchor"
edition = "2021"

Expand Down
2 changes: 1 addition & 1 deletion solana/programs/example-native-token-transfers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "example-native-token-transfers"
version = "2.0.0"
version = "3.0.0"
description = "Example implementation of native token transfer standard"
edition = "2021"

Expand Down
17 changes: 17 additions & 0 deletions solana/programs/example-native-token-transfers/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ use ntt_messages::{chain_id::ChainId, mode::Mode};

use crate::bitmap::Bitmap;

/// This is a hack to re-export some modules that anchor generates as
/// pub(crate), as it's not possible to directly re-export a module with a
/// relaxed visibility.
/// Instead, we define public modules with the *same* name, and pub use all the
/// members of the original.
/// Within this crate, this module should not be used. Outside of this crate,
/// importing `anchor_reexports::*` achieves what we want.
pub mod anchor_reexports {
pub mod __cpi_client_accounts_not_paused_config {
pub use super::super::__cpi_client_accounts_not_paused_config::*;
}

pub mod __client_accounts_not_paused_config {
pub use super::super::__client_accounts_not_paused_config::*;
}
}

#[account]
#[derive(InitSpace)]
pub struct Config {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::{
config::*, error::NTTError, queue::outbox::OutboxItem,
registered_transceiver::RegisteredTransceiver,
};
use anchor_lang::prelude::*;

pub const OUTBOX_ITEM_SIGNER_SEED: &[u8] = b"outbox_item_signer";

#[derive(Accounts)]
pub struct MarkOutboxItemAsReleased<'info> {
#[account(
seeds = [OUTBOX_ITEM_SIGNER_SEED],
seeds::program = transceiver.transceiver_address,
bump
)]
pub signer: Signer<'info>,

pub config: NotPausedConfig<'info>,

#[account(
mut,
constraint = !outbox_item.released.get(transceiver.id)? @ NTTError::MessageAlreadySent,
)]
pub outbox_item: Account<'info, OutboxItem>,

#[account(
constraint = config.enabled_transceivers.get(transceiver.id)? @ NTTError::DisabledTransceiver
)]
pub transceiver: Account<'info, RegisteredTransceiver>,
}

pub fn mark_outbox_item_as_released(ctx: Context<MarkOutboxItemAsReleased>) -> Result<bool> {
let accs = ctx.accounts;
let released = accs.outbox_item.try_release(accs.transceiver.id)?;
Ok(released)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
pub mod admin;
pub mod initialize;
pub mod luts;
pub mod mark_outbox_item_as_released;
pub mod redeem;
pub mod release_inbound;
pub mod transfer;

pub use admin::*;
pub use initialize::*;
pub use luts::*;
pub use mark_outbox_item_as_released::*;
pub use redeem::*;
pub use release_inbound::*;
pub use transfer::*;
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,29 @@ pub struct Redeem<'info> {

// NOTE: this works when the contract is paused
#[account(
constraint = config.threshold > 0 @ NTTError::ZeroThreshold,
constraint = config.threshold > 0 @ NTTError::ZeroThreshold
)]
pub config: Account<'info, Config>,

#[account(
seeds = [NttManagerPeer::SEED_PREFIX, transceiver_message.from_chain.id.to_be_bytes().as_ref()],
constraint = peer.address == transceiver_message.message.source_ntt_manager @ NTTError::InvalidNttManagerPeer,
seeds = [NttManagerPeer::SEED_PREFIX, ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::from_chain(&transceiver_message)?.id.to_be_bytes().as_ref()],
constraint = peer.address == ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::message(&transceiver_message.try_borrow_data()?[..])?.source_ntt_manager() @ NTTError::InvalidNttManagerPeer,
bump = peer.bump,
)]
pub peer: Account<'info, NttManagerPeer>,

#[account(
// check that the message is targeted to this chain
constraint = transceiver_message.message.ntt_manager_payload.payload.to_chain == config.chain_id @ NTTError::InvalidChainId,
constraint = ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::message(&transceiver_message.try_borrow_data()?[..])?.ntt_manager_payload().payload.to_chain == config.chain_id @ NTTError::InvalidChainId,
// check that we're the intended recipient
constraint = transceiver_message.message.recipient_ntt_manager == crate::ID.to_bytes() @ NTTError::InvalidRecipientNttManager,
constraint = ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::message(&transceiver_message.try_borrow_data()?[..])?.recipient_ntt_manager() == crate::ID.to_bytes() @ NTTError::InvalidRecipientNttManager,
// NOTE: we don't replay protect VAAs. Instead, we replay protect
// executing the messages themselves with the [`released`] flag.
owner = transceiver.transceiver_address,
)]
pub transceiver_message:
Account<'info, ValidatedTransceiverMessage<NativeTokenTransfer<Payload>>>,
/// CHECK: `transceiver_message` has to be manually deserialized as Anchor
/// `Account<T>` and `owner` constraints are mutually-exclusive
pub transceiver_message: UncheckedAccount<'info>,

#[account(
constraint = config.enabled_transceivers.get(transceiver.id)? @ NTTError::DisabledTransceiver
Expand All @@ -63,8 +64,8 @@ pub struct Redeem<'info> {
space = 8 + InboxItem::INIT_SPACE,
seeds = [
InboxItem::SEED_PREFIX,
transceiver_message.message.ntt_manager_payload.keccak256(
transceiver_message.from_chain
ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::message(&transceiver_message.try_borrow_data()?[..])?.ntt_manager_payload().keccak256(
ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::from_chain(&transceiver_message)?
).as_ref(),
],
bump,
Expand All @@ -87,7 +88,7 @@ pub struct Redeem<'info> {
mut,
seeds = [
InboxRateLimit::SEED_PREFIX,
transceiver_message.from_chain.id.to_be_bytes().as_ref(),
ValidatedTransceiverMessage::<NativeTokenTransfer<Payload>>::from_chain(&transceiver_message)?.id.to_be_bytes().as_ref(),
],
bump,
)]
Expand All @@ -105,8 +106,13 @@ pub struct RedeemArgs {}
pub fn redeem(ctx: Context<Redeem>, _args: RedeemArgs) -> Result<()> {
let accs = ctx.accounts;

let transceiver_message: ValidatedTransceiverMessage<NativeTokenTransfer<Payload>> =
ValidatedTransceiverMessage::try_from(
&accs.transceiver_message,
&accs.transceiver.transceiver_address,
)?;
let message: NttManagerMessage<NativeTokenTransfer<Payload>> =
accs.transceiver_message.message.ntt_manager_payload.clone();
transceiver_message.message.ntt_manager_payload.clone();

// Calculate the scaled amount based on the appropriate decimal encoding for the token.
// Return an error if the resulting amount overflows.
Expand Down
6 changes: 5 additions & 1 deletion solana/programs/example-native-token-transfers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub const TOKEN_AUTHORITY_SEED: &[u8] = b"token_authority";
/// user, atomically).
pub const SESSION_AUTHORITY_SEED: &[u8] = b"session_authority";

pub const VERSION: &str = "2.0.0";
pub const VERSION: &str = "3.0.0";

#[program]
pub mod example_native_token_transfers {
Expand Down Expand Up @@ -152,6 +152,10 @@ pub mod example_native_token_transfers {
instructions::set_inbound_limit(ctx, args)
}

pub fn mark_outbox_item_as_released(ctx: Context<MarkOutboxItemAsReleased>) -> Result<bool> {
instructions::mark_outbox_item_as_released(ctx)
}

// standalone transceiver stuff

pub fn set_wormhole_peer(
Expand Down
46 changes: 44 additions & 2 deletions solana/programs/example-native-token-transfers/src/messages.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use anchor_lang::prelude::*;
use ntt_messages::{chain_id::ChainId, transceiver::TransceiverMessageData};
use anchor_lang::{prelude::*, system_program, Discriminator};
use ntt_messages::{
chain_id::ChainId,
transceiver::{TransceiverMessageData, TransceiverMessageDataBytes},
};
use std::{collections::HashMap, marker::PhantomData};

#[account]
Expand All @@ -11,6 +14,45 @@ pub struct ValidatedTransceiverMessage<A: AnchorDeserialize + AnchorSerialize +

impl<A: AnchorDeserialize + AnchorSerialize + Space + Clone> ValidatedTransceiverMessage<A> {
pub const SEED_PREFIX: &'static [u8] = b"transceiver_message";

pub fn try_from(info: &UncheckedAccount, expected_owner: &Pubkey) -> Result<Self> {
if info.owner == &system_program::ID && info.lamports() == 0 {
return Err(ErrorCode::AccountNotInitialized.into());
}
if *info.owner != *expected_owner {
return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
.with_pubkeys((*info.owner, *expected_owner)));
}
let mut data: &[u8] = &info.try_borrow_data()?;
ValidatedTransceiverMessage::try_deserialize(&mut data)
}

pub fn from_chain(info: &UncheckedAccount) -> Result<ChainId> {
let data: &[u8] = &info.try_borrow_data().unwrap();
if data.len() < ValidatedTransceiverMessage::<A>::DISCRIMINATOR.len() {
return Err(ErrorCode::AccountDiscriminatorNotFound.into());
}
let given_disc = &data[..8];
if Self::DISCRIMINATOR != given_disc {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(ChainId {
// This is LE bytes because we deserialize using Borsh.
// Not to be confused with the wire format (which is BE bytes)
id: u16::from_le_bytes(data[8..10].try_into().unwrap()),
})
}

pub fn message(data: &[u8]) -> Result<TransceiverMessageDataBytes<A>> {
if data.len() < ValidatedTransceiverMessage::<A>::DISCRIMINATOR.len() {
return Err(ErrorCode::AccountDiscriminatorNotFound.into());
}
let given_disc = &data[..8];
if Self::DISCRIMINATOR != given_disc {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(TransceiverMessageDataBytes::parse(&data[10..]))
}
}

// This is a hack to get around the fact that the IDL generator doesn't support
Expand Down
2 changes: 1 addition & 1 deletion solana/programs/ntt-quoter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ntt-quoter"
version = "2.0.0"
version = "3.0.0"
edition = "2021"

[lib]
Expand Down
Loading
Loading