Skip to content

Commit

Permalink
solana: token bridge clean-up
Browse files Browse the repository at this point in the history
  • Loading branch information
a5-pickle committed Oct 2, 2023
1 parent 0072a7e commit 34925c1
Show file tree
Hide file tree
Showing 15 changed files with 68 additions and 64 deletions.
12 changes: 10 additions & 2 deletions solana/programs/token-bridge/src/legacy/processor/attest_token.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{legacy::instruction::LegacyAttestTokenArgs, utils, zero_copy::Mint};
use crate::{
error::TokenBridgeError, legacy::instruction::LegacyAttestTokenArgs, utils, zero_copy::Mint,
};
use anchor_lang::prelude::*;
use anchor_spl::metadata;
use core_bridge_program::sdk::{self as core_bridge_sdk, LoadZeroCopy};
Expand Down Expand Up @@ -148,7 +150,13 @@ impl<'info> AttestToken<'info> {
fn constraints(ctx: &Context<Self>) -> Result<()> {
// Make sure the mint authority is not the Token Bridge's. If it is, then this mint
// originated from a foreign network.
crate::utils::require_native_mint(&ctx.accounts.mint)
let mint = Mint::load(&ctx.accounts.mint)?;
require!(
!crate::utils::is_wrapped_mint(&mint),
TokenBridgeError::WrappedAsset
);

Ok(())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ use core_bridge_program::{
};
use wormhole_raw_vaas::token_bridge::TokenBridgeMessage;

pub fn validate_posted_token_transfer(
pub fn validate_token_transfer_vaa(
vaa_acc_info: &AccountInfo,
registered_emitter: &Account<LegacyAnchorized<0, RegisteredEmitter>>,
recipient_token: &AccountInfo,
recipient: &Option<AccountInfo>,
) -> Result<(u16, [u8; 32])> {
let vaa_key = vaa_acc_info.key();
let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?;
let msg =
crate::utils::require_valid_posted_token_bridge_vaa(&vaa_key, &vaa, registered_emitter)?;
let msg = crate::utils::require_valid_token_bridge_vaa(&vaa_key, &vaa, registered_emitter)?;

let transfer = if let TokenBridgeMessage::Transfer(inner) = msg {
inner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct CompleteTransferNative<'info> {
/// allows registering multiple emitter addresses for the same chain ID. These seeds are not
/// checked via Anchor macro, but will be checked in the access control function instead.
///
/// See the `require_valid_token_bridge_posted_vaa` instruction handler for more details.
/// See the `require_valid_token_bridge_vaa` instruction handler for more details.
registered_emitter: Account<'info, LegacyAnchorized<0, RegisteredEmitter>>,

/// CHECK: Recipient token account. Because we check the mint of the custody token account, we
Expand Down Expand Up @@ -139,9 +139,13 @@ impl<'info> CompleteTransferNative<'info> {

// Make sure the mint authority is not the Token Bridge's. If it is, then this mint
// originated from a foreign network.
crate::utils::require_native_mint(&ctx.accounts.mint)?;
let mint = Mint::load(&ctx.accounts.mint)?;
require!(
!crate::utils::is_wrapped_mint(&mint),
TokenBridgeError::WrappedAsset
);

let (token_chain, token_address) = super::validate_posted_token_transfer(
let (token_chain, token_address) = super::validate_token_transfer_vaa(
&ctx.accounts.vaa,
&ctx.accounts.registered_emitter,
&ctx.accounts.recipient_token,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct CompleteTransferWrapped<'info> {
/// allows registering multiple emitter addresses for the same chain ID. These seeds are not
/// checked via Anchor macro, but will be checked in the access control function instead.
///
/// See the `require_valid_token_bridge_posted_vaa` instruction handler for more details.
/// See the `require_valid_token_bridge_vaa` instruction handler for more details.
registered_emitter: Account<'info, LegacyAnchorized<0, RegisteredEmitter>>,

/// CHECK: Recipient token account. Because we verify the wrapped mint, we can depend on the
Expand All @@ -54,8 +54,9 @@ pub struct CompleteTransferWrapped<'info> {

/// CHECK: Wrapped mint (i.e. minted by Token Bridge program).
///
/// NOTE: Instead of checking the seeds, we check that the mint authority is the Token Bridge's
/// in access control.
/// NOTE: Because this mint is guaranteed to have a Wrapped Asset account (since this account's
/// pubkey is a part of the Wrapped Asset's PDA address), we do not need to check that this
/// mint is one that the Token Bridge program has mint authority for.
#[account(mut)]
wrapped_mint: AccountInfo<'info>,

Expand Down Expand Up @@ -133,13 +134,7 @@ impl<'info> core_bridge_program::legacy::utils::ProcessLegacyInstruction<'info,

impl<'info> CompleteTransferWrapped<'info> {
fn constraints(ctx: &Context<Self>) -> Result<()> {
let mint = crate::zero_copy::Mint::load(&ctx.accounts.wrapped_mint)?;
require!(
mint.mint_authority() == Some(ctx.accounts.mint_authority.key()),
ErrorCode::ConstraintMintMintAuthority
);

let (token_chain, token_address) = super::validate_posted_token_transfer(
let (token_chain, token_address) = super::validate_token_transfer_vaa(
&ctx.accounts.vaa,
&ctx.accounts.registered_emitter,
&ctx.accounts.recipient_token,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ use core_bridge_program::{
};
use wormhole_raw_vaas::token_bridge::TokenBridgeMessage;

pub fn validate_posted_token_transfer_with_payload(
pub fn validate_token_transfer_with_payload_vaa(
vaa_acc_info: &AccountInfo,
registered_emitter: &Account<LegacyAnchorized<0, RegisteredEmitter>>,
redeemer_authority: &Signer,
dst_token: &AccountInfo,
) -> Result<(u16, [u8; 32])> {
let vaa_key = vaa_acc_info.key();
let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?;
let msg =
crate::utils::require_valid_posted_token_bridge_vaa(&vaa_key, &vaa, registered_emitter)?;
let msg = crate::utils::require_valid_token_bridge_vaa(&vaa_key, &vaa, registered_emitter)?;

let transfer = if let TokenBridgeMessage::TransferWithMessage(inner) = msg {
inner
Expand All @@ -38,10 +37,8 @@ pub fn validate_posted_token_transfer_with_payload(

// The encoded transfer recipient can either be the signer of this instruction or a
// program whose signer is a PDA using the seeds [b"redeemer"] (and the encoded redeemer
// is the program ID). If the latter, the transfer redeemer can be any PDA that signs
// is the program ID). If the former, the transfer redeemer can be any PDA that signs
// for this instruction.
//
// NOTE: Requiring that the transfer redeemer be a signer is a patch.
let redeemer = Pubkey::from(transfer.redeemer());
let redeemer_authority = redeemer_authority.key();
if redeemer != redeemer_authority {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct CompleteTransferWithPayloadNative<'info> {
/// allows registering multiple emitter addresses for the same chain ID. These seeds are not
/// checked via Anchor macro, but will be checked in the access control function instead.
///
/// See the `require_valid_token_bridge_posted_vaa` instruction handler for more details.
/// See the `require_valid_token_bridge_vaa` instruction handler for more details.
registered_emitter: Account<'info, LegacyAnchorized<0, RegisteredEmitter>>,

/// CHECK: Destination token account. Because we check the mint of the custody token account, we
Expand Down Expand Up @@ -122,9 +122,13 @@ impl<'info> CompleteTransferWithPayloadNative<'info> {
fn constraints(ctx: &Context<Self>) -> Result<()> {
// Make sure the mint authority is not the Token Bridge's. If it is, then this mint
// originated from a foreign network.
crate::utils::require_native_mint(&ctx.accounts.mint)?;
let mint = Mint::load(&ctx.accounts.mint)?;
require!(
!crate::utils::is_wrapped_mint(&mint),
TokenBridgeError::WrappedAsset
);

let (token_chain, token_address) = super::validate_posted_token_transfer_with_payload(
let (token_chain, token_address) = super::validate_token_transfer_with_payload_vaa(
&ctx.accounts.vaa,
&ctx.accounts.registered_emitter,
&ctx.accounts.redeemer_authority,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct CompleteTransferWithPayloadWrapped<'info> {
/// allows registering multiple emitter addresses for the same chain ID. These seeds are not
/// checked via Anchor macro, but will be checked in the access control function instead.
///
/// See the `require_valid_token_bridge_posted_vaa` instruction handler for more details.
/// See the `require_valid_token_bridge_vaa` instruction handler for more details.
registered_emitter: Account<'info, LegacyAnchorized<0, RegisteredEmitter>>,

/// CHECK: Destination token account. Because we verify the wrapped mint, we can depend on the
Expand All @@ -53,8 +53,9 @@ pub struct CompleteTransferWithPayloadWrapped<'info> {

/// CHECK: Wrapped mint (i.e. minted by Token Bridge program).
///
/// NOTE: Instead of checking the seeds, we check that the mint authority is the Token Bridge's
/// in access control.
/// NOTE: Because this mint is guaranteed to have a Wrapped Asset account (since this account's
/// pubkey is a part of the Wrapped Asset's PDA address), we do not need to check that this
/// mint is one that the Token Bridge program has mint authority for.
#[account(mut)]
wrapped_mint: AccountInfo<'info>,

Expand Down Expand Up @@ -125,13 +126,7 @@ impl<'info> core_bridge_program::legacy::utils::ProcessLegacyInstruction<'info,

impl<'info> CompleteTransferWithPayloadWrapped<'info> {
fn constraints(ctx: &Context<Self>) -> Result<()> {
let mint = crate::zero_copy::Mint::load(&ctx.accounts.wrapped_mint)?;
require!(
mint.mint_authority() == Some(ctx.accounts.mint_authority.key()),
ErrorCode::ConstraintMintMintAuthority
);

let (token_chain, token_address) = super::validate_posted_token_transfer_with_payload(
let (token_chain, token_address) = super::validate_token_transfer_with_payload_vaa(
&ctx.accounts.vaa,
&ctx.accounts.registered_emitter,
&ctx.accounts.redeemer_authority,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct CreateOrUpdateWrapped<'info> {
/// allows registering multiple emitter addresses for the same chain ID. These seeds are not
/// checked via Anchor macro, but will be checked in the access control function instead.
///
/// See the `require_valid_token_bridge_posted_vaa` instruction handler for more details.
/// See the `require_valid_token_bridge_vaa` instruction handler for more details.
registered_emitter: Account<'info, LegacyAnchorized<0, RegisteredEmitter>>,

/// CHECK: Posted VAA account, which will be read via zero-copy deserialization in the
Expand Down Expand Up @@ -173,7 +173,7 @@ impl<'info> CreateOrUpdateWrapped<'info> {

// NOTE: Other attestation validation is performed using the try_attestation_* methods,
// which were used in the accounts context.
crate::utils::require_valid_posted_token_bridge_vaa(
crate::utils::require_valid_token_bridge_vaa(
&vaa.key(),
&core_bridge_sdk::VaaAccount::load(vaa).unwrap(),
&ctx.accounts.registered_emitter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ impl<'info> TransferTokensNative<'info> {
fn constraints(ctx: &Context<Self>, args: &TransferTokensArgs) -> Result<()> {
// Make sure the mint authority is not the Token Bridge's. If it is, then this mint
// originated from a foreign network.
crate::utils::require_native_mint(&ctx.accounts.mint)?;
let mint = Mint::load(&ctx.accounts.mint)?;
require!(
!crate::utils::is_wrapped_mint(&mint),
TokenBridgeError::WrappedAsset
);

// Cannot configure a fee greater than the total transfer amount.
require!(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
constants::{CUSTODY_AUTHORITY_SEED_PREFIX, TRANSFER_AUTHORITY_SEED_PREFIX},
error::TokenBridgeError,
legacy::instruction::TransferTokensWithPayloadArgs,
utils::{self, TruncateAmount},
zero_copy::Mint,
Expand Down Expand Up @@ -160,7 +161,13 @@ impl<'info> TransferTokensWithPayloadNative<'info> {
fn constraints(ctx: &Context<Self>) -> Result<()> {
// Make sure the mint authority is not the Token Bridge's. If it is, then this mint
// originated from a foreign network.
crate::utils::require_native_mint(&ctx.accounts.mint)
let mint = Mint::load(&ctx.accounts.mint)?;
require!(
!crate::utils::is_wrapped_mint(&mint),
TokenBridgeError::WrappedAsset
);

Ok(())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn require_valid_governance_vaa<'ctx>(
vaa_key: &'ctx Pubkey,
vaa: &'ctx core_bridge_sdk::VaaAccount<'ctx>,
) -> Result<TokenBridgeDecree<'ctx>> {
crate::utils::require_valid_posted_vaa_key(vaa_key)?;
crate::utils::require_valid_vaa_key(vaa_key)?;

let (emitter_address, emitter_chain, _) = vaa.try_emitter_info()?;
require!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ where
{
// If whether this mint is wrapped is unspecified, we derive the mint authority, which will cost
// some compute units.
let is_wrapped_asset = !crate::utils::is_native_mint(&Mint::load(&accounts.mint())?);
let is_wrapped_asset = crate::utils::is_wrapped_mint(&Mint::load(&accounts.mint())?);

complete_transfer_specified(accounts, is_wrapped_asset, signer_seeds)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ where
// If whether this mint is wrapped is unspecified, we derive the mint authority, which will cost
// some compute units.
let is_wrapped_asset =
!crate::utils::is_native_mint(&crate::zero_copy::Mint::load(&accounts.mint())?);
crate::utils::is_wrapped_mint(&crate::zero_copy::Mint::load(&accounts.mint())?);

transfer_tokens_specified(accounts, directive, is_wrapped_asset, signer_seeds)
}
Expand Down
25 changes: 8 additions & 17 deletions solana/programs/token-bridge/src/utils/token.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
use crate::{
constants::{MAX_DECIMALS, MINT_AUTHORITY_SEED_PREFIX},
error::TokenBridgeError,
zero_copy::Mint,
};
use anchor_lang::prelude::*;
use core_bridge_program::sdk::LoadZeroCopy;

/// With an account meant to be a Token Program mint account, make sure it is not a mint that the
/// Token Bridge program controls.
pub fn require_native_mint(acc_info: &AccountInfo) -> Result<()> {
// If there is a mint authority, make sure it is not the Token Bridge's mint authority, which
// controls burn and mint for its wrapped assets.
let mint = Mint::load(acc_info)?;
require!(is_native_mint(&mint), TokenBridgeError::WrappedAsset);

// Done.
Ok(())
}

pub fn is_native_mint(mint: &Mint) -> bool {
/// Basically check whether the mint authority is the Token Bridge's mint authority.
///
/// NOTE: This method does not guarantee that the mint is a mint created by the Token Bridge program
/// via `create_or_update_wrapped` instruction because someone can transfer mint authority for
/// another mint to the Token Bridge's mint authority.
pub fn is_wrapped_mint(mint: &Mint) -> bool {
if let Some(mint_authority) = mint.mint_authority() {
let (token_bridge_mint_authority, _) =
Pubkey::find_program_address(&[MINT_AUTHORITY_SEED_PREFIX], &crate::ID);
mint_authority != token_bridge_mint_authority
mint_authority == token_bridge_mint_authority
} else {
true
false
}
}

Expand Down
6 changes: 3 additions & 3 deletions solana/programs/token-bridge/src/utils/vaa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const INVALID_POSTED_VAA_KEYS: [&str; 7] = [
];

/// We disallow certain posted VAA accounts from being used to redeem Token Bridge transfers.
pub fn require_valid_posted_vaa_key(acc_key: &Pubkey) -> Result<()> {
pub fn require_valid_vaa_key(acc_key: &Pubkey) -> Result<()> {
// IYKYK.
require!(
!INVALID_POSTED_VAA_KEYS.contains(&acc_key.to_string().as_str()),
Expand All @@ -30,12 +30,12 @@ pub fn require_valid_posted_vaa_key(acc_key: &Pubkey) -> Result<()> {
/// - Transfer (Payload ID == 1)
/// - Attestation (Payload ID == 2)
/// - Transfer with Message (Payload ID == 3)
pub fn require_valid_posted_token_bridge_vaa<'ctx>(
pub fn require_valid_token_bridge_vaa<'ctx>(
vaa_acc_key: &'ctx Pubkey,
vaa: &'ctx core_bridge_sdk::VaaAccount<'ctx>,
registered_emitter: &'ctx Account<'_, LegacyAnchorized<0, RegisteredEmitter>>,
) -> Result<TokenBridgeMessage<'ctx>> {
require_valid_posted_vaa_key(vaa_acc_key)?;
require_valid_vaa_key(vaa_acc_key)?;

let (emitter_address, emitter_chain, _) = vaa.try_emitter_info()?;
let emitter_key = registered_emitter.key();
Expand Down

0 comments on commit 34925c1

Please sign in to comment.