//! Example Rust-based SBF program that queries sibling instructions

use {
    solana_account_info::AccountInfo,
    solana_instruction::{AccountMeta, Instruction, TRANSACTION_LEVEL_STACK_HEIGHT},
    solana_msg::msg,
    solana_program::{
        instruction::{get_processed_sibling_instruction, get_stack_height},
        program::invoke,
    },
    solana_program_error::ProgramResult,
    solana_pubkey::Pubkey,
};

solana_program_entrypoint::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    msg!("sibling");

    // account 0 is mint
    // account 1 is noop
    // account 2 is invoke_and_return
    // account 3 is sibling_inner

    // Invoke child instructions

    let instruction3 = Instruction::new_with_bytes(
        *accounts[2].key,
        &[3],
        vec![AccountMeta::new_readonly(*accounts[1].key, false)],
    );
    let instruction2 = Instruction::new_with_bytes(
        *accounts[1].key,
        &[2],
        vec![
            AccountMeta::new_readonly(*accounts[0].key, true),
            AccountMeta::new_readonly(*accounts[1].key, false),
        ],
    );
    let instruction1 = Instruction::new_with_bytes(
        *accounts[1].key,
        &[1],
        vec![
            AccountMeta::new_readonly(*accounts[1].key, false),
            AccountMeta::new_readonly(*accounts[0].key, true),
        ],
    );
    let instruction0 = Instruction::new_with_bytes(
        *accounts[3].key,
        &[0],
        vec![
            AccountMeta::new_readonly(*accounts[0].key, false),
            AccountMeta::new_readonly(*accounts[1].key, false),
            AccountMeta::new_readonly(*accounts[2].key, false),
            AccountMeta::new_readonly(*accounts[3].key, false),
        ],
    );
    invoke(&instruction3, accounts)?;
    invoke(&instruction2, accounts)?;
    invoke(&instruction1, accounts)?;
    invoke(&instruction0, accounts)?;

    // Check sibling instructions

    let sibling_instruction1 = Instruction::new_with_bytes(
        *accounts[1].key,
        &[43],
        vec![
            AccountMeta::new_readonly(*accounts[1].key, false),
            AccountMeta::new(*accounts[0].key, true),
        ],
    );
    let sibling_instruction0 = Instruction::new_with_bytes(
        *accounts[1].key,
        &[42],
        vec![
            AccountMeta::new(*accounts[0].key, true),
            AccountMeta::new_readonly(*accounts[1].key, false),
        ],
    );

    assert_eq!(TRANSACTION_LEVEL_STACK_HEIGHT, get_stack_height());
    assert_eq!(
        get_processed_sibling_instruction(0),
        Some(sibling_instruction0)
    );
    assert_eq!(
        get_processed_sibling_instruction(1),
        Some(sibling_instruction1)
    );
    assert_eq!(get_processed_sibling_instruction(2), None);

    Ok(())
}
