diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 685b0ba..34343dc 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -2377,7 +2377,7 @@ dependencies = [ [[package]] name = "wormhole-cctp-solana" -version = "0.0.1-alpha.3" +version = "0.0.1-alpha.7" dependencies = [ "anchor-lang", "anchor-spl", @@ -2393,7 +2393,7 @@ dependencies = [ [[package]] name = "wormhole-circle-integration-solana" -version = "0.0.1-alpha.3" +version = "0.0.1-alpha.7" dependencies = [ "anchor-lang", "anchor-spl", diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 1740ec7..68507e7 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" [workspace.package] edition = "2021" -version = "0.0.1-alpha.3" +version = "0.0.1-alpha.7" authors = ["Wormhole Contributors"] license = "Apache-2.0" homepage = "https://wormhole.com" diff --git a/solana/modules/wormhole-cctp/src/cpi/burn_and_publish.rs b/solana/modules/wormhole-cctp/src/cpi/burn_and_publish.rs index ce94c39..490b1da 100644 --- a/solana/modules/wormhole-cctp/src/cpi/burn_and_publish.rs +++ b/solana/modules/wormhole-cctp/src/cpi/burn_and_publish.rs @@ -7,7 +7,7 @@ pub struct BurnAndPublishArgs { /// 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]. - pub burn_source: Pubkey, + pub burn_source: Option, /// Destination caller address, which is encoded in the CCTP message. Only this address can /// receive a CCTP message via the CCTP Message Transmitter. @@ -73,6 +73,9 @@ pub fn burn_and_publish<'info>( }; let token_address = cctp_ctx.accounts.mint.key.to_bytes(); + let burn_source = burn_source + .unwrap_or(cctp_ctx.accounts.src_token.key()) + .to_bytes(); // We want to make this call as early as possible because the deposit for burn // message is an Anchor event (i.e. written to the program log). We hope that integrators will @@ -99,7 +102,7 @@ pub fn burn_and_publish<'info>( source_cctp_domain, destination_cctp_domain, cctp_nonce, - burn_source: burn_source.to_bytes(), + burn_source, mint_recipient, payload, }), diff --git a/solana/modules/wormhole-cctp/src/cpi/verify_vaa_and_mint.rs b/solana/modules/wormhole-cctp/src/cpi/verify_vaa_and_mint.rs index 3ed1011..e01fee7 100644 --- a/solana/modules/wormhole-cctp/src/cpi/verify_vaa_and_mint.rs +++ b/solana/modules/wormhole-cctp/src/cpi/verify_vaa_and_mint.rs @@ -5,17 +5,16 @@ use wormhole_raw_vaas::cctp::WormholeCctpMessage; /// Method to reconcile a CCTP message with a Wormhole VAA encoding the Wormhole CCTP deposit. After /// reconciliation, the method invokes the CCTP Message Transmitter to mint the local tokens to the -/// provided token account in the account context. This method returns a zero-copy [VaaAccount] -/// reader so an integrator can verify emitter information. +/// provided token account in the account context. /// /// This method reconciles both messages by making sure the source domain, destination domain and /// nonce match. /// -/// NOTE: In order to return a zero-copy [VaaAccount] reader, this method takes a reference to the -/// [AccountInfo] of the VAA account. It is the integrator's responsibility to ensure that the owner -/// of this account is Wormhole Core Bridge program. -pub fn verify_vaa_and_mint<'ctx, 'info>( - vaa: &'ctx AccountInfo<'info>, +/// NOTE: It is the integrator's responsibility to ensure that the owner of this account is Wormhole +/// Core Bridge program if this method is used. Otherwise, please use [verify_vaa_and_mint], which +/// performs the account owner check. +pub fn verify_vaa_and_mint_unchecked<'info>( + vaa: &VaaAccount<'_>, cctp_ctx: CpiContext< '_, '_, @@ -24,9 +23,7 @@ pub fn verify_vaa_and_mint<'ctx, 'info>( message_transmitter_program::cpi::ReceiveTokenMessengerMinterMessage<'info>, >, args: message_transmitter_program::cpi::ReceiveMessageArgs, -) -> Result> { - let vaa = VaaAccount::load(vaa)?; - +) -> Result<()> { let msg = WormholeCctpMessage::try_from(vaa.try_payload()?) .map_err(|_| error!(WormholeCctpError::CannotParseMessage))?; @@ -70,6 +67,43 @@ pub fn verify_vaa_and_mint<'ctx, 'info>( // Token Messenger Minter program to mint tokens. message_transmitter_program::cpi::receive_token_messenger_minter_message(cctp_ctx, args)?; + // Done. + Ok(()) +} + +/// Method to reconcile a CCTP message with a Wormhole VAA encoding the Wormhole CCTP deposit. After +/// reconciliation, the method invokes the CCTP Message Transmitter to mint the local tokens to the +/// provided token account in the account context. This method returns a zero-copy [VaaAccount] +/// reader so an integrator can verify emitter information. +/// +/// This method reconciles both messages by making sure the source domain, destination domain and +/// nonce match. +/// +/// NOTE: In order to return a zero-copy [VaaAccount] reader, this method takes a reference to the +/// [AccountInfo] of the VAA account. +pub fn verify_vaa_and_mint<'ctx, 'info>( + vaa: &'ctx AccountInfo<'info>, + cctp_ctx: CpiContext< + '_, + '_, + '_, + 'info, + message_transmitter_program::cpi::ReceiveTokenMessengerMinterMessage<'info>, + >, + args: message_transmitter_program::cpi::ReceiveMessageArgs, +) -> Result> { + // This is a very important check. We need to make sure that the VAA account is owned by the + // Wormhole Core Bridge program. Otherwise, an attacker can create a fake VAA account. + require_keys_eq!( + *vaa.owner, + wormhole_core_bridge_solana::sdk::id(), + ErrorCode::ConstraintOwner + ); + + let vaa = VaaAccount::load(vaa)?; + + verify_vaa_and_mint_unchecked(&vaa, cctp_ctx, args)?; + // Finally return the VAA account reader. Ok(vaa) } diff --git a/solana/modules/wormhole-cctp/src/wormhole/core_bridge_program/cpi/post_message.rs b/solana/modules/wormhole-cctp/src/wormhole/core_bridge_program/cpi/post_message.rs index 65d1460..58baaf2 100644 --- a/solana/modules/wormhole-cctp/src/wormhole/core_bridge_program/cpi/post_message.rs +++ b/solana/modules/wormhole-cctp/src/wormhole/core_bridge_program/cpi/post_message.rs @@ -1,5 +1,6 @@ use crate::wormhole::core_bridge_program::Commitment; use anchor_lang::{prelude::*, system_program}; +use wormhole_core_bridge_solana::state::Config; #[derive(Accounts)] pub struct PostMessage<'info> { @@ -50,7 +51,7 @@ pub fn post_message<'info>( // Pay Wormhole message fee. { let mut data: &[_] = &ctx.accounts.config.try_borrow_data()?; - let config = wormhole_core_bridge_solana::state::Config::deserialize(&mut data)?; + let Config { fee_lamports, .. } = Config::deserialize(&mut data)?; system_program::transfer( CpiContext::new( @@ -60,7 +61,7 @@ pub fn post_message<'info>( to: ctx.accounts.fee_collector.to_account_info(), }, ), - config.fee_lamports, + fee_lamports, )?; } diff --git a/solana/programs/circle-integration/src/processor/redeem_tokens_with_payload.rs b/solana/programs/circle-integration/src/processor/redeem_tokens_with_payload.rs index e9366f5..4d34eae 100644 --- a/solana/programs/circle-integration/src/processor/redeem_tokens_with_payload.rs +++ b/solana/programs/circle-integration/src/processor/redeem_tokens_with_payload.rs @@ -28,7 +28,9 @@ pub struct RedeemTokensWithPayload<'info> { /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader. - #[account(owner = core_bridge_program::id())] + /// + /// NOTE: The owner of this account is checked in + /// [verify_vaa_and_mint](wormhole_cctp_solana::cpi::verify_vaa_and_mint). vaa: AccountInfo<'info>, /// Account representing that a VAA has been consumed. diff --git a/solana/programs/circle-integration/src/processor/transfer_tokens_with_payload.rs b/solana/programs/circle-integration/src/processor/transfer_tokens_with_payload.rs index f8f8363..8fa579f 100644 --- a/solana/programs/circle-integration/src/processor/transfer_tokens_with_payload.rs +++ b/solana/programs/circle-integration/src/processor/transfer_tokens_with_payload.rs @@ -225,7 +225,7 @@ pub fn transfer_tokens_with_payload( &[custodian_seeds], ), wormhole_cctp_solana::cpi::BurnAndPublishArgs { - burn_source: ctx.accounts.burn_source.key(), + burn_source: Some(ctx.accounts.burn_source.key()), destination_caller: ctx.accounts.registered_emitter.address, destination_cctp_domain: ctx.accounts.registered_emitter.cctp_domain, amount,