瀏覽代碼

lang: Add some API documentation

Armani Ferrante 4 年之前
父節點
當前提交
60958ebc22
共有 6 個文件被更改,包括 188 次插入46 次删除
  1. 0 16
      README.md
  2. 40 0
      lang/attribute/access-control/src/lib.rs
  3. 14 1
      lang/attribute/account/src/lib.rs
  4. 41 1
      lang/attribute/error/src/lib.rs
  5. 41 2
      lang/derive/accounts/src/lib.rs
  6. 52 26
      lang/src/lib.rs

+ 0 - 16
README.md

@@ -143,22 +143,6 @@ state assocated with each user in individual `#[account]` structs.
 For more, see the [examples](https://github.com/project-serum/anchor/tree/master/examples)
 directory.
 
-## Accounts attribute syntax.
-
-There are several inert attributes that can be specified on a struct deriving `Accounts`.
-
-| Attribute | Location | Description |
-|:--|:--|:--|
-| `#[account(signer)]` | On raw `AccountInfo` structs. | Checks the given account signed the transaction. |
-| `#[account(mut)]` | On `AccountInfo`, `ProgramAccount` or `CpiAccount` structs. | Marks the account as mutable and persists the state transition. |
-| `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. |
-| `#[account(belongs_to = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Checks the `target` field on the account matches the `target` field in the struct deriving `Accounts`. |
-| `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Semantically different, but otherwise the same as `belongs_to`. |
-| `#[account(seeds = [<seeds>])]` | On `AccountInfo` structs | Seeds for the program derived address an `AccountInfo` struct represents. |
-| `#[account(owner = program \| skip)]` | On `AccountInfo` structs | Checks the owner of the account is the current program or skips the check. |
-| `#[account("<literal>")]` | On any type deriving `Accounts` | Executes the given code literal as a constraint. The literal should evaluate to a boolean. |
-| `#[account(rent_exempt = <skip>)]` | On `AccountInfo` or `ProgramAccount` structs | Optional attribute to skip the rent exemption check. By default, all accounts marked with `#[account(init)]` will be rent exempt, and so this should rarely (if ever) be used. Similarly, omitting `= skip` will mark the account rent exempt. |
-
 ## License
 
 Anchor is licensed under [Apache 2.0](./LICENSE).

+ 40 - 0
lang/attribute/access-control/src/lib.rs

@@ -6,6 +6,46 @@ use syn::parse_macro_input;
 /// Executes the given access control method before running the decorated
 /// instruction handler. Any method in scope of the attribute can be invoked
 /// with any arguments from the associated instruction handler.
+///
+/// # Example
+///
+/// ```
+/// use anchor_lang::prelude::*;
+///
+/// #[program]
+/// mod errors {
+///     use super::*;
+///
+///     #[access_control(Create::accounts(&ctx, bump_seed))]
+///     pub fn create(ctx: Context<Create>, bump_seed: u8) -> Result<()> {
+///       let my_account = &mut ctx.accounts.my_account;
+///       my_account.bump_seed = bump_seed;
+///     }
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct Create {
+///   #[account(init)]
+///   my_account: ProgramAccount<'info, MyAccount>,
+/// }
+///
+/// impl Create {
+///   pub fn accounts(ctx: &Context<Create>, bump_seed: u8) -> Result<()> {
+///     let seeds = &[ctx.accounts.my_account.to_account_info().key.as_ref(), &[bump_seed]];
+///     Pubkey::create_program_address(seeds, ctx.program_id)
+///       .map_err(|_| ErrorCode::InvalidNonce)?;
+///     Ok(())
+///   }
+/// }
+/// ...
+/// ```
+///
+/// This example demonstrates a useful pattern. Not only can you use
+/// `#[access_control]` to ensure any invariants or preconditions hold prior to
+/// executing an instruction, but also it can be used to finish any validation
+/// on the `Accounts` struct, particularly when instruction arguments are
+/// needed. Here, we use the given `bump_seed` to verify it creates a valid
+/// program-derived address.
 #[proc_macro_attribute]
 pub fn access_control(
     args: proc_macro::TokenStream,

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

@@ -3,7 +3,20 @@ extern crate proc_macro;
 use quote::quote;
 use syn::parse_macro_input;
 
-/// A data structure representing a Solana account.
+/// A data structure representing a Solana account, implementing various traits:
+///
+/// - [`AccountSerialize`](./trait.AccountSerialize.html)
+/// - [`AccountDeserialize`](./trait.AccountDeserialize.html)
+/// - [`AnchorSerialize`](./trait.AnchorSerialize.html)
+/// - [`AnchorDeserialize`](./trait.AnchorDeserialize.html)
+///
+/// When implementing account serialization traits the first 8 bytes are
+/// reserved for a unique account discriminator, self described by the first 8
+/// bytes of the SHA256 of the account's Rust ident.
+///
+/// As a result, any calls to `AccountDeserialize`'s `try_deserialize` will
+/// check this discriminator. If it doesn't match, an invalid account was given,
+/// and the account deserialization will exit with an error.
 #[proc_macro_attribute]
 pub fn account(
     args: proc_macro::TokenStream,

+ 41 - 1
lang/attribute/error/src/lib.rs

@@ -4,7 +4,47 @@ use anchor_syn::codegen::error as error_codegen;
 use anchor_syn::parser::error as error_parser;
 use syn::parse_macro_input;
 
-/// Generates an error type from an error code enum.
+/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
+/// used as return types from Anchor instruction handlers. Importantly, the
+/// attribute implements
+/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) on the
+/// `ErrorCode` to support converting from the user defined error enum *into*
+/// the generated `Error`.
+///
+/// # Example
+///
+/// ```
+/// use anchor_lang::prelude::*;
+///
+/// #[program]
+/// mod errors {
+///     use super::*;
+///     pub fn hello(_ctx: Context<Hello>) -> Result<()> {
+///         Err(MyError::Hello.into())
+///     }
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct Hello {}
+///
+/// #[error]
+/// pub enum MyError {
+///     #[msg("This is an error message clients cant automatically display")]
+///     Hello,
+/// }
+/// ```
+///
+/// Note that we generate a new `Error` type so that we can return either the
+/// user defined error enum *or* a
+/// [`ProgramError`](../solana_program/enum.ProgramError.html), which is used
+/// pervasively, throughout solana program crates. The generated `Error` type
+/// should almost never be used directly, as the user defined error is
+/// preferred. In the example above, `MyError::Hello.into()`.
+///
+/// # Msg
+///
+/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that
+/// parsers  and IDLs can map error codes to error messages.
 #[proc_macro_attribute]
 pub fn error(
     _args: proc_macro::TokenStream,

+ 41 - 2
lang/derive/accounts/src/lib.rs

@@ -5,8 +5,47 @@ use anchor_syn::parser::accounts as accounts_parser;
 use proc_macro::TokenStream;
 use syn::parse_macro_input;
 
-/// Implements an `Accounts` deserializer on the given struct, applying any
-/// constraints specified via `#[account]` attributes.
+/// Implements an [`Accounts`](./trait.Accounts.html) deserializer on the given
+/// struct, applying any constraints specified via inert `#[account(..)]`
+/// attributes upon deserialization.
+///
+/// # Example
+///
+/// ```
+/// #[derive(Accounts)]
+/// pub struct Auth<'info> {
+///     #[account(mut, has_one = authority)]
+///     pub data: ProgramAccount<'info, MyData>,
+///     #[account(signer)]
+///     pub authority: AccountInfo<'info>,
+/// }
+///
+/// #[account]
+/// pub struct MyData {
+///   authority: Pubkey,
+///   protected_data: u64,
+/// }
+/// ```
+///
+/// Here, any instance of the `Auth` struct created via
+/// [`try_accounts`](trait.Accounts.html#tymethod.try_accounts) is guaranteed
+/// to have been both
+///
+/// * Signed by `authority`.
+/// * Checked that `&data.authority == authority.key`.
+///
+/// The full list of available attributes is as follows.
+///
+/// | Attribute | Location | Description |
+/// |:--|:--|:--|
+/// | `#[account(signer)]` | On raw `AccountInfo` structs. | Checks the given account signed the transaction. |
+/// | `#[account(mut)]` | On `AccountInfo`, `ProgramAccount` or `CpiAccount` structs. | Marks the account as mutable and persists the state transition. |
+/// | `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. |
+/// | `#[account(belongs_to = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Checks the `target` field on the account matches the `target` field in the struct deriving `Accounts`. |
+/// | `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Semantically different, but otherwise the same as `belongs_to`. |
+/// | `#[account(seeds = [<seeds>])]` | On `AccountInfo` structs | Seeds for the program derived address an `AccountInfo` struct represents. |
+/// | `#[account("<literal>")]` | On any type deriving `Accounts` | Executes the given code literal as a constraint. The literal should evaluate to a boolean. |
+/// | `#[account(rent_exempt = <skip>)]` | On `AccountInfo` or `ProgramAccount` structs | Optional attribute to skip the rent exemption check. By default, all accounts marked with `#[account(init)]` will be rent exempt, and so this should rarely (if ever) be used. Similarly, omitting `= skip` will mark the account rent exempt. |
 #[proc_macro_derive(Accounts, attributes(account))]
 pub fn derive_anchor_deserialize(item: TokenStream) -> TokenStream {
     let strct = parse_macro_input!(item as syn::ItemStruct);

+ 52 - 26
lang/src/lib.rs

@@ -57,28 +57,41 @@ pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorS
 pub use error::Error;
 pub use solana_program;
 
-/// A data structure of accounts that can be deserialized from the input
-/// of a Solana program. Due to the freewheeling nature of the accounts array,
-/// implementations of this trait should perform any and all constraint checks
-/// (in addition to any done within `AccountDeserialize`) on accounts to ensure
-/// the accounts maintain any invariants required for the program to run
-/// securely.
+/// A data structure of validated accounts that can be deserialized from the
+/// input to a Solana program. Implementations of this trait should perform any
+/// and all requisite constraint checks on accounts to ensure the accounts
+/// maintain any invariants required for the program to run securely. In most
+/// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html)
+/// derive macro to implement this trait.
 pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
+    /// Returns the validated accounts struct. What constitutes "valid" is
+    /// program dependent. However, users of these types should never have to
+    /// worry about account substitution attacks. For example, if a program
+    /// expects a `Mint` account from the SPL token program  in a particular
+    /// field, then it should be impossible for this method to return `Ok` if any
+    /// other account type is given--from the SPL token program or elsewhere.
+    ///
+    /// `program_id` is the currently executing program. `accounts` is the
+    /// set of accounts to construct the type from. For every account used,
+    /// the implementation should mutate the slice, consuming the used entry
+    /// so that it cannot be used again.
     fn try_accounts(
         program_id: &Pubkey,
         accounts: &mut &[AccountInfo<'info>],
     ) -> Result<Self, ProgramError>;
 }
 
-/// The exit procedure for an accounts object.
+/// The exit procedure for an account. Any cleanup or persistance to storage
+/// should be done here.
 pub trait AccountsExit<'info>: ToAccountMetas + ToAccountInfos<'info> {
+    /// `program_id` is the currently executing program.
     fn exit(&self, program_id: &Pubkey) -> solana_program::entrypoint::ProgramResult;
 }
 
 /// A data structure of accounts providing a one time deserialization upon
-/// initialization, i.e., when the data array for a given account is zeroed.
-/// For all subsequent deserializations, it's expected that
-/// [Accounts](trait.Accounts.html) is used.
+/// account initialization, i.e., when the data array for a given account is
+/// zeroed. Any subsequent call to `try_accounts_init` should fail. For all
+/// subsequent deserializations, it's expected that [`Accounts`] is used.
 pub trait AccountsInit<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
     fn try_accounts_init(
         program_id: &Pubkey,
@@ -86,7 +99,9 @@ pub trait AccountsInit<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
     ) -> Result<Self, ProgramError>;
 }
 
-/// Transformation to `AccountMeta` structs.
+/// Transformation to
+/// [`AccountMeta`](../solana_program/instruction/struct.AccountMeta.html)
+/// structs.
 pub trait ToAccountMetas {
     /// `is_signer` is given as an optional override for the signer meta field.
     /// This covers the edge case when a program-derived-address needs to relay
@@ -96,7 +111,9 @@ pub trait ToAccountMetas {
     fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta>;
 }
 
-/// Transformation to `AccountInfo` structs.
+/// Transformation to
+/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html)
+/// structs.
 pub trait ToAccountInfos<'info> {
     fn to_account_infos(&self) -> Vec<AccountInfo<'info>>;
 }
@@ -106,30 +123,39 @@ pub trait ToAccountInfo<'info> {
     fn to_account_info(&self) -> AccountInfo<'info>;
 }
 
-/// A data structure that can be serialized and stored in an `AccountInfo` data
-/// array.
+/// A data structure that can be serialized and stored into account storage,
+/// i.e. an
+/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s
+/// mutable data slice.
 ///
-/// Implementors of this trait should ensure that any subsequent usage the
+/// Implementors of this trait should ensure that any subsequent usage of the
 /// `AccountDeserialize` trait succeeds if and only if the account is of the
-/// correct type. For example, the implementation provided by the `#[account]`
-/// attribute sets the first 8 bytes to be a unique account discriminator,
-/// defined as the first 8 bytes of the SHA256 of the account's Rust ident.
-/// Thus, any subsequent  calls via `AccountDeserialize`'s `try_deserialize`
-/// will check this discriminator. If it doesn't match, an invalid account
-/// was given, and the program will exit with an error.
+/// correct type.
+///
+/// In most cases, one can use the default implementation provided by the
+/// [`#[account]`](./attr.account.html) attribute.
 pub trait AccountSerialize {
-    /// Serilalizes the account data into `writer`.
+    /// Serializes the account data into `writer`.
     fn try_serialize<W: Write>(&self, writer: &mut W) -> Result<(), ProgramError>;
 }
 
-/// A data structure that can be deserialized from an `AccountInfo` data array.
+/// A data structure that can be deserialized and stored into account storage,
+/// i.e. an
+/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s
+/// mutable data slice.
 pub trait AccountDeserialize: Sized {
-    /// Deserializes the account data.
+    /// Deserializes previously initialized account data. Should fail for all
+    /// uninitialized accounts, where the bytes are zeroed. Implementations
+    /// should be unique to a particular account type so that one can never
+    /// successfully deserialize the data of one account type into another.
+    /// For example, if the SPL token program where to implement this trait,
+    /// it should impossible to deserialize a `Mint` account into a token
+    /// `Account`.
     fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError>;
 
     /// Deserializes account data without checking the account discriminator.
-    /// This should only be used on account initialization, when the
-    /// discriminator is not yet set (since the entire account data is zeroed).
+    /// This should only be used on account initialization, when the bytes of
+    /// the account are zeroed.
     fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError>;
 }