1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use anchor_lang::{error::Error, prelude::*, Bumps};

use crate::traits::{
    InvokeAccounts, InvokeCpiAccounts, InvokeCpiContextAccount, LightSystemAccount, SignerAccounts,
};
use light_system_program::{
    cpi::accounts::InvokeCpiInstruction, errors::SystemProgramError::CpiContextAccountUndefined,
    sdk::CompressedCpiContext, InstructionDataInvokeCpi,
};

// TODO: properly document compressed-cpi-context
// TODO: turn into a simple check!
// TOOD: CHECK needed bc can be different from own, if called from another program.
pub fn get_compressed_cpi_context_account<'info>(
    ctx: &Context<
        '_,
        '_,
        '_,
        'info,
        impl InvokeAccounts<'info>
            + LightSystemAccount<'info>
            + InvokeCpiAccounts<'info>
            + SignerAccounts<'info>
            + Bumps,
    >,
    compressed_cpi_context: &CompressedCpiContext,
) -> Result<AccountInfo<'info>> {
    let cpi_context_account = ctx
        .remaining_accounts
        .get(compressed_cpi_context.cpi_context_account_index as usize)
        .map(|account| account.to_account_info())
        .ok_or_else(|| Error::from(CpiContextAccountUndefined))?;
    Ok(cpi_context_account)
}

#[inline(always)]
pub fn setup_cpi_accounts<'info>(
    ctx: &Context<
        '_,
        '_,
        '_,
        'info,
        impl InvokeAccounts<'info>
            + LightSystemAccount<'info>
            + InvokeCpiAccounts<'info>
            + SignerAccounts<'info>
            + InvokeCpiContextAccount<'info>
            + Bumps,
    >,
) -> InvokeCpiInstruction<'info> {
    InvokeCpiInstruction {
        fee_payer: ctx.accounts.get_fee_payer().to_account_info(),
        authority: ctx.accounts.get_authority().to_account_info(),
        registered_program_pda: ctx.accounts.get_registered_program_pda().to_account_info(),
        noop_program: ctx.accounts.get_noop_program().to_account_info(),
        account_compression_authority: ctx
            .accounts
            .get_account_compression_authority()
            .to_account_info(),
        account_compression_program: ctx
            .accounts
            .get_account_compression_program()
            .to_account_info(),
        invoking_program: ctx.accounts.get_invoking_program().to_account_info(),
        sol_pool_pda: None,
        decompression_recipient: None,
        system_program: ctx.accounts.get_system_program().to_account_info(),
        cpi_context_account: ctx
            .accounts
            .get_cpi_context_account()
            .map(|acc| acc.to_account_info()),
    }
}

#[inline(always)]
pub fn invoke_cpi<'info, 'a, 'b, 'c>(
    ctx: &Context<
        '_,
        '_,
        '_,
        'info,
        impl InvokeAccounts<'info>
            + LightSystemAccount<'info>
            + InvokeCpiAccounts<'info>
            + SignerAccounts<'info>
            + InvokeCpiContextAccount<'info>
            + Bumps,
    >,
    cpi_accounts: light_system_program::cpi::accounts::InvokeCpiInstruction<'info>,
    inputs: Vec<u8>,
    signer_seeds: &'a [&'b [&'c [u8]]],
) -> Result<()> {
    light_system_program::cpi::invoke_cpi(
        CpiContext::new_with_signer(
            ctx.accounts.get_light_system_program().to_account_info(),
            cpi_accounts,
            signer_seeds,
        )
        .with_remaining_accounts(ctx.remaining_accounts.to_vec()),
        inputs,
    )
}

/// Invokes the light system program to verify and apply a zk-compressed state
/// transition. Serializes CPI instruction data, configures necessary accounts,
/// and executes the CPI.
pub fn verify<'info, 'a, 'b, 'c>(
    ctx: &Context<
        '_,
        '_,
        '_,
        'info,
        impl InvokeAccounts<'info>
            + LightSystemAccount<'info>
            + InvokeCpiAccounts<'info>
            + SignerAccounts<'info>
            + InvokeCpiContextAccount<'info>
            + Bumps,
    >,
    inputs_struct: &InstructionDataInvokeCpi,
    signer_seeds: &'a [&'b [&'c [u8]]],
) -> Result<()> {
    let mut inputs: Vec<u8> = Vec::new();
    InstructionDataInvokeCpi::serialize(inputs_struct, &mut inputs).unwrap();

    let cpi_accounts = setup_cpi_accounts(ctx);
    invoke_cpi(ctx, cpi_accounts, inputs, signer_seeds)
}