anchor/lang/syn/src/codegen/program/idl.rs

323 lines
12 KiB
Rust

use quote::quote;
pub fn idl_accounts_and_functions() -> proc_macro2::TokenStream {
quote! {
use anchor_lang::idl::ERASED_AUTHORITY;
#[account("internal")]
#[derive(Debug)]
pub struct IdlAccount {
// Address that can modify the IDL.
pub authority: Pubkey,
// Length of compressed idl bytes.
pub data_len: u32,
// Followed by compressed idl bytes.
}
impl IdlAccount {
pub fn address(program_id: &Pubkey) -> Pubkey {
let program_signer = Pubkey::find_program_address(&[], program_id).0;
Pubkey::create_with_seed(&program_signer, IdlAccount::seed(), program_id)
.expect("Seed is always valid")
}
pub fn seed() -> &'static str {
"anchor:idl"
}
}
// Hacky workaround because of some internals to how account attribute
// works. Namespaces are the root of most of the problem.
impl anchor_lang::Owner for IdlAccount {
fn owner() -> Pubkey {
crate::ID
}
}
// Accounts for the Create instruction.
#[derive(Accounts)]
pub struct IdlCreateAccounts<'info> {
// Payer of the transaction.
#[account(signer)]
pub from: AccountInfo<'info>,
// The deterministically defined "state" account being created via
// `create_account_with_seed`.
#[account(mut)]
pub to: AccountInfo<'info>,
// The program-derived-address signing off on the account creation.
// Seeds = &[] + bump seed.
#[account(seeds = [], bump)]
pub base: AccountInfo<'info>,
// The system program.
pub system_program: Program<'info, System>,
// The program whose state is being constructed.
#[account(executable)]
pub program: AccountInfo<'info>,
}
// Accounts for Idl instructions.
#[derive(Accounts)]
pub struct IdlAccounts<'info> {
#[account(mut, has_one = authority)]
pub idl: Account<'info, IdlAccount>,
#[account(constraint = authority.key != &ERASED_AUTHORITY)]
pub authority: Signer<'info>,
}
// Accounts for resize account instruction
#[derive(Accounts)]
pub struct IdlResizeAccount<'info> {
#[account(mut, has_one = authority)]
pub idl: Account<'info, IdlAccount>,
#[account(mut, constraint = authority.key != &ERASED_AUTHORITY)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
// Accounts for creating an idl buffer.
#[derive(Accounts)]
pub struct IdlCreateBuffer<'info> {
#[account(zero)]
pub buffer: Account<'info, IdlAccount>,
#[account(constraint = authority.key != &ERASED_AUTHORITY)]
pub authority: Signer<'info>,
}
// Accounts for upgrading the canonical IdlAccount with the buffer.
#[derive(Accounts)]
pub struct IdlSetBuffer<'info> {
// The buffer with the new idl data.
#[account(mut, constraint = buffer.authority == idl.authority)]
pub buffer: Account<'info, IdlAccount>,
// The idl account to be updated with the buffer's data.
#[account(mut, has_one = authority)]
pub idl: Account<'info, IdlAccount>,
#[account(constraint = authority.key != &ERASED_AUTHORITY)]
pub authority: Signer<'info>,
}
// Accounts for closing the canonical Idl buffer.
#[derive(Accounts)]
pub struct IdlCloseAccount<'info> {
#[account(mut, has_one = authority, close = sol_destination)]
pub account: Account<'info, IdlAccount>,
#[account(constraint = authority.key != &ERASED_AUTHORITY)]
pub authority: Signer<'info>,
#[account(mut)]
pub sol_destination: AccountInfo<'info>,
}
use std::cell::{Ref, RefMut};
pub trait IdlTrailingData<'info> {
fn trailing_data(self) -> Ref<'info, [u8]>;
fn trailing_data_mut(self) -> RefMut<'info, [u8]>;
}
impl<'a, 'info: 'a> IdlTrailingData<'a> for &'a Account<'info, IdlAccount> {
fn trailing_data(self) -> Ref<'a, [u8]> {
let info: &AccountInfo<'info> = self.as_ref();
Ref::map(info.try_borrow_data().unwrap(), |d| &d[44..])
}
fn trailing_data_mut(self) -> RefMut<'a, [u8]> {
let info: &AccountInfo<'info> = self.as_ref();
RefMut::map(info.try_borrow_mut_data().unwrap(), |d| &mut d[44..])
}
}
// One time IDL account initializer. Will fail on subsequent
// invocations.
#[inline(never)]
pub fn __idl_create_account(
program_id: &Pubkey,
accounts: &mut IdlCreateAccounts,
data_len: u64,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlCreateAccount");
if program_id != accounts.program.key {
return Err(anchor_lang::error::ErrorCode::IdlInstructionInvalidProgram.into());
}
// Create the IDL's account.
let from = accounts.from.key;
let (base, nonce) = Pubkey::find_program_address(&[], program_id);
let seed = IdlAccount::seed();
let owner = accounts.program.key;
let to = Pubkey::create_with_seed(&base, seed, owner).unwrap();
// Space: account discriminator || authority pubkey || vec len || vec data
let space = std::cmp::min(8 + 32 + 4 + data_len as usize, 10_000);
let rent = Rent::get()?;
let lamports = rent.minimum_balance(space);
let seeds = &[&[nonce][..]];
let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed(
from,
&to,
&base,
seed,
lamports,
space as u64,
owner,
);
anchor_lang::solana_program::program::invoke_signed(
&ix,
&[
accounts.from.clone(),
accounts.to.clone(),
accounts.base.clone(),
accounts.system_program.to_account_info().clone(),
],
&[seeds],
)?;
// Deserialize the newly created account.
let mut idl_account = {
let mut account_data = accounts.to.try_borrow_data()?;
let mut account_data_slice: &[u8] = &account_data;
IdlAccount::try_deserialize_unchecked(
&mut account_data_slice,
)?
};
// Set the authority.
idl_account.authority = *accounts.from.key;
// Store the new account data.
let mut data = accounts.to.try_borrow_mut_data()?;
let dst: &mut [u8] = &mut data;
let mut cursor = std::io::Cursor::new(dst);
idl_account.try_serialize(&mut cursor)?;
Ok(())
}
#[inline(never)]
pub fn __idl_resize_account(
program_id: &Pubkey,
accounts: &mut IdlResizeAccount,
data_len: u64,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlResizeAccount");
let data_len: usize = data_len as usize;
// We're not going to support increasing the size of accounts that already contain data
// because that would be messy and possibly dangerous
if accounts.idl.data_len != 0 {
return Err(anchor_lang::error::ErrorCode::IdlAccountNotEmpty.into());
}
let new_account_space = accounts.idl.to_account_info().data_len().checked_add(std::cmp::min(
data_len
.checked_sub(accounts.idl.to_account_info().data_len())
.expect("data_len should always be >= the current account space"),
10_000,
))
.unwrap();
if new_account_space > accounts.idl.to_account_info().data_len() {
let sysvar_rent = Rent::get()?;
let new_rent_minimum = sysvar_rent.minimum_balance(new_account_space);
anchor_lang::system_program::transfer(
anchor_lang::context::CpiContext::new(
accounts.system_program.to_account_info(),
anchor_lang::system_program::Transfer {
from: accounts.authority.to_account_info(),
to: accounts.idl.to_account_info().clone(),
},
),
new_rent_minimum
.checked_sub(accounts.idl.to_account_info().lamports())
.unwrap(),
)?;
accounts.idl.to_account_info().realloc(new_account_space, false)?;
}
Ok(())
}
#[inline(never)]
pub fn __idl_close_account(
program_id: &Pubkey,
accounts: &mut IdlCloseAccount,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlCloseAccount");
Ok(())
}
#[inline(never)]
pub fn __idl_create_buffer(
program_id: &Pubkey,
accounts: &mut IdlCreateBuffer,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlCreateBuffer");
let mut buffer = &mut accounts.buffer;
buffer.authority = *accounts.authority.key;
Ok(())
}
#[inline(never)]
pub fn __idl_write(
program_id: &Pubkey,
accounts: &mut IdlAccounts,
idl_data: Vec<u8>,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlWrite");
let prev_len: usize = ::std::convert::TryInto::<usize>::try_into(accounts.idl.data_len).unwrap();
let new_len: usize = prev_len + idl_data.len() as usize;
accounts.idl.data_len = accounts.idl.data_len.checked_add(::std::convert::TryInto::<u32>::try_into(idl_data.len()).unwrap()).unwrap();
use IdlTrailingData;
let mut idl_bytes = accounts.idl.trailing_data_mut();
let idl_expansion = &mut idl_bytes[prev_len..new_len];
require_eq!(idl_expansion.len(), idl_data.len());
idl_expansion.copy_from_slice(&idl_data[..]);
Ok(())
}
#[inline(never)]
pub fn __idl_set_authority(
program_id: &Pubkey,
accounts: &mut IdlAccounts,
new_authority: Pubkey,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlSetAuthority");
accounts.idl.authority = new_authority;
Ok(())
}
#[inline(never)]
pub fn __idl_set_buffer(
program_id: &Pubkey,
accounts: &mut IdlSetBuffer,
) -> anchor_lang::Result<()> {
#[cfg(not(feature = "no-log-ix-name"))]
anchor_lang::prelude::msg!("Instruction: IdlSetBuffer");
accounts.idl.data_len = accounts.buffer.data_len;
use IdlTrailingData;
let buffer_len = ::std::convert::TryInto::<usize>::try_into(accounts.buffer.data_len).unwrap();
let mut target = accounts.idl.trailing_data_mut();
let source = &accounts.buffer.trailing_data()[..buffer_len];
require_gte!(target.len(), buffer_len);
target[..buffer_len].copy_from_slice(source);
// zero the remainder of target?
Ok(())
}
}
}