From 5ff9947ba2c3ad11fbb58041e63b502d042ea96e Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 11:45:33 +1300 Subject: [PATCH] lang: Error on undocumented unsafe account types (#1452) --- .github/workflows/tests.yaml | 2 + CHANGELOG.md | 1 + cli/src/config.rs | 20 ++++++- cli/src/lib.rs | 7 ++- lang/syn/Cargo.toml | 2 +- lang/syn/src/idl/file.rs | 4 ++ lang/syn/src/parser/context.rs | 52 ++++++++++++++++++- tests/auction-house | 2 +- tests/cashiers-check/Anchor.toml | 3 ++ tests/cfo/Anchor.toml | 3 ++ tests/cfo/deps/stake | 2 +- tests/cfo/deps/swap | 2 +- tests/chat/Anchor.toml | 3 ++ tests/custom-coder/Anchor.toml | 3 ++ tests/errors/Anchor.toml | 3 ++ tests/escrow/Anchor.toml | 3 ++ tests/ido-pool/Anchor.toml | 3 ++ tests/interface/Anchor.toml | 3 ++ tests/lockup/Anchor.toml | 3 ++ tests/misc/programs/misc/src/context.rs | 49 +++++++++++++++++ tests/misc/programs/misc2/src/lib.rs | 1 + tests/multisig/Anchor.toml | 3 ++ tests/pda-derivation/Anchor.toml | 1 + tests/pyth/Anchor.toml | 3 ++ tests/safety-checks/.gitignore | 6 +++ tests/safety-checks/Anchor.toml | 13 +++++ tests/safety-checks/Cargo.toml | 4 ++ tests/safety-checks/migrations/deploy.ts | 12 +++++ .../programs/account-info/Cargo.toml | 19 +++++++ .../programs/account-info/Xargo.toml | 2 + .../programs/account-info/src/lib.rs | 16 ++++++ .../programs/unchecked-account/Cargo.toml | 19 +++++++ .../programs/unchecked-account/Xargo.toml | 2 + .../programs/unchecked-account/src/lib.rs | 16 ++++++ tests/safety-checks/test.sh | 27 ++++++++++ tests/safety-checks/tests/safety-checks.ts | 16 ++++++ tests/safety-checks/tsconfig.json | 10 ++++ tests/spl/token-proxy/Anchor.toml | 3 ++ tests/swap/Anchor.toml | 3 ++ tests/zero-copy/Anchor.toml | 3 ++ 40 files changed, 342 insertions(+), 7 deletions(-) create mode 100644 tests/safety-checks/.gitignore create mode 100644 tests/safety-checks/Anchor.toml create mode 100644 tests/safety-checks/Cargo.toml create mode 100644 tests/safety-checks/migrations/deploy.ts create mode 100644 tests/safety-checks/programs/account-info/Cargo.toml create mode 100644 tests/safety-checks/programs/account-info/Xargo.toml create mode 100644 tests/safety-checks/programs/account-info/src/lib.rs create mode 100644 tests/safety-checks/programs/unchecked-account/Cargo.toml create mode 100644 tests/safety-checks/programs/unchecked-account/Xargo.toml create mode 100644 tests/safety-checks/programs/unchecked-account/src/lib.rs create mode 100755 tests/safety-checks/test.sh create mode 100644 tests/safety-checks/tests/safety-checks.ts create mode 100644 tests/safety-checks/tsconfig.json diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d1b4fb1960..9dd20afb6d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -278,6 +278,8 @@ jobs: path: tests/auction-house - cmd: cd tests/floats && yarn && anchor test path: tests/floats + - cmd: cd tests/safety-checks && ./test.sh + path: tests/safety-checks steps: - uses: actions/checkout@v2 - uses: ./.github/actions/setup/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ded05a60b..9ccccc5497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ incremented for features. * lang: Enforce that the payer for an init-ed account be marked `mut` ([#1271](https://github.com/project-serum/anchor/pull/1271)). * lang: All error-related code is now in the error module ([#1426](https://github.com/project-serum/anchor/pull/1426)). +* lang: Require doc comments when using AccountInfo or UncheckedAccount types ([#1452](https://github.com/project-serum/anchor/pull/1452)). ## [0.21.0] - 2022-02-07 diff --git a/cli/src/config.rs b/cli/src/config.rs index 828e21d3f8..0160f51d28 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -166,6 +166,7 @@ impl WithPath { path.join("src/lib.rs"), version, self.features.seeds, + false, )?; r.push(Program { lib_name, @@ -256,9 +257,26 @@ pub struct Config { pub test: Option, } -#[derive(Default, Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct FeaturesConfig { + #[serde(default)] pub seeds: bool, + #[serde(default = "default_safety_checks")] + pub safety_checks: bool, +} + +impl Default for FeaturesConfig { + fn default() -> Self { + Self { + seeds: false, + // Anchor safety checks on by default + safety_checks: true, + } + } +} + +fn default_safety_checks() -> bool { + true } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 6460219379..a126020080 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1388,7 +1388,12 @@ fn extract_idl(cfg: &WithPath, file: &str) -> Result> { let manifest_from_path = std::env::current_dir()?.join(PathBuf::from(&*file).parent().unwrap()); let cargo = Manifest::discover_from_path(manifest_from_path)? .ok_or_else(|| anyhow!("Cargo.toml not found"))?; - anchor_syn::idl::file::parse(&*file, cargo.version(), cfg.features.seeds) + anchor_syn::idl::file::parse( + &*file, + cargo.version(), + cfg.features.seeds, + cfg.features.safety_checks, + ) } fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> { diff --git a/lang/syn/Cargo.toml b/lang/syn/Cargo.toml index ee98c3472d..73a228d2ce 100644 --- a/lang/syn/Cargo.toml +++ b/lang/syn/Cargo.toml @@ -16,7 +16,7 @@ anchor-debug = [] seeds = [] [dependencies] -proc-macro2 = "1.0" +proc-macro2 = { version = "1.0", features=["span-locations"]} proc-macro2-diagnostics = "0.9" quote = "1.0" syn = { version = "1.0.60", features = ["full", "extra-traits", "parsing"] } diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs index 119f173a5c..d3759c7603 100644 --- a/lang/syn/src/idl/file.rs +++ b/lang/syn/src/idl/file.rs @@ -18,8 +18,12 @@ pub fn parse( filename: impl AsRef, version: String, seeds_feature: bool, + safety_checks: bool, ) -> Result> { let ctx = CrateContext::parse(filename)?; + if safety_checks { + ctx.safety_checks()?; + } let program_mod = match parse_program_mod(&ctx) { None => return Ok(None), diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 42ad1b5710..1a924bd48d 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -1,6 +1,6 @@ +use anyhow::anyhow; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; - use syn::parse::{Error as ParseError, Result as ParseResult}; /// Crate parse context @@ -40,6 +40,41 @@ impl CrateContext { modules: ParsedModule::parse_recursive(root.as_ref())?, }) } + + // Perform Anchor safety checks on the parsed create + pub fn safety_checks(&self) -> Result<(), anyhow::Error> { + // Check all structs for unsafe field types, i.e. AccountInfo and UncheckedAccount. + for (_, ctx) in self.modules.iter() { + for unsafe_field in ctx.unsafe_struct_fields() { + // Check if unsafe field type has been documented with a /// SAFETY: doc string. + let is_documented = unsafe_field.attrs.iter().any(|attr| { + attr.tokens.clone().into_iter().any(|token| match token { + // Check for doc comments containing CHECK + proc_macro2::TokenTree::Literal(s) => s.to_string().contains("CHECK"), + _ => false, + }) + }); + if !is_documented { + let ident = unsafe_field.ident.as_ref().unwrap(); + let span = ident.span(); + // Error if undocumented. + return Err(anyhow!( + r#" + {}:{}:{} + Struct field "{}" is unsafe, but is not documented. + Please add a `/// CHECK:` doc comment explaining why no checks through types are necessary. + See https://book.anchor-lang.com/chapter_3/the_accounts_struct.html#safety-checks for more information. + "#, + ctx.file.canonicalize().unwrap().display(), + span.start().line, + span.start().column, + ident.to_string() + )); + }; + } + } + Ok(()) + } } /// Module parse context @@ -181,6 +216,21 @@ impl ParsedModule { }) } + fn unsafe_struct_fields(&self) -> impl Iterator { + self.structs() + .flat_map(|s| &s.fields) + .filter(|f| match &f.ty { + syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) => { + segments.len() == 1 && segments[0].ident == "UncheckedAccount" + || segments[0].ident == "AccountInfo" + } + _ => false, + }) + } + fn enums(&self) -> impl Iterator { self.items.iter().filter_map(|i| match i { syn::Item::Enum(item) => Some(item), diff --git a/tests/auction-house b/tests/auction-house index 2bfe49bdac..2b1b1e0498 160000 --- a/tests/auction-house +++ b/tests/auction-house @@ -1 +1 @@ -Subproject commit 2bfe49bdac2333d0e413a1e452c0ab7b502266fa +Subproject commit 2b1b1e04986106715ab53794bcb63d3641673f64 diff --git a/tests/cashiers-check/Anchor.toml b/tests/cashiers-check/Anchor.toml index 4a7367c059..6b39d2572f 100644 --- a/tests/cashiers-check/Anchor.toml +++ b/tests/cashiers-check/Anchor.toml @@ -7,3 +7,6 @@ cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/cfo/Anchor.toml b/tests/cfo/Anchor.toml index d1204d1e91..640628a366 100644 --- a/tests/cfo/Anchor.toml +++ b/tests/cfo/Anchor.toml @@ -41,3 +41,6 @@ program = "./deps/stake/target/deploy/registry.so" [[test.genesis]] address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks" program = "./deps/stake/target/deploy/lockup.so" + +[features] +safety_checks = false diff --git a/tests/cfo/deps/stake b/tests/cfo/deps/stake index f04b2aaf88..990eaa7944 160000 --- a/tests/cfo/deps/stake +++ b/tests/cfo/deps/stake @@ -1 +1 @@ -Subproject commit f04b2aaf8817dac4c8d89e75eaaa0c099dfbf166 +Subproject commit 990eaa7944c6682838fdaa6c14cc07ed680007f0 diff --git a/tests/cfo/deps/swap b/tests/cfo/deps/swap index b3021f1444..96e3b1e2a5 160000 --- a/tests/cfo/deps/swap +++ b/tests/cfo/deps/swap @@ -1 +1 @@ -Subproject commit b3021f1444280a372721133bb2e5acca2d271283 +Subproject commit 96e3b1e2a53a95ef56e6ec2da68348ffd6a5c091 diff --git a/tests/chat/Anchor.toml b/tests/chat/Anchor.toml index 7d982c56dd..1249c7228d 100644 --- a/tests/chat/Anchor.toml +++ b/tests/chat/Anchor.toml @@ -7,3 +7,6 @@ chat = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/custom-coder/Anchor.toml b/tests/custom-coder/Anchor.toml index 589023aa22..ff7a52de2d 100644 --- a/tests/custom-coder/Anchor.toml +++ b/tests/custom-coder/Anchor.toml @@ -11,3 +11,6 @@ wallet = "~/.config/solana/id.json" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" + +[features] +safety_checks = false diff --git a/tests/errors/Anchor.toml b/tests/errors/Anchor.toml index a84e48eb42..52f2897d04 100644 --- a/tests/errors/Anchor.toml +++ b/tests/errors/Anchor.toml @@ -7,3 +7,6 @@ errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/escrow/Anchor.toml b/tests/escrow/Anchor.toml index 6639e2a570..f5dab7ec67 100644 --- a/tests/escrow/Anchor.toml +++ b/tests/escrow/Anchor.toml @@ -7,3 +7,6 @@ escrow = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run ts-mocha -t 1000000 tests/*.ts" + +[features] +safety_checks = false diff --git a/tests/ido-pool/Anchor.toml b/tests/ido-pool/Anchor.toml index 9c3c33bd9c..bb19294358 100644 --- a/tests/ido-pool/Anchor.toml +++ b/tests/ido-pool/Anchor.toml @@ -7,3 +7,6 @@ ido_pool = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/interface/Anchor.toml b/tests/interface/Anchor.toml index be62966b83..f77ab31faf 100644 --- a/tests/interface/Anchor.toml +++ b/tests/interface/Anchor.toml @@ -8,3 +8,6 @@ counter_auth = "Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/lockup/Anchor.toml b/tests/lockup/Anchor.toml index 04a6a65d58..4367e1dcc1 100644 --- a/tests/lockup/Anchor.toml +++ b/tests/lockup/Anchor.toml @@ -8,3 +8,6 @@ registry = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index f4a9504ffd..cda496cbe8 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -28,6 +28,7 @@ pub struct TestTokenSeedsInit<'info> { )] pub my_pda: Account<'info, TokenAccount>, #[account(mut)] + /// CHECK: pub authority: AccountInfo<'info>, pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, @@ -60,6 +61,7 @@ pub struct TestValidateAssociatedToken<'info> { )] pub token: Account<'info, TokenAccount>, pub mint: Account<'info, Mint>, + /// CHECK: pub wallet: AccountInfo<'info>, } @@ -70,7 +72,9 @@ pub struct TestInstructionConstraint<'info> { seeds = [b"my-seed", my_account.key.as_ref()], bump = nonce, )] + /// CHECK: pub my_pda: AccountInfo<'info>, + /// CHECK: pub my_account: AccountInfo<'info>, } @@ -86,6 +90,7 @@ pub struct TestPdaInit<'info> { pub my_pda: Account<'info, DataU16>, #[account(mut)] pub my_payer: Signer<'info>, + /// CHECK: pub foo: AccountInfo<'info>, pub system_program: Program<'info, System>, } @@ -112,6 +117,7 @@ pub struct TestPdaMutZeroCopy<'info> { bump = my_pda.load()?.bump, )] pub my_pda: Loader<'info, DataZeroCopy>, + /// CHECK: pub my_payer: AccountInfo<'info>, } @@ -135,29 +141,35 @@ pub struct InitializeSkipRentExempt<'info> { #[derive(Accounts)] pub struct InitializeNoRentExempt<'info> { + /// CHECK: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestOwner<'info> { #[account(owner = *misc.key)] + /// CHECK: pub data: AccountInfo<'info>, + /// CHECK: pub misc: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestExecutable<'info> { #[account(executable)] + /// CHECK: pub program: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestStateCpi<'info> { #[account(signer)] + /// CHECK: pub authority: AccountInfo<'info>, #[account(mut, state = misc2_program)] pub cpi_state: CpiState<'info, Misc2State>, #[account(executable)] + /// CHECK: pub misc2_program: AccountInfo<'info>, } @@ -165,6 +177,7 @@ pub struct TestStateCpi<'info> { pub struct TestClose<'info> { #[account(mut, close = sol_dest)] pub data: Account<'info, Data>, + /// CHECK: sol_dest: AccountInfo<'info>, } @@ -259,6 +272,7 @@ pub struct TestInitWithEmptySeeds<'info> { #[derive(Accounts)] pub struct TestEmptySeedsConstraint<'info> { #[account(seeds = [], bump)] + /// CHECK: pub pda: AccountInfo<'info>, } @@ -283,10 +297,12 @@ pub struct TestInitIfNeeded<'info> { #[derive(Accounts)] pub struct TestInitIfNeededChecksOwner<'info> { #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] + /// CHECK: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>, + /// CHECK: pub owner: AccountInfo<'info>, } @@ -294,6 +310,7 @@ pub struct TestInitIfNeededChecksOwner<'info> { #[instruction(seed_data: String)] pub struct TestInitIfNeededChecksSeeds<'info> { #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] + /// CHECK: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -310,7 +327,9 @@ pub struct TestInitMintIfNeeded<'info> { pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, + /// CHECK: pub mint_authority: AccountInfo<'info>, + /// CHECK: pub freeze_authority: AccountInfo<'info>, } @@ -324,6 +343,7 @@ pub struct TestInitTokenIfNeeded<'info> { pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, + /// CHECK: pub authority: AccountInfo<'info>, } @@ -343,6 +363,7 @@ pub struct TestInitAssociatedTokenIfNeeded<'info> { pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, pub associated_token_program: Program<'info, AssociatedToken>, + /// CHECK: pub authority: AccountInfo<'info>, } @@ -360,18 +381,21 @@ pub struct TestConstArraySize<'info> { #[derive(Accounts)] pub struct NoRentExempt<'info> { + /// CHECK: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct EnforceRentExempt<'info> { #[account(rent_exempt = enforce)] + /// CHECK: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitDecreaseLamports<'info> { #[account(init, payer = user, space = 1000)] + /// CHECK: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, @@ -381,6 +405,7 @@ pub struct InitDecreaseLamports<'info> { #[derive(Accounts)] pub struct InitIfNeededChecksRentExemption<'info> { #[account(init_if_needed, payer = user, space = 1000)] + /// CHECK: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, @@ -393,9 +418,11 @@ pub struct TestProgramIdConstraint<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] + /// CHECK: second: AccountInfo<'info>, } @@ -404,8 +431,30 @@ pub struct TestProgramIdConstraintUsingFindPda<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] + /// CHECK: second: AccountInfo<'info>, } + +#[derive(Accounts)] +pub struct TestUnsafeFieldSafetyErrors<'info> { + #[doc = "test"] + /// CHECK: + pub data: UncheckedAccount<'info>, + #[account(mut)] + /// CHECK: + pub data_two: UncheckedAccount<'info>, + #[account( + seeds = [b"my-seed", signer.key.as_ref()], + bump + )] + /// CHECK: + pub data_three: UncheckedAccount<'info>, + /// CHECK: + pub data_four: UncheckedAccount<'info>, + pub signer: Signer<'info>, + pub system_program: Program<'info, System>, +} diff --git a/tests/misc/programs/misc2/src/lib.rs b/tests/misc/programs/misc2/src/lib.rs index 0b97eb01ab..4ee2c9ae71 100644 --- a/tests/misc/programs/misc2/src/lib.rs +++ b/tests/misc/programs/misc2/src/lib.rs @@ -33,5 +33,6 @@ pub mod misc2 { #[derive(Accounts)] pub struct Auth<'info> { #[account(signer)] + /// CHECK: pub authority: AccountInfo<'info>, } diff --git a/tests/multisig/Anchor.toml b/tests/multisig/Anchor.toml index e76d60f889..f8bc62e0d5 100644 --- a/tests/multisig/Anchor.toml +++ b/tests/multisig/Anchor.toml @@ -7,3 +7,6 @@ multisig = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/pda-derivation/Anchor.toml b/tests/pda-derivation/Anchor.toml index 7893565164..2b17356f2c 100644 --- a/tests/pda-derivation/Anchor.toml +++ b/tests/pda-derivation/Anchor.toml @@ -1,5 +1,6 @@ [features] seeds = true +safety_checks = false [provider] cluster = "localnet" diff --git a/tests/pyth/Anchor.toml b/tests/pyth/Anchor.toml index 8407b65ba9..333e47210b 100644 --- a/tests/pyth/Anchor.toml +++ b/tests/pyth/Anchor.toml @@ -7,3 +7,6 @@ pyth = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" + +[features] +safety_checks = false diff --git a/tests/safety-checks/.gitignore b/tests/safety-checks/.gitignore new file mode 100644 index 0000000000..51448d4dab --- /dev/null +++ b/tests/safety-checks/.gitignore @@ -0,0 +1,6 @@ + +.anchor +.DS_Store +target +**/*.rs.bk +node_modules diff --git a/tests/safety-checks/Anchor.toml b/tests/safety-checks/Anchor.toml new file mode 100644 index 0000000000..778993edc0 --- /dev/null +++ b/tests/safety-checks/Anchor.toml @@ -0,0 +1,13 @@ +[programs.localnet] +unchecked_account = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" +account_info = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" + +[registry] +url = "https://anchor.projectserum.com" + +[provider] +cluster = "localnet" +wallet = "/home/armaniferrante/.config/solana/id.json" + +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/safety-checks/Cargo.toml b/tests/safety-checks/Cargo.toml new file mode 100644 index 0000000000..a60de986d3 --- /dev/null +++ b/tests/safety-checks/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "programs/*" +] diff --git a/tests/safety-checks/migrations/deploy.ts b/tests/safety-checks/migrations/deploy.ts new file mode 100644 index 0000000000..5e3df0dc30 --- /dev/null +++ b/tests/safety-checks/migrations/deploy.ts @@ -0,0 +1,12 @@ +// Migrations are an early feature. Currently, they're nothing more than this +// single deploy script that's invoked from the CLI, injecting a provider +// configured from the workspace's Anchor.toml. + +const anchor = require("@project-serum/anchor"); + +module.exports = async function (provider) { + // Configure client to use the provider. + anchor.setProvider(provider); + + // Add your deploy script here. +}; diff --git a/tests/safety-checks/programs/account-info/Cargo.toml b/tests/safety-checks/programs/account-info/Cargo.toml new file mode 100644 index 0000000000..032a72274b --- /dev/null +++ b/tests/safety-checks/programs/account-info/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "account-info" +version = "0.1.0" +description = "Created with Anchor" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "account_info" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.21.0" diff --git a/tests/safety-checks/programs/account-info/Xargo.toml b/tests/safety-checks/programs/account-info/Xargo.toml new file mode 100644 index 0000000000..475fb71ed1 --- /dev/null +++ b/tests/safety-checks/programs/account-info/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/tests/safety-checks/programs/account-info/src/lib.rs b/tests/safety-checks/programs/account-info/src/lib.rs new file mode 100644 index 0000000000..670e10738f --- /dev/null +++ b/tests/safety-checks/programs/account-info/src/lib.rs @@ -0,0 +1,16 @@ +use anchor_lang::prelude::*; + +declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); + +#[program] +pub mod account_info { + use super::*; + pub fn initialize(ctx: Context) -> ProgramResult { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Initialize<'info> { + unchecked: AccountInfo<'info>, +} diff --git a/tests/safety-checks/programs/unchecked-account/Cargo.toml b/tests/safety-checks/programs/unchecked-account/Cargo.toml new file mode 100644 index 0000000000..69443051c2 --- /dev/null +++ b/tests/safety-checks/programs/unchecked-account/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "unchecked-account" +version = "0.1.0" +description = "Created with Anchor" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "safety_checks" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = { path = "../../../../lang" } diff --git a/tests/safety-checks/programs/unchecked-account/Xargo.toml b/tests/safety-checks/programs/unchecked-account/Xargo.toml new file mode 100644 index 0000000000..475fb71ed1 --- /dev/null +++ b/tests/safety-checks/programs/unchecked-account/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/tests/safety-checks/programs/unchecked-account/src/lib.rs b/tests/safety-checks/programs/unchecked-account/src/lib.rs new file mode 100644 index 0000000000..4d86648db0 --- /dev/null +++ b/tests/safety-checks/programs/unchecked-account/src/lib.rs @@ -0,0 +1,16 @@ +use anchor_lang::prelude::*; + +declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); + +#[program] +pub mod unchecked_account { + use super::*; + pub fn initialize(ctx: Context) -> ProgramResult { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Initialize<'info> { + unchecked: UncheckedAccount<'info>, +} diff --git a/tests/safety-checks/test.sh b/tests/safety-checks/test.sh new file mode 100755 index 0000000000..d9d4cec5da --- /dev/null +++ b/tests/safety-checks/test.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +echo "Building programs" + +# +# Build the UncheckedAccount variant. +# +pushd programs/unchecked-account/ +anchor build +if [ $? -eq 0 ]; then + echo "Error: expected failure" + exit 1 +fi +popd + +# +# Build the AccountInfo variant. +# +pushd programs/account-info/ +anchor build +if [ $? -eq 0 ]; then + echo "Error: expected failure" + exit 1 +fi +popd + +echo "Success. All builds failed." diff --git a/tests/safety-checks/tests/safety-checks.ts b/tests/safety-checks/tests/safety-checks.ts new file mode 100644 index 0000000000..2eed33f1b8 --- /dev/null +++ b/tests/safety-checks/tests/safety-checks.ts @@ -0,0 +1,16 @@ +import * as anchor from "@project-serum/anchor"; +import { Program } from "@project-serum/anchor"; +import { SafetyChecks } from "../target/types/safety_checks"; + +describe("safety-checks", () => { + // Configure the client to use the local cluster. + anchor.setProvider(anchor.Provider.env()); + + const program = anchor.workspace.SafetyChecks as Program; + + it("Is initialized!", async () => { + // Add your test here. + const tx = await program.rpc.initialize({}); + console.log("Your transaction signature", tx); + }); +}); diff --git a/tests/safety-checks/tsconfig.json b/tests/safety-checks/tsconfig.json new file mode 100644 index 0000000000..cd5d2e3d06 --- /dev/null +++ b/tests/safety-checks/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } +} diff --git a/tests/spl/token-proxy/Anchor.toml b/tests/spl/token-proxy/Anchor.toml index 5c5f278453..fea8211808 100644 --- a/tests/spl/token-proxy/Anchor.toml +++ b/tests/spl/token-proxy/Anchor.toml @@ -7,3 +7,6 @@ token_proxy = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/swap/Anchor.toml b/tests/swap/Anchor.toml index 0d8eff623a..993c8f1a46 100644 --- a/tests/swap/Anchor.toml +++ b/tests/swap/Anchor.toml @@ -11,3 +11,6 @@ program = "./deps/serum-dex/dex/target/deploy/serum_dex.so" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/zero-copy/Anchor.toml b/tests/zero-copy/Anchor.toml index 385c550c3e..a2c3623277 100644 --- a/tests/zero-copy/Anchor.toml +++ b/tests/zero-copy/Anchor.toml @@ -11,3 +11,6 @@ test = "yarn run mocha -t 1000000 tests/" [programs.localnet] zero_cpi = "ErjUjtqKE5AGWUsjseSJCVLtddM6rhaMbDqmhzraF9h6" zero_copy = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" + +[features] +safety_checks = false