浏览代码

lang: improved Account reference (#1207)

Paul 3 年之前
父节点
当前提交
a7eccb6e82
共有 2 个文件被更改,包括 116 次插入2 次删除
  1. 5 1
      lang/attribute/account/src/lib.rs
  2. 111 1
      lang/src/accounts/account.rs

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

@@ -5,12 +5,16 @@ use syn::parse_macro_input;
 
 mod id;
 
-/// A data structure representing a Solana account, implementing various traits:
+/// An attribute for a data structure representing a Solana account.
+///
+/// `#[account]` generates trait implementations for the following traits:
 ///
 /// - [`AccountSerialize`](./trait.AccountSerialize.html)
 /// - [`AccountDeserialize`](./trait.AccountDeserialize.html)
 /// - [`AnchorSerialize`](./trait.AnchorSerialize.html)
 /// - [`AnchorDeserialize`](./trait.AnchorDeserialize.html)
+/// - [`Owner`](./trait.Owner.html)
+/// - [`Discriminator`](./trait.Discriminator.html)
 ///
 /// When implementing account serialization traits the first 8 bytes are
 /// reserved for a unique account discriminator, self described by the first 8

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

@@ -1,3 +1,5 @@
+//! Account container that checks ownership on deserialization.
+
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -8,7 +10,115 @@ use solana_program::pubkey::Pubkey;
 use std::fmt;
 use std::ops::{Deref, DerefMut};
 
-/// Account container that checks ownership on deserialization.
+/// Wrapper around [`AccountInfo`](crate::solana_program::account_info::AccountInfo)
+/// that verifies program ownership and deserializes underlying data into a Rust type.
+///
+/// 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).
+/// The `#[account]` attribute implements the Owner trait for
+/// a struct using the `crate::ID` declared by [`declareId`](crate::declare_id)
+/// in the same program. It follows that Account can also be used
+/// with a `T` that comes from a different program.
+///
+/// Checks:
+///
+/// - `Account.info.owner == T::owner()`
+/// - `!(Account.info.owner == SystemProgram && Account.info.lamports() == 0)`
+///
+/// Example
+/// ```ignore
+/// use anchor_lang::prelude::*;
+/// use other_program::Auth;
+///
+/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+///
+/// #[program]
+/// mod hello_anchor {
+///     use super::*;
+///     pub fn set_data(ctx: Context<SetData>, data: u64) -> ProgramResult {
+///         if (*ctx.accounts.auth_account).authorized {
+///             (*ctx.accounts.my_account).data = data;
+///         }
+///         Ok(())
+///     }
+/// }
+///
+/// #[account]
+/// #[derive(Default)]
+/// pub struct MyData {
+///     pub data: u64
+/// }
+///
+/// #[derive(Accounts)]
+/// pub struct SetData<'info> {
+///     #[account(mut)]
+///     pub my_account: Account<'info, MyData> // checks that my_account.info.owner == Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
+///     pub auth_account: Account<'info, Auth> // checks that auth_account.info.owner == FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9
+/// }
+///
+/// // In a different program
+///
+/// ...
+/// declare_id!("FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9");
+/// #[account]
+/// #[derive(Default)]
+/// pub struct Auth {
+///     pub authorized: bool
+/// }
+/// ...
+/// ```
+///
+/// Account can also be used with non-anchor programs. The data types from
+/// those programs are not annotated with `#[account]` so you have to
+/// - create a wrapper type around the structs you want to wrap with Account
+/// - implement the functions required by Account yourself
+/// instead of using `#[account]`. You only have to implement a fraction of the
+/// 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))
+/// ```ignore
+/// #[derive(Clone)]
+/// pub struct Mint(spl_token::state::Mint);
+///
+/// // This is necessary so we can use "anchor_spl::token::Mint::LEN"
+/// // because rust does not resolve "anchor_spl::token::Mint::LEN" to
+/// // "spl_token::state::Mint::LEN" automatically
+/// impl Mint {
+///     pub const LEN: usize = spl_token::state::Mint::LEN;
+/// }
+///
+/// // You don't have to implement the "try_deserialize" function
+/// // from this trait. It delegates to
+/// // "try_deserialize_unchecked" by default which is what we want here
+/// // because non-anchor accounts don't have a discriminator to check
+/// impl anchor_lang::AccountDeserialize for Mint {
+///     fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
+///         spl_token::state::Mint::unpack(buf).map(Mint)
+///     }
+/// }
+/// // AccountSerialize defaults to a no-op which is what we want here
+/// // because it's a foreign program, so our program does not
+/// // have permission to write to the foreign program's accounts anyway
+/// impl anchor_lang::AccountSerialize for Mint {}
+///
+/// impl anchor_lang::Owner for Mint {
+///     fn owner() -> Pubkey {
+///         // pub use spl_token::ID is used at the top of the file
+///         ID
+///     }
+/// }
+///
+/// // Implement the "std::ops::Deref" trait for better user experience
+/// impl Deref for Mint {
+///     type Target = spl_token::state::Mint;
+///
+///     fn deref(&self) -> &Self::Target {
+///         &self.0
+///     }
+/// }
+/// ```
 #[derive(Clone)]
 pub struct Account<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> {
     account: T,