Skip to content

Commit 60958eb

Browse files
lang: Add some API documentation
1 parent d94fd39 commit 60958eb

File tree

6 files changed

+188
-46
lines changed

6 files changed

+188
-46
lines changed

README.md

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,6 @@ state assocated with each user in individual `#[account]` structs.
143143
For more, see the [examples](https://github.com/project-serum/anchor/tree/master/examples)
144144
directory.
145145

146-
## Accounts attribute syntax.
147-
148-
There are several inert attributes that can be specified on a struct deriving `Accounts`.
149-
150-
| Attribute | Location | Description |
151-
|:--|:--|:--|
152-
| `#[account(signer)]` | On raw `AccountInfo` structs. | Checks the given account signed the transaction. |
153-
| `#[account(mut)]` | On `AccountInfo`, `ProgramAccount` or `CpiAccount` structs. | Marks the account as mutable and persists the state transition. |
154-
| `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. |
155-
| `#[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`. |
156-
| `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Semantically different, but otherwise the same as `belongs_to`. |
157-
| `#[account(seeds = [<seeds>])]` | On `AccountInfo` structs | Seeds for the program derived address an `AccountInfo` struct represents. |
158-
| `#[account(owner = program \| skip)]` | On `AccountInfo` structs | Checks the owner of the account is the current program or skips the check. |
159-
| `#[account("<literal>")]` | On any type deriving `Accounts` | Executes the given code literal as a constraint. The literal should evaluate to a boolean. |
160-
| `#[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. |
161-
162146
## License
163147

164148
Anchor is licensed under [Apache 2.0](./LICENSE).

lang/attribute/access-control/src/lib.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,46 @@ use syn::parse_macro_input;
66
/// Executes the given access control method before running the decorated
77
/// instruction handler. Any method in scope of the attribute can be invoked
88
/// with any arguments from the associated instruction handler.
9+
///
10+
/// # Example
11+
///
12+
/// ```
13+
/// use anchor_lang::prelude::*;
14+
///
15+
/// #[program]
16+
/// mod errors {
17+
/// use super::*;
18+
///
19+
/// #[access_control(Create::accounts(&ctx, bump_seed))]
20+
/// pub fn create(ctx: Context<Create>, bump_seed: u8) -> Result<()> {
21+
/// let my_account = &mut ctx.accounts.my_account;
22+
/// my_account.bump_seed = bump_seed;
23+
/// }
24+
/// }
25+
///
26+
/// #[derive(Accounts)]
27+
/// pub struct Create {
28+
/// #[account(init)]
29+
/// my_account: ProgramAccount<'info, MyAccount>,
30+
/// }
31+
///
32+
/// impl Create {
33+
/// pub fn accounts(ctx: &Context<Create>, bump_seed: u8) -> Result<()> {
34+
/// let seeds = &[ctx.accounts.my_account.to_account_info().key.as_ref(), &[bump_seed]];
35+
/// Pubkey::create_program_address(seeds, ctx.program_id)
36+
/// .map_err(|_| ErrorCode::InvalidNonce)?;
37+
/// Ok(())
38+
/// }
39+
/// }
40+
/// ...
41+
/// ```
42+
///
43+
/// This example demonstrates a useful pattern. Not only can you use
44+
/// `#[access_control]` to ensure any invariants or preconditions hold prior to
45+
/// executing an instruction, but also it can be used to finish any validation
46+
/// on the `Accounts` struct, particularly when instruction arguments are
47+
/// needed. Here, we use the given `bump_seed` to verify it creates a valid
48+
/// program-derived address.
949
#[proc_macro_attribute]
1050
pub fn access_control(
1151
args: proc_macro::TokenStream,

lang/attribute/account/src/lib.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,20 @@ extern crate proc_macro;
33
use quote::quote;
44
use syn::parse_macro_input;
55

6-
/// A data structure representing a Solana account.
6+
/// A data structure representing a Solana account, implementing various traits:
7+
///
8+
/// - [`AccountSerialize`](./trait.AccountSerialize.html)
9+
/// - [`AccountDeserialize`](./trait.AccountDeserialize.html)
10+
/// - [`AnchorSerialize`](./trait.AnchorSerialize.html)
11+
/// - [`AnchorDeserialize`](./trait.AnchorDeserialize.html)
12+
///
13+
/// When implementing account serialization traits the first 8 bytes are
14+
/// reserved for a unique account discriminator, self described by the first 8
15+
/// bytes of the SHA256 of the account's Rust ident.
16+
///
17+
/// As a result, any calls to `AccountDeserialize`'s `try_deserialize` will
18+
/// check this discriminator. If it doesn't match, an invalid account was given,
19+
/// and the account deserialization will exit with an error.
720
#[proc_macro_attribute]
821
pub fn account(
922
args: proc_macro::TokenStream,

lang/attribute/error/src/lib.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,47 @@ use anchor_syn::codegen::error as error_codegen;
44
use anchor_syn::parser::error as error_parser;
55
use syn::parse_macro_input;
66

7-
/// Generates an error type from an error code enum.
7+
/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
8+
/// used as return types from Anchor instruction handlers. Importantly, the
9+
/// attribute implements
10+
/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) on the
11+
/// `ErrorCode` to support converting from the user defined error enum *into*
12+
/// the generated `Error`.
13+
///
14+
/// # Example
15+
///
16+
/// ```
17+
/// use anchor_lang::prelude::*;
18+
///
19+
/// #[program]
20+
/// mod errors {
21+
/// use super::*;
22+
/// pub fn hello(_ctx: Context<Hello>) -> Result<()> {
23+
/// Err(MyError::Hello.into())
24+
/// }
25+
/// }
26+
///
27+
/// #[derive(Accounts)]
28+
/// pub struct Hello {}
29+
///
30+
/// #[error]
31+
/// pub enum MyError {
32+
/// #[msg("This is an error message clients cant automatically display")]
33+
/// Hello,
34+
/// }
35+
/// ```
36+
///
37+
/// Note that we generate a new `Error` type so that we can return either the
38+
/// user defined error enum *or* a
39+
/// [`ProgramError`](../solana_program/enum.ProgramError.html), which is used
40+
/// pervasively, throughout solana program crates. The generated `Error` type
41+
/// should almost never be used directly, as the user defined error is
42+
/// preferred. In the example above, `MyError::Hello.into()`.
43+
///
44+
/// # Msg
45+
///
46+
/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that
47+
/// parsers and IDLs can map error codes to error messages.
848
#[proc_macro_attribute]
949
pub fn error(
1050
_args: proc_macro::TokenStream,

lang/derive/accounts/src/lib.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,47 @@ use anchor_syn::parser::accounts as accounts_parser;
55
use proc_macro::TokenStream;
66
use syn::parse_macro_input;
77

8-
/// Implements an `Accounts` deserializer on the given struct, applying any
9-
/// constraints specified via `#[account]` attributes.
8+
/// Implements an [`Accounts`](./trait.Accounts.html) deserializer on the given
9+
/// struct, applying any constraints specified via inert `#[account(..)]`
10+
/// attributes upon deserialization.
11+
///
12+
/// # Example
13+
///
14+
/// ```
15+
/// #[derive(Accounts)]
16+
/// pub struct Auth<'info> {
17+
/// #[account(mut, has_one = authority)]
18+
/// pub data: ProgramAccount<'info, MyData>,
19+
/// #[account(signer)]
20+
/// pub authority: AccountInfo<'info>,
21+
/// }
22+
///
23+
/// #[account]
24+
/// pub struct MyData {
25+
/// authority: Pubkey,
26+
/// protected_data: u64,
27+
/// }
28+
/// ```
29+
///
30+
/// Here, any instance of the `Auth` struct created via
31+
/// [`try_accounts`](trait.Accounts.html#tymethod.try_accounts) is guaranteed
32+
/// to have been both
33+
///
34+
/// * Signed by `authority`.
35+
/// * Checked that `&data.authority == authority.key`.
36+
///
37+
/// The full list of available attributes is as follows.
38+
///
39+
/// | Attribute | Location | Description |
40+
/// |:--|:--|:--|
41+
/// | `#[account(signer)]` | On raw `AccountInfo` structs. | Checks the given account signed the transaction. |
42+
/// | `#[account(mut)]` | On `AccountInfo`, `ProgramAccount` or `CpiAccount` structs. | Marks the account as mutable and persists the state transition. |
43+
/// | `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. |
44+
/// | `#[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`. |
45+
/// | `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Semantically different, but otherwise the same as `belongs_to`. |
46+
/// | `#[account(seeds = [<seeds>])]` | On `AccountInfo` structs | Seeds for the program derived address an `AccountInfo` struct represents. |
47+
/// | `#[account("<literal>")]` | On any type deriving `Accounts` | Executes the given code literal as a constraint. The literal should evaluate to a boolean. |
48+
/// | `#[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. |
1049
#[proc_macro_derive(Accounts, attributes(account))]
1150
pub fn derive_anchor_deserialize(item: TokenStream) -> TokenStream {
1251
let strct = parse_macro_input!(item as syn::ItemStruct);

lang/src/lib.rs

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,36 +57,51 @@ pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorS
5757
pub use error::Error;
5858
pub use solana_program;
5959

60-
/// A data structure of accounts that can be deserialized from the input
61-
/// of a Solana program. Due to the freewheeling nature of the accounts array,
62-
/// implementations of this trait should perform any and all constraint checks
63-
/// (in addition to any done within `AccountDeserialize`) on accounts to ensure
64-
/// the accounts maintain any invariants required for the program to run
65-
/// securely.
60+
/// A data structure of validated accounts that can be deserialized from the
61+
/// input to a Solana program. Implementations of this trait should perform any
62+
/// and all requisite constraint checks on accounts to ensure the accounts
63+
/// maintain any invariants required for the program to run securely. In most
64+
/// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html)
65+
/// derive macro to implement this trait.
6666
pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
67+
/// Returns the validated accounts struct. What constitutes "valid" is
68+
/// program dependent. However, users of these types should never have to
69+
/// worry about account substitution attacks. For example, if a program
70+
/// expects a `Mint` account from the SPL token program in a particular
71+
/// field, then it should be impossible for this method to return `Ok` if any
72+
/// other account type is given--from the SPL token program or elsewhere.
73+
///
74+
/// `program_id` is the currently executing program. `accounts` is the
75+
/// set of accounts to construct the type from. For every account used,
76+
/// the implementation should mutate the slice, consuming the used entry
77+
/// so that it cannot be used again.
6778
fn try_accounts(
6879
program_id: &Pubkey,
6980
accounts: &mut &[AccountInfo<'info>],
7081
) -> Result<Self, ProgramError>;
7182
}
7283

73-
/// The exit procedure for an accounts object.
84+
/// The exit procedure for an account. Any cleanup or persistance to storage
85+
/// should be done here.
7486
pub trait AccountsExit<'info>: ToAccountMetas + ToAccountInfos<'info> {
87+
/// `program_id` is the currently executing program.
7588
fn exit(&self, program_id: &Pubkey) -> solana_program::entrypoint::ProgramResult;
7689
}
7790

7891
/// A data structure of accounts providing a one time deserialization upon
79-
/// initialization, i.e., when the data array for a given account is zeroed.
80-
/// For all subsequent deserializations, it's expected that
81-
/// [Accounts](trait.Accounts.html) is used.
92+
/// account initialization, i.e., when the data array for a given account is
93+
/// zeroed. Any subsequent call to `try_accounts_init` should fail. For all
94+
/// subsequent deserializations, it's expected that [`Accounts`] is used.
8295
pub trait AccountsInit<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
8396
fn try_accounts_init(
8497
program_id: &Pubkey,
8598
accounts: &mut &[AccountInfo<'info>],
8699
) -> Result<Self, ProgramError>;
87100
}
88101

89-
/// Transformation to `AccountMeta` structs.
102+
/// Transformation to
103+
/// [`AccountMeta`](../solana_program/instruction/struct.AccountMeta.html)
104+
/// structs.
90105
pub trait ToAccountMetas {
91106
/// `is_signer` is given as an optional override for the signer meta field.
92107
/// This covers the edge case when a program-derived-address needs to relay
@@ -96,7 +111,9 @@ pub trait ToAccountMetas {
96111
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta>;
97112
}
98113

99-
/// Transformation to `AccountInfo` structs.
114+
/// Transformation to
115+
/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html)
116+
/// structs.
100117
pub trait ToAccountInfos<'info> {
101118
fn to_account_infos(&self) -> Vec<AccountInfo<'info>>;
102119
}
@@ -106,30 +123,39 @@ pub trait ToAccountInfo<'info> {
106123
fn to_account_info(&self) -> AccountInfo<'info>;
107124
}
108125

109-
/// A data structure that can be serialized and stored in an `AccountInfo` data
110-
/// array.
126+
/// A data structure that can be serialized and stored into account storage,
127+
/// i.e. an
128+
/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s
129+
/// mutable data slice.
111130
///
112-
/// Implementors of this trait should ensure that any subsequent usage the
131+
/// Implementors of this trait should ensure that any subsequent usage of the
113132
/// `AccountDeserialize` trait succeeds if and only if the account is of the
114-
/// correct type. For example, the implementation provided by the `#[account]`
115-
/// attribute sets the first 8 bytes to be a unique account discriminator,
116-
/// defined as the first 8 bytes of the SHA256 of the account's Rust ident.
117-
/// Thus, any subsequent calls via `AccountDeserialize`'s `try_deserialize`
118-
/// will check this discriminator. If it doesn't match, an invalid account
119-
/// was given, and the program will exit with an error.
133+
/// correct type.
134+
///
135+
/// In most cases, one can use the default implementation provided by the
136+
/// [`#[account]`](./attr.account.html) attribute.
120137
pub trait AccountSerialize {
121-
/// Serilalizes the account data into `writer`.
138+
/// Serializes the account data into `writer`.
122139
fn try_serialize<W: Write>(&self, writer: &mut W) -> Result<(), ProgramError>;
123140
}
124141

125-
/// A data structure that can be deserialized from an `AccountInfo` data array.
142+
/// A data structure that can be deserialized and stored into account storage,
143+
/// i.e. an
144+
/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s
145+
/// mutable data slice.
126146
pub trait AccountDeserialize: Sized {
127-
/// Deserializes the account data.
147+
/// Deserializes previously initialized account data. Should fail for all
148+
/// uninitialized accounts, where the bytes are zeroed. Implementations
149+
/// should be unique to a particular account type so that one can never
150+
/// successfully deserialize the data of one account type into another.
151+
/// For example, if the SPL token program where to implement this trait,
152+
/// it should impossible to deserialize a `Mint` account into a token
153+
/// `Account`.
128154
fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError>;
129155

130156
/// Deserializes account data without checking the account discriminator.
131-
/// This should only be used on account initialization, when the
132-
/// discriminator is not yet set (since the entire account data is zeroed).
157+
/// This should only be used on account initialization, when the bytes of
158+
/// the account are zeroed.
133159
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError>;
134160
}
135161

0 commit comments

Comments
 (0)