Skip to content

Commit

Permalink
lang, spl: Program and Signer types (#705)
Browse files Browse the repository at this point in the history
  • Loading branch information
armaniferrante authored Sep 11, 2021
1 parent e2bd41b commit b1ef743
Show file tree
Hide file tree
Showing 37 changed files with 472 additions and 106 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ incremented for features.

## [Unreleased]

### Features

* lang: `Program` type introduced for executable accounts ([#705](https://github.com/project-serum/anchor/pull/705)).
* lang: `Signer` type introduced for signing accounts where data is not used ([#705](https://github.com/project-serum/anchor/pull/705)).

### Breaking Changes

* lang: `#[account(owner = <pubkey>)]` now requires a `Pubkey` instead of an account ([#691](https://github.com/project-serum/anchor/pull/691)).
Expand Down
3 changes: 3 additions & 0 deletions examples/tutorial/basic-0/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
cluster = "localnet"
wallet = "~/.config/solana/id.json"

[programs.localnet]
basic_0 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"

[scripts]
test = "mocha -t 1000000 tests/"
2 changes: 2 additions & 0 deletions examples/tutorial/basic-0/programs/basic-0/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
mod basic_0 {
use super::*;
Expand Down
5 changes: 3 additions & 2 deletions examples/tutorial/basic-1/programs/basic-1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ mod basic_1 {
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub my_account: Account<'info, MyAccount>,
pub user: AccountInfo<'info>,
pub system_program: AccountInfo<'info>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
Expand Down
11 changes: 4 additions & 7 deletions examples/tutorial/basic-2/programs/basic-2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_program;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

Expand All @@ -25,18 +24,16 @@ mod basic_2 {
pub struct Create<'info> {
#[account(init, payer = user, space = 8 + 40)]
pub counter: Account<'info, Counter>,
#[account(signer)]
pub user: AccountInfo<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
#[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>,
#[account(signer)]
pub authority: AccountInfo<'info>,
pub authority: Signer<'info>,
}

#[account]
Expand Down
10 changes: 5 additions & 5 deletions examples/tutorial/basic-3/programs/puppet-master/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// #region core
use anchor_lang::prelude::*;
use puppet::{self, Puppet, SetData};
use puppet::program::Puppet;
use puppet::{self, Data, SetData};

declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");

#[program]
mod puppet_master {
use super::*;
pub fn pull_strings(ctx: Context<PullStrings>, data: u64) -> ProgramResult {
let cpi_program = ctx.accounts.puppet_program.clone();
let cpi_program = ctx.accounts.puppet_program.to_account_info();
let cpi_accounts = SetData {
puppet: ctx.accounts.puppet.clone(),
};
Expand All @@ -20,8 +21,7 @@ mod puppet_master {
#[derive(Accounts)]
pub struct PullStrings<'info> {
#[account(mut)]
pub puppet: Account<'info, Puppet>,
#[account(address = puppet::ID)]
pub puppet_program: AccountInfo<'info>,
pub puppet: Account<'info, Data>,
pub puppet_program: Program<'info, Puppet>,
}
// #endregion core
16 changes: 7 additions & 9 deletions examples/tutorial/basic-3/programs/puppet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_program;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod puppet {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
Ok(())
}

Expand All @@ -20,20 +19,19 @@ pub mod puppet {
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub puppet: Account<'info, Puppet>,
#[account(signer)]
pub user: AccountInfo<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
pub puppet: Account<'info, Data>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct SetData<'info> {
#[account(mut)]
pub puppet: Account<'info, Puppet>,
pub puppet: Account<'info, Data>,
}

#[account]
pub struct Puppet {
pub struct Data {
pub data: u64,
}
2 changes: 1 addition & 1 deletion examples/tutorial/basic-3/tests/basic-3.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("basic-3", () => {
});

// Check the state updated.
puppetAccount = await puppet.account.puppet.fetch(newPuppetAccount.publicKey);
puppetAccount = await puppet.account.data.fetch(newPuppetAccount.publicKey);
assert.ok(puppetAccount.data.eq(new anchor.BN(111)));
});
});
3 changes: 1 addition & 2 deletions examples/tutorial/basic-4/programs/basic-4/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ pub mod basic_4 {

#[derive(Accounts)]
pub struct Auth<'info> {
#[account(signer)]
authority: AccountInfo<'info>,
authority: Signer<'info>,
}
// #endregion code

Expand Down
6 changes: 6 additions & 0 deletions lang/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ pub enum ErrorCode {
AccountNotMutable,
#[msg("The given account is not owned by the executing program")]
AccountNotProgramOwned,
#[msg("Program ID was not as expected")]
InvalidProgramId,
#[msg("Program account is not executable")]
InvalidProgramExecutable,
#[msg("The given account did not sign")]
AccountNotSigner,

// State.
#[msg("The given state account does not have the correct address")]
Expand Down
15 changes: 13 additions & 2 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ mod error;
#[doc(hidden)]
pub mod idl;
mod loader;
mod program;
mod program_account;
mod signer;
pub mod state;
mod system_program;
mod sysvar;
mod vec;

Expand All @@ -61,12 +64,15 @@ pub use crate::cpi_account::CpiAccount;
#[allow(deprecated)]
pub use crate::cpi_state::CpiState;
pub use crate::loader::Loader;
pub use crate::program::Program;
#[doc(hidden)]
#[allow(deprecated)]
pub use crate::program_account::ProgramAccount;
pub use crate::signer::Signer;
#[doc(hidden)]
#[allow(deprecated)]
pub use crate::state::ProgramState;
pub use crate::system_program::System;
pub use crate::sysvar::Sysvar;
pub use anchor_attribute_access_control::access_control;
pub use anchor_attribute_account::{account, declare_id, zero_copy};
Expand Down Expand Up @@ -217,6 +223,11 @@ pub trait Owner {
fn owner() -> Pubkey;
}

/// Defines the id of a program.
pub trait Id {
fn id() -> Pubkey;
}

/// Defines the Pubkey of an account.
pub trait Key {
fn key(&self) -> Pubkey;
Expand All @@ -234,8 +245,8 @@ pub mod prelude {
pub use super::{
access_control, account, declare_id, emit, error, event, interface, program, require,
state, zero_copy, Account, AccountDeserialize, AccountSerialize, Accounts, AccountsExit,
AnchorDeserialize, AnchorSerialize, Context, CpiContext, Key, Loader, Owner,
ProgramAccount, Sysvar, ToAccountInfo, ToAccountInfos, ToAccountMetas,
AnchorDeserialize, AnchorSerialize, Context, CpiContext, Key, Loader, Owner, Program,
ProgramAccount, Signer, System, Sysvar, ToAccountInfo, ToAccountInfos, ToAccountMetas,
};

#[allow(deprecated)]
Expand Down
97 changes: 97 additions & 0 deletions lang/src/program.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use crate::error::ErrorCode;
use crate::*;
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;
use std::ops::Deref;

/// Account container that checks ownership on deserialization.
#[derive(Clone)]
pub struct Program<'info, T: Id + AccountDeserialize + Clone> {
account: T,
info: AccountInfo<'info>,
}

impl<'a, T: Id + AccountDeserialize + Clone> Program<'a, T> {
fn new(info: AccountInfo<'a>, account: T) -> Program<'a, T> {
Self { info, account }
}

/// Deserializes the given `info` into a `Program`.
#[inline(never)]
pub fn try_from(info: &AccountInfo<'a>) -> Result<Program<'a, T>, ProgramError> {
if info.key != &T::id() {
return Err(ErrorCode::InvalidProgramId.into());
}
if !info.executable {
return Err(ErrorCode::InvalidProgramExecutable.into());
}
// Programs have no data so use an empty slice.
let mut empty = &[][..];
Ok(Program::new(info.clone(), T::try_deserialize(&mut empty)?))
}
}

impl<'info, T: Id + Clone> Accounts<'info> for Program<'info, T>
where
T: Id + AccountDeserialize,
{
#[inline(never)]
fn try_accounts(
_program_id: &Pubkey,
accounts: &mut &[AccountInfo<'info>],
_ix_data: &[u8],
) -> Result<Self, ProgramError> {
if accounts.is_empty() {
return Err(ErrorCode::AccountNotEnoughKeys.into());
}
let account = &accounts[0];
*accounts = &accounts[1..];
Program::try_from(account)
}
}

impl<'info, T: Id + AccountDeserialize + Clone> ToAccountMetas for Program<'info, T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
let is_signer = is_signer.unwrap_or(self.info.is_signer);
let meta = match self.info.is_writable {
false => AccountMeta::new_readonly(*self.info.key, is_signer),
true => AccountMeta::new(*self.info.key, is_signer),
};
vec![meta]
}
}

impl<'info, T: Id + AccountDeserialize + Clone> ToAccountInfos<'info> for Program<'info, T> {
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
vec![self.info.clone()]
}
}

impl<'info, T: Id + AccountDeserialize + Clone> ToAccountInfo<'info> for Program<'info, T> {
fn to_account_info(&self) -> AccountInfo<'info> {
self.info.clone()
}
}

impl<'info, T: Id + AccountDeserialize + Clone> AsRef<AccountInfo<'info>> for Program<'info, T> {
fn as_ref(&self) -> &AccountInfo<'info> {
&self.info
}
}

impl<'info, T: Id + AccountDeserialize + Clone> Deref for Program<'info, T> {
type Target = AccountInfo<'info>;

fn deref(&self) -> &Self::Target {
&self.info
}
}

impl<'info, T: AccountDeserialize + Id + Clone> AccountsExit<'info> for Program<'info, T> {
fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
// No-op.
Ok(())
}
}
Loading

0 comments on commit b1ef743

Please sign in to comment.