Skip to content

Commit

Permalink
lang: Add owner constraint (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
armaniferrante authored Apr 12, 2021
1 parent 290e342 commit a94e24a
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ incremented for features.
## Features

* lang: CPI clients for program state instructions ([#43](https://github.com/project-serum/anchor/pull/43)).
* lang: Add `#[account(owner = <program>)]` constraint ([#178](https://github.com/project-serum/anchor/pull/178)).

## Fixes

Expand Down
11 changes: 11 additions & 0 deletions examples/misc/programs/misc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ pub mod misc {
Ok(())
}

pub fn test_owner(_ctx: Context<TestOwner>) -> ProgramResult {
Ok(())
}

pub fn test_executable(_ctx: Context<TestExecutable>) -> ProgramResult {
Ok(())
}
Expand All @@ -52,6 +56,13 @@ pub struct Initialize<'info> {
rent: Sysvar<'info, Rent>,
}

#[derive(Accounts)]
pub struct TestOwner<'info> {
#[account(owner = misc)]
data: AccountInfo<'info>,
misc: AccountInfo<'info>,
}

#[derive(Accounts)]
pub struct TestExecutable<'info> {
#[account(executable)]
Expand Down
26 changes: 25 additions & 1 deletion examples/misc/tests/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ describe("misc", () => {
assert.ok(accountInfo.data.length === 99);
});

const data = new anchor.web3.Account();

it("Can use u128 and i128", async () => {
const data = new anchor.web3.Account();
const tx = await program.rpc.initialize(
new anchor.BN(1234),
new anchor.BN(22),
Expand All @@ -44,6 +45,29 @@ describe("misc", () => {
assert.ok(accInfo.executable);
});

it("Can use the owner constraint", async () => {
await program.rpc.testOwner({
accounts: {
data: data.publicKey,
misc: program.programId,
},
});

await assert.rejects(
async () => {
await program.rpc.testOwner({
accounts: {
data: program.provider.wallet.publicKey,
misc: program.programId,
},
});
},
(err) => {
return true;
}
);
});

it("Can use the executable attribtue", async () => {
await program.rpc.testExecutable({
accounts: {
Expand Down
1 change: 1 addition & 0 deletions lang/derive/accounts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use syn::parse_macro_input;
/// | `#[account(rent_exempt = <skip>)]` | On `AccountInfo` or `ProgramAccount` structs | Optional attribute to skip the rent exemption check. By default, all accounts marked with `#[account(init)]` will be rent exempt, and so this should rarely (if ever) be used. Similarly, omitting `= skip` will mark the account rent exempt. |
/// | `#[account(executable)]` | On `AccountInfo` structs | Checks the given account is an executable program. |
/// | `#[account(state = <target>)]` | On `CpiState` structs | Checks the given state is the canonical state account for the target program. |
/// | `#[account(owner = <target>)]` | On `CpiState`, `CpiAccount`, and `AccountInfo` | Checks the account owner matches the target. |
#[proc_macro_derive(Accounts, attributes(account))]
pub fn derive_anchor_deserialize(item: TokenStream) -> TokenStream {
let strct = parse_macro_input!(item as syn::ItemStruct);
Expand Down
20 changes: 8 additions & 12 deletions lang/syn/src/codegen/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,18 +374,11 @@ pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenS

pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
let ident = &f.ident;
let info = match f.ty {
Ty::AccountInfo => quote! { #ident },
Ty::ProgramAccount(_) => quote! { #ident.to_account_info() },
_ => panic!("Invalid syntax: owner cannot be specified."),
};
match c {
ConstraintOwner::Skip => quote! {},
ConstraintOwner::Program => quote! {
if #info.owner != program_id {
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(1)); // todo: error codes
}
},
let owner_target = c.owner_target.clone();
quote! {
if #ident.to_account_info().owner != #owner_target.to_account_info().key {
return Err(ProgramError::Custom(76)); // todo: proper error.
}
}
}

Expand Down Expand Up @@ -448,5 +441,8 @@ pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2:
if #ident.to_account_info().key != &anchor_lang::CpiState::<#account_ty>::address(#program_target.to_account_info().key) {
return Err(ProgramError::Custom(1)); // todo: proper error.
}
if #ident.to_account_info().owner != #program_target.to_account_info().key {
return Err(ProgramError::Custom(1)); // todo: proper error.
}
}
}
5 changes: 2 additions & 3 deletions lang/syn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,8 @@ pub struct ConstraintLiteral {
}

#[derive(Debug)]
pub enum ConstraintOwner {
Program,
Skip,
pub struct ConstraintOwner {
pub owner_target: proc_macro2::Ident,
}

#[derive(Debug)]
Expand Down
9 changes: 2 additions & 7 deletions lang/syn/src/parser/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,16 +243,11 @@ fn parse_constraints(anchor: &syn::Attribute) -> (Vec<Constraint>, bool, bool, b
}
_ => panic!("invalid syntax"),
};
let owner = match inner_tts.next().unwrap() {
let owner_target = match inner_tts.next().unwrap() {
proc_macro2::TokenTree::Ident(ident) => ident,
_ => panic!("invalid syntax"),
};
let constraint = match owner.to_string().as_str() {
"program" => ConstraintOwner::Program,
"skip" => ConstraintOwner::Skip,
_ => panic!("invalid syntax"),
};
constraints.push(Constraint::Owner(constraint));
constraints.push(Constraint::Owner(ConstraintOwner { owner_target }));
}
"rent_exempt" => {
match inner_tts.next() {
Expand Down

0 comments on commit a94e24a

Please sign in to comment.