diff --git a/CHANGELOG.md b/CHANGELOG.md index ad8948dfc6..f923289684 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 8890fc6ee9..aa9d44dff5 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 3e3c67d4ee..16bdd87cc7 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 a5d70a8baf..55c932198f 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 d09778d385..43e3425512 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 89650a2407..9fa05c7fc1 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 18d9b4f451..ed2500ed15 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 dc3406c0c8..c2090685f1 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"