diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs index 659d0dcd7..8fe53b31d 100644 --- a/lang/attribute/interface/src/lib.rs +++ b/lang/attribute/interface/src/lib.rs @@ -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, 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, auth_program: Pubkey) -> Result { +/// Ok(Self { +/// count: 0, +/// auth_program, +/// }) +/// } +/// +/// #[access_control(SetCount::accounts(&self, &ctx))] +/// pub fn set_count(&mut self, ctx: Context, 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) -> 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) -> Result { +/// Ok(Self {}) +/// } +/// } +/// +/// impl<'info> Auth<'info, Empty> for CounterAuth { +/// fn is_authorized(_ctx: Context, 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, diff --git a/lang/attribute/state/src/lib.rs b/lang/attribute/state/src/lib.rs index 277f44549..6740a11ee 100644 --- a/lang/attribute/state/src/lib.rs +++ b/lang/attribute/state/src/lib.rs @@ -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, diff --git a/lang/src/ctor.rs b/lang/src/ctor.rs index 38fc13974..4b68c6127 100644 --- a/lang/src/ctor.rs +++ b/lang/src/ctor.rs @@ -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. diff --git a/lang/src/lib.rs b/lang/src/lib.rs index d04f80de2..e0f148ea0 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -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;