Browse Source

lang: rest of accounts docs reference (#1231)

Paul 3 years ago
parent
commit
3ddad6cbb6

+ 1 - 1
lang/attribute/account/src/lib.rs

@@ -56,7 +56,7 @@ mod id;
 /// To facilitate this, all fields in an account must be constrained to be
 /// "plain old  data", i.e., they must implement
 /// [`Pod`](../bytemuck/trait.Pod.html). Please review the
-/// [`safety`](file:///home/armaniferrante/Documents/code/src/github.com/project-serum/anchor/target/doc/bytemuck/trait.Pod.html#safety)
+/// [`safety`](../bytemuck/trait.Pod.html#safety)
 /// section before using.
 #[proc_macro_attribute]
 pub fn account(

+ 114 - 1
lang/src/accounts/account.rs

@@ -13,6 +13,13 @@ use std::ops::{Deref, DerefMut};
 /// Wrapper around [`AccountInfo`](crate::solana_program::account_info::AccountInfo)
 /// that verifies program ownership and deserializes underlying data into a Rust type.
 ///
+/// # Table of Contents
+/// - [Basic Functionality](#basic-functionality)
+/// - [Using Account with non-anchor types](#using-account-with-non-anchor-types)
+/// - [Out of the box wrapper types](#out-of-the-box-wrapper-types)
+///
+/// # Basic Functionality
+///
 /// Account checks that `Account.info.owner == T::owner()`.
 /// This means that the data type that Accounts wraps around (`=T`) needs to
 /// implement the [Owner trait](crate::Owner).
@@ -79,7 +86,7 @@ use std::ops::{Deref, DerefMut};
 /// functions `#[account]` generates. See the example below for the code you have
 /// to write.
 ///
-/// The mint wrapper type Anchor provides out of the box for the token program ([source](https://github.com/project-serum/anchor/blob/master/spl/src/token.rs))
+/// The mint wrapper type that Anchor provides out of the box for the token program ([source](https://github.com/project-serum/anchor/blob/master/spl/src/token.rs))
 /// ```ignore
 /// #[derive(Clone)]
 /// pub struct Mint(spl_token::state::Mint);
@@ -121,6 +128,96 @@ use std::ops::{Deref, DerefMut};
 ///     }
 /// }
 /// ```
+///
+/// ## Out of the box wrapper types
+///
+/// ### Accessing BPFUpgradeableLoader Data
+///
+/// Anchor provides wrapper types to access data stored in programs owned by the BPFUpgradeableLoader
+/// such as the upgrade authority. If you're interested in the data of a program account, you can use
+/// ```ignore
+/// Account<'info, BpfUpgradeableLoaderState>
+/// ```
+/// and then match on its contents inside your instruction function.
+///
+/// Alternatively, you can use
+/// ```ignore
+/// Account<'info, ProgramData>
+/// ```
+/// to let anchor do the matching for you and return the ProgramData variant of BpfUpgradeableLoaderState.
+///
+/// # Example
+/// ```ignore
+/// use anchor_lang::prelude::*;
+/// use crate::program::MyProgram;
+///
+/// declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
+///
+/// #[program]
+/// pub mod my_program {
+///     use super::*;
+///
+///     pub fn set_initial_admin(
+///         ctx: Context<SetInitialAdmin>,
+///         admin_key: Pubkey
+///     ) -> ProgramResult {
+///         ctx.accounts.admin_settings.admin_key = admin_key;
+///         Ok(())
+///     }
+///
+///     pub fn set_admin(...){...}
+///
+///     pub fn set_settings(...){...}
+/// }
+///
+/// #[account]
+/// #[derive(Default, Debug)]
+/// pub struct AdminSettings {
+///     admin_key: Pubkey
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct SetInitialAdmin<'info> {
+///     #[account(init, payer = authority, seeds = [b"admin"], bump)]
+///     pub admin_settings: Account<'info, AdminSettings>,
+///     #[account(mut)]
+///     pub authority: Signer<'info>,
+///     #[account(constraint = program.programdata_address() == Some(program_data.key()))]
+///     pub program: Program<'info, MyProgram>,
+///     #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
+///     pub program_data: Account<'info, ProgramData>,
+///     pub system_program: Program<'info, System>,
+/// }
+/// ```
+///
+/// This example solves a problem you may face if your program has admin settings: How do you set the
+/// admin key for restricted functionality after deployment? Setting the admin key itself should
+/// be a restricted action but how do you restrict it without having set an admin key?
+/// You're stuck in a loop.
+/// One solution is to use the upgrade authority of the program as the initial
+/// (or permanent) admin key.
+///
+/// ### SPL Types
+///
+/// Anchor provides wrapper types to access accounts owned by the token program. Use
+/// ```ignore
+/// use anchor_spl::token::TokenAccount;
+///
+/// #[derive(Accounts)]
+/// pub struct Example {
+///     pub my_acc: Account<'info, TokenAccount>
+/// }
+/// ```
+/// to access token accounts and
+/// ```ignore
+/// use anchor_spl::token::Mint;
+///
+/// #[derive(Accounts)]
+/// pub struct Example {
+///     pub my_acc: Account<'info, Mint>
+/// }
+/// ```
+/// to access mint accounts.
 #[derive(Clone)]
 pub struct Account<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> {
     account: T,
@@ -186,6 +283,22 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
         self.account
     }
 
+    /// Sets the inner account.
+    ///
+    /// Instead of this:
+    /// ```ignore
+    /// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> ProgramResult {
+    ///     (*ctx.accounts.user_to_create).name = new_user.name;
+    ///     (*ctx.accounts.user_to_create).age = new_user.age;
+    ///     (*ctx.accounts.user_to_create).address = new_user.address;
+    /// }
+    /// ```
+    /// You can do this:
+    /// ```ignore
+    /// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> ProgramResult {
+    ///     ctx.accounts.user_to_create.set_inner(new_user);
+    /// }
+    /// ```
     pub fn set_inner(&mut self, inner: T) {
         self.account = inner;
     }

+ 4 - 0
lang/src/accounts/account_info.rs

@@ -1,3 +1,7 @@
+//! AccountInfo can be used as a type but
+//! [Unchecked Account](crate::accounts::unchecked_account::UncheckedAccount)
+//! should be used instead.
+
 use crate::error::ErrorCode;
 use crate::{Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas};
 use solana_program::account_info::AccountInfo;

+ 15 - 0
lang/src/accounts/boxed.rs

@@ -1,3 +1,18 @@
+//! Box<T> type to save stack space.
+//!
+//! Sometimes accounts are too large for the stack,
+//! leading to stack violations.
+//!
+//! Boxing the account can help.
+//!
+//! # Example
+//! ```ignore
+//! #[derive(Accounts)]
+//! pub struct Example {
+//!     pub my_acc: Box<Account<'info, MyData>>
+//! }
+//! ```
+
 use crate::{Accounts, AccountsClose, AccountsExit, ToAccountInfos, ToAccountMetas};
 use solana_program::account_info::AccountInfo;
 use solana_program::entrypoint::ProgramResult;

+ 73 - 7
lang/src/accounts/loader_account.rs

@@ -1,3 +1,5 @@
+//! Type facilitating on demand zero copy deserialization.
+
 use crate::error::ErrorCode;
 use crate::{
     Accounts, AccountsClose, AccountsExit, Key, Owner, ToAccountInfo, ToAccountInfos,
@@ -15,17 +17,81 @@ use std::io::Write;
 use std::marker::PhantomData;
 use std::ops::DerefMut;
 
-/// Account AccountLoader facilitating on demand zero copy deserialization.
+/// Type facilitating on demand zero copy deserialization.
+///
 /// Note that using accounts in this way is distinctly different from using,
 /// for example, the [`Account`](./struct.Account.html). Namely,
-/// one must call `load`, `load_mut`, or `load_init`, before reading or writing
-/// to the account. For more details on zero-copy-deserialization, see the
+/// one must call
+/// - `load_init` after initializing an account (this will ignore the missing
+/// account discriminator that gets added only after the user's instruction code)
+/// - `load` when the account is not mutable
+/// - `load_mut` when the account is mutable
+///
+/// For more details on zero-copy-deserialization, see the
 /// [`account`](./attr.account.html) attribute.
+/// <p style=";padding:0.75em;border: 1px solid #ee6868">
+/// <strong>⚠️ </strong> When using this type it's important to be mindful
+/// of any calls to the <code>load</code> functions so as not to
+/// induce a <code>RefCell</code> panic, especially when sharing accounts across CPI
+/// boundaries. When in doubt, one should make sure all refs resulting from
+/// a call to a <code>load</code> function are dropped before CPI.
+/// This can be done explicitly by calling <code>drop(my_var)</code> or implicitly
+/// by wrapping the code using the <code>Ref</code> in braces <code>{..}</code> or
+/// moving it into its own function.
+/// </p>
+///
+/// # Example
+/// ```ignore
+/// use anchor_lang::prelude::*;
+///
+/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+///
+/// #[program]
+/// pub mod bar {
+///     use super::*;
+///
+///     pub fn create_bar(ctx: Context<CreateBar>, data: u64) -> ProgramResult {
+///         let bar = &mut ctx.accounts.bar.load_init()?;
+///         bar.authority = ctx.accounts.authority.key();
+///         bar.data = data;
+///         Ok(())
+///     }
+///
+///     pub fn update_bar(ctx: Context<UpdateBar>, data: u64) -> ProgramResult {
+///         (*ctx.accounts.bar.load_mut()?).data = data;
+///         Ok(())
+///     }
+/// }
+///
+/// #[account(zero_copy)]
+/// #[derive(Default)]
+/// pub struct Bar {
+///     authority: Pubkey,
+///     data: u64
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct CreateBar<'info> {
+///     #[account(
+///         init,
+///         payer = authority
+///     )]
+///     bar: AccountLoader<'info, Bar>,
+///     #[account(mut)]
+///     authority: Signer<'info>,
+///     system_program: AccountInfo<'info>,
+/// }
 ///
-/// When using it's important to be mindful of any calls to `load` so as not to
-/// induce a `RefCell` panic, especially when sharing accounts across CPI
-/// boundaries. When in doubt, one should make sure all refs resulting from a
-/// call to `load` are dropped before CPI.
+/// #[derive(Accounts)]
+/// pub struct UpdateBar<'info> {
+///     #[account(
+///         mut,
+///         has_one = authority,
+///     )]
+///     pub bar: AccountLoader<'info, Bar>,
+///     pub authority: Signer<'info>,
+/// }
+/// ```
 #[derive(Clone)]
 pub struct AccountLoader<'info, T: ZeroCopy + Owner> {
     acc_info: AccountInfo<'info>,

+ 48 - 1
lang/src/accounts/program.rs

@@ -1,3 +1,5 @@
+//! Type validating that the account is the given Program
+
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -9,7 +11,52 @@ use std::fmt;
 use std::marker::PhantomData;
 use std::ops::Deref;
 
-/// Account container that checks ownership on deserialization.
+/// Type validating that the account is the given Program
+///
+/// The type has a `programdata_address` property that will be set
+/// if the program is owned by the [`BPFUpgradeableLoader`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/index.html)
+/// and will contain the `programdata_address` property of the `Program` variant of the [`UpgradeableLoaderState`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html) enum.
+///
+/// Checks:
+///
+/// - `Account.info.key == Program`
+/// - `Account.info.executable == true`
+///
+/// # Example
+/// ```ignore
+///
+/// #[program]
+/// mod my_program {
+///     fn set_admin_settings(...){...}
+/// }
+///
+/// #[account]
+/// #[derive(Default)]
+/// pub struct AdminSettings {
+///     ...
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct SetAdminSettings<'info> {
+///     #[account(mut, seeds = [b"admin"], bump)]
+///     pub admin_settings: Account<'info, AdminSettings>,
+///     #[account(constraint = program.programdata_address() == Some(program_data.key()))]
+///     pub program: Program<'info, MyProgram>,
+///     #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
+///     pub program_data: Account<'info, ProgramData>,
+///     pub authority: Signer<'info>,
+/// }
+/// ```
+/// The given program has a function with which the upgrade authority can set admin settings.
+///
+/// The required constraints are as follows:
+///
+/// - `program` is the account of the program itself.
+/// Its constraint checks that `program_data` is the account that contains the program's upgrade authority.
+/// Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()`
+/// will be `None` if it's not).
+/// - `program_data`'s constraint checks that its upgrade authority is the `authority` account.
+/// - Finally, `authority` needs to sign the transaction.
 #[derive(Clone)]
 pub struct Program<'info, T: Id + Clone> {
     info: AccountInfo<'info>,

+ 25 - 0
lang/src/accounts/signer.rs

@@ -1,3 +1,4 @@
+//! Type validating that the account signed the transaction
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -10,6 +11,30 @@ use std::ops::Deref;
 /// Type validating that the account signed the transaction. No other ownership
 /// or type checks are done. If this is used, one should not try to access the
 /// underlying account data.
+///
+/// Checks:
+///
+/// - `Signer.info.is_signer == true`
+///
+/// # Example
+/// ```ignore
+/// #[account]
+/// #[derive(Default)]
+/// pub struct MyData {
+///     pub data: u64
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct Example<'info> {
+///     #[account(init, payer = payer)]
+///     pub my_acc: Account<'info, MyData>,
+///     #[account(mut)]
+///     pub payer: Signer<'info>,
+///     pub system_program: Program<'info, System>
+/// }
+/// ```
+///
+/// When creating an account with `init`, the `payer` needs to sign the transaction.
 #[derive(Debug, Clone)]
 pub struct Signer<'info> {
     info: AccountInfo<'info>,

+ 7 - 0
lang/src/accounts/system_account.rs

@@ -1,3 +1,5 @@
+//! Type validating that the account is owned by the system program
+
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -8,6 +10,11 @@ use solana_program::pubkey::Pubkey;
 use solana_program::system_program;
 use std::ops::Deref;
 
+/// Type validating that the account is owned by the system program
+///
+/// Checks:
+///
+/// - `SystemAccount.info.owner == SystemProgram`
 #[derive(Debug, Clone)]
 pub struct SystemAccount<'info> {
     info: AccountInfo<'info>,

+ 23 - 1
lang/src/accounts/sysvar.rs

@@ -1,3 +1,5 @@
+//! Type validating that the account is a sysvar and deserializing it
+
 use crate::error::ErrorCode;
 use crate::{Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas};
 use solana_program::account_info::AccountInfo;
@@ -8,7 +10,27 @@ use solana_program::pubkey::Pubkey;
 use std::fmt;
 use std::ops::{Deref, DerefMut};
 
-/// Container for sysvars.
+/// Type validating that the account is a sysvar and deserializing it.
+///
+/// If possible, sysvars should not be used via accounts
+/// but by using the [`get`](https://docs.rs/solana-program/latest/solana_program/sysvar/trait.Sysvar.html#method.get)
+/// function on the desired sysvar. This is because using `get`
+/// does not run the risk of Anchor having a bug in its `Sysvar` type
+/// and using `get` also decreases tx size, making space for other
+/// accounts that cannot be requested via syscall.
+///
+/// # Example
+/// ```ignore
+/// // OK - via account in the account validation struct
+/// #[derive(Accounts)]
+/// pub struct Example<'info> {
+///     pub clock: Sysvar<'info, Clock>
+/// }
+/// // BETTER - via syscall in the instruction function
+/// fn better(ctx: Context<Better>) -> ProgramResult {
+///     let clock = Clock::get()?;
+/// }
+/// ```
 pub struct Sysvar<'info, T: solana_program::sysvar::Sysvar> {
     info: AccountInfo<'info>,
     account: T,

+ 5 - 1
lang/src/accounts/unchecked_account.rs

@@ -1,3 +1,6 @@
+//! Explicit wrapper for AccountInfo types to emphasize
+//! that no checks are performed
+
 use crate::error::ErrorCode;
 use crate::{Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas};
 use solana_program::account_info::AccountInfo;
@@ -7,7 +10,8 @@ use solana_program::program_error::ProgramError;
 use solana_program::pubkey::Pubkey;
 use std::ops::Deref;
 
-/// Explicit wrapper for AccountInfo types.
+/// Explicit wrapper for AccountInfo types to emphasize
+/// that no checks are performed
 #[derive(Debug, Clone)]
 pub struct UncheckedAccount<'info>(AccountInfo<'info>);
 

+ 6 - 5
tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs

@@ -2,9 +2,6 @@ use anchor_lang::prelude::*;
 
 declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
 
-// TODO: Once anchor can deserialize data of programs (=programdata_address) automatically, add another test to this file.
-// Instead of using UpgradeableLoaderState, it should use Program<'info, MY_PROGRAM>
-
 #[program]
 pub mod bpf_upgradeable_state {
     use super::*;
@@ -48,8 +45,10 @@ pub enum CustomError {
 }
 
 #[derive(Accounts)]
-#[instruction(admin_data: u64)]
 pub struct SetAdminSettings<'info> {
+    // In a real program, this should be a PDA,
+    // so the authority cannot create multiple settings accounts.
+    // Not done here for easier testing
     #[account(init, payer = authority)]
     pub settings: Account<'info, Settings>,
     #[account(mut)]
@@ -62,8 +61,10 @@ pub struct SetAdminSettings<'info> {
 }
 
 #[derive(Accounts)]
-#[instruction(admin_data: u64)]
 pub struct SetAdminSettingsUseProgramState<'info> {
+    // In a real program, this should be a PDA,
+    // so the authority cannot create multiple settings accounts.
+    // Not done here for easier testing
     #[account(init, payer = authority)]
     pub settings: Account<'info, Settings>,
     #[account(mut)]