From 17c9463592dee108d393b37475971a7e3197146a Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 30 Jan 2022 21:59:25 +0100 Subject: [PATCH] Lang: feature flag for init_if_needed (#1258) Co-authored-by: Armani Ferrante --- CHANGELOG.md | 3 ++- cli/Cargo.toml | 2 +- lang/Cargo.toml | 1 + lang/derive/accounts/Cargo.toml | 1 + lang/derive/accounts/src/lib.rs | 14 ++++++++++++-- lang/syn/Cargo.toml | 1 + lang/syn/src/parser/accounts/constraints.rs | 11 +++++++++++ tests/misc/programs/misc/Cargo.toml | 2 +- 8 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad8948df..f9232896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,13 +14,14 @@ incremented for features. ### Features * lang: Add `seeds::program` constraint for specifying which program_id to use when deriving PDAs.([#1197](https://github.com/project-serum/anchor/pull/1197)) -* lang: `Context` now has a new `bumps: BTree` argument, mapping account name to bump seed "found" by the accounts context. This allows one to access bump seeds without having to pass them in from the client or recalculate them in the handler ([#1367](calculated )). +* lang: `Context` now has a new `bumps: BTree` argument, mapping account name to bump seed "found" by the accounts context. This allows one to access bump seeds without having to pass them in from the client or recalculate them in the handler ([#1367](https://github.com/project-serum/anchor/pull/1367)). * ts: Remove error logging in the event parser when log websocket encounters a program error ([#1313](https://github.com/project-serum/anchor/pull/1313)). * ts: Add new `methods` namespace to the program client, introducing a more ergonomic builder API ([#1324](https://github.com/project-serum/anchor/pull/1324)). * ts: Add registry utility for fetching the latest verified build ([#1371](https://github.com/project-serum/anchor/pull/1371)). ### Breaking +* lang: Put `init_if_needed` behind a feature flag to decrease wrong usage ([#1258](https://github.com/project-serum/anchor/pull/1258)). * lang: rename `loader_account` module to `account_loader` module ([#1279](https://github.com/project-serum/anchor/pull/1279)) * lang: The `Accounts` trait's `try_accounts` method now has an additional `bumps: &mut BTreeMap` argument, which accumulates bump seeds ([#1367](https://github.com/project-serum/anchor/pull/1367)). * ts: `Coder` is now an interface and the existing class has been renamed to `BorshCoder`. This change allows the generation of Anchor clients for non anchor programs ([#1259](https://github.com/project-serum/anchor/pull/1259/files)). diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8890fc6e..aa9d44df 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -18,7 +18,7 @@ anyhow = "1.0.32" syn = { version = "1.0.60", features = ["full", "extra-traits"] } anchor-lang = { path = "../lang" } anchor-client = { path = "../client" } -anchor-syn = { path = "../lang/syn", features = ["idl"] } +anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"] } serde_json = "1.0" shellexpand = "2.1.0" toml = "0.5.8" diff --git a/lang/Cargo.toml b/lang/Cargo.toml index 3e3c67d4..16bdd87c 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -8,6 +8,7 @@ license = "Apache-2.0" description = "Solana Sealevel eDSL" [features] +init-if-needed = ["anchor-derive-accounts/init-if-needed"] derive = [] default = [] anchor-debug = [ diff --git a/lang/derive/accounts/Cargo.toml b/lang/derive/accounts/Cargo.toml index a5d70a8b..55c93219 100644 --- a/lang/derive/accounts/Cargo.toml +++ b/lang/derive/accounts/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" proc-macro = true [features] +init-if-needed = ["anchor-syn/init-if-needed"] default = [] anchor-debug = ["anchor-syn/anchor-debug"] diff --git a/lang/derive/accounts/src/lib.rs b/lang/derive/accounts/src/lib.rs index d09778d3..43e34255 100644 --- a/lang/derive/accounts/src/lib.rs +++ b/lang/derive/accounts/src/lib.rs @@ -200,8 +200,18 @@ use syn::parse_macro_input; /// /// /// Exact same functionality as the init constraint but only runs if the account does not exist yet.
-/// If it does exist, it still checks whether the given init constraints are correct, -/// e.g. that the account has the expected amount of space and, if it's a PDA, the correct seeds etc. +/// If the account does exist, it still checks whether the given init constraints are correct, +/// e.g. that the account has the expected amount of space and, if it's a PDA, the correct seeds etc.

+/// This feature should be used with care and is therefore behind a feature flag. +/// You can enable it by importing anchor-lang with the init-if-needed cargo feature.
+/// When using init_if_needed, you need to make sure you properly protect yourself +/// against re-initialization attacks. You need to include checks in your code that check +/// that the initialized account cannot be reset to its initial settings after the first time it was +/// initialized (unless that it what you want).
+/// Because of the possibility of re-initialization attacks and the general guideline that instructions +/// should avoid having multiple execution flows (which is important so they remain easy to understand), +/// consider breaking up your instruction into two instructions - one for initializing and one for using +/// the account - unless you have a good reason not to do so. ///

/// Example: ///
diff --git a/lang/syn/Cargo.toml b/lang/syn/Cargo.toml
index 89650a24..9fa05c7f 100644
--- a/lang/syn/Cargo.toml
+++ b/lang/syn/Cargo.toml
@@ -8,6 +8,7 @@ description = "Anchor syntax parsing and code generation tools"
 edition = "2018"
 
 [features]
+init-if-needed = []
 idl = []
 hash = []
 default = []
diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs
index 18d9b4f4..ed2500ed 100644
--- a/lang/syn/src/parser/accounts/constraints.rs
+++ b/lang/syn/src/parser/accounts/constraints.rs
@@ -373,6 +373,17 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
     pub fn build(mut self) -> ParseResult {
         // Init.
         if let Some(i) = &self.init {
+            if cfg!(not(feature = "init-if-needed")) && i.if_needed {
+                return Err(ParseError::new(
+                    i.span(),
+                    "init_if_needed requires that anchor-lang be imported \
+                    with the init-if-needed cargo feature enabled. \
+                    Carefully read the init_if_needed docs before using this feature \
+                    to make sure you know how to protect yourself against \
+                    re-initialization attacks.",
+                ));
+            }
+
             match self.mutable {
                 Some(m) => {
                     return Err(ParseError::new(
diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml
index dc3406c0..c2090685 100644
--- a/tests/misc/programs/misc/Cargo.toml
+++ b/tests/misc/programs/misc/Cargo.toml
@@ -15,7 +15,7 @@ cpi = ["no-entrypoint"]
 default = []
 
 [dependencies]
-anchor-lang = { path = "../../../../lang" }
+anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
 anchor-spl = { path = "../../../../spl" }
 misc2 = { path = "../misc2", features = ["cpi"] }
 spl-associated-token-account = "=1.0.3"