Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: embedding well known canisters at build time #3872

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions Cargo.lock

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

14 changes: 14 additions & 0 deletions e2e/tests-dfx/call.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
},
)'
}
9 changes: 9 additions & 0 deletions e2e/tests-dfx/sign_send.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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\]"
}
5 changes: 5 additions & 0 deletions src/dfx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions src/dfx-core/assets/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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::<Value>(
&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()
.values()
.last()
NikolaMilosa marked this conversation as resolved.
Show resolved Hide resolved
.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<CanisterName, Principal> {{
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()
Comment on lines +34 to +48

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a global mapping of well-known canister ids, could we initialize this into a LazyLock rather than adding it as a member to CanisterIdStore?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version of rust used by sdk doesn't have LazyLock stabilized.

}

fn main() {
define_well_known_canisters();
}
62 changes: 62 additions & 0 deletions src/dfx-core/assets/canister_ids.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
23 changes: 22 additions & 1 deletion src/dfx-core/src/config/model/canister_id_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -94,6 +97,8 @@ pub struct CanisterIdStore {
// which does not include remote canister ids
ids: CanisterIds,

well_known_ids: HashMap<CanisterName, Principal>,

// Only canisters that will time out at some point have their timestamp of acquisition saved
acquisition_timestamps: CanisterTimestamps,

Expand Down Expand Up @@ -166,6 +171,7 @@ impl CanisterIdStore {
acquisition_timestamps,
remote_ids,
pull_ids,
well_known_ids: map_wellknown_canisters(),
};

if let NetworkTypeDescriptor::Playground {
Expand All @@ -189,6 +195,16 @@ impl CanisterIdStore {
self.remote_ids
.as_ref()
.and_then(|remote_ids| self.get_name_in(canister_id, remote_ids))
.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)
})
NikolaMilosa marked this conversation as resolved.
Show resolved Hide resolved
.or_else(|| self.get_name_in_project(canister_id))
.or_else(|| self.get_name_in_pull_ids(canister_id))
}
Expand Down Expand Up @@ -246,6 +262,7 @@ impl CanisterIdStore {
.as_ref()
.and_then(|remote_ids| self.find_in(canister_name, remote_ids))
.or_else(|| self.find_in(canister_name, &self.ids))
.or_else(|| self.well_known_ids.get(canister_name).copied())
.or_else(|| self.pull_ids.get(canister_name).copied())
}
pub fn get_name_id_map(&self) -> BTreeMap<String, String> {
Expand Down Expand Up @@ -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<CanisterId, CanisterIdStoreError> {
self.find(canister_name).ok_or_else(|| {
let network = if self.network_descriptor.name == "local" {
Expand Down
18 changes: 15 additions & 3 deletions src/dfx/src/commands/canister/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()?;
Expand Down
10 changes: 9 additions & 1 deletion src/dfx/src/lib/operations/canister/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,13 +357,21 @@ pub fn get_canister_id_and_candid_path(
) -> DfxResult<(CanisterId, Option<PathBuf>)> {
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)) {
Expand Down
Loading