Browse Source

lang: Add some more docs

Armani Ferrante 4 years ago
parent
commit
82d7e5ae2b
4 changed files with 147 additions and 7 deletions
  1. 137 0
      lang/attribute/interface/src/lib.rs
  2. 2 0
      lang/attribute/state/src/lib.rs
  3. 7 6
      lang/src/ctor.rs
  4. 1 1
      lang/src/lib.rs

+ 137 - 0
lang/attribute/interface/src/lib.rs

@@ -5,6 +5,143 @@ use heck::SnakeCase;
 use quote::quote;
 use quote::quote;
 use syn::parse_macro_input;
 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]
 #[proc_macro_attribute]
 pub fn interface(
 pub fn interface(
     _args: proc_macro::TokenStream,
     _args: proc_macro::TokenStream,

+ 2 - 0
lang/attribute/state/src/lib.rs

@@ -3,6 +3,8 @@ extern crate proc_macro;
 use quote::quote;
 use quote::quote;
 use syn::parse_macro_input;
 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]
 #[proc_macro_attribute]
 pub fn state(
 pub fn state(
     _args: proc_macro::TokenStream,
     _args: proc_macro::TokenStream,

+ 7 - 6
lang/src/ctor.rs

@@ -2,12 +2,13 @@ use crate::{Accounts, Sysvar};
 use solana_program::account_info::AccountInfo;
 use solana_program::account_info::AccountInfo;
 use solana_program::sysvar::rent::Rent;
 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)]
 #[derive(Accounts)]
 pub struct Ctor<'info> {
 pub struct Ctor<'info> {
     // Payer of the transaction.
     // Payer of the transaction.

+ 1 - 1
lang/src/lib.rs

@@ -54,7 +54,7 @@ pub use anchor_attribute_interface::interface;
 pub use anchor_attribute_program::program;
 pub use anchor_attribute_program::program;
 pub use anchor_attribute_state::state;
 pub use anchor_attribute_state::state;
 pub use anchor_derive_accounts::Accounts;
 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 borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
 pub use error::Error;
 pub use error::Error;
 pub use solana_program;
 pub use solana_program;