lang: Add owner constraint (#178)
This commit is contained in:
parent
290e342ade
commit
a94e24aea4
|
@ -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
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue