lang: adjust `realloc` implementation to safeguard max increase and idempotency (#1986)
This commit is contained in:
parent
9b61bbc626
commit
c47fb2877e
|
@ -360,6 +360,8 @@ jobs:
|
||||||
path: tests/escrow
|
path: tests/escrow
|
||||||
- cmd: cd tests/pyth && anchor test --skip-lint && npx tsc --noEmit
|
- cmd: cd tests/pyth && anchor test --skip-lint && npx tsc --noEmit
|
||||||
path: tests/pyth
|
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
|
- cmd: cd tests/system-accounts && anchor test --skip-lint
|
||||||
path: tests/system-accounts
|
path: tests/system-accounts
|
||||||
- cmd: cd tests/misc && anchor test --skip-lint && npx tsc --noEmit
|
- cmd: cd tests/misc && anchor test --skip-lint && npx tsc --noEmit
|
||||||
|
|
|
@ -12,9 +12,9 @@ The minor version will be incremented upon a breaking change and the patch versi
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
* lang: Add `realloc`, `realloc::payer`, and `realloc::zero` as a new constraint group for program accounts ([#1986](https://github.com/coral-xyz/anchor/pull/1986)).
|
||||||
* lang: Add `PartialEq` and `Eq` for `anchor_lang::Error` ([#1544](https://github.com/coral-xyz/anchor/pull/1544)).
|
* lang: Add `PartialEq` and `Eq` for `anchor_lang::Error` ([#1544](https://github.com/coral-xyz/anchor/pull/1544)).
|
||||||
* cli: Add `--skip-build` to `anchor publish` ([#1786](https://github.
|
* cli: Add `--skip-build` to `anchor publish` ([#1786](https://github.com/coral-xyz/anchor/pull/1841)).
|
||||||
com/project-serum/anchor/pull/1841)).
|
|
||||||
* cli: Add `--program-keypair` to `anchor deploy` ([#1786](https://github.com/coral-xyz/anchor/pull/1786)).
|
* cli: Add `--program-keypair` to `anchor deploy` ([#1786](https://github.com/coral-xyz/anchor/pull/1786)).
|
||||||
* cli: Add compilation optimizations to cli template ([#1807](https://github.com/coral-xyz/anchor/pull/1807)).
|
* cli: Add compilation optimizations to cli template ([#1807](https://github.com/coral-xyz/anchor/pull/1807)).
|
||||||
* cli: `build` now adds docs to idl. This can be turned off with `--no-docs` ([#1561](https://github.com/coral-xyz/anchor/pull/1561)).
|
* cli: `build` now adds docs to idl. This can be turned off with `--no-docs` ([#1561](https://github.com/coral-xyz/anchor/pull/1561)).
|
||||||
|
@ -41,6 +41,7 @@ com/project-serum/anchor/pull/1841)).
|
||||||
* ts: Change `BROWSER` env variable to `ANCHOR_BROWSER` ([#1233](https://github.com/coral-xyz/anchor/pull/1233)).
|
* ts: Change `BROWSER` env variable to `ANCHOR_BROWSER` ([#1233](https://github.com/coral-xyz/anchor/pull/1233)).
|
||||||
* ts: Add transaction signature to `EventCallback` parameters ([#1851](https://github.com/coral-xyz/anchor/pull/1851)).
|
* ts: Add transaction signature to `EventCallback` parameters ([#1851](https://github.com/coral-xyz/anchor/pull/1851)).
|
||||||
* ts: Change `EventParser#parseLogs` implementation to be a generator instead of callback function ([#2018](https://github.com/coral-xyz/anchor/pull/2018)).
|
* ts: Change `EventParser#parseLogs` implementation to be a generator instead of callback function ([#2018](https://github.com/coral-xyz/anchor/pull/2018)).
|
||||||
|
* lang: Adds a new `&mut reallocs: BTreeSet<Pubkey>` argument to `Accounts::try_accounts` ([#1986](https://github.com/coral-xyz/anchor/pull/1986)).
|
||||||
|
|
||||||
## [0.24.2] - 2022-04-13
|
## [0.24.2] - 2022-04-13
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ use syn::parse_macro_input;
|
||||||
///
|
///
|
||||||
/// - [Normal Constraints](#normal-constraints)
|
/// - [Normal Constraints](#normal-constraints)
|
||||||
/// - [SPL Constraints](#spl-constraints)
|
/// - [SPL Constraints](#spl-constraints)
|
||||||
|
///
|
||||||
/// # Normal Constraints
|
/// # Normal Constraints
|
||||||
/// <table>
|
/// <table>
|
||||||
/// <thead>
|
/// <thead>
|
||||||
|
@ -418,6 +419,48 @@ use syn::parse_macro_input;
|
||||||
/// </code></pre>
|
/// </code></pre>
|
||||||
/// </td>
|
/// </td>
|
||||||
/// </tr>
|
/// </tr>
|
||||||
|
/// <tr>
|
||||||
|
/// <td>
|
||||||
|
/// <code>#[account(realloc = <space>, realloc::payer = <target>, realloc::zero = <bool>)]</code>
|
||||||
|
/// </td>
|
||||||
|
/// <td>
|
||||||
|
/// Used to <a href="https://docs.rs/solana-program/latest/solana_program/account_info/struct.AccountInfo.html#method.realloc" target = "_blank" rel = "noopener noreferrer">realloc</a>
|
||||||
|
/// program account space at the beginning of an instruction.
|
||||||
|
/// <br><br>
|
||||||
|
/// The account must be marked as <code>mut</code> and applied to either <code>Account</code> or <code>AccountLoader</code> types.
|
||||||
|
/// <br><br>
|
||||||
|
/// If the change in account data length is additive, lamports will be transferred from the <code>realloc::payer</code> 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 <code>realloc::payer</code>.
|
||||||
|
/// <br><br>
|
||||||
|
/// The <code>realloc::zero</code> constraint is required in order to determine whether the new memory should be zero initialized after
|
||||||
|
/// reallocation. Please read the documentation on the <code>AccountInfo::realloc</code> function linked above to understand the
|
||||||
|
/// caveats regarding compute units when providing <code>true</code or <code>false</code> to this flag.
|
||||||
|
/// <br><br>
|
||||||
|
/// The manual use of `AccountInfo::realloc` is discouraged in favor of the `realloc` constraint group due to the lack of native runtime checks
|
||||||
|
/// to prevent reallocation over the `MAX_PERMITTED_DATA_INCREASE` limit (which can unintentionally cause account data overwrite other accounts).
|
||||||
|
/// The constraint group also ensure account reallocation idempotency but checking and restricting duplicate account reallocation within a single ix.
|
||||||
|
/// <br><br>
|
||||||
|
/// Example:
|
||||||
|
/// <pre>
|
||||||
|
/// #[derive(Accounts)]
|
||||||
|
/// pub struct Example {
|
||||||
|
/// #[account(mut)]
|
||||||
|
/// pub payer: Signer<'info>,
|
||||||
|
/// #[account(
|
||||||
|
/// mut,
|
||||||
|
/// seeds = [b"example"],
|
||||||
|
/// bump,
|
||||||
|
/// realloc = 8 + std::mem::size_of::<MyType>() + 100,
|
||||||
|
/// realloc::payer = payer,
|
||||||
|
/// realloc::zero = false,
|
||||||
|
/// )]
|
||||||
|
/// pub acc: Account<'info, MyType>,
|
||||||
|
/// pub system_program: Program<'info, System>,
|
||||||
|
/// }
|
||||||
|
/// </pre>
|
||||||
|
/// </td>
|
||||||
|
/// </tr>
|
||||||
/// </tbody>
|
/// </tbody>
|
||||||
/// </table>
|
/// </table>
|
||||||
///
|
///
|
||||||
|
|
|
@ -10,7 +10,7 @@ use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use solana_program::system_program;
|
use solana_program::system_program;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
@ -321,6 +321,7 @@ where
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
impl<'info> Accounts<'info> for AccountInfo<'info> {
|
impl<'info> Accounts<'info> for AccountInfo<'info> {
|
||||||
fn try_accounts(
|
fn try_accounts(
|
||||||
|
@ -15,6 +15,7 @@ impl<'info> Accounts<'info> for AccountInfo<'info> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -11,7 +11,7 @@ use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -221,6 +221,7 @@ impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAcc
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
impl<'info, T: Accounts<'info>> Accounts<'info> for Box<T> {
|
impl<'info, T: Accounts<'info>> Accounts<'info> for Box<T> {
|
||||||
|
@ -26,8 +26,9 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Box<T> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
ix_data: &[u8],
|
ix_data: &[u8],
|
||||||
bumps: &mut BTreeMap<String, u8>,
|
bumps: &mut BTreeMap<String, u8>,
|
||||||
|
reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
T::try_accounts(program_id, accounts, ix_data, bumps).map(Box::new)
|
T::try_accounts(program_id, accounts, ix_data, bumps, reallocs).map(Box::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ where
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
/// Boxed container for the program state singleton, used when the state
|
/// Boxed container for the program state singleton, used when the state
|
||||||
|
@ -72,6 +72,7 @@ where
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -9,7 +9,7 @@ use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -163,6 +163,7 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -8,7 +8,7 @@ use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState};
|
use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState};
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -147,6 +147,7 @@ where
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
/// Boxed container for a deserialized `account`. Use this to reference any
|
/// Boxed container for a deserialized `account`. Use this to reference any
|
||||||
|
@ -83,6 +83,7 @@ where
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Type validating that the account signed the transaction. No other ownership
|
/// Type validating that the account signed the transaction. No other ownership
|
||||||
|
@ -61,6 +61,7 @@ impl<'info> Accounts<'info> for Signer<'info> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
pub const PROGRAM_STATE_SEED: &str = "unversioned";
|
pub const PROGRAM_STATE_SEED: &str = "unversioned";
|
||||||
|
@ -74,6 +74,7 @@ where
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -6,7 +6,7 @@ use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use solana_program::system_program;
|
use solana_program::system_program;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Type validating that the account is owned by the system program
|
/// Type validating that the account is owned by the system program
|
||||||
|
@ -40,6 +40,7 @@ impl<'info> Accounts<'info> for SystemAccount<'info> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ impl<'info, T: solana_program::sysvar::Sysvar> Accounts<'info> for Sysvar<'info,
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Explicit wrapper for AccountInfo types to emphasize
|
/// Explicit wrapper for AccountInfo types to emphasize
|
||||||
|
@ -26,6 +26,7 @@ impl<'info> Accounts<'info> for UncheckedAccount<'info> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
_ix_data: &[u8],
|
_ix_data: &[u8],
|
||||||
_bumps: &mut BTreeMap<String, u8>,
|
_bumps: &mut BTreeMap<String, u8>,
|
||||||
|
_reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||||
|
|
|
@ -178,6 +178,12 @@ pub enum ErrorCode {
|
||||||
/// 3015 - The given public key does not match the required sysvar
|
/// 3015 - The given public key does not match the required sysvar
|
||||||
#[msg("The given public key does not match the required sysvar")]
|
#[msg("The given public key does not match the required sysvar")]
|
||||||
AccountSysvarMismatch,
|
AccountSysvarMismatch,
|
||||||
|
/// 3016 - The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit
|
||||||
|
#[msg("The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit")]
|
||||||
|
AccountReallocExceedsLimit,
|
||||||
|
/// 3017 - The account was duplicated for more than one reallocation
|
||||||
|
#[msg("The account was duplicated for more than one reallocation")]
|
||||||
|
AccountDuplicateReallocs,
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
/// 4000 - The given state account does not have the correct address
|
/// 4000 - The given state account does not have the correct address
|
||||||
|
|
|
@ -27,7 +27,7 @@ use bytemuck::{Pod, Zeroable};
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
mod account_meta;
|
mod account_meta;
|
||||||
|
@ -82,6 +82,7 @@ pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
ix_data: &[u8],
|
ix_data: &[u8],
|
||||||
bumps: &mut BTreeMap<String, u8>,
|
bumps: &mut BTreeMap<String, u8>,
|
||||||
|
reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self>;
|
) -> Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas};
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec<T> {
|
impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec<T> {
|
||||||
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
|
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
|
||||||
|
@ -26,9 +26,11 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Vec<T> {
|
||||||
accounts: &mut &[AccountInfo<'info>],
|
accounts: &mut &[AccountInfo<'info>],
|
||||||
ix_data: &[u8],
|
ix_data: &[u8],
|
||||||
bumps: &mut BTreeMap<String, u8>,
|
bumps: &mut BTreeMap<String, u8>,
|
||||||
|
reallocs: &mut BTreeSet<Pubkey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let mut vec: Vec<T> = Vec::new();
|
let mut vec: Vec<T> = Vec::new();
|
||||||
T::try_accounts(program_id, accounts, ix_data, bumps).map(|item| vec.push(item))?;
|
T::try_accounts(program_id, accounts, ix_data, bumps, reallocs)
|
||||||
|
.map(|item| vec.push(item))?;
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,9 +80,11 @@ mod tests {
|
||||||
Epoch::default(),
|
Epoch::default(),
|
||||||
);
|
);
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts = &[account1, account2][..];
|
let mut accounts = &[account1, account2][..];
|
||||||
let parsed_accounts =
|
let parsed_accounts =
|
||||||
Vec::<Test>::try_accounts(&program_id, &mut accounts, &[], &mut bumps).unwrap();
|
Vec::<Test>::try_accounts(&program_id, &mut accounts, &[], &mut bumps, &mut reallocs)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(accounts.len(), parsed_accounts.len());
|
assert_eq!(accounts.len(), parsed_accounts.len());
|
||||||
}
|
}
|
||||||
|
@ -90,7 +94,9 @@ mod tests {
|
||||||
fn test_accounts_trait_for_vec_empty() {
|
fn test_accounts_trait_for_vec_empty() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts = &[][..];
|
let mut accounts = &[][..];
|
||||||
Vec::<Test>::try_accounts(&program_id, &mut accounts, &[], &mut bumps).unwrap();
|
Vec::<Test>::try_accounts(&program_id, &mut accounts, &[], &mut bumps, &mut reallocs)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
|
||||||
associated_token,
|
associated_token,
|
||||||
token_account,
|
token_account,
|
||||||
mint,
|
mint,
|
||||||
|
realloc,
|
||||||
} = c_group.clone();
|
} = c_group.clone();
|
||||||
|
|
||||||
let mut constraints = Vec::new();
|
let mut constraints = Vec::new();
|
||||||
|
@ -69,6 +70,9 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
|
||||||
if let Some(c) = init {
|
if let Some(c) = init {
|
||||||
constraints.push(Constraint::Init(c));
|
constraints.push(Constraint::Init(c));
|
||||||
}
|
}
|
||||||
|
if let Some(c) = realloc {
|
||||||
|
constraints.push(Constraint::Realloc(c));
|
||||||
|
}
|
||||||
if let Some(c) = seeds {
|
if let Some(c) = seeds {
|
||||||
constraints.push(Constraint::Seeds(c));
|
constraints.push(Constraint::Seeds(c));
|
||||||
}
|
}
|
||||||
|
@ -130,6 +134,7 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
|
||||||
Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c),
|
Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c),
|
||||||
Constraint::TokenAccount(c) => generate_constraint_token_account(f, c),
|
Constraint::TokenAccount(c) => generate_constraint_token_account(f, c),
|
||||||
Constraint::Mint(c) => generate_constraint_mint(f, c),
|
Constraint::Mint(c) => generate_constraint_mint(f, c),
|
||||||
|
Constraint::Realloc(c) => generate_constraint_realloc(f, c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,6 +325,59 @@ pub fn generate_constraint_rent_exempt(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_macro2::TokenStream {
|
||||||
|
let field = &f.ident;
|
||||||
|
let account_name = field.to_string();
|
||||||
|
let new_space = &c.space;
|
||||||
|
let payer = &c.payer;
|
||||||
|
let zero = &c.zero;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
// Blocks duplicate account reallocs in a single instruction to prevent accidental account overwrites
|
||||||
|
// and to ensure the calculation of the change in bytes is based on account size at program entry
|
||||||
|
// which inheritantly guarantee idempotency.
|
||||||
|
if __reallocs.contains(&#field.key()) {
|
||||||
|
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountDuplicateReallocs).with_account_name(#account_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
let __anchor_rent = anchor_lang::prelude::Rent::get()?;
|
||||||
|
let __field_info = #field.to_account_info();
|
||||||
|
let __new_rent_minimum = __anchor_rent.minimum_balance(#new_space);
|
||||||
|
|
||||||
|
let __delta_space = (::std::convert::TryInto::<isize>::try_into(#new_space).unwrap())
|
||||||
|
.checked_sub(::std::convert::TryInto::try_into(__field_info.data_len()).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if __delta_space != 0 {
|
||||||
|
if __delta_space > 0 {
|
||||||
|
if ::std::convert::TryInto::<usize>::try_into(__delta_space).unwrap() > anchor_lang::solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
|
||||||
|
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountReallocExceedsLimit).with_account_name(#account_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if __new_rent_minimum > __field_info.lamports() {
|
||||||
|
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(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
__new_rent_minimum.checked_sub(__field_info.lamports()).unwrap(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let __lamport_amt = __field_info.lamports().checked_sub(__new_rent_minimum).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)?;
|
||||||
|
__reallocs.insert(#field.key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||||
let field = &f.ident;
|
let field = &f.ident;
|
||||||
let name_str = f.ident.to_string();
|
let name_str = f.ident.to_string();
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
#[cfg(feature = "anchor-debug")]
|
#[cfg(feature = "anchor-debug")]
|
||||||
::solana_program::log::sol_log(stringify!(#name));
|
::solana_program::log::sol_log(stringify!(#name));
|
||||||
let #name: #ty = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps)?;
|
let #name: #ty = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps, __reallocs)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AccountField::Field(f) => {
|
AccountField::Field(f) => {
|
||||||
|
@ -47,7 +47,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
#[cfg(feature = "anchor-debug")]
|
#[cfg(feature = "anchor-debug")]
|
||||||
::solana_program::log::sol_log(stringify!(#typed_name));
|
::solana_program::log::sol_log(stringify!(#typed_name));
|
||||||
let #typed_name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps)
|
let #typed_name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps, __reallocs)
|
||||||
.map_err(|e| e.with_account_name(#name))?;
|
.map_err(|e| e.with_account_name(#name))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
||||||
accounts: &mut &[anchor_lang::solana_program::account_info::AccountInfo<'info>],
|
accounts: &mut &[anchor_lang::solana_program::account_info::AccountInfo<'info>],
|
||||||
ix_data: &[u8],
|
ix_data: &[u8],
|
||||||
__bumps: &mut std::collections::BTreeMap<String, u8>,
|
__bumps: &mut std::collections::BTreeMap<String, u8>,
|
||||||
|
__reallocs: &mut std::collections::BTreeSet<anchor_lang::solana_program::pubkey::Pubkey>,
|
||||||
) -> anchor_lang::Result<Self> {
|
) -> anchor_lang::Result<Self> {
|
||||||
// Deserialize instruction, if declared.
|
// Deserialize instruction, if declared.
|
||||||
#ix_de
|
#ix_de
|
||||||
|
|
|
@ -26,36 +26,41 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
match ix {
|
match ix {
|
||||||
anchor_lang::idl::IdlInstruction::Create { data_len } => {
|
anchor_lang::idl::IdlInstruction::Create { data_len } => {
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts =
|
let mut accounts =
|
||||||
anchor_lang::idl::IdlCreateAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps)?;
|
anchor_lang::idl::IdlCreateAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?;
|
||||||
__idl_create_account(program_id, &mut accounts, data_len)?;
|
__idl_create_account(program_id, &mut accounts, data_len)?;
|
||||||
accounts.exit(program_id)?;
|
accounts.exit(program_id)?;
|
||||||
},
|
},
|
||||||
anchor_lang::idl::IdlInstruction::CreateBuffer => {
|
anchor_lang::idl::IdlInstruction::CreateBuffer => {
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts =
|
let mut accounts =
|
||||||
anchor_lang::idl::IdlCreateBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps)?;
|
anchor_lang::idl::IdlCreateBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?;
|
||||||
__idl_create_buffer(program_id, &mut accounts)?;
|
__idl_create_buffer(program_id, &mut accounts)?;
|
||||||
accounts.exit(program_id)?;
|
accounts.exit(program_id)?;
|
||||||
},
|
},
|
||||||
anchor_lang::idl::IdlInstruction::Write { data } => {
|
anchor_lang::idl::IdlInstruction::Write { data } => {
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts =
|
let mut accounts =
|
||||||
anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps)?;
|
anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?;
|
||||||
__idl_write(program_id, &mut accounts, data)?;
|
__idl_write(program_id, &mut accounts, data)?;
|
||||||
accounts.exit(program_id)?;
|
accounts.exit(program_id)?;
|
||||||
},
|
},
|
||||||
anchor_lang::idl::IdlInstruction::SetAuthority { new_authority } => {
|
anchor_lang::idl::IdlInstruction::SetAuthority { new_authority } => {
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts =
|
let mut accounts =
|
||||||
anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps)?;
|
anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?;
|
||||||
__idl_set_authority(program_id, &mut accounts, new_authority)?;
|
__idl_set_authority(program_id, &mut accounts, new_authority)?;
|
||||||
accounts.exit(program_id)?;
|
accounts.exit(program_id)?;
|
||||||
},
|
},
|
||||||
anchor_lang::idl::IdlInstruction::SetBuffer => {
|
anchor_lang::idl::IdlInstruction::SetBuffer => {
|
||||||
let mut bumps = std::collections::BTreeMap::new();
|
let mut bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut reallocs = std::collections::BTreeSet::new();
|
||||||
let mut accounts =
|
let mut accounts =
|
||||||
anchor_lang::idl::IdlSetBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps)?;
|
anchor_lang::idl::IdlSetBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?;
|
||||||
__idl_set_buffer(program_id, &mut accounts)?;
|
__idl_set_buffer(program_id, &mut accounts)?;
|
||||||
accounts.exit(program_id)?;
|
accounts.exit(program_id)?;
|
||||||
},
|
},
|
||||||
|
@ -216,13 +221,14 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
let instruction::state::#variant_arm = ix;
|
let instruction::state::#variant_arm = ix;
|
||||||
|
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut __reallocs = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
let ctor_accounts =
|
let ctor_accounts =
|
||||||
anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps)?;
|
anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps, &mut __reallocs)?;
|
||||||
let mut ctor_user_def_accounts =
|
let mut ctor_user_def_accounts =
|
||||||
#anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps)?;
|
#anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps, &mut __reallocs)?;
|
||||||
|
|
||||||
// Create the solana account for the ctor data.
|
// Create the solana account for the ctor data.
|
||||||
let from = ctor_accounts.from.key;
|
let from = ctor_accounts.from.key;
|
||||||
|
@ -295,13 +301,14 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
let instruction::state::#variant_arm = ix;
|
let instruction::state::#variant_arm = ix;
|
||||||
|
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
let mut __reallocs = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
let ctor_accounts =
|
let ctor_accounts =
|
||||||
anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps)?;
|
anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps, &mut __reallocs)?;
|
||||||
let mut ctor_user_def_accounts =
|
let mut ctor_user_def_accounts =
|
||||||
#anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps)?;
|
#anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps, &mut __reallocs)?;
|
||||||
|
|
||||||
// Invoke the ctor.
|
// Invoke the ctor.
|
||||||
let instance = #mod_name::#name::new(
|
let instance = #mod_name::#name::new(
|
||||||
|
@ -405,12 +412,15 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
// Bump collector.
|
// Bump collector.
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
|
||||||
|
// Realloc tracker
|
||||||
|
let mut __reallocs= std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Load state.
|
// Load state.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
if remaining_accounts.is_empty() {
|
if remaining_accounts.is_empty() {
|
||||||
return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into());
|
return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into());
|
||||||
}
|
}
|
||||||
let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps)?;
|
let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps, &mut __reallocs)?;
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
let mut accounts = #anchor_ident::try_accounts(
|
let mut accounts = #anchor_ident::try_accounts(
|
||||||
|
@ -418,6 +428,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
ix_data,
|
ix_data,
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
let ctx =
|
let ctx =
|
||||||
anchor_lang::context::Context::new(
|
anchor_lang::context::Context::new(
|
||||||
|
@ -461,6 +472,9 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
// Bump collector.
|
// Bump collector.
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
|
||||||
|
// Realloc tracker.
|
||||||
|
let mut __reallocs = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Load state.
|
// Load state.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
if remaining_accounts.is_empty() {
|
if remaining_accounts.is_empty() {
|
||||||
|
@ -471,6 +485,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
&[],
|
&[],
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
|
@ -479,6 +494,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
ix_data,
|
ix_data,
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
let ctx =
|
let ctx =
|
||||||
anchor_lang::context::Context::new(
|
anchor_lang::context::Context::new(
|
||||||
|
@ -589,6 +605,9 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
// Bump collector.
|
// Bump collector.
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
|
||||||
|
// Realloc tracker.
|
||||||
|
let mut __reallocs= std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Deserialize the program state account.
|
// Deserialize the program state account.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
if remaining_accounts.is_empty() {
|
if remaining_accounts.is_empty() {
|
||||||
|
@ -599,6 +618,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
&[],
|
&[],
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
|
@ -607,6 +627,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
ix_data,
|
ix_data,
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
let ctx =
|
let ctx =
|
||||||
anchor_lang::context::Context::new(
|
anchor_lang::context::Context::new(
|
||||||
|
@ -651,6 +672,8 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
// Bump collector.
|
// Bump collector.
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
|
||||||
|
let mut __reallocs = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
let mut accounts = #anchor_ident::try_accounts(
|
let mut accounts = #anchor_ident::try_accounts(
|
||||||
|
@ -658,6 +681,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
ix_data,
|
ix_data,
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Execute user defined function.
|
// Execute user defined function.
|
||||||
|
@ -719,6 +743,8 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
// Bump collector.
|
// Bump collector.
|
||||||
let mut __bumps = std::collections::BTreeMap::new();
|
let mut __bumps = std::collections::BTreeMap::new();
|
||||||
|
|
||||||
|
let mut __reallocs = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
// Deserialize accounts.
|
// Deserialize accounts.
|
||||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||||
let mut accounts = #anchor::try_accounts(
|
let mut accounts = #anchor::try_accounts(
|
||||||
|
@ -726,6 +752,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
&mut remaining_accounts,
|
&mut remaining_accounts,
|
||||||
ix_data,
|
ix_data,
|
||||||
&mut __bumps,
|
&mut __bumps,
|
||||||
|
&mut __reallocs,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Invoke user defined handler.
|
// Invoke user defined handler.
|
||||||
|
|
|
@ -635,6 +635,7 @@ pub struct ConstraintGroup {
|
||||||
associated_token: Option<ConstraintAssociatedToken>,
|
associated_token: Option<ConstraintAssociatedToken>,
|
||||||
token_account: Option<ConstraintTokenAccountGroup>,
|
token_account: Option<ConstraintTokenAccountGroup>,
|
||||||
mint: Option<ConstraintTokenMintGroup>,
|
mint: Option<ConstraintTokenMintGroup>,
|
||||||
|
realloc: Option<ConstraintReallocGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstraintGroup {
|
impl ConstraintGroup {
|
||||||
|
@ -678,6 +679,7 @@ pub enum Constraint {
|
||||||
Address(ConstraintAddress),
|
Address(ConstraintAddress),
|
||||||
TokenAccount(ConstraintTokenAccountGroup),
|
TokenAccount(ConstraintTokenAccountGroup),
|
||||||
Mint(ConstraintTokenMintGroup),
|
Mint(ConstraintTokenMintGroup),
|
||||||
|
Realloc(ConstraintReallocGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constraint token is a single keyword in a `#[account(<TOKEN>)]` attribute.
|
// Constraint token is a single keyword in a `#[account(<TOKEN>)]` attribute.
|
||||||
|
@ -709,6 +711,9 @@ pub enum ConstraintToken {
|
||||||
MintDecimals(Context<ConstraintMintDecimals>),
|
MintDecimals(Context<ConstraintMintDecimals>),
|
||||||
Bump(Context<ConstraintTokenBump>),
|
Bump(Context<ConstraintTokenBump>),
|
||||||
ProgramSeed(Context<ConstraintProgramSeed>),
|
ProgramSeed(Context<ConstraintProgramSeed>),
|
||||||
|
Realloc(Context<ConstraintRealloc>),
|
||||||
|
ReallocPayer(Context<ConstraintReallocPayer>),
|
||||||
|
ReallocZero(Context<ConstraintReallocZero>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ConstraintToken {
|
impl Parse for ConstraintToken {
|
||||||
|
@ -733,6 +738,28 @@ pub struct ConstraintMut {
|
||||||
pub error: Option<Expr>,
|
pub error: Option<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ConstraintSigner {
|
pub struct ConstraintSigner {
|
||||||
pub error: Option<Expr>,
|
pub error: Option<Expr>,
|
||||||
|
|
|
@ -196,6 +196,47 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"realloc" => {
|
||||||
|
if stream.peek(Token![=]) {
|
||||||
|
stream.parse::<Token![=]>()?;
|
||||||
|
let span = ident
|
||||||
|
.span()
|
||||||
|
.join(stream.span())
|
||||||
|
.unwrap_or_else(|| ident.span());
|
||||||
|
ConstraintToken::Realloc(Context::new(
|
||||||
|
span,
|
||||||
|
ConstraintRealloc {
|
||||||
|
space: stream.parse()?,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
stream.parse::<Token![:]>()?;
|
||||||
|
stream.parse::<Token![:]>()?;
|
||||||
|
let kw = stream.call(Ident::parse_any)?.to_string();
|
||||||
|
stream.parse::<Token![=]>()?;
|
||||||
|
|
||||||
|
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::<Token![=]>()?;
|
stream.parse::<Token![=]>()?;
|
||||||
let span = ident
|
let span = ident
|
||||||
|
@ -313,6 +354,9 @@ pub struct ConstraintGroupBuilder<'ty> {
|
||||||
pub mint_decimals: Option<Context<ConstraintMintDecimals>>,
|
pub mint_decimals: Option<Context<ConstraintMintDecimals>>,
|
||||||
pub bump: Option<Context<ConstraintTokenBump>>,
|
pub bump: Option<Context<ConstraintTokenBump>>,
|
||||||
pub program_seed: Option<Context<ConstraintProgramSeed>>,
|
pub program_seed: Option<Context<ConstraintProgramSeed>>,
|
||||||
|
pub realloc: Option<Context<ConstraintRealloc>>,
|
||||||
|
pub realloc_payer: Option<Context<ConstraintReallocPayer>>,
|
||||||
|
pub realloc_zero: Option<Context<ConstraintReallocZero>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ty> ConstraintGroupBuilder<'ty> {
|
impl<'ty> ConstraintGroupBuilder<'ty> {
|
||||||
|
@ -344,6 +388,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
||||||
mint_decimals: None,
|
mint_decimals: None,
|
||||||
bump: None,
|
bump: None,
|
||||||
program_seed: None,
|
program_seed: None,
|
||||||
|
realloc: None,
|
||||||
|
realloc_payer: None,
|
||||||
|
realloc_zero: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +486,22 @@ 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.
|
// Zero.
|
||||||
if let Some(z) = &self.zeroed {
|
if let Some(z) = &self.zeroed {
|
||||||
match self.mutable {
|
match self.mutable {
|
||||||
|
@ -526,6 +589,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
||||||
mint_decimals,
|
mint_decimals,
|
||||||
bump,
|
bump,
|
||||||
program_seed,
|
program_seed,
|
||||||
|
realloc,
|
||||||
|
realloc_payer,
|
||||||
|
realloc_zero,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Converts Option<Context<T>> -> Option<T>.
|
// Converts Option<Context<T>> -> Option<T>.
|
||||||
|
@ -644,6 +710,11 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})).transpose()?,
|
})).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),
|
zeroed: into_inner!(zeroed),
|
||||||
mutable: into_inner!(mutable),
|
mutable: into_inner!(mutable),
|
||||||
signer: into_inner!(signer),
|
signer: into_inner!(signer),
|
||||||
|
@ -690,6 +761,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
||||||
ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c),
|
ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c),
|
||||||
ConstraintToken::Bump(c) => self.add_bump(c),
|
ConstraintToken::Bump(c) => self.add_bump(c),
|
||||||
ConstraintToken::ProgramSeed(c) => self.add_program_seed(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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,6 +831,56 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_realloc(&mut self, c: Context<ConstraintRealloc>) -> 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<ConstraintReallocPayer>) -> 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<ConstraintReallocZero>) -> 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<ConstraintClose>) -> ParseResult<()> {
|
fn add_close(&mut self, c: Context<ConstraintClose>) -> ParseResult<()> {
|
||||||
if !matches!(self.f_ty, Some(Ty::ProgramAccount(_)))
|
if !matches!(self.f_ty, Some(Ty::ProgramAccount(_)))
|
||||||
&& !matches!(self.f_ty, Some(Ty::Account(_)))
|
&& !matches!(self.f_ty, Some(Ty::Account(_)))
|
||||||
|
|
|
@ -55,7 +55,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> {
|
||||||
init_fields[0].ident.span(),
|
init_fields[0].ident.span(),
|
||||||
"the init constraint requires \
|
"the init constraint requires \
|
||||||
the system_program field to exist in the account \
|
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.",
|
the system_program field to your validation struct.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> {
|
||||||
init_fields[0].ident.span(),
|
init_fields[0].ident.span(),
|
||||||
"the init constraint requires \
|
"the init constraint requires \
|
||||||
the token_program field to exist in the account \
|
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.",
|
the token_program field to your validation struct.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> {
|
||||||
init_fields[0].ident.span(),
|
init_fields[0].ident.span(),
|
||||||
"the init constraint requires \
|
"the init constraint requires \
|
||||||
the associated_token_program field to exist in the account \
|
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.",
|
the associated_token_program field to your validation struct.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,61 @@ 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit aa0d8adc94192607b35661d7ae48a26b0491fd16
|
Subproject commit fd78344ab5f34c36a91bdaf8b9edf2fbd8a93510
|
|
@ -24,6 +24,7 @@
|
||||||
"permissioned-markets",
|
"permissioned-markets",
|
||||||
"pda-derivation",
|
"pda-derivation",
|
||||||
"pyth",
|
"pyth",
|
||||||
|
"realloc",
|
||||||
"spl/token-proxy",
|
"spl/token-proxy",
|
||||||
"swap",
|
"swap",
|
||||||
"system-accounts",
|
"system-accounts",
|
||||||
|
@ -48,8 +49,8 @@
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
"@types/chai": "^4.3.0",
|
"@types/chai": "^4.3.0",
|
||||||
"@types/mocha": "^9.1.0",
|
"@types/mocha": "^9.1.0",
|
||||||
"mocha": "^9.1.3",
|
"mocha": "^10.0.0",
|
||||||
"ts-mocha": "^8.0.0",
|
"ts-mocha": "^10.0.0",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^4.4.4",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"tsc": "^2.0.4"
|
"tsc": "^2.0.4"
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
[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"
|
|
@ -0,0 +1,4 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"programs/*"
|
||||||
|
]
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
[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" }
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,112 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||||
|
|
||||||
|
#[program]
|
||||||
|
pub mod realloc {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
|
||||||
|
ctx.accounts.sample.data = vec![0];
|
||||||
|
ctx.accounts.sample.bump = *ctx.bumps.get("sample").unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn realloc(ctx: Context<Realloc>, len: u16) -> Result<()> {
|
||||||
|
ctx.accounts
|
||||||
|
.sample
|
||||||
|
.data
|
||||||
|
.resize_with(len as usize, Default::default);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn realloc2(ctx: Context<Realloc2>, len: u16) -> Result<()> {
|
||||||
|
ctx.accounts
|
||||||
|
.sample1
|
||||||
|
.data
|
||||||
|
.resize_with(len as usize, Default::default);
|
||||||
|
|
||||||
|
ctx.accounts
|
||||||
|
.sample2
|
||||||
|
.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: u16)]
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
#[instruction(len: u16)]
|
||||||
|
pub struct Realloc2<'info> {
|
||||||
|
#[account(mut)]
|
||||||
|
pub authority: Signer<'info>,
|
||||||
|
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
seeds = [b"sample"],
|
||||||
|
bump = sample1.bump,
|
||||||
|
realloc = Sample::space(len as usize),
|
||||||
|
realloc::payer = authority,
|
||||||
|
realloc::zero = false,
|
||||||
|
)]
|
||||||
|
pub sample1: Account<'info, Sample>,
|
||||||
|
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
seeds = [b"sample"],
|
||||||
|
bump = sample2.bump,
|
||||||
|
realloc = Sample::space((len + 10) as usize),
|
||||||
|
realloc::payer = authority,
|
||||||
|
realloc::zero = false,
|
||||||
|
)]
|
||||||
|
pub sample2: Account<'info, Sample>,
|
||||||
|
|
||||||
|
pub system_program: Program<'info, System>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[account]
|
||||||
|
pub struct Sample {
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub bump: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample {
|
||||||
|
pub fn space(len: usize) -> usize {
|
||||||
|
8 + (4 + len) + 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
import * as anchor from "@project-serum/anchor";
|
||||||
|
import { AnchorError, 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<Realloc>;
|
||||||
|
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("initialized", async () => {
|
||||||
|
await program.methods
|
||||||
|
.initialize()
|
||||||
|
.accounts({ authority: authority.publicKey, sample })
|
||||||
|
.rpc();
|
||||||
|
|
||||||
|
const samples = await program.account.sample.all();
|
||||||
|
assert.lengthOf(samples, 1);
|
||||||
|
assert.lengthOf(samples[0].account.data, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if delta bytes exceeds permitted limit", async () => {
|
||||||
|
try {
|
||||||
|
await program.methods
|
||||||
|
.realloc(10250)
|
||||||
|
.accounts({ authority: authority.publicKey, sample })
|
||||||
|
.rpc();
|
||||||
|
assert.ok(false);
|
||||||
|
} catch (e) {
|
||||||
|
assert.isTrue(e instanceof AnchorError);
|
||||||
|
const err: AnchorError = e;
|
||||||
|
const errMsg =
|
||||||
|
"The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit";
|
||||||
|
assert.strictEqual(err.error.errorMessage, errMsg);
|
||||||
|
assert.strictEqual(err.error.errorCode.number, 3016);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails with duplicate account reallocations", async () => {
|
||||||
|
try {
|
||||||
|
await program.methods
|
||||||
|
.realloc2(1000)
|
||||||
|
.accounts({
|
||||||
|
authority: authority.publicKey,
|
||||||
|
sample1: sample,
|
||||||
|
sample2: sample,
|
||||||
|
})
|
||||||
|
.rpc();
|
||||||
|
} catch (e) {
|
||||||
|
assert.isTrue(e instanceof AnchorError);
|
||||||
|
const err: AnchorError = e;
|
||||||
|
const errMsg =
|
||||||
|
"The account was duplicated for more than one reallocation";
|
||||||
|
assert.strictEqual(err.error.errorMessage, errMsg);
|
||||||
|
assert.strictEqual(err.error.errorCode.number, 3017);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["mocha", "chai"],
|
||||||
|
"typeRoots": ["./node_modules/@types"],
|
||||||
|
"lib": ["es2015"],
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es6",
|
||||||
|
"esModuleInterop": true
|
||||||
|
}
|
||||||
|
}
|
100
tests/yarn.lock
100
tests/yarn.lock
|
@ -66,6 +66,7 @@
|
||||||
js-sha256 "^0.9.0"
|
js-sha256 "^0.9.0"
|
||||||
pako "^2.0.3"
|
pako "^2.0.3"
|
||||||
snake-case "^3.0.4"
|
snake-case "^3.0.4"
|
||||||
|
superstruct "^0.15.4"
|
||||||
toml "^3.0.0"
|
toml "^3.0.0"
|
||||||
|
|
||||||
"@project-serum/borsh@^0.2.2":
|
"@project-serum/borsh@^0.2.2":
|
||||||
|
@ -341,6 +342,13 @@ brace-expansion@^1.1.7:
|
||||||
balanced-match "^1.0.0"
|
balanced-match "^1.0.0"
|
||||||
concat-map "0.0.1"
|
concat-map "0.0.1"
|
||||||
|
|
||||||
|
brace-expansion@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
|
||||||
|
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^1.0.0"
|
||||||
|
|
||||||
braces@~3.0.2:
|
braces@~3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||||
|
@ -448,6 +456,21 @@ chokidar@3.5.2:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
chokidar@3.5.3:
|
||||||
|
version "3.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
|
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||||
|
dependencies:
|
||||||
|
anymatch "~3.1.2"
|
||||||
|
braces "~3.0.2"
|
||||||
|
glob-parent "~5.1.2"
|
||||||
|
is-binary-path "~2.1.0"
|
||||||
|
is-glob "~4.0.1"
|
||||||
|
normalize-path "~3.0.0"
|
||||||
|
readdirp "~3.6.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
circular-json@^0.5.9:
|
circular-json@^0.5.9:
|
||||||
version "0.5.9"
|
version "0.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d"
|
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d"
|
||||||
|
@ -510,6 +533,13 @@ debug@4.3.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
debug@4.3.4:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
decamelize@^4.0.0:
|
decamelize@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
|
||||||
|
@ -666,6 +696,18 @@ glob@7.1.7:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
glob@7.2.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||||
|
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
growl@1.10.5:
|
growl@1.10.5:
|
||||||
version "1.10.5"
|
version "1.10.5"
|
||||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
||||||
|
@ -874,6 +916,13 @@ minimatch@3.0.4, minimatch@^3.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
minimatch@5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
|
||||||
|
integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@^1.2.5:
|
minimist@^1.2.0, minimist@^1.2.5:
|
||||||
version "1.2.6"
|
version "1.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||||
|
@ -886,6 +935,34 @@ mkdirp@^0.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
|
mocha@^10.0.0:
|
||||||
|
version "10.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9"
|
||||||
|
integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==
|
||||||
|
dependencies:
|
||||||
|
"@ungap/promise-all-settled" "1.1.2"
|
||||||
|
ansi-colors "4.1.1"
|
||||||
|
browser-stdout "1.3.1"
|
||||||
|
chokidar "3.5.3"
|
||||||
|
debug "4.3.4"
|
||||||
|
diff "5.0.0"
|
||||||
|
escape-string-regexp "4.0.0"
|
||||||
|
find-up "5.0.0"
|
||||||
|
glob "7.2.0"
|
||||||
|
he "1.2.0"
|
||||||
|
js-yaml "4.1.0"
|
||||||
|
log-symbols "4.1.0"
|
||||||
|
minimatch "5.0.1"
|
||||||
|
ms "2.1.3"
|
||||||
|
nanoid "3.3.3"
|
||||||
|
serialize-javascript "6.0.0"
|
||||||
|
strip-json-comments "3.1.1"
|
||||||
|
supports-color "8.1.1"
|
||||||
|
workerpool "6.2.1"
|
||||||
|
yargs "16.2.0"
|
||||||
|
yargs-parser "20.2.4"
|
||||||
|
yargs-unparser "2.0.0"
|
||||||
|
|
||||||
mocha@^9.1.3:
|
mocha@^9.1.3:
|
||||||
version "9.1.3"
|
version "9.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb"
|
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb"
|
||||||
|
@ -931,6 +1008,11 @@ nanoid@3.1.25:
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
|
||||||
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
|
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
|
||||||
|
|
||||||
|
nanoid@3.3.3:
|
||||||
|
version "3.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
|
||||||
|
integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
|
||||||
|
|
||||||
no-case@^3.0.4:
|
no-case@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
||||||
|
@ -1136,6 +1218,11 @@ superstruct@^0.14.2:
|
||||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b"
|
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b"
|
||||||
integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==
|
integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==
|
||||||
|
|
||||||
|
superstruct@^0.15.4:
|
||||||
|
version "0.15.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.5.tgz#0f0a8d3ce31313f0d84c6096cd4fa1bfdedc9dab"
|
||||||
|
integrity sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==
|
||||||
|
|
||||||
supports-color@8.1.1:
|
supports-color@8.1.1:
|
||||||
version "8.1.1"
|
version "8.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
||||||
|
@ -1187,10 +1274,10 @@ traverse-chain@~0.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
|
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
|
||||||
integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=
|
integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=
|
||||||
|
|
||||||
ts-mocha@^8.0.0:
|
ts-mocha@^10.0.0:
|
||||||
version "8.0.0"
|
version "10.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-8.0.0.tgz#962d0fa12eeb6468aa1a6b594bb3bbc818da3ef0"
|
resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-10.0.0.tgz#41a8d099ac90dbbc64b06976c5025ffaebc53cb9"
|
||||||
integrity sha512-Kou1yxTlubLnD5C3unlCVO7nh0HERTezjoVhVw/M5S1SqoUec0WgllQvPk3vzPMc6by8m6xD1uR1yRf8lnVUbA==
|
integrity sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ts-node "7.0.1"
|
ts-node "7.0.1"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
@ -1287,6 +1374,11 @@ workerpool@6.1.5:
|
||||||
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581"
|
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581"
|
||||||
integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==
|
integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==
|
||||||
|
|
||||||
|
workerpool@6.2.1:
|
||||||
|
version "6.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
|
||||||
|
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
|
||||||
|
|
||||||
wrap-ansi@^7.0.0:
|
wrap-ansi@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
|
|
|
@ -364,6 +364,9 @@ export const LangErrorCode = {
|
||||||
AccountNotProgramData: 3013,
|
AccountNotProgramData: 3013,
|
||||||
AccountNotAssociatedTokenAccount: 3014,
|
AccountNotAssociatedTokenAccount: 3014,
|
||||||
AccountSysvarMismatch: 3015,
|
AccountSysvarMismatch: 3015,
|
||||||
|
AccountReallocExceedsLimit: 3016,
|
||||||
|
AccountDuplicateReallocs: 3017,
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
StateInvalidAddress: 4000,
|
StateInvalidAddress: 4000,
|
||||||
|
|
||||||
|
@ -502,6 +505,14 @@ export const LangErrorMessage = new Map([
|
||||||
LangErrorCode.AccountSysvarMismatch,
|
LangErrorCode.AccountSysvarMismatch,
|
||||||
"The given public key does not match the required sysvar",
|
"The given public key does not match the required sysvar",
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
LangErrorCode.AccountReallocExceedsLimit,
|
||||||
|
"The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
LangErrorCode.AccountDuplicateReallocs,
|
||||||
|
"The account was duplicated for more than one reallocation",
|
||||||
|
],
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in New Issue