diff --git a/solana/bridge/program/src/api/post_vaa.rs b/solana/bridge/program/src/api/post_vaa.rs index af43c5809..fac0e08da 100644 --- a/solana/bridge/program/src/api/post_vaa.rs +++ b/solana/bridge/program/src/api/post_vaa.rs @@ -22,6 +22,7 @@ use crate::{ GuardianSetMismatch, PostVAAConsensusFailed, PostVAAGuardianSetExpired, + VAAInvalid, }, }; use byteorder::{ @@ -33,7 +34,10 @@ use serde::{ Serialize, }; use sha3::Digest; -use solana_program::program_error::ProgramError; +use solana_program::{ + program_error::ProgramError, + pubkey::Pubkey, +}; use solitaire::{ processors::seeded::Seeded, CreationLamports::Exempt, @@ -174,6 +178,26 @@ fn check_active<'r>( Ok(()) } +// Static list of invalid signature accounts that are not allowed to post VAAs. +static INVALID_SIGNATURES: &'static [&'static str; 16] = &[ + "18eK1799CaNMGCUnnCt1Kq2uwKkax6T2WmtrDsZuVFQ", + "2g6NCUUPaD6AxdHPQMVLpjpAvBfKMek6dDiGUe2A6T33", + "3hYV5968hNzbqUfcvnQ6v9D5h32hEwGJn19c47N3unNj", + "76eEyhaEKs4mesjiQiu8bghvwDHNxJW3EfcpbNC78y1z", + "7PdcxSn7xk2UN5VYmKnJ2Q64PdBhbBQFf4RwHqhQCMgv", + "94wXN3z3Pph2vMVaviZSouo7oCDqt4fekvqT3FYJSrWA", + "AXe9VXd9jjXkBxSdvgj4bHSZNeqxY73sSQEsp1tnekY4", + "B2hS49B8n4Ad6cxZLoAjz7Hux7Kf17D5xUX3neDPHpug", + "BTXnYYjnfXByqJprarqzp65Yha2XwQVmg8V8KWBhr6aA", + "Bzb5G4Y8QcaMVMQq3r8q1SuKSxtgnWSFdKCEisJCbcBP", + "CJfRUQxyonG6B5mnztsNUqxknbFT89DJdrdrzV9F96mU", + "CK1j9TxWP1T5w1QzFu4vPDAbUR34mfVqvk5wziE8TzST", + "E8qKJMwzBCiHCHUmBEcL631kN5CjfsHNx24osFLfHg69", + "EtMw1nQ4AQaH53RjYz3pRk12rrqWjcYjPDETphYJzmCX", + "EVNwqfgkUnJoMqBqiHgDfa3TLZPQocX1hpcbAXbpcSLv", + "FixSiDfTxvoy5Zgjp5KdFU8U23ChwCxPWY3WTkmMW2fU", +]; + /// The signatures in this instruction must be from the right guardian set. #[inline(always)] fn check_valid_sigs<'r>( @@ -183,6 +207,12 @@ fn check_valid_sigs<'r>( if signatures.guardian_set_index != guardian_set.index { return Err(GuardianSetMismatch.into()); } + + // Reject blacklisted signature accounts. + if INVALID_SIGNATURES.contains(&&*signatures.info().key.to_string()) { + return Err(VAAInvalid.into()); + } + Ok(()) } diff --git a/solana/bridge/program/src/error.rs b/solana/bridge/program/src/error.rs index 7e17bde5c..dc28a4884 100644 --- a/solana/bridge/program/src/error.rs +++ b/solana/bridge/program/src/error.rs @@ -23,6 +23,7 @@ pub enum Error { PostVAAGuardianSetExpired, TooManyGuardians, VAAAlreadyExecuted, + VAAInvalid, } /// Errors thrown by the program will bubble up to the solitaire wrapper, which needs a way to diff --git a/solana/bridge/program/src/vaa.rs b/solana/bridge/program/src/vaa.rs index 0f323a46a..69f512f6e 100644 --- a/solana/bridge/program/src/vaa.rs +++ b/solana/bridge/program/src/vaa.rs @@ -8,6 +8,7 @@ use crate::{ InvalidGovernanceChain, InvalidGovernanceModule, VAAAlreadyExecuted, + VAAInvalid, }, Claim, ClaimDerivationData, @@ -23,7 +24,10 @@ use serde::{ Deserialize, Serialize, }; -use solana_program::pubkey::Pubkey; +use solana_program::{ + account_info::AccountInfo, + pubkey::Pubkey, +}; use solitaire::{ processors::seeded::Seeded, trace, @@ -143,6 +147,10 @@ impl<'b, T: DeserializePayload> PayloadMessage<'b, T> { pub fn meta(&self) -> &PostedVAAData { &self.0 } + + pub fn info(&self) -> AccountInfo<'b> { + self.0.info().clone() + } } #[derive(FromAccounts)] diff --git a/solana/modules/token_bridge/program/src/api/complete_transfer.rs b/solana/modules/token_bridge/program/src/api/complete_transfer.rs index d115bca74..4fbcf567a 100644 --- a/solana/modules/token_bridge/program/src/api/complete_transfer.rs +++ b/solana/modules/token_bridge/program/src/api/complete_transfer.rs @@ -15,6 +15,7 @@ use crate::{ messages::PayloadTransfer, types::*, TokenBridgeError::*, + INVALID_VAAS, }; use bridge::{ vaa::ClaimableVAA, @@ -124,6 +125,9 @@ pub fn complete_native( if accs.vaa.to != accs.to.info().key.to_bytes() { return Err(InvalidRecipient.into()); } + if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { + return Err(InvalidVAA.into()); + } // Prevent vaa double signing accs.vaa.verify(ctx.program_id)?; @@ -243,6 +247,9 @@ pub fn complete_wrapped( if accs.vaa.to != accs.to.info().key.to_bytes() { return Err(InvalidRecipient.into()); } + if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { + return Err(InvalidVAA.into()); + } accs.vaa.verify(ctx.program_id)?; accs.vaa.claim(ctx, accs.payer.key)?; diff --git a/solana/modules/token_bridge/program/src/api/create_wrapped.rs b/solana/modules/token_bridge/program/src/api/create_wrapped.rs index e988d819b..95f042eef 100644 --- a/solana/modules/token_bridge/program/src/api/create_wrapped.rs +++ b/solana/modules/token_bridge/program/src/api/create_wrapped.rs @@ -16,7 +16,9 @@ use crate::{ TokenBridgeError::{ InvalidChain, InvalidMetadata, + InvalidVAA, }, + INVALID_VAAS, }; use bridge::{ vaa::ClaimableVAA, @@ -127,6 +129,10 @@ pub fn create_wrapped( accs.chain_registration .verify_derivation(ctx.program_id, &derivation_data)?; + if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { + return Err(InvalidVAA.into()); + } + accs.vaa.verify(ctx.program_id)?; accs.vaa.claim(ctx, accs.payer.key)?; diff --git a/solana/modules/token_bridge/program/src/api/governance.rs b/solana/modules/token_bridge/program/src/api/governance.rs index caad0254b..c243220b7 100644 --- a/solana/modules/token_bridge/program/src/api/governance.rs +++ b/solana/modules/token_bridge/program/src/api/governance.rs @@ -12,7 +12,9 @@ use crate::{ TokenBridgeError::{ InvalidChain, InvalidGovernanceKey, + InvalidVAA, }, + INVALID_VAAS, }; use bridge::{ vaa::{ @@ -101,9 +103,12 @@ pub fn upgrade_contract( accs: &mut UpgradeContract, _data: UpgradeContractData, ) -> Result<()> { + if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { + return Err(InvalidVAA.into()); + } + verify_governance(&accs.vaa)?; accs.vaa.verify(ctx.program_id)?; - accs.vaa.claim(ctx, accs.payer.key)?; let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade( @@ -151,12 +156,16 @@ pub struct RegisterChainData {} pub fn register_chain( ctx: &ExecutionContext, accs: &mut RegisterChain, - data: RegisterChainData, + _data: RegisterChainData, ) -> Result<()> { let derivation_data: EndpointDerivationData = (&*accs).into(); accs.endpoint .verify_derivation(ctx.program_id, &derivation_data)?; + if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { + return Err(InvalidVAA.into()); + } + // Claim VAA verify_governance(&accs.vaa)?; accs.vaa.verify(ctx.program_id)?; diff --git a/solana/modules/token_bridge/program/src/lib.rs b/solana/modules/token_bridge/program/src/lib.rs index 1062ae1bf..6721c4ca3 100644 --- a/solana/modules/token_bridge/program/src/lib.rs +++ b/solana/modules/token_bridge/program/src/lib.rs @@ -53,6 +53,17 @@ pub use api::{ use solitaire::*; use std::error::Error; +// Static list of invalid VAA Message accounts. +pub(crate) static INVALID_VAAS: &'static [&'static str; 7] = &[ + "28Tx7c3W8rggVNyUQEAL9Uq6pUng4xJLAeLA6V8nLH1Z", + "32YEuzLCvSyHoV6NFpaTXfiAB8sHiAnYcvP2BBeLeGWq", + "427N2RrDHYooLvyWCiEiNR4KtGsGFTMuXiGwtuChWRSd", + "56Vf4Y2SCxJBf4TSR24fPF8qLHhC8ZuTJvHS6mLGWieD", + "7SzK4pmh9fM9SWLTCKmbjQC8EvDgPmtwdaBeTRztkM98", + "G2VJNjmQsz6wfVZkTUzYAB8ZzRS2hZbpUd5Cr4DTpz6t", + "GvAarWUV8khMLrTRouzBh3xSr8AeLDXxoKNJ6FgxGyg5", +]; + pub enum TokenBridgeError { AlreadyExecuted, InvalidChain, @@ -66,6 +77,7 @@ pub enum TokenBridgeError { WrongAccountOwner, InvalidFee, InvalidRecipient, + InvalidVAA, } impl From for SolitaireError {