From 70d922301e809a6c936a5fe73b99df9aba130fe6 Mon Sep 17 00:00:00 2001 From: acheron <98934430+acheroncrypto@users.noreply.github.com> Date: Sun, 28 May 2023 22:34:53 +0200 Subject: [PATCH] cli: Add `anchor keys sync` command (#2505) --- CHANGELOG.md | 1 + Cargo.lock | 19 ++++++++---- cli/Cargo.toml | 1 + cli/src/lib.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 431168dfae..88f20100d0 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/Cargo.lock b/Cargo.lock index d2730894c1..064b6c4b25 100644 --- a/Cargo.lock +++ b/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" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 37a6f0e597..cb0cb80e5b 100644 --- a/cli/Cargo.toml +++ b/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" diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 8d305f5446..8d36a8e630 100644 --- a/cli/src/lib.rs +++ b/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, + }, } #[derive(Debug, Parser)] @@ -3763,6 +3771,7 @@ fn registry_api_token(_cfg_override: &ConfigOverride) -> Result { 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/.json`. +fn keys_sync(cfg_override: &ConfigOverride, program_name: Option) -> 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,