diff --git a/solana/crates/kolme-solana-bridge/src/tests.rs b/solana/crates/kolme-solana-bridge/src/tests.rs index 6459400b..c31bd388 100644 --- a/solana/crates/kolme-solana-bridge/src/tests.rs +++ b/solana/crates/kolme-solana-bridge/src/tests.rs @@ -25,7 +25,7 @@ use kolme_solana_bridge_client::{ transfer_payload, TokenProgram, INITIALIZE_IX, REGULAR_IX, SIGNED_IX }; -use crate::{SignedIxError, TOKEN_HOLDER_SEED}; +use crate::{SignedIxError, InitIxError, TOKEN_HOLDER_SEED}; const KEYS_LEN: usize = 5; const PROCESSOR_KEY: usize = 0; @@ -551,3 +551,254 @@ fn secp256k1_is_normalized() { // assert!(Secp256k1Signature(sig.to_bytes().into()).is_normalized()); // } } + +fn setup_program_with_sender() -> (Program, Keypair) { + let mut p = Program::new(); + let sender = Keypair::new(); + p.svm.airdrop(&sender.pubkey(), 1_000_000_000).unwrap(); + p.init_default(&sender).unwrap(); + (p, sender) +} + +#[test] +fn invalid_payload_rejected() { + let (mut p, sender) = setup_program_with_sender(); + + let payload = Payload { + id: 0, + program_id: TOKEN.to_bytes(), + accounts: vec![], + instruction_data: vec![255, 255], + signer: None, + }; + + let (data, metas) = p.make_signed_msg(&payload, &[EXECUTOR1_KEY, EXECUTOR3_KEY]); + + let result = p.signed(&sender, &data, &metas); + assert!(result.is_err(), "Expected error due to invalid payload"); +} + +#[test] +fn zero_amount_transfer() { + let (mut p, sender) = setup_program_with_sender(); + + let sender_ata = p.make_ata(&sender); + p.mint(&sender_ata, 1_000_000_000); + + let data = RegularMsgIxData { + keys: vec![], + transfer_amounts: vec![0], + }; + + let result = p.regular(&sender, &data, &[p.token]); + assert!(result.is_ok()); + + let sender_ata_data: SplAccount = get_spl_account(&p.svm, &sender_ata).unwrap(); + assert_eq!(sender_ata_data.amount, 1_000_000_000); +} + +#[test] +fn duplicate_executor_signatures_rejected() { + let (mut p, sender) = setup_program_with_sender(); + + let receiver = Keypair::new(); + let payload = p.transfer_payload(0, receiver.pubkey(), 1000); + let (data, metas) = p.make_signed_msg(&payload, &[EXECUTOR1_KEY, EXECUTOR1_KEY]); + + let result = p.signed(&sender, &data, &metas).unwrap_err(); + assert_eq!( + result.err, + TransactionError::InstructionError( + 0, + InstructionError::Custom(SignedIxError::DuplicateExecutorKey as u32) + ) + ); +} + +#[test] +fn large_token_amount_transfer() { + let (mut p, sender) = setup_program_with_sender(); + + let sender_ata = p.make_ata(&sender); + let max_amount = u64::MAX / 2; + p.mint(&sender_ata, max_amount); + + let data = RegularMsgIxData { + keys: vec![], + transfer_amounts: vec![max_amount], + }; + p.regular(&sender, &data, &[p.token]).unwrap(); + + let holder_acc = token_holder_acc(&p.token, &sender.pubkey()); + let holder_ata = spl_client::address::get_associated_token_address(&holder_acc, &p.token); + let holder_ata_data: SplAccount = get_spl_account(&p.svm, &holder_ata).unwrap(); + assert_eq!(holder_ata_data.amount, max_amount); +} + +#[test] +fn insufficient_token_balance_rejected() { + let (mut p, sender) = setup_program_with_sender(); + + let sender_ata = p.make_ata(&sender); + let mint_amount = 1_000_000_000; + p.mint(&sender_ata, mint_amount); + + let data = RegularMsgIxData { + keys: vec![], + transfer_amounts: vec![mint_amount + 1], + }; + + let result = p.regular(&sender, &data, &[p.token]).unwrap_err(); + assert_eq!( + result.err, + TransactionError::InstructionError(0, InstructionError::Custom(1)) + ); +} + +#[test] +fn transfer_to_uninitialized_ata() { + let mut p = Program::new(); + let sender = Keypair::new(); + let receiver = Keypair::new(); + p.svm.airdrop(&sender.pubkey(), 1_000_000_000).unwrap(); + p.init_default(&sender).unwrap(); + + let sender_ata = p.make_ata(&sender); + let mint_amount = 1_000_000_000; + p.mint(&sender_ata, mint_amount); + + let receiver_ata = + spl_client::address::get_associated_token_address(&receiver.pubkey(), &p.token); + let payload = p.transfer_payload(0, receiver_ata, mint_amount / 2); + let (data, metas) = p.make_signed_msg(&payload, &[EXECUTOR1_KEY, EXECUTOR3_KEY]); + + let result = p.signed(&sender, &data, &metas).unwrap_err(); + assert_eq!( + result.err, + TransactionError::InstructionError(0, InstructionError::InvalidAccountData) + ); +} + +#[test] +fn multiple_token_transfers_in_one_instruction() { + let (mut p, sender) = setup_program_with_sender(); + + let token_1 = p.token; + let token_2 = CreateMint::new(&mut p.svm, &p.token_owner).send().unwrap(); + + let sender_ata_1 = p.make_ata(&sender); + let sender_ata_2 = CreateAssociatedTokenAccount::new(&mut p.svm, &sender, &token_2) + .send() + .unwrap(); + + let mint_amount = 1_000_000_000; + p.mint(&sender_ata_1, mint_amount); + MintTo::new( + &mut p.svm, + &p.token_owner, + &token_2, + &sender_ata_2, + mint_amount, + ) + .send() + .unwrap(); + + let data = RegularMsgIxData { + keys: vec![], + transfer_amounts: vec![mint_amount / 2, mint_amount / 4], + }; + p.regular(&sender, &data, &[token_1, token_2]).unwrap(); + + let holder_ata_1 = spl_client::address::get_associated_token_address( + &token_holder_acc(&token_1, &sender.pubkey()), + &token_1, + ); + let holder_ata_2 = spl_client::address::get_associated_token_address( + &token_holder_acc(&token_2, &sender.pubkey()), + &token_2, + ); + + let holder_ata_1_data: SplAccount = get_spl_account(&p.svm, &holder_ata_1).unwrap(); + let holder_ata_2_data: SplAccount = get_spl_account(&p.svm, &holder_ata_2).unwrap(); + + assert_eq!(holder_ata_1_data.amount, mint_amount / 2); + assert_eq!(holder_ata_2_data.amount, mint_amount / 4); +} + +#[test] +fn tampered_payload_rejected() { + let (mut p, sender) = setup_program_with_sender(); + + let receiver = Keypair::new(); + let payload = p.transfer_payload(0, receiver.pubkey(), 1000); + let (mut data, metas) = p.make_signed_msg(&payload, &[EXECUTOR1_KEY, EXECUTOR3_KEY]); + + let tampered_payload = base64::engine::general_purpose::STANDARD.encode("tampered"); + data.payload = tampered_payload; + + let result = p.signed(&sender, &data, &metas); + assert!(result.is_err(), "Expected error due to tampered payload"); +} + +#[test] +fn invalid_needed_executors_rejected() { + let mut p = Program::new(); + let sender = Keypair::new(); + p.svm.airdrop(&sender.pubkey(), 1_000_000_000).unwrap(); + + let mut executors = Vec::with_capacity(KEYS_LEN - 1); + for i in 1..KEYS_LEN { + executors.push(Secp256k1PubkeyCompressed( + p.keys[i] + .verifying_key() + .to_sec1_bytes() + .deref() + .try_into() + .unwrap(), + )); + } + + let data = InitializeIxData { + needed_executors: (KEYS_LEN) as u8, + processor: Secp256k1PubkeyCompressed( + p.keys[PROCESSOR_KEY] + .verifying_key() + .to_sec1_bytes() + .deref() + .try_into() + .unwrap(), + ), + executors, + }; + + let result = p.init(&sender, &data).unwrap_err(); + assert_eq!( + result.err, + TransactionError::InstructionError( + 0, + InstructionError::Custom(InitIxError::InsufficientExecutors as u32) + ) + ); + + let data_zero = InitializeIxData { + needed_executors: 0, + processor: Secp256k1PubkeyCompressed( + p.keys[PROCESSOR_KEY] + .verifying_key() + .to_sec1_bytes() + .deref() + .try_into() + .unwrap(), + ), + executors: vec![], + }; + + let result_zero = p.init(&sender, &data_zero).unwrap_err(); + assert_eq!( + result_zero.err, + TransactionError::InstructionError( + 0, + InstructionError::Custom(InitIxError::NoExecutorsProvided as u32) + ) + ); +}