diff --git a/Cargo.lock b/Cargo.lock index 65d7ce94aaee19..daf8aa27390450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7652,6 +7652,7 @@ dependencies = [ "num_enum", "percentage", "qualifier_attr", + "rand 0.7.3", "rand 0.8.5", "rand_chacha 0.3.1", "rayon", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 014995d592b612..35e23a8a194a99 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -107,6 +107,7 @@ assert_matches = { workspace = true } ed25519-dalek = { workspace = true } libsecp256k1 = { workspace = true } memoffset = { workspace = true } +rand0-7 = { package = "rand", version = "0.7" } rand_chacha = { workspace = true } solana-accounts-db = { workspace = true, features = ["dev-context-only-utils"] } solana-logger = { workspace = true } diff --git a/runtime/src/verify_precompiles.rs b/runtime/src/verify_precompiles.rs index 6d203d856c14e4..2256526b19f5c2 100644 --- a/runtime/src/verify_precompiles.rs +++ b/runtime/src/verify_precompiles.rs @@ -26,3 +26,140 @@ pub fn verify_precompiles(message: &impl SVMMessage, feature_set: &FeatureSet) - Ok(()) } + +#[cfg(test)] +mod tests { + use { + super::*, + rand0_7::{thread_rng, Rng}, + solana_sdk::{ + ed25519_instruction::new_ed25519_instruction, + hash::Hash, + pubkey::Pubkey, + secp256k1_instruction::new_secp256k1_instruction, + signature::Keypair, + signer::Signer, + system_instruction, system_transaction, + transaction::{SanitizedTransaction, Transaction}, + }, + }; + + #[test] + fn test_verify_precompiles_simple_transaction() { + let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer( + &Keypair::new(), + &Pubkey::new_unique(), + 1, + Hash::default(), + )); + assert!(verify_precompiles(&tx, &FeatureSet::all_enabled()).is_ok()); + } + + #[test] + fn test_verify_precompiles_secp256k1() { + let secp_privkey = libsecp256k1::SecretKey::random(&mut thread_rng()); + let message_arr = b"hello"; + let mut secp_instruction = new_secp256k1_instruction(&secp_privkey, message_arr); + let mint_keypair = Keypair::new(); + let feature_set = FeatureSet::all_enabled(); + + let tx = + SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + &[secp_instruction.clone()], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + Hash::default(), + )); + + assert!(verify_precompiles(&tx, &feature_set).is_ok()); + + let index = thread_rng().gen_range(0, secp_instruction.data.len()); + secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12); + let tx = + SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + &[secp_instruction], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + Hash::default(), + )); + + assert!(verify_precompiles(&tx, &feature_set).is_err()); + } + + #[test] + fn test_verify_precompiles_ed25519() { + let privkey = ed25519_dalek::Keypair::generate(&mut thread_rng()); + let message_arr = b"hello"; + let mut instruction = new_ed25519_instruction(&privkey, message_arr); + let mint_keypair = Keypair::new(); + let feature_set = FeatureSet::all_enabled(); + + let tx = + SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + &[instruction.clone()], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + Hash::default(), + )); + + assert!(verify_precompiles(&tx, &feature_set).is_ok()); + + let index = loop { + let index = thread_rng().gen_range(0, instruction.data.len()); + // byte 1 is not used, so this would not cause the verify to fail + if index != 1 { + break index; + } + }; + + instruction.data[index] = instruction.data[index].wrapping_add(12); + let tx = + SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + &[instruction], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + Hash::default(), + )); + assert!(verify_precompiles(&tx, &feature_set).is_err()); + } + + #[test] + fn test_verify_precompiles_mixed() { + let message_arr = b"hello"; + let secp_privkey = libsecp256k1::SecretKey::random(&mut thread_rng()); + let mut secp_instruction = new_secp256k1_instruction(&secp_privkey, message_arr); + let ed25519_privkey = ed25519_dalek::Keypair::generate(&mut thread_rng()); + let ed25519_instruction = new_ed25519_instruction(&ed25519_privkey, message_arr); + + let mint_keypair = Keypair::new(); + let feature_set = FeatureSet::all_enabled(); + + let tx = + SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + &[ + secp_instruction.clone(), + ed25519_instruction.clone(), + system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 1), + ], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + Hash::default(), + )); + assert!(verify_precompiles(&tx, &feature_set).is_ok()); + + let index = thread_rng().gen_range(0, secp_instruction.data.len()); + secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12); + let tx = + SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer( + &[ + secp_instruction, + ed25519_instruction, + system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 1), + ], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + Hash::default(), + )); + assert!(verify_precompiles(&tx, &feature_set).is_err()); + } +}