Bläddra i källkod

idl: Check ambiguous discriminators (#3157)

acheron 1 år sedan
förälder
incheckning
438c481384

+ 1 - 0
CHANGELOG.md

@@ -33,6 +33,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 - lang: Use associated discriminator constants instead of hardcoding in `#[account]` ([#3144](https://github.com/coral-xyz/anchor/pull/3144)).
 - lang: Add `discriminator` argument to `#[account]` attribute ([#3149](https://github.com/coral-xyz/anchor/pull/3149)).
 - lang: Add `discriminator` argument to `#[event]` attribute ([#3152](https://github.com/coral-xyz/anchor/pull/3152)).
+- idl: Check ambiguous discriminators ([#3157](https://github.com/coral-xyz/anchor/pull/3157)).
 
 ### Fixes
 

+ 24 - 0
idl/src/build.rs

@@ -293,5 +293,29 @@ fn verify(idl: &Idl) -> Result<()> {
         ));
     }
 
+    // Check potential discriminator collisions
+    macro_rules! check_discriminator_collision {
+        ($field:ident) => {
+            if let Some((outer, inner)) = idl.$field.iter().find_map(|outer| {
+                idl.$field
+                    .iter()
+                    .filter(|inner| inner.name != outer.name)
+                    .find(|inner| outer.discriminator.starts_with(&inner.discriminator))
+                    .map(|inner| (outer, inner))
+            }) {
+                return Err(anyhow!(
+                    "Ambiguous discriminators for {} `{}` and `{}`",
+                    stringify!($field),
+                    outer.name,
+                    inner.name
+                ));
+            }
+        };
+    }
+
+    check_discriminator_collision!(accounts);
+    check_discriminator_collision!(events);
+    check_discriminator_collision!(instructions);
+
     Ok(())
 }

+ 12 - 1
tests/custom-discriminator/Anchor.toml

@@ -1,8 +1,19 @@
+[workspace]
+exclude = ["programs/ambiguous-discriminator"]
+
+[features]
+resolution = true
+skip-lint = false
+
 [programs.localnet]
+ambiguous-discriminator = "AmbiguousDiscriminator111111111111111111111"
 custom_discriminator = "CustomDiscriminator111111111111111111111111"
 
+[registry]
+url = "https://api.apr.dev"
+
 [provider]
-cluster = "localnet"
+cluster = "Localnet"
 wallet = "~/.config/solana/id.json"
 
 [scripts]

+ 20 - 0
tests/custom-discriminator/programs/ambiguous-discriminator/Cargo.toml

@@ -0,0 +1,20 @@
+[package]
+name = "ambiguous-discriminator"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "ambiguous_discriminator"
+
+[features]
+default = []
+cpi = ["no-entrypoint"]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+idl-build = ["anchor-lang/idl-build"]
+
+[dependencies]
+anchor-lang = { path = "../../../../lang" }

+ 2 - 0
tests/custom-discriminator/programs/ambiguous-discriminator/Xargo.toml

@@ -0,0 +1,2 @@
+[target.bpfel-unknown-unknown.dependencies.std]
+features = []

+ 31 - 0
tests/custom-discriminator/programs/ambiguous-discriminator/src/lib.rs

@@ -0,0 +1,31 @@
+use anchor_lang::prelude::*;
+
+declare_id!("AmbiguousDiscriminator111111111111111111111");
+
+#[program]
+pub mod ambiguous_discriminator {
+    use super::*;
+
+    /// Compilation should error due to ambiguous discriminators.
+    pub fn check_accounts(_ctx: Context<CheckAccounts>) -> Result<()> {
+        Ok(())
+    }
+}
+
+#[derive(Accounts)]
+pub struct CheckAccounts<'info> {
+    pub some_account: Account<'info, SomeAccount>,
+    pub another_account: Account<'info, AnotherAccount>,
+}
+
+#[account(discriminator = 1)]
+pub struct SomeAccount {
+    pub a: u8,
+    pub b: u16,
+    pub c: u32,
+}
+
+#[account(discriminator = [1, 2, 3, 4])]
+pub struct AnotherAccount {
+    pub a: u32,
+}

+ 22 - 0
tests/custom-discriminator/tests/ambiguous-discriminator.ts

@@ -0,0 +1,22 @@
+import { spawnSync } from "child_process";
+
+describe("ambiguous-discriminator", () => {
+  it("Returns ambiguous discriminator error on builds", () => {
+    const result = spawnSync("anchor", [
+      "idl",
+      "build",
+      "-p",
+      "ambiguous-discriminator",
+    ]);
+    if (result.status === 0) {
+      throw new Error("Ambiguous errors did not make building the IDL fail");
+    }
+
+    const output = result.output.toString();
+    if (!output.includes("Error: Program ambiguous-discriminator not found")) {
+      throw new Error(
+        `Ambiguous discriminators did not return the expected error: "${output}"`
+      );
+    }
+  });
+});