diff --git a/CHANGELOG.md b/CHANGELOG.md index 80460fff17..fc47c2d740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The minor version will be incremented upon a breaking change and the patch versi - cli: Don't regenerate idl in read_all_programs(). ([#2332](https://github.com/coral-xyz/anchor/pull/2332)). - ts: `provider.simulate` will send the transaction with `sigVerify: false` if no `signers` are present ([#2331](https://github.com/coral-xyz/anchor/pull/2331)). +- idl: Update the IDL program to use non-deprecated account types ([#2365](https://github.com/coral-xyz/anchor/pull/2365)). ### Breaking diff --git a/lang/src/ctor.rs b/lang/src/ctor.rs deleted file mode 100644 index c24d392e9f..0000000000 --- a/lang/src/ctor.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{Accounts, ToAccountInfo}; -use solana_program::account_info::AccountInfo; - -/// The Ctor accounts that can be used to create any account within the program -/// itself (instead of creating the account on the client). -/// -/// This is used to create accounts at deterministic addresses, as a function of -/// nothing but a program ID--for example, to create state global program -/// structs and program IDL accounts. It's currently used **internally** within -/// the Anchor `#[program]` codegen. -#[derive(Accounts)] -pub struct Ctor<'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. - pub base: AccountInfo<'info>, - // The system program. - pub system_program: AccountInfo<'info>, - // The program whose state is being constructed. - pub program: AccountInfo<'info>, -} diff --git a/lang/src/idl.rs b/lang/src/idl.rs index bcb3d521a4..24544bd482 100644 --- a/lang/src/idl.rs +++ b/lang/src/idl.rs @@ -17,8 +17,6 @@ //! Note that IDL account instructions are automatically inserted into all //! Anchor programs. To remove them, one can use the `no-idl` feature. -#[allow(deprecated)] -use crate::accounts::program_account::ProgramAccount; use crate::prelude::*; use solana_program::pubkey::Pubkey; @@ -28,6 +26,7 @@ use solana_program::pubkey::Pubkey; // // Sha256(anchor:idl)[..8]; pub const IDL_IX_TAG: u64 = 0x0a69e9a778bcf440; +pub const IDL_IX_TAG_LE: [u8; 8] = IDL_IX_TAG.to_le_bytes(); // The Pubkey that is stored as the 'authority' on the IdlAccount when the authority // is "erased". @@ -50,72 +49,16 @@ pub enum IdlInstruction { Resize { data_len: u64 }, } -// Accounts for the Create instruction. -pub type IdlCreateAccounts<'info> = crate::ctor::Ctor<'info>; - -// Accounts for Idl instructions. -#[derive(Accounts)] -pub struct IdlAccounts<'info> { - #[account(mut, has_one = authority)] - #[allow(deprecated)] - pub idl: ProgramAccount<'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)] - #[allow(deprecated)] - pub idl: ProgramAccount<'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)] - #[allow(deprecated)] - pub buffer: ProgramAccount<'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)] - #[allow(deprecated)] - pub buffer: ProgramAccount<'info, IdlAccount>, - // The idl account to be updated with the buffer's data. - #[account(mut, has_one = authority)] - #[allow(deprecated)] - pub idl: ProgramAccount<'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)] - #[allow(deprecated)] - pub account: ProgramAccount<'info, IdlAccount>, - #[account(constraint = authority.key != &ERASED_AUTHORITY)] - pub authority: Signer<'info>, - #[account(mut)] - pub sol_destination: AccountInfo<'info>, -} - // The account holding a program's IDL. This is stored on chain so that clients // can fetch it and generate a client with nothing but a program's ID. // // Note: we use the same account for the "write buffer", similar to the // bpf upgradeable loader's mechanism. +// +// TODO: IdlAccount exists here only because it's needed by the CLI, the IDL +// itself uses an IdlAccount defined inside the program itself, see program/idl.rs. +// Ideally it would be deleted and a better solution for sharing the type with CLI +// could be found. #[account("internal")] #[derive(Debug)] pub struct IdlAccount { @@ -136,22 +79,3 @@ impl IdlAccount { "anchor:idl" } } - -use std::cell::{Ref, RefMut}; - -pub trait IdlTrailingData<'info> { - fn trailing_data(self) -> Ref<'info, [u8]>; - fn trailing_data_mut(self) -> RefMut<'info, [u8]>; -} - -#[allow(deprecated)] -impl<'a, 'info: 'a> IdlTrailingData<'a> for &'a ProgramAccount<'info, IdlAccount> { - fn trailing_data(self) -> Ref<'a, [u8]> { - let info = self.as_ref(); - Ref::map(info.try_borrow_data().unwrap(), |d| &d[44..]) - } - fn trailing_data_mut(self) -> RefMut<'a, [u8]> { - let info = self.as_ref(); - RefMut::map(info.try_borrow_mut_data().unwrap(), |d| &mut d[44..]) - } -} diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 68fd7d36c2..9bba6382ee 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -36,7 +36,6 @@ mod bpf_upgradeable_state; mod bpf_writer; mod common; pub mod context; -mod ctor; pub mod error; #[doc(hidden)] pub mod idl; @@ -276,8 +275,6 @@ pub mod __private { /// The discriminator anchor uses to mark an account as closed. pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255]; - pub use crate::ctor::Ctor; - pub use anchor_attribute_account::ZeroCopyAccessor; pub use anchor_attribute_event::EventIndex; diff --git a/lang/syn/src/codegen/program/dispatch.rs b/lang/syn/src/codegen/program/dispatch.rs index c0bf87b627..dac9015082 100644 --- a/lang/syn/src/codegen/program/dispatch.rs +++ b/lang/syn/src/codegen/program/dispatch.rs @@ -61,21 +61,23 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { sighash }; - // If the method identifier is the IDL tag, then execute an IDL - // instruction, injected into all Anchor programs. - if cfg!(not(feature = "no-idl")) { - if sighash == anchor_lang::idl::IDL_IX_TAG.to_le_bytes() { - return __private::__idl::__idl_dispatch( - program_id, - accounts, - &ix_data, - ); - } - } use anchor_lang::Discriminator; match sighash { #(#global_dispatch_arms)* + anchor_lang::idl::IDL_IX_TAG_LE => { + // If the method identifier is the IDL tag, then execute an IDL + // instruction, injected into all Anchor programs. + if cfg!(not(feature = "no-idl")) { + __private::__idl::__idl_dispatch( + program_id, + accounts, + &ix_data, + ) + } else { + Err(anchor_lang::error::ErrorCode::IdlInstructionStub.into()) + } + } _ => { #fallback_fn } diff --git a/lang/syn/src/codegen/program/handlers.rs b/lang/syn/src/codegen/program/handlers.rs index 990a617611..26b94858b2 100644 --- a/lang/syn/src/codegen/program/handlers.rs +++ b/lang/syn/src/codegen/program/handlers.rs @@ -1,4 +1,5 @@ use crate::codegen::program::common::*; +use crate::program_codegen::idl::idl_accounts_and_functions; use crate::Program; use heck::CamelCase; use quote::{quote, ToTokens}; @@ -8,6 +9,9 @@ use quote::{quote, ToTokens}; // so. pub fn generate(program: &Program) -> proc_macro2::TokenStream { let program_name = &program.name; + // A constant token stream that stores the accounts and functions, required to live + // inside the target program in order to get the program ID. + let idl_accounts_and_functions = idl_accounts_and_functions(); let non_inlined_idl: proc_macro2::TokenStream = { quote! { // Entry for all IDL related instructions. Use the "no-idl" feature @@ -28,7 +32,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlCreateAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlCreateAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_create_account(program_id, &mut accounts, data_len)?; accounts.exit(program_id)?; }, @@ -36,7 +40,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlResizeAccount::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlResizeAccount::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_resize_account(program_id, &mut accounts, data_len)?; accounts.exit(program_id)?; }, @@ -44,7 +48,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlCloseAccount::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlCloseAccount::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_close_account(program_id, &mut accounts)?; accounts.exit(program_id)?; }, @@ -52,7 +56,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlCreateBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlCreateBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_create_buffer(program_id, &mut accounts)?; accounts.exit(program_id)?; }, @@ -60,7 +64,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_write(program_id, &mut accounts, data)?; accounts.exit(program_id)?; }, @@ -68,7 +72,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_set_authority(program_id, &mut accounts, new_authority)?; accounts.exit(program_id)?; }, @@ -76,7 +80,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); let mut accounts = - anchor_lang::idl::IdlSetBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + IdlSetBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; __idl_set_buffer(program_id, &mut accounts)?; accounts.exit(program_id)?; }, @@ -84,204 +88,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { Ok(()) } - #[inline(never)] - #[cfg(feature = "no-idl")] - pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> anchor_lang::Result<()> { - Err(anchor_lang::error::ErrorCode::IdlInstructionStub.into()) - } - - // One time IDL account initializer. Will faill on subsequent - // invocations. - #[inline(never)] - pub fn __idl_create_account( - program_id: &Pubkey, - accounts: &mut anchor_lang::idl::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 = anchor_lang::idl::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.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; - anchor_lang::idl::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 anchor_lang::idl::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 anchor_lang::idl::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 anchor_lang::idl::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 anchor_lang::idl::IdlAccounts, - idl_data: Vec, - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!("Instruction: IdlWrite"); - - let prev_len: usize = ::std::convert::TryInto::::try_into(accounts.idl.data_len).unwrap(); - let new_len: usize = prev_len + idl_data.len(); - accounts.idl.data_len = accounts.idl.data_len.checked_add(::std::convert::TryInto::::try_into(idl_data.len()).unwrap()).unwrap(); - - use anchor_lang::idl::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 anchor_lang::idl::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 anchor_lang::idl::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 anchor_lang::idl::IdlTrailingData; - let buffer_len = ::std::convert::TryInto::::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(()) - } } }; @@ -364,6 +170,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { use super::*; #non_inlined_idl + #idl_accounts_and_functions } diff --git a/lang/syn/src/codegen/program/idl.rs b/lang/syn/src/codegen/program/idl.rs new file mode 100644 index 0000000000..a1ef7f4d7f --- /dev/null +++ b/lang/syn/src/codegen/program/idl.rs @@ -0,0 +1,322 @@ +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, + ) -> anchor_lang::Result<()> { + #[cfg(not(feature = "no-log-ix-name"))] + anchor_lang::prelude::msg!("Instruction: IdlWrite"); + + let prev_len: usize = ::std::convert::TryInto::::try_into(accounts.idl.data_len).unwrap(); + let new_len: usize = prev_len + idl_data.len(); + accounts.idl.data_len = accounts.idl.data_len.checked_add(::std::convert::TryInto::::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::::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(()) + } + } +} diff --git a/lang/syn/src/codegen/program/mod.rs b/lang/syn/src/codegen/program/mod.rs index ea5aff0c1b..22bec8adcf 100644 --- a/lang/syn/src/codegen/program/mod.rs +++ b/lang/syn/src/codegen/program/mod.rs @@ -7,6 +7,7 @@ mod cpi; mod dispatch; mod entry; mod handlers; +mod idl; mod instruction; pub fn generate(program: &Program) -> proc_macro2::TokenStream {