diff --git a/CHANGELOG.md b/CHANGELOG.md index 6498416b8a..605d9aa78d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ # UNRELEASED +### feat: embedding well known canisters at build time + +From this feature onwards, dfx doesn't require `canister_ids.json` to contain information about all the well known canisters on nns. The list of well known canister's can be found [here](https://github.com/dfinity/ic/blob/master/rs/nns/canister_ids.json). This means that `dfx canister call ` will automatically map to the correct canister id. + ### feat: Also report Motoko stable compatibility warnings Report upgrade compatibility warnings for Motoko, such as deleted stable variables, in addition to compatibility errors. @@ -702,7 +706,7 @@ For reference, these formats were removed (any '-' characters were replaced by ' ### feat: add `dfx canister logs ` for fetching canister's logs (preview) -There is a new subcommand `logs` to fetch canister's logs. +There is a new subcommand `logs` to fetch canister's logs. When printing the log entries it tries to guess if the content can be converted to UTF-8 text and prints an array of hex bytes if it fails. **Note** @@ -718,7 +722,7 @@ The query parameter format is not removed because Safari does not support localh ### fix: .env files sometimes missing some canister ids -Made it so `dfx deploy` and `dfx canister install` will always write +Made it so `dfx deploy` and `dfx canister install` will always write environment variables for all canisters in the project that have canister ids to the .env file, even if they aren't being deployed/installed or a dependency of a canister being deployed/installed. @@ -728,7 +732,7 @@ or a dependency of a canister being deployed/installed. There are a few subcommands that take `--argument`/`--argument-file` options to set canister call/init arguments. We unify the related logic to provide consistent user experience. - + The notable changes are: - `dfx deploy` now accepts `--argument-file`. @@ -736,7 +740,7 @@ The notable changes are: ### feat: candid assist feature -Ask for user input when Candid argument is not provided in `dfx canister call`, `dfx canister install` and `dfx deploy`. +Ask for user input when Candid argument is not provided in `dfx canister call`, `dfx canister install` and `dfx deploy`. Previously, we cannot call `dfx deploy --all` when multiple canisters require init args, unless the init args are specified in `dfx.json`. With the Candid assist feature, dfx now asks for init args in terminal when a canister requires init args. ### fix: restored access to URLs like http://localhost:8080/api/v2/status through icx-proxy @@ -885,7 +889,7 @@ If you build with custom canister type, add the following into `dfx.json`: ``` "metadata": [ - { + { "name": "candid:service" } ] @@ -920,7 +924,7 @@ Fix the bug that when parsing `vec \{1;2;3\}` with `blob` type, dfx silently ign ### fix: support `import` for local did file If the local did file contains `import` or init args, dfx will rewrite the did file when storing in canister metadata. -Due to current limitations of the Candid parser, comments will be dropped during rewriting. +Due to current limitations of the Candid parser, comments will be dropped during rewriting. If the local did file doesn't contain `import` or init args, we will not perform the rewriting, thus preserving the comments. ### fix: subtyping check reports the special opt rule as error @@ -1309,7 +1313,7 @@ This incorporates the following executed proposals: - [124537](https://dashboard.internetcomputer.org/proposal/124537) - [124488](https://dashboard.internetcomputer.org/proposal/124488) - [124487](https://dashboard.internetcomputer.org/proposal/124487) - + # 0.15.0 ## DFX diff --git a/Cargo.lock b/Cargo.lock index 92afd5dd59..7e5c2ae55c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1629,6 +1629,7 @@ dependencies = [ "ic-agent", "ic-identity-hsm", "ic-utils 0.37.1", + "itertools 0.10.5", "k256 0.11.6", "keyring", "lazy_static", diff --git a/e2e/tests-dfx/call.bash b/e2e/tests-dfx/call.bash index 0454cb68ad..b24eb48da6 100644 --- a/e2e/tests-dfx/call.bash +++ b/e2e/tests-dfx/call.bash @@ -297,3 +297,17 @@ teardown() { assert_command dfx canister call inter2_mo read assert_match '(8 : nat)' } + +@test "call well known canisters" { + assert_command dfx canister --ic call governance list_proposals '( + record { + include_reward_status = vec {}; + omit_large_fields = null; + before_proposal = null; + limit = 1 : nat32; + exclude_topic = vec {}; + include_all_manage_neuron_proposals = null; + include_status = vec {}; + }, + )' +} diff --git a/e2e/tests-dfx/sign_send.bash b/e2e/tests-dfx/sign_send.bash index b266510a1e..9b6400bc00 100644 --- a/e2e/tests-dfx/sign_send.bash +++ b/e2e/tests-dfx/sign_send.bash @@ -80,3 +80,12 @@ teardown() { rm "$TMP_NAME_FILE" } + +@test "sign query message for a well known canister" { + cd "$E2E_TEMP_DIR" + mkdir not-a-project-dir + cd not-a-project-dir + + assert_command dfx canister sign --query registry read --network ic + assert_match "Query message generated at \[message.json\]" +} diff --git a/src/dfx-core/Cargo.toml b/src/dfx-core/Cargo.toml index 27ad0ea0b8..d0f692e42c 100644 --- a/src/dfx-core/Cargo.toml +++ b/src/dfx-core/Cargo.toml @@ -6,6 +6,11 @@ edition.workspace = true repository.workspace = true license.workspace = true rust-version.workspace = true +build = "assets/build.rs" + +[build-dependencies] +serde_json.workspace = true +itertools.workspace = true [dependencies] aes-gcm.workspace = true diff --git a/src/dfx-core/assets/build.rs b/src/dfx-core/assets/build.rs new file mode 100644 index 0000000000..e0d89e759d --- /dev/null +++ b/src/dfx-core/assets/build.rs @@ -0,0 +1,53 @@ +use std::{env, fs::File, io::Write, path::Path}; + +use itertools::Itertools; +use serde_json::Value; + +fn define_well_known_canisters() { + let well_known_canisters = serde_json::from_str::( + &std::fs::read_to_string(format!( + "{}/assets/canister_ids.json", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap(), + ) + .unwrap(); + let well_known_canisters = well_known_canisters.as_object().unwrap(); + let well_known_canisters = well_known_canisters.iter().map(|(key, val)| { + ( + key.as_str(), + val.as_object() + .unwrap() + .get("mainnet") + .unwrap() + .as_str() + .unwrap(), + ) + }); + + let out_dir = env::var("OUT_DIR").unwrap(); + let loader_path = Path::new(&out_dir).join("well_known_canisters.rs"); + let mut f = File::create(loader_path).unwrap(); + f.write_all( + format!( + " +const WELL_KNOWN_CANISTERS: &[(&str, &str)] = &[ +{} +]; + +pub fn map_wellknown_canisters() -> HashMap {{ + WELL_KNOWN_CANISTERS.iter().map(|(key, value)| (key.to_string(), (*value).try_into().unwrap())).collect() +}} +", + well_known_canisters + .map(|(key, val)| format!("(\"{}\", \"{}\")", key, val)) + .join(",\n") + ) + .as_bytes(), + ) + .unwrap() +} + +fn main() { + define_well_known_canisters(); +} diff --git a/src/dfx-core/assets/canister_ids.json b/src/dfx-core/assets/canister_ids.json new file mode 100644 index 0000000000..70b41a1ca5 --- /dev/null +++ b/src/dfx-core/assets/canister_ids.json @@ -0,0 +1,62 @@ +{ + "registry": { + "local": "rwlgt-iiaaa-aaaaa-aaaaa-cai", + "mainnet": "rwlgt-iiaaa-aaaaa-aaaaa-cai", + "small01": "rwlgt-iiaaa-aaaaa-aaaaa-cai" + }, + "governance": { + "local": "rrkah-fqaaa-aaaaa-aaaaq-cai", + "mainnet": "rrkah-fqaaa-aaaaa-aaaaq-cai", + "small01": "rrkah-fqaaa-aaaaa-aaaaq-cai" + }, + "ledger": { + "local": "ryjl3-tyaaa-aaaaa-aaaba-cai", + "mainnet": "ryjl3-tyaaa-aaaaa-aaaba-cai", + "small01": "ryjl3-tyaaa-aaaaa-aaaba-cai" + }, + "icp-ledger-archive": { + "local": "qjdve-lqaaa-aaaaa-aaaeq-cai", + "mainnet": "qjdve-lqaaa-aaaaa-aaaeq-cai", + "small01": "qjdve-lqaaa-aaaaa-aaaeq-cai" + }, + "icp-ledger-archive-1": { + "local": "qsgjb-riaaa-aaaaa-aaaga-cai", + "mainnet": "qsgjb-riaaa-aaaaa-aaaga-cai", + "small01": "qsgjb-riaaa-aaaaa-aaaga-cai" + }, + "root": { + "local": "r7inp-6aaaa-aaaaa-aaabq-cai", + "mainnet": "r7inp-6aaaa-aaaaa-aaabq-cai", + "small01": "r7inp-6aaaa-aaaaa-aaabq-cai" + }, + "cycles-minting": { + "local": "rkp4c-7iaaa-aaaaa-aaaca-cai", + "mainnet": "rkp4c-7iaaa-aaaaa-aaaca-cai", + "small01": "rkp4c-7iaaa-aaaaa-aaaca-cai" + }, + "lifeline": { + "local": "rno2w-sqaaa-aaaaa-aaacq-cai", + "mainnet": "rno2w-sqaaa-aaaaa-aaacq-cai", + "small01": "rno2w-sqaaa-aaaaa-aaacq-cai" + }, + "genesis-token": { + "local": "renrk-eyaaa-aaaaa-aaada-cai", + "mainnet": "renrk-eyaaa-aaaaa-aaada-cai", + "small01": "renrk-eyaaa-aaaaa-aaada-cai" + }, + "sns-wasm": { + "local": "qaa6y-5yaaa-aaaaa-aaafa-cai", + "mainnet": "qaa6y-5yaaa-aaaaa-aaafa-cai", + "small01": "qaa6y-5yaaa-aaaaa-aaafa-cai" + }, + "identity": { + "local": "rdmx6-jaaaa-aaaaa-aaadq-cai", + "mainnet": "rdmx6-jaaaa-aaaaa-aaadq-cai", + "small01": "rdmx6-jaaaa-aaaaa-aaadq-cai" + }, + "nns-ui": { + "local": "qoctq-giaaa-aaaaa-aaaea-cai", + "mainnet": "qoctq-giaaa-aaaaa-aaaea-cai", + "small01": "qoctq-giaaa-aaaaa-aaaea-cai" + } +} diff --git a/src/dfx-core/src/config/model/canister_id_store.rs b/src/dfx-core/src/config/model/canister_id_store.rs index 4053497c1c..be97b60042 100644 --- a/src/dfx-core/src/config/model/canister_id_store.rs +++ b/src/dfx-core/src/config/model/canister_id_store.rs @@ -9,14 +9,17 @@ use candid::Principal as CanisterId; use ic_agent::export::Principal; use serde::{Deserialize, Serialize, Serializer}; use slog::{warn, Logger}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::ops::{Deref, DerefMut, Sub}; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; use std::time::{Duration, SystemTime}; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; +include!(concat!(env!("OUT_DIR"), "/well_known_canisters.rs")); + pub type CanisterName = String; pub type NetworkName = String; pub type CanisterIdString = String; @@ -94,6 +97,8 @@ pub struct CanisterIdStore { // which does not include remote canister ids ids: CanisterIds, + well_known_ids: HashMap, + // Only canisters that will time out at some point have their timestamp of acquisition saved acquisition_timestamps: CanisterTimestamps, @@ -166,6 +171,7 @@ impl CanisterIdStore { acquisition_timestamps, remote_ids, pull_ids, + well_known_ids: map_wellknown_canisters(), }; if let NetworkTypeDescriptor::Playground { @@ -191,6 +197,16 @@ impl CanisterIdStore { .and_then(|remote_ids| self.get_name_in(canister_id, remote_ids)) .or_else(|| self.get_name_in_project(canister_id)) .or_else(|| self.get_name_in_pull_ids(canister_id)) + .or_else(|| { + let principal = match Principal::from_str(canister_id) { + Ok(p) => p, + Err(_) => return None, + }; + self.well_known_ids + .iter() + .find(|(_, id)| &&principal == id) + .map(|(name, _)| name) + }) } pub fn get_name_in_project(&self, canister_id: &str) -> Option<&String> { @@ -247,6 +263,7 @@ impl CanisterIdStore { .and_then(|remote_ids| self.find_in(canister_name, remote_ids)) .or_else(|| self.find_in(canister_name, &self.ids)) .or_else(|| self.pull_ids.get(canister_name).copied()) + .or_else(|| self.well_known_ids.get(canister_name).copied()) } pub fn get_name_id_map(&self) -> BTreeMap { let mut ids: BTreeMap<_, _> = self @@ -293,6 +310,10 @@ impl CanisterIdStore { .and_then(|s| CanisterId::from_text(s).ok()) } + pub fn is_well_known(&self, canister_id: &CanisterId) -> bool { + self.well_known_ids.values().any(|val| val == canister_id) + } + pub fn get(&self, canister_name: &str) -> Result { self.find(canister_name).ok_or_else(|| { let network = if self.network_descriptor.name == "local" { diff --git a/src/dfx/src/commands/canister/sign.rs b/src/dfx/src/commands/canister/sign.rs index 58bbc62488..feeb2dbb05 100644 --- a/src/dfx/src/commands/canister/sign.rs +++ b/src/dfx/src/commands/canister/sign.rs @@ -2,9 +2,10 @@ use crate::commands::canister::call::get_effective_canister_id; use crate::lib::environment::Environment; use crate::lib::error::DfxResult; use crate::lib::operations::canister::get_canister_id_and_candid_path; +use crate::lib::root_key::fetch_root_key_if_needed; use crate::lib::sign::signed_message::SignedMessageV1; use crate::util::clap::argument_from_cli::ArgumentFromCliPositionalOpt; -use crate::util::{blob_from_arguments, get_candid_type}; +use crate::util::{blob_from_arguments, fetch_remote_did_file, get_candid_type}; use anyhow::{anyhow, bail}; use candid::Principal; use candid_parser::utils::CandidSource; @@ -81,8 +82,19 @@ pub async fn exec( let (canister_id, maybe_candid_path) = get_canister_id_and_candid_path(env, opts.canister_name.as_str())?; - let method_type = - maybe_candid_path.and_then(|path| get_candid_type(CandidSource::File(&path), method_name)); + let method_type = match maybe_candid_path + .and_then(|path| get_candid_type(CandidSource::File(&path), method_name)) + { + Some(mt) => Some(mt), + None => { + let agent = env.get_agent(); + fetch_root_key_if_needed(env).await?; + fetch_remote_did_file(agent, canister_id) + .await + .and_then(|did| get_candid_type(CandidSource::Text(&did), method_name)) + } + }; + let is_query_method = method_type.as_ref().map(|(_, f)| f.is_query()); let (argument_from_cli, argument_type) = opts.argument_from_cli.get_argument_and_type()?; diff --git a/src/dfx/src/lib/operations/canister/mod.rs b/src/dfx/src/lib/operations/canister/mod.rs index 501dc9284d..e5831300a8 100644 --- a/src/dfx/src/lib/operations/canister/mod.rs +++ b/src/dfx/src/lib/operations/canister/mod.rs @@ -357,13 +357,21 @@ pub fn get_canister_id_and_candid_path( ) -> DfxResult<(CanisterId, Option)> { let canister_id_store = env.get_canister_id_store()?; let (canister_name, canister_id) = if let Ok(id) = Principal::from_text(canister) { + if canister_id_store.is_well_known(&id) { + return Ok((id, None)); + } + if let Some(canister_name) = canister_id_store.get_name(canister) { (canister_name.to_string(), id) } else { return Ok((id, None)); } } else { - (canister.to_string(), canister_id_store.get(canister)?) + let canister_id = canister_id_store.get(canister)?; + if canister_id_store.is_well_known(&canister_id) { + return Ok((canister_id, None)); + } + (canister.to_string(), canister_id) }; let config = env.get_config_or_anyhow()?; let candid_path = match CanisterInfo::load(&config, &canister_name, Some(canister_id)) {