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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pem = "1.0.2"
proptest = "1.0.0"
reqwest = { version = "0.12.4", default-features = false, features = [
"rustls-tls",
"blocking",
] }
ring = "0.16.11"
schemars = "0.8"
Expand All @@ -72,7 +73,7 @@ tempfile = "3.3.0"
thiserror = "1.0.24"
time = "0.3.9"
tokio = "1.35"
url = { version="2.1.0", features=["serde"] }
url = { version = "2.1.0", features = ["serde"] }
walkdir = "2.3.2"

[profile.release]
Expand Down
4 changes: 4 additions & 0 deletions src/dfx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ edition.workspace = true
repository.workspace = true
license.workspace = true
rust-version.workspace = true
build = "src/build.rs"

[build-dependencies]
reqwest.workspace = true

[dependencies]
aes-gcm.workspace = true
Expand Down
35 changes: 35 additions & 0 deletions src/dfx-core/src/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::{env, fs::File, io::Write, path::Path};

const CANISTER_IDS_URL: &str = "https://raw.githubusercontent.com/dfinity/ic/1402bf35308ec9bd87356c26f7c430f49b49423a/rs/nns/canister_ids.json";
NikolaMilosa marked this conversation as resolved.
Show resolved Hide resolved
fn define_well_known_canisters() {
let well_known_canisters = reqwest::blocking::get(CANISTER_IDS_URL)
.unwrap()
.error_for_status()
.unwrap()
.text()
.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(
NikolaMilosa marked this conversation as resolved.
Show resolved Hide resolved
format!(
"
const WELL_KNOWN_CANISTERS: &str = r#\"
{}
\"#;

pub fn map_wellknown_canisters() -> CanisterIds {{
serde_json::from_str(WELL_KNOWN_CANISTERS).unwrap_or(CanisterIds::new())
}}
",
well_known_canisters.replace("mainnet", "ic")
)
.as_bytes(),
)
.unwrap()
}

fn main() {
define_well_known_canisters();
}
14 changes: 14 additions & 0 deletions src/dfx-core/src/config/model/canister_id_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ 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 +96,8 @@ pub struct CanisterIdStore {
// which does not include remote canister ids
ids: CanisterIds,

well_known_ids: CanisterIds,

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

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

if let NetworkTypeDescriptor::Playground {
Expand All @@ -189,6 +194,7 @@ impl CanisterIdStore {
self.remote_ids
.as_ref()
.and_then(|remote_ids| self.get_name_in(canister_id, remote_ids))
.or_else(|| self.get_name_in(canister_id, &self.well_known_ids))
.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 +252,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.find_in(canister_name, &self.well_known_ids))
.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 +300,13 @@ impl CanisterIdStore {
.and_then(|s| CanisterId::from_text(s).ok())
}

pub fn is_well_known(&self, canister_id: &CanisterId) -> bool {
let canister_str = canister_id.to_string();
self.well_known_ids
.values()
.any(|val| val.values().any(|k| &canister_str == k))
}

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
17 changes: 14 additions & 3 deletions src/dfx/src/commands/canister/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ 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::sign_transport::SignTransport;
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, Context};
use candid::Principal;
use candid_parser::utils::CandidSource;
Expand Down Expand Up @@ -75,6 +76,9 @@ pub async fn exec(
opts: CanisterSignOpts,
call_sender: &CallSender,
) -> DfxResult {
let agent = env.get_agent();
fetch_root_key_if_needed(env).await?;
NikolaMilosa marked this conversation as resolved.
Show resolved Hide resolved

let log = env.get_logger();
if *call_sender != CallSender::SelectedId {
bail!("`sign` currently doesn't support proxying through the wallet canister, please use `dfx canister sign --no-wallet ...`.");
Expand All @@ -85,8 +89,15 @@ 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 => 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