lang: Add some more docs

This commit is contained in:
Armani Ferrante 2021-02-08 14:22:50 +08:00
parent a903d48e1f
commit 82d7e5ae2b
No known key found for this signature in database
GPG Key ID: D597A80BCF8E12B7
4 changed files with 147 additions and 7 deletions

View File

@ -5,6 +5,143 @@ use heck::SnakeCase;
use quote::quote;
use syn::parse_macro_input;
/// The `#[interface]` attribute allows one to define an external program
/// dependency, without having any knowledge about the program, other than
/// the fact that it implements the given trait.
///
/// Additionally, the attribute generates a client that can be used to perform
/// CPI to these external dependencies.
///
/// # Example
///
/// In the following example, we have a counter program, where the count
/// can only be set if the configured external program authorizes it.
///
/// ## Defining an `#[interface]`
///
/// First we define the program that depends on an external interface.
///
/// ```ignore
/// #![feature(proc_macro_hygiene)]
///
/// use anchor_lang::prelude::*;
///
/// #[interface]
/// pub trait Auth<'info, T: Accounts<'info>> {
/// fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> ProgramResult;
/// }
///
/// #[program]
/// pub mod counter {
/// use super::*;
///
/// #[state]
/// pub struct Counter {
/// pub count: u64,
/// pub auth_program: Pubkey,
/// }
///
/// impl Counter {
/// pub fn new(_ctx: Context<Empty>, auth_program: Pubkey) -> Result<Self> {
/// Ok(Self {
/// count: 0,
/// auth_program,
/// })
/// }
///
/// #[access_control(SetCount::accounts(&self, &ctx))]
/// pub fn set_count(&mut self, ctx: Context<SetCount>, new_count: u64) -> Result<()> {
/// // Ask the auth program if we should approve the transaction.
/// let cpi_program = ctx.accounts.auth_program.clone();
/// let cpi_ctx = CpiContext::new(cpi_program, Empty {});
///
/// // This is the client generated by the `#[interface]` attribute.
/// auth::is_authorized(cpi_ctx, self.count, new_count)?;
///
/// // Approved, so update.
/// self.count = new_count;
/// Ok(())
/// }
/// }
/// }
///
/// #[derive(Accounts)]
/// pub struct Empty {}
///
/// #[derive(Accounts)]
/// pub struct SetCount<'info> {
/// auth_program: AccountInfo<'info>,
/// }
///
/// impl<'info> SetCount<'info> {
/// pub fn accounts(counter: &Counter, ctx: &Context<SetCount>) -> Result<()> {
/// if ctx.accounts.auth_program.key != &counter.auth_program {
/// return Err(ErrorCode::InvalidAuthProgram.into());
/// }
/// Ok(())
/// }
/// }
///
/// #[error]
/// pub enum ErrorCode {
/// #[msg("Invalid auth program.")]
/// InvalidAuthProgram,
/// }
///```
///
/// ## Defining an implementation
///
/// Now we define the program that implements the interface, which the above
/// program will call.
///
/// ```ignore
/// #![feature(proc_macro_hygiene)]
///
/// use anchor_lang::prelude::*;
/// use counter::Auth;
///
/// #[program]
/// pub mod counter_auth {
/// use super::*;
///
/// #[state]
/// pub struct CounterAuth {}
///
/// // TODO: remove this impl block after addressing
/// // https://github.com/project-serum/anchor/issues/71.
/// impl CounterAuth {
/// pub fn new(_ctx: Context<Empty>) -> Result<Self, ProgramError> {
/// Ok(Self {})
/// }
/// }
///
/// impl<'info> Auth<'info, Empty> for CounterAuth {
/// fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult {
/// if current % 2 == 0 {
/// if new % 2 == 0 {
/// return Err(ProgramError::Custom(50)); // Arbitrary error code.
/// }
/// } else {
/// if new % 2 == 1 {
/// return Err(ProgramError::Custom(60)); // Arbitrary error code.
/// }
/// }
/// Ok(())
/// }
/// }
/// }
/// #[derive(Accounts)]
/// pub struct Empty {}
/// ```
///
/// # Returning Values Across CPI
///
/// The caller above uses a `Result` to act as a boolean. However, in order
/// for this feature to be maximally useful, we need a way to return values from
/// interfaces. For now, one can do this by writing to a shared account, e.g.,
/// with the SPL's [Shared Memory Program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory).
/// In the future, Anchor will add the ability to return values across CPI
/// without having to worry about the details of shared memory accounts.
#[proc_macro_attribute]
pub fn interface(
_args: proc_macro::TokenStream,

View File

@ -3,6 +3,8 @@ extern crate proc_macro;
use quote::quote;
use syn::parse_macro_input;
/// The `#[state]` attribute defines the program's state struct, i.e., the
/// program's global account singleton giving the program the illusion of state.
#[proc_macro_attribute]
pub fn state(
_args: proc_macro::TokenStream,

View File

@ -2,12 +2,13 @@ use crate::{Accounts, Sysvar};
use solana_program::account_info::AccountInfo;
use solana_program::sysvar::rent::Rent;
// 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.
/// 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.

View File

@ -54,7 +54,7 @@ pub use anchor_attribute_interface::interface;
pub use anchor_attribute_program::program;
pub use anchor_attribute_state::state;
pub use anchor_derive_accounts::Accounts;
/// Default serialization format for anchor instructions and accounts.
/// Borsh is the default serialization format for instructions and accounts.
pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
pub use error::Error;
pub use solana_program;