1
use crate::error::ClientError;
2
use crate::util::execution_address;
3
use bonsol_schema::root_as_execution_request_v1;
4
use solana_program::account_info::AccountInfo;
5
use solana_program::program_error::ProgramError;
6
use solana_program::program_memory::sol_memcmp;
7
use solana_program::pubkey::Pubkey;
8

            
9
pub struct BonsolCallback<'a> {
10
    pub input_digest: &'a [u8],
11
    pub committed_outputs: &'a [u8],
12
}
13
/// This is the callback handler for the bonsol program, use this to properly validate an incoming callback from bonsol
14
/// Ensure you strip the instruction prefix from the data before passing it to this function and that the Execution Id
15
/// matches the one in the execution request account
16
pub fn handle_callback<'a>(
17
    image_id: &str,
18
    execution_account: &Pubkey,
19
    accounts: &[AccountInfo],
20
    stripped_data: &'a [u8],
21
) -> Result<BonsolCallback<'a>, ProgramError> {
22
    let er_info = accounts
23
        .first()
24
        .ok_or::<ProgramError>(ClientError::InvalidCallbackInstructionAccounts.into())?;
25

            
26
    if sol_memcmp(er_info.key.as_ref(), execution_account.as_ref(), 32) != 0 {
27
        return Err(ClientError::InvalidCallbackInstructionAccounts.into());
28
    }
29
    if sol_memcmp(er_info.owner.as_ref(), crate::util::ID.as_ref(), 32) != 0 {
30
        return Err(ClientError::InvalidCallbackInstructionAccounts.into());
31
    }
32
    if !er_info.is_signer {
33
        return Err(ClientError::InvalidCallbackSignature.into());
34
    }
35
    let er_data = &er_info.try_borrow_data()?;
36
    if er_data.len() < 2 {
37
        return Err(ClientError::ExecutionRequestReused.into());
38
    }
39
    // Ensure this is a valid execution request data
40
    let er =
41
        root_as_execution_request_v1(er_data).map_err(|_| ProgramError::InvalidInstructionData)?;
42
    if er.image_id() != Some(image_id) {
43
        return Err(ClientError::InvalidCallbackImageId.into());
44
    }
45
    let (input_digest, committed_outputs) = stripped_data.split_at(32);
46
    Ok(BonsolCallback {
47
        input_digest,
48
        committed_outputs,
49
    })
50
}
51

            
52
pub fn handle_callback_id<'a>(
53
    image_id: &str,
54
    execution_id: &str,
55
    request_account: &Pubkey,
56
    accounts: &'a [AccountInfo<'a>],
57
    data: &'a [u8],
58
) -> Result<BonsolCallback<'a>, ProgramError> {
59
    let (execution_account, _) = execution_address(request_account, execution_id.as_bytes());
60
    handle_callback(image_id, &execution_account, accounts, data)
61
}