diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d9277eff..50f4cc94 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -70,7 +70,9 @@ jobs: if: steps.cache-solana-toolchain.outputs.cache-hit != 'true' with: version: ${{ env.SOLANA_VERSION }} - - name: Build test program - run: cargo build-sbf --manifest-path test-program/Cargo.toml + - name: Build test programs + run: | + cargo build-sbf --manifest-path test-programs/cpi-target/Cargo.toml + cargo build-sbf --manifest-path test-programs/primary/Cargo.toml - name: Test run: cargo test diff --git a/Cargo.toml b/Cargo.toml index 250159e0..858626e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ members = [ "bencher", "harness", - "test-program", + "test-programs/cpi-target", + "test-programs/primary", ] resolver = "2" diff --git a/bencher/tests/markdown.rs b/bencher/tests/markdown.rs index 1d47bb5a..30e10d66 100644 --- a/bencher/tests/markdown.rs +++ b/bencher/tests/markdown.rs @@ -14,7 +14,7 @@ fn test_markdown() { let instruction = Instruction::new_with_bytes(program_id, &[0], vec![]); let accounts = vec![]; - let mollusk = Mollusk::new(&program_id, "test_program"); + let mollusk = Mollusk::new(&program_id, "test_program_primary"); MolluskComputeUnitBencher::new(mollusk) .bench((String::from("bench0"), instruction.clone(), &accounts)) diff --git a/harness/tests/bpf_program.rs b/harness/tests/bpf_program.rs index e3f9b6c0..2ced14b1 100644 --- a/harness/tests/bpf_program.rs +++ b/harness/tests/bpf_program.rs @@ -17,7 +17,7 @@ fn test_write_data() { let program_id = Pubkey::new_unique(); - let mollusk = Mollusk::new(&program_id, "test_program"); + let mollusk = Mollusk::new(&program_id, "test_program_primary"); let data = &[1, 2, 3, 4, 5]; let space = data.len(); @@ -46,7 +46,7 @@ fn test_write_data() { &[(key, account.clone())], &[ Check::err(ProgramError::MissingRequiredSignature), - Check::compute_units(272), + Check::compute_units(279), ], ); } @@ -61,7 +61,7 @@ fn test_write_data() { &[(key, account.clone())], &[ Check::err(ProgramError::AccountDataTooSmall), - Check::compute_units(281), + Check::compute_units(290), ], ); } @@ -72,7 +72,7 @@ fn test_write_data() { &[(key, account.clone())], &[ Check::success(), - Check::compute_units(350), + Check::compute_units(358), Check::account(&key) .data(data) .lamports(lamports) @@ -88,7 +88,7 @@ fn test_transfer() { let program_id = Pubkey::new_unique(); - let mollusk = Mollusk::new(&program_id, "test_program"); + let mollusk = Mollusk::new(&program_id, "test_program_primary"); let payer = Pubkey::new_unique(); let payer_lamports = 100_000_000; @@ -128,7 +128,7 @@ fn test_transfer() { ], &[ Check::err(ProgramError::MissingRequiredSignature), - Check::compute_units(598), + Check::compute_units(605), ], ); } @@ -146,7 +146,7 @@ fn test_transfer() { Check::err(ProgramError::Custom( SystemError::ResultWithNegativeLamports as u32, )), - Check::compute_units(2256), + Check::compute_units(2261), ], ); } @@ -161,7 +161,7 @@ fn test_transfer() { ], &[ Check::success(), - Check::compute_units(2366), + Check::compute_units(2371), Check::account(&payer) .lamports(payer_lamports - transfer_amount) .build(), @@ -178,7 +178,7 @@ fn test_close_account() { let program_id = Pubkey::new_unique(); - let mollusk = Mollusk::new(&program_id, "test_program"); + let mollusk = Mollusk::new(&program_id, "test_program_primary"); let key = Pubkey::new_unique(); let account = AccountSharedData::new(50_000_000, 50, &program_id); @@ -207,7 +207,7 @@ fn test_close_account() { ], &[ Check::err(ProgramError::MissingRequiredSignature), - Check::compute_units(598), + Check::compute_units(605), ], ); } @@ -222,7 +222,7 @@ fn test_close_account() { ], &[ Check::success(), - Check::compute_units(2558), + Check::compute_units(2563), Check::account(&key) .data(&[]) .lamports(0) diff --git a/test-programs/cpi-target/Cargo.toml b/test-programs/cpi-target/Cargo.toml new file mode 100644 index 00000000..1cd19a8c --- /dev/null +++ b/test-programs/cpi-target/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "test-program-cpi-target" +version = "0.1.0" +edition = "2021" + +[dependencies] +solana-program = { workspace = true } + +[lib] +crate-type = ["cdylib", "lib"] diff --git a/test-programs/cpi-target/src/lib.rs b/test-programs/cpi-target/src/lib.rs new file mode 100644 index 00000000..18668fb6 --- /dev/null +++ b/test-programs/cpi-target/src/lib.rs @@ -0,0 +1,37 @@ +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + program_error::ProgramError, + pubkey::Pubkey, +}; + +solana_program::declare_id!("MD24T7azhc2q9ZXaeskbLpmVA41k7StzTGgcfvGcpHj"); + +solana_program::entrypoint!(process_instruction); + +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + input: &[u8], +) -> ProgramResult { + // Simply write the input data to the first account. + let accounts_iter = &mut accounts.iter(); + + let account_info = next_account_info(accounts_iter)?; + + if !account_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + + if account_info.owner != program_id { + return Err(ProgramError::IncorrectProgramId); + } + + if input.len() > account_info.data_len() { + return Err(ProgramError::AccountDataTooSmall); + } + + account_info.try_borrow_mut_data()?[..].copy_from_slice(input); + + Ok(()) +} diff --git a/test-program/Cargo.toml b/test-programs/primary/Cargo.toml similarity index 82% rename from test-program/Cargo.toml rename to test-programs/primary/Cargo.toml index 28539b8a..f592eb65 100644 --- a/test-program/Cargo.toml +++ b/test-programs/primary/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test-program" +name = "test-program-primary" version = "0.1.0" edition = "2021" diff --git a/test-program/src/lib.rs b/test-programs/primary/src/lib.rs similarity index 76% rename from test-program/src/lib.rs rename to test-programs/primary/src/lib.rs index dc9d36fa..594ee232 100644 --- a/test-program/src/lib.rs +++ b/test-programs/primary/src/lib.rs @@ -2,9 +2,10 @@ use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, incinerator, + instruction::{AccountMeta, Instruction}, program::invoke, program_error::ProgramError, - pubkey::Pubkey, + pubkey::{Pubkey, PUBKEY_BYTES}, system_instruction, system_program, }; @@ -74,6 +75,23 @@ fn process_instruction( &[account_info.clone(), incinerator_info.clone()], )?; } + Some((4, rest)) if rest.len() >= PUBKEY_BYTES => { + // Invoke the "CPI Target" test program, which will write the rest + // of the input data to the first account (after the provided + // program ID). + let account_info = next_account_info(accounts_iter)?; + + let (program_id_bytes, data) = rest.split_at(PUBKEY_BYTES); + + let program_id = Pubkey::new_from_array(program_id_bytes.try_into().unwrap()); + let instruction = Instruction::new_with_bytes( + program_id, + data, + vec![AccountMeta::new(*account_info.key, true)], + ); + + invoke(&instruction, &[account_info.clone()])?; + } _ => return Err(ProgramError::InvalidInstructionData), }