Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lang, spl: Program and Signer types #705

Merged
merged 15 commits into from
Sep 11, 2021
Merged
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