Procházet zdrojové kódy

Update IDL program (#2365)

Henry-E před 2 roky
rodič
revize
2a05075760

+ 1 - 0
CHANGELOG.md

@@ -22,6 +22,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
 

+ 0 - 27
lang/src/ctor.rs

@@ -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>,
-}

+ 6 - 82
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..])
-    }
-}

+ 0 - 3
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;
@@ -282,8 +281,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;

+ 13 - 11
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
                 }

+ 12 - 205
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<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();
-                accounts.idl.data_len = accounts.idl.data_len.checked_add(::std::convert::TryInto::<u32>::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::<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(())
-            }
         }
     };
 
@@ -364,6 +170,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                 use super::*;
 
                 #non_inlined_idl
+                #idl_accounts_and_functions
             }
 
 

+ 322 - 0
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<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();
+            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(())
+        }
+    }
+}

+ 1 - 0
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 {