diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 5fca30ebe..d453b6487 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -360,8 +360,6 @@ jobs: path: tests/escrow - cmd: cd tests/pyth && anchor test --skip-lint && npx tsc --noEmit path: tests/pyth - - cmd: cd tests/realloc && anchor test --skip-lint && npx tsc --noEmit - path: tests/realloc - cmd: cd tests/system-accounts && anchor test --skip-lint path: tests/system-accounts - cmd: cd tests/misc && anchor test --skip-lint && npx tsc --noEmit diff --git a/CHANGELOG.md b/CHANGELOG.md index ce426da67..3ef147b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,6 @@ The minor version will be incremented upon a breaking change and the patch versi ### Features -* lang: Add `realloc`, `realloc::payer`, and `realloc::zero` as a new constraint group for program accounts ([#1943](https://github.com/project-serum/anchor/pull/1943)). * lang: Add `PartialEq` and `Eq` for `anchor_lang::Error` ([#1544](https://github.com/project-serum/anchor/pull/1544)). * cli: Add `--skip-build` to `anchor publish` ([#1786](https://github. com/project-serum/anchor/pull/1841)). diff --git a/lang/derive/accounts/src/lib.rs b/lang/derive/accounts/src/lib.rs index 5ab883e27..1c94ced5a 100644 --- a/lang/derive/accounts/src/lib.rs +++ b/lang/derive/accounts/src/lib.rs @@ -44,7 +44,6 @@ use syn::parse_macro_input; /// /// - [Normal Constraints](#normal-constraints) /// - [SPL Constraints](#spl-constraints) -/// /// # Normal Constraints /// /// @@ -419,44 +418,6 @@ use syn::parse_macro_input; /// /// /// -/// -/// -/// -/// /// ///
-/// #[account(realloc = <space>, realloc::payer = <target>, realloc::zero = <bool>)] -/// -/// Used to realloc -/// program account space at the beginning of an instruction. -///

-/// The account must be marked as mut and applied to either Account or AccountLoader types. -///

-/// If the change in account data length is additive, lamports will be transferred from the realloc::payer into the -/// program account in order to maintain rent exemption. Likewise, if the change is subtractive, lamports will be transferred from -/// the program account back into the realloc::payer. -///

-/// The realloc::zero constraint is required in order to determine whether the new memory should be zero initialized after -/// reallocation. Please read the documentation on the AccountInfo::realloc function linked above to understand the -/// caveats regarding compute units when providing truefalse to this flag. -///

-/// Example: -///
-/// #[derive(Accounts)]
-/// pub struct Example {
-///     #[account(mut)]
-///     pub payer: Signer<'info>,
-///     #[account(
-///         mut,
-///         seeds = [b"example"],
-///         bump,
-///         realloc = 8 + std::mem::size_of::() + 100,
-///         realloc::payer = payer,
-///         realloc::zero = false,
-///     )]
-///     pub acc: Account<'info, MyType>,
-///     pub system_program: Program<'info, System>,
-/// }
-///                 
-///
/// diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 08acc4a89..75f4a00f5 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -59,7 +59,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { associated_token, token_account, mint, - realloc, } = c_group.clone(); let mut constraints = Vec::new(); @@ -70,9 +69,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { if let Some(c) = init { constraints.push(Constraint::Init(c)); } - if let Some(c) = realloc { - constraints.push(Constraint::Realloc(c)); - } if let Some(c) = seeds { constraints.push(Constraint::Seeds(c)); } @@ -134,7 +130,6 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c), Constraint::TokenAccount(c) => generate_constraint_token_account(f, c), Constraint::Mint(c) => generate_constraint_mint(f, c), - Constraint::Realloc(c) => generate_constraint_realloc(f, c), } } @@ -325,46 +320,6 @@ pub fn generate_constraint_rent_exempt( } } -fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_macro2::TokenStream { - let field = &f.ident; - let new_space = &c.space; - let payer = &c.payer; - let zero = &c.zero; - - quote! { - let __anchor_rent = Rent::get()?; - let __field_info = #field.to_account_info(); - let __additive = #new_space > __field_info.data_len(); - - let __delta_space = if __additive { - #new_space.checked_sub(__field_info.data_len()).unwrap() - } else { - __field_info.data_len().checked_sub(#new_space).unwrap() - }; - - if __delta_space > 0 { - if __additive { - anchor_lang::system_program::transfer( - anchor_lang::context::CpiContext::new( - system_program.to_account_info(), - anchor_lang::system_program::Transfer { - from: #payer.to_account_info(), - to: __field_info.clone(), - }, - ), - __anchor_rent.minimum_balance(#new_space).checked_sub(__field_info.lamports()).unwrap(), - )?; - } else { - let __lamport_amt = __field_info.lamports().checked_sub(__anchor_rent.minimum_balance(#new_space)).unwrap(); - **#payer.to_account_info().lamports.borrow_mut() = #payer.to_account_info().lamports().checked_add(__lamport_amt).unwrap(); - **__field_info.lamports.borrow_mut() = __field_info.lamports().checked_sub(__lamport_amt).unwrap(); - } - - #field.to_account_info().realloc(#new_space, #zero)?; - } - } -} - fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream { let field = &f.ident; let name_str = f.ident.to_string(); diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 5aeac91b4..c589d87bf 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -635,7 +635,6 @@ pub struct ConstraintGroup { associated_token: Option, token_account: Option, mint: Option, - realloc: Option, } impl ConstraintGroup { @@ -679,7 +678,6 @@ pub enum Constraint { Address(ConstraintAddress), TokenAccount(ConstraintTokenAccountGroup), Mint(ConstraintTokenMintGroup), - Realloc(ConstraintReallocGroup), } // Constraint token is a single keyword in a `#[account()]` attribute. @@ -711,9 +709,6 @@ pub enum ConstraintToken { MintDecimals(Context), Bump(Context), ProgramSeed(Context), - Realloc(Context), - ReallocPayer(Context), - ReallocZero(Context), } impl Parse for ConstraintToken { @@ -738,28 +733,6 @@ pub struct ConstraintMut { pub error: Option, } -#[derive(Debug, Clone)] -pub struct ConstraintReallocGroup { - pub payer: Expr, - pub space: Expr, - pub zero: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintRealloc { - pub space: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintReallocPayer { - pub target: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintReallocZero { - pub zero: Expr, -} - #[derive(Debug, Clone)] pub struct ConstraintSigner { pub error: Option, diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index ac71a6808..fcd38c1e1 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -196,47 +196,6 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { )) } } - "realloc" => { - if stream.peek(Token![=]) { - stream.parse::()?; - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - ConstraintToken::Realloc(Context::new( - span, - ConstraintRealloc { - space: stream.parse()?, - }, - )) - } else { - stream.parse::()?; - stream.parse::()?; - let kw = stream.call(Ident::parse_any)?.to_string(); - stream.parse::()?; - - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - - match kw.as_str() { - "payer" => ConstraintToken::ReallocPayer(Context::new( - span, - ConstraintReallocPayer { - target: stream.parse()?, - }, - )), - "zero" => ConstraintToken::ReallocZero(Context::new( - span, - ConstraintReallocZero { - zero: stream.parse()?, - }, - )), - _ => return Err(ParseError::new(ident.span(), "Invalid attribute. realloc::payer and realloc::zero are the only valid attributes")), - } - } - } _ => { stream.parse::()?; let span = ident @@ -354,9 +313,6 @@ pub struct ConstraintGroupBuilder<'ty> { pub mint_decimals: Option>, pub bump: Option>, pub program_seed: Option>, - pub realloc: Option>, - pub realloc_payer: Option>, - pub realloc_zero: Option>, } impl<'ty> ConstraintGroupBuilder<'ty> { @@ -388,9 +344,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_decimals: None, bump: None, program_seed: None, - realloc: None, - realloc_payer: None, - realloc_zero: None, } } @@ -486,22 +439,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { } } - // Realloc. - if let Some(r) = &self.realloc { - if self.realloc_payer.is_none() { - return Err(ParseError::new( - r.span(), - "realloc::payer must be provided when using realloc", - )); - } - if self.realloc_zero.is_none() { - return Err(ParseError::new( - r.span(), - "realloc::zero must be provided when using realloc", - )); - } - } - // Zero. if let Some(z) = &self.zeroed { match self.mutable { @@ -589,9 +526,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_decimals, bump, program_seed, - realloc, - realloc_payer, - realloc_zero, } = self; // Converts Option> -> Option. @@ -710,11 +644,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { } }, })).transpose()?, - realloc: realloc.as_ref().map(|r| ConstraintReallocGroup { - payer: into_inner!(realloc_payer).unwrap().target, - space: r.space.clone(), - zero: into_inner!(realloc_zero).unwrap().zero, - }), zeroed: into_inner!(zeroed), mutable: into_inner!(mutable), signer: into_inner!(signer), @@ -761,9 +690,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c), ConstraintToken::Bump(c) => self.add_bump(c), ConstraintToken::ProgramSeed(c) => self.add_program_seed(c), - ConstraintToken::Realloc(c) => self.add_realloc(c), - ConstraintToken::ReallocPayer(c) => self.add_realloc_payer(c), - ConstraintToken::ReallocZero(c) => self.add_realloc_zero(c), } } @@ -831,56 +757,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { Ok(()) } - fn add_realloc(&mut self, c: Context) -> ParseResult<()> { - if !matches!(self.f_ty, Some(Ty::Account(_))) - && !matches!(self.f_ty, Some(Ty::AccountLoader(_))) - { - return Err(ParseError::new( - c.span(), - "realloc must be on an Account or AccountLoader", - )); - } - if self.mutable.is_none() { - return Err(ParseError::new( - c.span(), - "mut must be provided before realloc", - )); - } - if self.realloc.is_some() { - return Err(ParseError::new(c.span(), "realloc already provided")); - } - self.realloc.replace(c); - Ok(()) - } - - fn add_realloc_payer(&mut self, c: Context) -> ParseResult<()> { - if self.realloc.is_none() { - return Err(ParseError::new( - c.span(), - "realloc must be provided before realloc::payer", - )); - } - if self.realloc_payer.is_some() { - return Err(ParseError::new(c.span(), "realloc::payer already provided")); - } - self.realloc_payer.replace(c); - Ok(()) - } - - fn add_realloc_zero(&mut self, c: Context) -> ParseResult<()> { - if self.realloc.is_none() { - return Err(ParseError::new( - c.span(), - "realloc must be provided before realloc::zero", - )); - } - if self.realloc_zero.is_some() { - return Err(ParseError::new(c.span(), "realloc::zero already provided")); - } - self.realloc_zero.replace(c); - Ok(()) - } - fn add_close(&mut self, c: Context) -> ParseResult<()> { if !matches!(self.f_ty, Some(Ty::ProgramAccount(_))) && !matches!(self.f_ty, Some(Ty::Account(_))) diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index 5d9d19bab..a31f21841 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -55,7 +55,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { init_fields[0].ident.span(), "the init constraint requires \ the system_program field to exist in the account \ - validation struct. Use the Program type to add \ + validation struct. Use the program type to add \ the system_program field to your validation struct.", )); } @@ -70,7 +70,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { init_fields[0].ident.span(), "the init constraint requires \ the token_program field to exist in the account \ - validation struct. Use the Program type to add \ + validation struct. Use the program type to add \ the token_program field to your validation struct.", )); } @@ -86,7 +86,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { init_fields[0].ident.span(), "the init constraint requires \ the associated_token_program field to exist in the account \ - validation struct. Use the Program type to add \ + validation struct. Use the program type to add \ the associated_token_program field to your validation struct.", )); } @@ -141,61 +141,6 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { } } } - - // REALLOC - let realloc_fields: Vec<&Field> = fields - .iter() - .filter_map(|f| match f { - AccountField::Field(field) if field.constraints.realloc.is_some() => Some(field), - _ => None, - }) - .collect(); - - if !realloc_fields.is_empty() { - // realloc needs system program. - if fields.iter().all(|f| f.ident() != "system_program") { - return Err(ParseError::new( - realloc_fields[0].ident.span(), - "the realloc constraint requires \ - the system_program field to exist in the account \ - validation struct. Use the Program type to add \ - the system_program field to your validation struct.", - )); - } - - for field in realloc_fields { - // Get allocator for realloc-ed account - let associated_payer_name = match field.constraints.realloc.clone().unwrap().payer { - // composite allocator, check not supported - Expr::Field(_) => continue, - field_name => field_name.to_token_stream().to_string(), - }; - - // Check allocator is mutable - let associated_payer_field = fields.iter().find_map(|f| match f { - AccountField::Field(field) if *f.ident() == associated_payer_name => Some(field), - _ => None, - }); - - match associated_payer_field { - Some(associated_payer_field) => { - if !associated_payer_field.constraints.is_mutable() { - return Err(ParseError::new( - field.ident.span(), - "the realloc::payer specified for an realloc constraint must be mutable.", - )); - } - } - _ => { - return Err(ParseError::new( - field.ident.span(), - "the realloc::payer specified does not exist.", - )); - } - } - } - } - Ok(()) } diff --git a/tests/package.json b/tests/package.json index a0b35d787..35112548c 100644 --- a/tests/package.json +++ b/tests/package.json @@ -24,7 +24,6 @@ "permissioned-markets", "pda-derivation", "pyth", - "realloc", "spl/token-proxy", "swap", "system-accounts", diff --git a/tests/realloc/Anchor.toml b/tests/realloc/Anchor.toml deleted file mode 100644 index 7589b901a..000000000 --- a/tests/realloc/Anchor.toml +++ /dev/null @@ -1,15 +0,0 @@ -[features] -seeds = false - -[programs.localnet] -realloc = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/realloc/Cargo.toml b/tests/realloc/Cargo.toml deleted file mode 100644 index a60de986d..000000000 --- a/tests/realloc/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/realloc/package.json b/tests/realloc/package.json deleted file mode 100644 index 5734989ba..000000000 --- a/tests/realloc/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "realloc", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } - } - \ No newline at end of file diff --git a/tests/realloc/programs/realloc/Cargo.toml b/tests/realloc/programs/realloc/Cargo.toml deleted file mode 100644 index 4f014303e..000000000 --- a/tests/realloc/programs/realloc/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "realloc" -version = "0.1.0" -description = "Created with Anchor" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "realloc" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[profile.release] -overflow-checks = true - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/realloc/programs/realloc/Xargo.toml b/tests/realloc/programs/realloc/Xargo.toml deleted file mode 100644 index 475fb71ed..000000000 --- a/tests/realloc/programs/realloc/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/realloc/programs/realloc/src/lib.rs b/tests/realloc/programs/realloc/src/lib.rs deleted file mode 100644 index ac1ea468c..000000000 --- a/tests/realloc/programs/realloc/src/lib.rs +++ /dev/null @@ -1,70 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod realloc { - use super::*; - - pub fn initialize(ctx: Context) -> Result<()> { - ctx.accounts.sample.data = vec![0]; - ctx.accounts.sample.bump = *ctx.bumps.get("sample").unwrap(); - Ok(()) - } - - pub fn realloc(ctx: Context, len: u8) -> Result<()> { - ctx.accounts - .sample - .data - .resize_with(len as usize, Default::default); - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(mut)] - pub authority: Signer<'info>, - - #[account( - init, - payer = authority, - seeds = [b"sample"], - bump, - space = Sample::space(1), - )] - pub sample: Account<'info, Sample>, - - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -#[instruction(len: u8)] -pub struct Realloc<'info> { - #[account(mut)] - pub authority: Signer<'info>, - - #[account( - mut, - seeds = [b"sample"], - bump = sample.bump, - realloc = Sample::space(len as usize), - realloc::payer = authority, - realloc::zero = false, - )] - pub sample: Account<'info, Sample>, - - pub system_program: Program<'info, System>, -} - -#[account] -pub struct Sample { - pub data: Vec, - pub bump: u8, -} - -impl Sample { - pub fn space(len: usize) -> usize { - 8 + (4 + len) + 1 - } -} diff --git a/tests/realloc/tests/realloc.ts b/tests/realloc/tests/realloc.ts deleted file mode 100644 index e74921760..000000000 --- a/tests/realloc/tests/realloc.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { assert } from "chai"; -import { Realloc } from "../target/types/realloc"; - -describe("realloc", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.Realloc as Program; - const authority = (program.provider as any).wallet - .payer as anchor.web3.Keypair; - - let sample: anchor.web3.PublicKey; - - before(async () => { - [sample] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from("sample")], - program.programId - ); - }); - - it("Is initialized!", async () => { - await program.methods - .initialize() - .accounts({ authority: authority.publicKey, sample }) - .rpc(); - - const s = await program.account.sample.fetch(sample); - assert.lengthOf(s.data, 1); - }); - - it("realloc additive", async () => { - await program.methods - .realloc(5) - .accounts({ authority: authority.publicKey, sample }) - .rpc(); - - const s = await program.account.sample.fetch(sample); - assert.lengthOf(s.data, 5); - }); - - it("realloc substractive", async () => { - await program.methods - .realloc(1) - .accounts({ authority: authority.publicKey, sample }) - .rpc(); - - const s = await program.account.sample.fetch(sample); - assert.lengthOf(s.data, 1); - }); -}); diff --git a/tests/realloc/tsconfig.json b/tests/realloc/tsconfig.json deleted file mode 100644 index cd5d2e3d0..000000000 --- a/tests/realloc/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -}