Bläddra i källkod

cli: Add `anchor keys sync` command (#2505)

acheron 2 år sedan
förälder
incheckning
70d922301e
4 ändrade filer med 99 tillägg och 6 borttagningar
  1. 1 0
      CHANGELOG.md
  2. 13 6
      Cargo.lock
  3. 1 0
      cli/Cargo.toml
  4. 84 0
      cli/src/lib.rs

+ 1 - 0
CHANGELOG.md

@@ -18,6 +18,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 - bench: Add benchmarking for compute units usage ([#2466](https://github.com/coral-xyz/anchor/pull/2466))
 - cli: `idl set-buffer`, `idl set-authority` and `idl close` take an option `--print-only`. which prints transaction in a base64 Borsh compatible format but not sent to the cluster. It's helpful when managing authority under a multisig, e.g., a user can create a proposal for a `Custom Instruction` in SPL Governance ([#2486](https://github.com/coral-xyz/anchor/pull/2486)).
 - lang: Add `emit_cpi!` and `#[event_cpi]` macros(behind `event-cpi` feature flag) to store event logs in transaction metadata ([#2438](https://github.com/coral-xyz/anchor/pull/2438)).
+- cli: Add `keys sync` command to sync program id declarations ([#2505](https://github.com/coral-xyz/anchor/pull/2505)).
 
 ### Fixes
 

+ 13 - 6
Cargo.lock

@@ -67,9 +67,9 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.19"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
 dependencies = [
  "memchr",
 ]
@@ -179,6 +179,7 @@ dependencies = [
  "heck 0.4.0",
  "pathdiff",
  "portpicker",
+ "regex",
  "reqwest",
  "semver 1.0.16",
  "serde",
@@ -2155,7 +2156,7 @@ dependencies = [
  "petgraph",
  "pico-args",
  "regex",
- "regex-syntax",
+ "regex-syntax 0.6.27",
  "string_cache",
  "term",
  "tiny-keccak",
@@ -3106,13 +3107,13 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.6.0"
+version = "1.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax",
+ "regex-syntax 0.7.2",
 ]
 
 [[package]]
@@ -3121,6 +3122,12 @@ version = "0.6.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
 
+[[package]]
+name = "regex-syntax"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+
 [[package]]
 name = "remove_dir_all"
 version = "0.5.3"

+ 1 - 0
cli/Cargo.toml

@@ -40,6 +40,7 @@ dirs = "4.0"
 heck = "0.4.0"
 flate2 = "1.0.19"
 tar = "0.4.35"
+regex = "1.8.3"
 reqwest = { version = "0.11.4", default-features = false, features = ["multipart", "blocking", "rustls-tls"] }
 tokio = "1.24"
 pathdiff = "0.2.0"

+ 84 - 0
cli/src/lib.rs

@@ -14,6 +14,7 @@ use flate2::read::ZlibDecoder;
 use flate2::write::{GzEncoder, ZlibEncoder};
 use flate2::Compression;
 use heck::{ToKebabCase, ToSnakeCase};
+use regex::RegexBuilder;
 use reqwest::blocking::multipart::{Form, Part};
 use reqwest::blocking::Client;
 use semver::{Version, VersionReq};
@@ -323,7 +324,14 @@ pub enum Command {
 
 #[derive(Debug, Parser)]
 pub enum KeysCommand {
+    /// List all of the program keys.
     List,
+    /// Sync the program's `declare_id!` pubkey with the program's actual pubkey.
+    Sync {
+        /// Only sync the given program instead of all programs
+        #[clap(short, long)]
+        program_name: Option<String>,
+    },
 }
 
 #[derive(Debug, Parser)]
@@ -3763,6 +3771,7 @@ fn registry_api_token(_cfg_override: &ConfigOverride) -> Result<String> {
 fn keys(cfg_override: &ConfigOverride, cmd: KeysCommand) -> Result<()> {
     match cmd {
         KeysCommand::List => keys_list(cfg_override),
+        KeysCommand::Sync { program_name } => keys_sync(cfg_override, program_name),
     }
 }
 
@@ -3776,6 +3785,81 @@ fn keys_list(cfg_override: &ConfigOverride) -> Result<()> {
     })
 }
 
+/// Sync the program's `declare_id!` pubkey with the pubkey from `target/deploy/<KEYPAIR>.json`.
+fn keys_sync(cfg_override: &ConfigOverride, program_name: Option<String>) -> Result<()> {
+    with_workspace(cfg_override, |cfg| {
+        let programs = cfg.read_all_programs()?;
+        let programs = match program_name {
+            Some(program_name) => vec![programs
+                .into_iter()
+                .find(|program| program.lib_name == program_name)
+                .ok_or_else(|| anyhow!("`{program_name}` is not found"))?],
+            None => programs,
+        };
+
+        let declare_id_regex = RegexBuilder::new(r#"^(([\w]+::)*)declare_id!\("(\w*)"\)"#)
+            .multi_line(true)
+            .build()
+            .unwrap();
+
+        for program in programs {
+            // Get the pubkey from the keypair file
+            let actual_program_id = program.pubkey()?.to_string();
+
+            // Handle declaration in program files
+            let src_path = program.path.join("src");
+            let files_to_check = vec![src_path.join("lib.rs"), src_path.join("id.rs")];
+
+            for path in files_to_check {
+                let mut content = match fs::read_to_string(&path) {
+                    Ok(content) => content,
+                    Err(_) => continue,
+                };
+
+                let incorrect_program_id = declare_id_regex
+                    .captures(&content)
+                    .and_then(|captures| captures.get(3))
+                    .filter(|program_id_match| program_id_match.as_str() != actual_program_id);
+                if let Some(program_id_match) = incorrect_program_id {
+                    println!("Found incorrect program id declaration in {path:?}");
+
+                    // Update the program id
+                    content.replace_range(program_id_match.range(), &actual_program_id);
+                    fs::write(&path, content)?;
+
+                    println!("Updated to {actual_program_id}\n");
+                    break;
+                }
+            }
+
+            // Handle declaration in Anchor.toml
+            'outer: for programs in cfg.programs.values_mut() {
+                for (name, mut deployment) in programs {
+                    // Skip other programs
+                    if name != &program.lib_name {
+                        continue;
+                    }
+
+                    if deployment.address.to_string() != actual_program_id {
+                        println!("Found incorrect program id declaration in Anchor.toml for the program `{name}`");
+
+                        // Update the program id
+                        deployment.address = Pubkey::from_str(&actual_program_id).unwrap();
+                        fs::write(cfg.path(), cfg.to_string())?;
+
+                        println!("Updated to {actual_program_id}\n");
+                        break 'outer;
+                    }
+                }
+            }
+        }
+
+        println!("All program id declarations are synced.");
+
+        Ok(())
+    })
+}
+
 fn localnet(
     cfg_override: &ConfigOverride,
     skip_build: bool,