Skip to content

Commit

Permalink
cli: Add anchor keys sync command (coral-xyz#2505)
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto authored May 28, 2023
1 parent 0c8498d commit 70d9223
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 13 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
84 changes: 84 additions & 0 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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),
}
}

Expand All @@ -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,
Expand Down

0 comments on commit 70d9223

Please sign in to comment.