Przeglądaj źródła

lang: Add owner constraint (#178)

Armani Ferrante 4 lat temu
rodzic
commit
a94e24aea4

+ 1 - 0
CHANGELOG.md

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

+ 11 - 0
examples/misc/programs/misc/src/lib.rs

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

+ 25 - 1
examples/misc/tests/misc.js

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

+ 1 - 0
lang/derive/accounts/src/lib.rs

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

+ 8 - 12
lang/syn/src/codegen/accounts.rs

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

+ 2 - 3
lang/syn/src/lib.rs

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

+ 2 - 7
lang/syn/src/parser/accounts.rs

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