lang: Add owner constraint (#178)

This commit is contained in:
Armani Ferrante 2021-04-12 12:54:35 +08:00 committed by GitHub
parent 290e342ade
commit a94e24aea4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 23 deletions

View File

@ -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

View File

@ -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(())
}
@ -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)]

View File

@ -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),
@ -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: {

View File

@ -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);

View File

@ -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.
}
}
}
@ -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.
}
}
}

View File

@ -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)]

View File

@ -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() {