# Account Constraints and Access Control

This tutorial covers how to specify constraints and access control on accounts, a problem somewhat unique to the parallel nature of Solana.

On Solana, a transaction must specify all accounts required for execution. And because an untrusted client specifies those accounts, a program must responsibly validate all such accounts are what the client claims they are--in addition to any instruction specific access control the program needs to do.

For example, you could imagine easily writing a faulty token program that forgets to check if the signer of a transaction claiming to be the owner of a Token Account actually matches the owner on that account. Furthermore, imagine what might happen if the program expects a Mint account but a malicious user gives a token Account.

To address these problems, Anchor provides several types, traits, and macros. It's easiest to understand by seeing how they're used in an example, but a couple include

  • Accounts (opens new window): derive macro implementing the Accounts trait (opens new window), allowing a struct to transform from the untrusted &[AccountInfo] slice given to a Solana program into a validated struct of deserialized account types.
  • #[account] (opens new window): attribute macro implementing AccountSerialize (opens new window) and AccountDeserialize (opens new window), automatically prepending a unique 8 byte discriminator to the account array. The discriminator is defined by the first 8 bytes of the Sha256 hash of the account's Rust identifier--i.e., the struct type name--and ensures no account can be substituted for another.
  • Account (opens new window): a wrapper type for a deserialized account implementing AccountDeserialize. Using this type within an Accounts struct will ensure the account is owned by the address defined by declare_id! where the inner account was defined.

With the above, we can define preconditions for our any instruction handler expecting a certain set of accounts, allowing us to more easily reason about the security of our programs.

# Clone the Repo

To get started, clone the repo.

git clone https://github.com/project-serum/anchor

And change directories to the example (opens new window).

cd anchor/examples/tutorial/basic-2

# Defining a Program

Here we have a simple Counter program, where anyone can create a counter, but only the assigned authority can increment it.

use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod basic_2 {
    use super::*;
    pub fn create(ctx: Context<Create>, authority: Pubkey) -> ProgramResult {
        let counter = &mut ctx.accounts.counter;
        counter.authority = authority;
        counter.count = 0;
        Ok(())
    }
    pub fn increment(ctx: Context<Increment>) -> ProgramResult {
        let counter = &mut ctx.accounts.counter;
        counter.count += 1;
        Ok(())
    }
}
#[derive(Accounts)]
pub struct Create<'info> {
    #[account(init, payer = user, space = 8 + 40)]
    pub counter: Account<'info, Counter>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Increment<'info> {
    #[account(mut, has_one = authority)]
    pub counter: Account<'info, Counter>,
    pub authority: Signer<'info>,
}
#[account]
pub struct Counter {
    pub authority: Pubkey,
    pub count: u64,
}

If you've gone through the previous tutorials the create instruction should be straightforward. Let's focus on the increment instruction, specifically the Increment struct deriving Accounts.

#[derive(Accounts)]
pub struct Increment<'info> {
    #[account(mut, has_one = authority)]
    pub counter: Account<'info, Counter>,
    pub authority: Signer<'info>,
}

Here, several #[account(..)] attributes are used.

  • mut: tells the program to persist all changes to the account.
  • has_one: enforces the constraint that Increment.counter.authority == Increment.authority.key.
  • signer: enforces the constraint that the authority account signed the transaction.

If any of these constraints do not hold, then the increment instruction will never be executed. This allows us to completely separate account validation from our program's business logic, allowing us to reason about each concern more easily. For more, see the full list (opens new window) of account constraints.

# Next Steps

We've covered the basics for writing a single program using Anchor on Solana. But the power of blockchains come not from a single program, but from combining multiple composable programs (buzzword...check). Next, we'll see how to call one program from another.