(cherry picked from commit aa96ad042b)
Co-authored-by: Jack May <jack@solana.com>
			
			
This commit is contained in:
		@@ -15,6 +15,8 @@ static const uint8_t TEST_ALLOC_ACCESS_VIOLATION = 8;
 | 
			
		||||
static const uint8_t TEST_INSTRUCTION_DATA_TOO_LARGE = 9;
 | 
			
		||||
static const uint8_t TEST_INSTRUCTION_META_TOO_LARGE = 10;
 | 
			
		||||
static const uint8_t TEST_RETURN_ERROR = 11;
 | 
			
		||||
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 12;
 | 
			
		||||
static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 13;
 | 
			
		||||
 | 
			
		||||
static const int MINT_INDEX = 0;
 | 
			
		||||
static const int ARGUMENT_INDEX = 1;
 | 
			
		||||
@@ -228,6 +230,20 @@ extern uint64_t entrypoint(const uint8_t *input) {
 | 
			
		||||
                 10 + 5 - 1 - 1 - 1 - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sol_log("Test privilege deescalation");
 | 
			
		||||
    {
 | 
			
		||||
      sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
      sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
      SolAccountMeta arguments[] = {
 | 
			
		||||
          {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}};
 | 
			
		||||
      uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION};
 | 
			
		||||
      const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                                          arguments, SOL_ARRAY_SIZE(arguments),
 | 
			
		||||
                                          data, SOL_ARRAY_SIZE(data)};
 | 
			
		||||
      sol_assert(SUCCESS ==
 | 
			
		||||
                 sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sol_log("Verify data values are retained and updated");
 | 
			
		||||
    for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) {
 | 
			
		||||
      sol_assert(accounts[ARGUMENT_INDEX].data[i] == i);
 | 
			
		||||
@@ -436,6 +452,38 @@ extern uint64_t entrypoint(const uint8_t *input) {
 | 
			
		||||
    sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  case TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: {
 | 
			
		||||
    sol_log("Test privilege deescalation escalation signer");
 | 
			
		||||
    sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
    sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
    SolAccountMeta arguments[] = {
 | 
			
		||||
        {accounts[INVOKED_PROGRAM_INDEX].key, false, false},
 | 
			
		||||
        {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}};
 | 
			
		||||
    uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER};
 | 
			
		||||
    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                                        arguments, SOL_ARRAY_SIZE(arguments),
 | 
			
		||||
                                        data, SOL_ARRAY_SIZE(data)};
 | 
			
		||||
    sol_assert(SUCCESS ==
 | 
			
		||||
               sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: {
 | 
			
		||||
    sol_log("Test privilege deescalation escalation writable");
 | 
			
		||||
    sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
    sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
    SolAccountMeta arguments[] = {
 | 
			
		||||
        {accounts[INVOKED_PROGRAM_INDEX].key, false, false},
 | 
			
		||||
        {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}};
 | 
			
		||||
    uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE};
 | 
			
		||||
    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                                        arguments, SOL_ARRAY_SIZE(arguments),
 | 
			
		||||
                                        data, SOL_ARRAY_SIZE(data)};
 | 
			
		||||
    sol_assert(SUCCESS ==
 | 
			
		||||
               sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    sol_panic();
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,3 +12,6 @@ const uint8_t VERIFY_WRITER = 4;
 | 
			
		||||
const uint8_t VERIFY_PRIVILEGE_ESCALATION = 5;
 | 
			
		||||
const uint8_t NESTED_INVOKE = 6;
 | 
			
		||||
const uint8_t RETURN_OK = 7;
 | 
			
		||||
const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8;
 | 
			
		||||
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9;
 | 
			
		||||
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@
 | 
			
		||||
#include <solana_sdk.h>
 | 
			
		||||
 | 
			
		||||
extern uint64_t entrypoint(const uint8_t *input) {
 | 
			
		||||
  sol_log("Invoked C program");
 | 
			
		||||
 | 
			
		||||
  SolAccountInfo accounts[4];
 | 
			
		||||
  SolParameters params = (SolParameters){.ka = accounts};
 | 
			
		||||
 | 
			
		||||
@@ -157,9 +159,54 @@ extern uint64_t entrypoint(const uint8_t *input) {
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case VERIFY_PRIVILEGE_ESCALATION: {
 | 
			
		||||
    sol_log("Success");
 | 
			
		||||
    sol_log("Should never get here!");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  case VERIFY_PRIVILEGE_DEESCALATION: {
 | 
			
		||||
    sol_log("verify privilege deescalation");
 | 
			
		||||
    static const int INVOKED_ARGUMENT_INDEX = 0;
 | 
			
		||||
    sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
    sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: {
 | 
			
		||||
    sol_log("verify privilege deescalation escalation signer");
 | 
			
		||||
    static const int INVOKED_PROGRAM_INDEX = 0;
 | 
			
		||||
    static const int INVOKED_ARGUMENT_INDEX = 1;
 | 
			
		||||
    sol_assert(sol_deserialize(input, ¶ms, 2));
 | 
			
		||||
 | 
			
		||||
    sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
    sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
    SolAccountMeta arguments[] = {
 | 
			
		||||
        {accounts[INVOKED_ARGUMENT_INDEX].key, true, false}};
 | 
			
		||||
    uint8_t data[] = {VERIFY_PRIVILEGE_ESCALATION};
 | 
			
		||||
    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                                        arguments, SOL_ARRAY_SIZE(arguments),
 | 
			
		||||
                                        data, SOL_ARRAY_SIZE(data)};
 | 
			
		||||
    sol_assert(SUCCESS ==
 | 
			
		||||
               sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: {
 | 
			
		||||
    sol_log("verify privilege deescalation escalation writable");
 | 
			
		||||
    static const int INVOKED_PROGRAM_INDEX = 0;
 | 
			
		||||
    static const int INVOKED_ARGUMENT_INDEX = 1;
 | 
			
		||||
    sol_assert(sol_deserialize(input, ¶ms, 2));
 | 
			
		||||
 | 
			
		||||
    sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
    sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
    SolAccountMeta arguments[] = {
 | 
			
		||||
        {accounts[INVOKED_ARGUMENT_INDEX].key, false, true}};
 | 
			
		||||
    uint8_t data[] = {VERIFY_PRIVILEGE_ESCALATION};
 | 
			
		||||
    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                                        arguments, SOL_ARRAY_SIZE(arguments),
 | 
			
		||||
                                        data, SOL_ARRAY_SIZE(data)};
 | 
			
		||||
    sol_assert(SUCCESS ==
 | 
			
		||||
               sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  case NESTED_INVOKE: {
 | 
			
		||||
    sol_log("invoke");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,8 @@ const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8;
 | 
			
		||||
const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9;
 | 
			
		||||
const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
 | 
			
		||||
const TEST_RETURN_ERROR: u8 = 11;
 | 
			
		||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
 | 
			
		||||
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
 | 
			
		||||
 | 
			
		||||
// const MINT_INDEX: usize = 0;
 | 
			
		||||
const ARGUMENT_INDEX: usize = 1;
 | 
			
		||||
@@ -306,6 +308,18 @@ fn process_instruction(
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            msg!("Test privilege deescalation");
 | 
			
		||||
            {
 | 
			
		||||
                assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
                assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
                let invoked_instruction = create_instruction(
 | 
			
		||||
                    *accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                    &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)],
 | 
			
		||||
                    vec![VERIFY_PRIVILEGE_DEESCALATION],
 | 
			
		||||
                );
 | 
			
		||||
                invoke(&invoked_instruction, accounts)?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            msg!("Verify data values are retained and updated");
 | 
			
		||||
            {
 | 
			
		||||
                let data = accounts[ARGUMENT_INDEX].try_borrow_data()?;
 | 
			
		||||
@@ -492,6 +506,34 @@ fn process_instruction(
 | 
			
		||||
            );
 | 
			
		||||
            let _ = invoke(&instruction, accounts);
 | 
			
		||||
        }
 | 
			
		||||
        TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => {
 | 
			
		||||
            msg!("Test privilege deescalation escalation signer");
 | 
			
		||||
            assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
            assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
            let invoked_instruction = create_instruction(
 | 
			
		||||
                *accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                &[
 | 
			
		||||
                    (accounts[INVOKED_PROGRAM_INDEX].key, false, false),
 | 
			
		||||
                    (accounts[INVOKED_ARGUMENT_INDEX].key, false, false),
 | 
			
		||||
                ],
 | 
			
		||||
                vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER],
 | 
			
		||||
            );
 | 
			
		||||
            invoke(&invoked_instruction, accounts)?;
 | 
			
		||||
        }
 | 
			
		||||
        TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => {
 | 
			
		||||
            msg!("Test privilege deescalation escalation writable");
 | 
			
		||||
            assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
            assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
            let invoked_instruction = create_instruction(
 | 
			
		||||
                *accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                &[
 | 
			
		||||
                    (accounts[INVOKED_PROGRAM_INDEX].key, false, false),
 | 
			
		||||
                    (accounts[INVOKED_ARGUMENT_INDEX].key, false, false),
 | 
			
		||||
                ],
 | 
			
		||||
                vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE],
 | 
			
		||||
            );
 | 
			
		||||
            invoke(&invoked_instruction, accounts)?;
 | 
			
		||||
        }
 | 
			
		||||
        _ => panic!(),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,9 @@ pub const VERIFY_WRITER: u8 = 4;
 | 
			
		||||
pub const VERIFY_PRIVILEGE_ESCALATION: u8 = 5;
 | 
			
		||||
pub const NESTED_INVOKE: u8 = 6;
 | 
			
		||||
pub const RETURN_OK: u8 = 7;
 | 
			
		||||
pub const VERIFY_PRIVILEGE_DEESCALATION: u8 = 8;
 | 
			
		||||
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 9;
 | 
			
		||||
pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 10;
 | 
			
		||||
 | 
			
		||||
pub fn create_instruction(
 | 
			
		||||
    program_id: Pubkey,
 | 
			
		||||
 
 | 
			
		||||
@@ -161,7 +161,41 @@ fn process_instruction(
 | 
			
		||||
            assert!(!accounts[ARGUMENT_INDEX].is_writable);
 | 
			
		||||
        }
 | 
			
		||||
        VERIFY_PRIVILEGE_ESCALATION => {
 | 
			
		||||
            msg!("Success");
 | 
			
		||||
            msg!("Should never get here!");
 | 
			
		||||
        }
 | 
			
		||||
        VERIFY_PRIVILEGE_DEESCALATION => {
 | 
			
		||||
            msg!("verify privilege deescalation");
 | 
			
		||||
            const INVOKED_ARGUMENT_INDEX: usize = 0;
 | 
			
		||||
            assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
            assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
        }
 | 
			
		||||
        VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => {
 | 
			
		||||
            msg!("verify privilege deescalation escalation signer");
 | 
			
		||||
            const INVOKED_PROGRAM_INDEX: usize = 0;
 | 
			
		||||
            const INVOKED_ARGUMENT_INDEX: usize = 1;
 | 
			
		||||
 | 
			
		||||
            assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
            assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
            let invoked_instruction = create_instruction(
 | 
			
		||||
                *accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                &[(accounts[INVOKED_ARGUMENT_INDEX].key, true, false)],
 | 
			
		||||
                vec![VERIFY_PRIVILEGE_ESCALATION],
 | 
			
		||||
            );
 | 
			
		||||
            invoke(&invoked_instruction, accounts)?;
 | 
			
		||||
        }
 | 
			
		||||
        VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => {
 | 
			
		||||
            msg!("verify privilege deescalation escalation writable");
 | 
			
		||||
            const INVOKED_PROGRAM_INDEX: usize = 0;
 | 
			
		||||
            const INVOKED_ARGUMENT_INDEX: usize = 1;
 | 
			
		||||
 | 
			
		||||
            assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer);
 | 
			
		||||
            assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable);
 | 
			
		||||
            let invoked_instruction = create_instruction(
 | 
			
		||||
                *accounts[INVOKED_PROGRAM_INDEX].key,
 | 
			
		||||
                &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, true)],
 | 
			
		||||
                vec![VERIFY_PRIVILEGE_ESCALATION],
 | 
			
		||||
            );
 | 
			
		||||
            invoke(&invoked_instruction, accounts)?;
 | 
			
		||||
        }
 | 
			
		||||
        NESTED_INVOKE => {
 | 
			
		||||
            msg!("nested invoke");
 | 
			
		||||
 
 | 
			
		||||
@@ -249,7 +249,11 @@ fn process_transaction_and_record_inner(
 | 
			
		||||
        false,
 | 
			
		||||
        &mut ExecuteTimings::default(),
 | 
			
		||||
    );
 | 
			
		||||
    let inner_instructions = inner.swap_remove(0);
 | 
			
		||||
    let inner_instructions = if inner.is_empty() {
 | 
			
		||||
        Some(vec![vec![]])
 | 
			
		||||
    } else {
 | 
			
		||||
        inner.swap_remove(0)
 | 
			
		||||
    };
 | 
			
		||||
    let result = results
 | 
			
		||||
        .fee_collection_results
 | 
			
		||||
        .swap_remove(0)
 | 
			
		||||
@@ -676,7 +680,7 @@ fn test_program_bpf_error_handling() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_program_bpf_invoke() {
 | 
			
		||||
fn test_program_bpf_invoke_sanity() {
 | 
			
		||||
    solana_logger::setup();
 | 
			
		||||
 | 
			
		||||
    const TEST_SUCCESS: u8 = 1;
 | 
			
		||||
@@ -690,6 +694,8 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
    const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9;
 | 
			
		||||
    const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
 | 
			
		||||
    const TEST_RETURN_ERROR: u8 = 11;
 | 
			
		||||
    const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
 | 
			
		||||
    const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
 | 
			
		||||
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
@@ -787,11 +793,11 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        assert!(result.is_ok());
 | 
			
		||||
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
 | 
			
		||||
        let expected_invoked_programs = match program.0 {
 | 
			
		||||
            Languages::C => vec![
 | 
			
		||||
                solana_sdk::system_program::id(),
 | 
			
		||||
@@ -805,6 +811,7 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
            ],
 | 
			
		||||
            Languages::Rust => vec![
 | 
			
		||||
                solana_sdk::system_program::id(),
 | 
			
		||||
@@ -821,11 +828,11 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
                invoked_program_id.clone(),
 | 
			
		||||
            ],
 | 
			
		||||
        };
 | 
			
		||||
        assert_eq!(invoked_programs.len(), expected_invoked_programs.len());
 | 
			
		||||
        assert_eq!(invoked_programs, expected_invoked_programs);
 | 
			
		||||
 | 
			
		||||
        let no_invoked_programs: Vec<Pubkey> = inner_instructions[1]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
@@ -834,281 +841,96 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
 | 
			
		||||
        // failure cases
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[
 | 
			
		||||
        let do_invoke_failure_test_local =
 | 
			
		||||
            |test: u8, expected_error: TransactionError, expected_invoked_programs: &[Pubkey]| {
 | 
			
		||||
                println!("Running failure test #{:?}", test);
 | 
			
		||||
                let instruction_data = &[test, bump_seed1, bump_seed2, bump_seed3];
 | 
			
		||||
                let signers = vec![
 | 
			
		||||
                    &mint_keypair,
 | 
			
		||||
                    &argument_keypair,
 | 
			
		||||
                    &invoked_argument_keypair,
 | 
			
		||||
                    &from_keypair,
 | 
			
		||||
                ];
 | 
			
		||||
                let instruction =
 | 
			
		||||
                    Instruction::new(invoke_program_id, instruction_data, account_metas.clone());
 | 
			
		||||
                let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
                let tx = Transaction::new(&signers, message.clone(), bank.last_blockhash());
 | 
			
		||||
                let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
                let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
                    .collect();
 | 
			
		||||
                assert_eq!(result.unwrap_err(), expected_error);
 | 
			
		||||
                assert_eq!(invoked_programs, expected_invoked_programs);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_PRIVILEGE_ESCALATION_SIGNER,
 | 
			
		||||
                bump_seed1,
 | 
			
		||||
                bump_seed2,
 | 
			
		||||
                bump_seed3,
 | 
			
		||||
            ],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | 
			
		||||
            &[invoked_program_id.clone()],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![invoked_program_id.clone()]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_PRIVILEGE_ESCALATION_WRITABLE,
 | 
			
		||||
                bump_seed1,
 | 
			
		||||
                bump_seed2,
 | 
			
		||||
                bump_seed3,
 | 
			
		||||
            ],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![invoked_program_id.clone()]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation)
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | 
			
		||||
            &[invoked_program_id.clone()],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_PPROGRAM_NOT_EXECUTABLE,
 | 
			
		||||
                bump_seed1,
 | 
			
		||||
                bump_seed2,
 | 
			
		||||
                bump_seed3,
 | 
			
		||||
            ],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::AccountNotExecutable)
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::AccountNotExecutable),
 | 
			
		||||
            &[],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_EMPTY_ACCOUNTS_SLICE,
 | 
			
		||||
                bump_seed1,
 | 
			
		||||
                bump_seed2,
 | 
			
		||||
                bump_seed3,
 | 
			
		||||
            ],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::MissingAccount)
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::MissingAccount),
 | 
			
		||||
            &[],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[TEST_CAP_SEEDS, bump_seed1, bump_seed2, bump_seed3],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded)
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_CAP_SEEDS,
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded),
 | 
			
		||||
            &[],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[TEST_CAP_SIGNERS, bump_seed1, bump_seed2, bump_seed3],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_CAP_SIGNERS,
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
 | 
			
		||||
            &[],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_INSTRUCTION_DATA_TOO_LARGE,
 | 
			
		||||
                bump_seed1,
 | 
			
		||||
                bump_seed2,
 | 
			
		||||
                bump_seed3,
 | 
			
		||||
            ],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded)
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded),
 | 
			
		||||
            &[],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_INSTRUCTION_META_TOO_LARGE,
 | 
			
		||||
                bump_seed1,
 | 
			
		||||
                bump_seed2,
 | 
			
		||||
                bump_seed3,
 | 
			
		||||
            ],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded)
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded),
 | 
			
		||||
            &[],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let instruction = Instruction::new(
 | 
			
		||||
            invoke_program_id,
 | 
			
		||||
            &[TEST_RETURN_ERROR, bump_seed1, bump_seed2, bump_seed3],
 | 
			
		||||
            account_metas.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let message = Message::new(&[instruction], Some(&mint_pubkey));
 | 
			
		||||
        let tx = Transaction::new(
 | 
			
		||||
            &[
 | 
			
		||||
                &mint_keypair,
 | 
			
		||||
                &argument_keypair,
 | 
			
		||||
                &invoked_argument_keypair,
 | 
			
		||||
                &from_keypair,
 | 
			
		||||
            ],
 | 
			
		||||
            message.clone(),
 | 
			
		||||
            bank.last_blockhash(),
 | 
			
		||||
        );
 | 
			
		||||
        let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | 
			
		||||
        let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
        assert_eq!(invoked_programs, vec![invoked_program_id.clone()]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err(),
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::Custom(42))
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_RETURN_ERROR,
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::Custom(42)),
 | 
			
		||||
            &[invoked_program_id.clone()],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Check final state
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER,
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | 
			
		||||
            &[invoked_program_id.clone()],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        do_invoke_failure_test_local(
 | 
			
		||||
            TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE,
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | 
			
		||||
            &[invoked_program_id.clone()],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Check resulting state
 | 
			
		||||
 | 
			
		||||
        assert_eq!(43, bank.get_balance(&derived_key1));
 | 
			
		||||
        let account = bank.get_account(&derived_key1).unwrap();
 | 
			
		||||
@@ -1157,9 +979,11 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
            TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    // Check for program id spoofing
 | 
			
		||||
    {
 | 
			
		||||
#[cfg(feature = "bpf_rust")]
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_program_bpf_program_id_spoofing() {
 | 
			
		||||
    let GenesisConfigInfo {
 | 
			
		||||
        genesis_config,
 | 
			
		||||
        mint_keypair,
 | 
			
		||||
@@ -1209,8 +1033,9 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
    assert_eq!(0, bank.get_balance(&to_pubkey));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    // Check the caller has access to cpi program
 | 
			
		||||
    {
 | 
			
		||||
#[cfg(feature = "bpf_rust")]
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_program_bpf_caller_has_access_to_cpi_program() {
 | 
			
		||||
    let GenesisConfigInfo {
 | 
			
		||||
        genesis_config,
 | 
			
		||||
        mint_keypair,
 | 
			
		||||
@@ -1245,7 +1070,6 @@ fn test_program_bpf_invoke() {
 | 
			
		||||
        TransactionError::InstructionError(0, InstructionError::MissingAccount)
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "bpf_rust")]
 | 
			
		||||
#[test]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user