diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 738ae32f12..9204a1c175 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -53,9 +53,9 @@ jobs: matrix: # We build a dynamic-linked linux binary because otherwise HSM support fails with: # Error: IO: Dynamic loading not supported - os: [macos-12, ubuntu-20.04, ubuntu-22.04, windows-2022] + os: [macos-13-large, ubuntu-20.04, ubuntu-22.04, windows-2022] include: - - os: macos-12 + - os: macos-13-large target: x86_64-apple-darwin binary_path: target/x86_64-apple-darwin/release/dfx - os: ubuntu-20.04 @@ -109,7 +109,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-12, ubuntu-20.04, ubuntu-22.04] + os: [macos-13-large, ubuntu-20.04, ubuntu-22.04] steps: - uses: actions/checkout@v4 - name: Download dfx binary @@ -182,7 +182,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-12, ubuntu-20.04, ubuntu-22.04] + os: [macos-13-large, ubuntu-20.04, ubuntu-22.04] steps: - name: Checking out repo uses: actions/checkout@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 06c4dbba6a..25a2c23b2b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12, windows-latest ] + os: [ ubuntu-latest, macos-13-large, windows-latest ] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/prepare-dfx-assets.yml b/.github/workflows/prepare-dfx-assets.yml index 73a7aa4171..52a768be7a 100644 --- a/.github/workflows/prepare-dfx-assets.yml +++ b/.github/workflows/prepare-dfx-assets.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12 ] + os: [ ubuntu-latest, macos-13-large ] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3b235fc519..e95094effb 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,7 +31,7 @@ jobs: # Error: IO: Dynamic loading not supported target: [ x86_64-apple-darwin, x86_64-unknown-linux-gnu ] include: - - os: macos-12 + - os: macos-13-large target: x86_64-apple-darwin binary_path: target/x86_64-apple-darwin/release name: x86_64-darwin diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 22ee0a7f90..18b6987b41 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -43,7 +43,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12 ] + os: [ ubuntu-latest, macos-13-large ] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a29863ab7..169c256710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ # UNRELEASED +### chore: update agent version in frontend templates, and include `resolve.dedupe` in Vite config + +### chore: improve error message when trying to use the local replica when it is not running + ### Frontend canister Allow setting permissions lists in init arguments just like in upgrade arguments. @@ -9,6 +13,11 @@ Allow setting permissions lists in init arguments just like in upgrade arguments - Module hash: 2c24b5e1584890a7965011d5d1d827aca68c489c9a6308475730420fa53372e8 - https://github.com/dfinity/sdk/pull/3965 +### Candid UI + +- Module hash: f45db224b40fac516c877e3108dc809d4b22fa42d05ee8dfa5002536a3a3daed +- Bump agent-js to fix error code + # 0.24.2 ### feat: Support canister log allowed viewer list diff --git a/docs/cli-reference/dfx-identity.mdx b/docs/cli-reference/dfx-identity.mdx index cd596c4da5..9eac471822 100644 --- a/docs/cli-reference/dfx-identity.mdx +++ b/docs/cli-reference/dfx-identity.mdx @@ -136,7 +136,7 @@ dfx identity export alice >generated-id.pem ## dfx identity import -Use the `dfx identity import` command to create a user identity by importing the user’s key information or security certificate from a PEM file. +Use the `dfx identity import` command to create a user identity by importing the user’s key information or security certificate from a PEM file or seed phrase file. *Password policy*: If an identity is imported using `--storage-mode password-protected`, the following requirements apply to the password: - The password needs to be longer than 8 characters. diff --git a/e2e/tests-dfx/error_context.bash b/e2e/tests-dfx/error_context.bash index 622116ae47..a09aaf365d 100644 --- a/e2e/tests-dfx/error_context.bash +++ b/e2e/tests-dfx/error_context.bash @@ -211,3 +211,13 @@ teardown() { assert_contains "it did not contain a function that dfx was looking for" assert_contains "dfx identity set-wallet --identity " } + +@test "Local replica not running has nice error messages" { + dfx_new + assert_command_fail dfx ping local + assert_contains "You are trying to connect to the local replica but dfx cannot connect to it." + assert_command_fail dfx deploy + assert_contains "You are trying to connect to the local replica but dfx cannot connect to it." + assert_command_fail dfx canister call um5iw-rqaaa-aaaaq-qaaba-cai some_method + assert_contains "You are trying to connect to the local replica but dfx cannot connect to it." +} diff --git a/scripts/test-uis.py b/scripts/test-uis.py index 41876fdb92..a203a15fc2 100644 --- a/scripts/test-uis.py +++ b/scripts/test-uis.py @@ -32,51 +32,21 @@ ("Error", "/index.js"), ("Invalid asm.js: Unexpected token", "/index.js"), ("Expected to find result for path [object Object], but instead found nothing.", "/index.js"), + ("If you want to use Internet Identity, please provide a URL to your local Internet Identity service using the `ii` query parameter", "/index.js"), ( """ -Error: Server returned an error: - Code: 404 (Not Found) - Body: Custom section name not found. - - at j.readState (http://localhost:4943/index.js:2:11709) - at async http://localhost:4943/index.js:2:97683 - at async Promise.all (index 0) - at async Module.UA (http://localhost:4943/index.js:2:98732) - at async Object.getNames (http://localhost:4943/index.js:2:266156) - at async http://localhost:4943/index.js:2:275479""".strip(), +AgentError: Call failed: + Canister: aaaaa-aa + Method: fetch_canister_logs (query) + "Status": "rejected" + "Code": "CanisterReject" + "Message": "IC0406: Caller 2vxsx-fae is not allowed to query ic00 method fetch_canister_logs" + """.strip(), "/index.js", - ), - ( - """ -Error: Server returned an error: - Code: 404 (Not Found) - Body: Custom section name not found.""".strip(), - "/index.js", - ), + ) ] _CANDID_UI_ERRORS_TO_IGNORE = [ - ("Error", "/index.js"), - ("Failed to load resource: the server responded with a status of 404 (Not Found)", "/read_state"), - ( - "Error: Please provide a URL to your local Internet Identity service using the `ii` query parameter", - "/index.js", - ), - ( - """ -Error: Please provide a URL to your local Internet Identity service using the `ii` query parameter - at http://localhost:4943/index.js:2:300040 - at t.renderAuth (http://localhost:4943/index.js:2:301237) - at async http://localhost:4943/index.js:2:314291""".strip(), - "/index.js", - ), - ( - """ -Error: Please provide a URL to your local Internet Identity service using the `ii` query parameter - at http://127.0.0.1:4943/index.js:2:300040 - at t.renderAuth (http://127.0.0.1:4943/index.js:2:301237) - at async http://127.0.0.1:4943/index.js:2:314291""".strip(), - "/index.js", - ), + ("Error", "/index.js") ] # `page.route` does not support additional function parameters _FRONTEND_URL = None @@ -161,7 +131,7 @@ def _check_console_logs(console_logs): for actual_text, endpoint in ( _CANDID_UI_ERRORS_TO_IGNORE if log.type == "error" else _CANDID_UI_WARNINGS_TO_IGNORE ): - if actual_text == log.text.strip() and endpoint in url: + if log.text.strip().startswith(actual_text) and endpoint in url: logging.warning( f'Found {log.type}, but it was expected (log.type="{actual_text}", endpoint="{endpoint}")' ) diff --git a/scripts/workflows/e2e-matrix.py b/scripts/workflows/e2e-matrix.py index f65d6a832d..c48f831211 100755 --- a/scripts/workflows/e2e-matrix.py +++ b/scripts/workflows/e2e-matrix.py @@ -16,7 +16,7 @@ def test_scripts(prefix): matrix = { "test": test, "backend": ["pocketic", "replica"], - "os": ["macos-12", "ubuntu-20.04"], + "os": ["macos-13-large", "ubuntu-20.04"], "exclude": [ { "backend": "pocketic", diff --git a/scripts/workflows/provision-darwin.sh b/scripts/workflows/provision-darwin.sh index c85ea49273..279c4cc1b1 100755 --- a/scripts/workflows/provision-darwin.sh +++ b/scripts/workflows/provision-darwin.sh @@ -20,7 +20,7 @@ if [ "$E2E_TEST" = "tests-dfx/bitcoin.bash" ]; then brew fetch --retry bitcoin brew install bitcoin fi -if [ "$E2E_TEST" = "tests-dfx/build_rust.bash" ]; then +if [ "$E2E_TEST" = "tests-dfx/build_rust.bash" ] && command -v cargo-audit &>/dev/null; then cargo uninstall cargo-audit fi if [ "$E2E_TEST" = "tests-dfx/certificate.bash" ]; then diff --git a/src/dfx-core/src/config/cache.rs b/src/dfx-core/src/config/cache.rs index 11c813f810..13e39a2b61 100644 --- a/src/dfx-core/src/config/cache.rs +++ b/src/dfx-core/src/config/cache.rs @@ -2,13 +2,13 @@ use crate::config::directories::project_dirs; use crate::error::cache::{ DeleteCacheError, EnsureCacheVersionsDirError, GetBinaryCommandPathError, GetCacheRootError, - IsCacheInstalledError, ListCacheVersionsError, + GetVersionFromCachePathError, IsCacheInstalledError, ListCacheVersionsError, }; #[cfg(not(windows))] use crate::foundation::get_user_home; use crate::fs::composite::ensure_dir_exists; use semver::Version; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub trait Cache { fn version_str(&self) -> String; @@ -50,6 +50,21 @@ pub fn get_cache_path_for_version(v: &str) -> Result Ok(p) } +pub fn get_version_from_cache_path( + cache_path: &Path, +) -> Result { + let version = cache_path + .file_name() + .ok_or(GetVersionFromCachePathError::NoCachePathFilename( + cache_path.to_path_buf(), + ))? + .to_str() + .ok_or(GetVersionFromCachePathError::CachePathFilenameNotUtf8( + cache_path.to_path_buf(), + ))?; + Ok(Version::parse(version)?) +} + /// Return the binary cache root. It constructs it if not present /// already. pub fn ensure_cache_versions_dir() -> Result { diff --git a/src/dfx-core/src/error/cache.rs b/src/dfx-core/src/error/cache.rs index f74e684eb0..b431c4eecc 100644 --- a/src/dfx-core/src/error/cache.rs +++ b/src/dfx-core/src/error/cache.rs @@ -5,6 +5,7 @@ use crate::error::fs::{ }; use crate::error::get_current_exe::GetCurrentExeError; use crate::error::get_user_home::GetUserHomeError; +use std::path::PathBuf; use thiserror::Error; #[derive(Error, Debug)] @@ -34,6 +35,18 @@ pub enum EnsureCacheVersionsDirError { GetCacheRoot(#[from] GetCacheRootError), } +#[derive(Error, Debug)] +pub enum GetVersionFromCachePathError { + #[error("no filename in cache path '{0}'")] + NoCachePathFilename(PathBuf), + + #[error("filename in cache path '{0}' is not valid UTF-8")] + CachePathFilenameNotUtf8(PathBuf), + + #[error("cannot parse version from cache path filename")] + ParseVersion(#[from] semver::Error), +} + #[derive(Error, Debug)] pub enum GetCacheRootError { #[error(transparent)] diff --git a/src/dfx-core/src/error/interface.rs b/src/dfx-core/src/error/interface.rs new file mode 100644 index 0000000000..79a7929949 --- /dev/null +++ b/src/dfx-core/src/error/interface.rs @@ -0,0 +1,12 @@ +use crate::error::cache::GetVersionFromCachePathError; +use crate::error::extension::NewExtensionManagerError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NewExtensionManagerFromCachePathError { + #[error(transparent)] + GetVersionFromCachePath(#[from] GetVersionFromCachePathError), + + #[error(transparent)] + NewExtensionManager(#[from] NewExtensionManagerError), +} diff --git a/src/dfx-core/src/error/mod.rs b/src/dfx-core/src/error/mod.rs index 5d720b50ce..ff13e4e49e 100644 --- a/src/dfx-core/src/error/mod.rs +++ b/src/dfx-core/src/error/mod.rs @@ -12,6 +12,7 @@ pub mod fs; pub mod get_current_exe; pub mod get_user_home; pub mod identity; +pub mod interface; pub mod keyring; pub mod load_dfx_config; pub mod load_networks_config; diff --git a/src/dfx-core/src/interface/builder.rs b/src/dfx-core/src/interface/builder.rs index 20776e6552..e7390e2c93 100644 --- a/src/dfx-core/src/interface/builder.rs +++ b/src/dfx-core/src/interface/builder.rs @@ -1,12 +1,16 @@ use crate::{ + config::cache::get_version_from_cache_path, config::model::{ dfinity::{Config, NetworksConfig}, network_descriptor::NetworkDescriptor, }, error::{ builder::{BuildAgentError, BuildDfxInterfaceError, BuildIdentityError}, + extension::NewExtensionManagerError, + interface::NewExtensionManagerFromCachePathError, network_config::NetworkConfigError, }, + extension::manager::ExtensionManager, identity::{identity_manager::InitializeIdentity, IdentityManager}, network::{ provider::{create_network_descriptor, LocalBindDetermination}, @@ -16,6 +20,8 @@ use crate::{ }; use ic_agent::{agent::route_provider::RoundRobinRouteProvider, Agent, Identity}; use reqwest::Client; +use semver::Version; +use std::path::Path; use std::sync::Arc; #[derive(PartialEq)] @@ -42,14 +48,17 @@ pub struct DfxInterfaceBuilder { /// There is no need to set this for the local network, where the root key is fetched by default. /// This would typically be set for a testnet, or an alias for the local network. force_fetch_root_key_insecure_non_mainnet_only: bool, + + extension_manager: Option, } impl DfxInterfaceBuilder { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self { identity: IdentityPicker::Selected, network: NetworkPicker::Local, force_fetch_root_key_insecure_non_mainnet_only: false, + extension_manager: None, } } @@ -77,6 +86,26 @@ impl DfxInterfaceBuilder { self.with_network(NetworkPicker::Named(name.to_string())) } + pub fn with_extension_manager( + self, + version: Version, + ) -> Result { + let extension_manager = Some(ExtensionManager::new(&version)?); + Ok(Self { + extension_manager, + ..self + }) + } + + pub fn with_extension_manager_from_cache_path( + self, + cache_path: &Path, + ) -> Result { + let version = get_version_from_cache_path(cache_path)?; + + Ok(self.with_extension_manager(version)?) + } + pub fn with_force_fetch_root_key_insecure_non_mainnet_only(self) -> Self { Self { force_fetch_root_key_insecure_non_mainnet_only: true, @@ -88,7 +117,7 @@ impl DfxInterfaceBuilder { let fetch_root_key = self.network == NetworkPicker::Local || self.force_fetch_root_key_insecure_non_mainnet_only; let networks_config = NetworksConfig::new()?; - let config = Config::from_current_dir(None)?.map(Arc::new); + let config = Config::from_current_dir(self.extension_manager.as_ref())?.map(Arc::new); let network_descriptor = self.build_network_descriptor(config.clone(), &networks_config)?; let identity = self.build_identity()?; let agent = self.build_agent(identity.clone(), &network_descriptor)?; diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json index 04d9cd02c5..6c1c237ecd 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json @@ -13,9 +13,9 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0" + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3" }, "devDependencies": { "@types/react": "^18.2.14", diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js index 670b0347b6..9755aa9919 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js @@ -39,5 +39,6 @@ export default defineConfig({ ), }, ], + dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json index 69b8edd5af..85cf87751c 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json @@ -11,9 +11,9 @@ "format": "prettier --write \"src/**/*.{json,js,jsx,ts,tsx,css,scss}\"" }, "dependencies": { - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0" + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3" }, "devDependencies": { "@sveltejs/adapter-static": "^3.0.4", diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js index 51010df640..70d491d6cd 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js @@ -39,5 +39,6 @@ export default defineConfig({ ), }, ], + dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json index e96aeb2059..36d9b2fef0 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json @@ -21,9 +21,9 @@ "vitest": "^2.0.5" }, "dependencies": { - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0", + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3", "lit-html": "^2.8.0" } } diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js index 18ceb500c7..9d6f44396e 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js @@ -38,5 +38,6 @@ export default defineConfig({ ), }, ], + dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json index daa41481a1..8367abd0d5 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json @@ -23,8 +23,8 @@ "dependencies": { "pinia": "^2.1.6", "vue": "^3.3.4", - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0" + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3" } } \ No newline at end of file diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js index 014c37999a..92afec3511 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js @@ -34,6 +34,7 @@ export default defineConfig({ alias: [ { find: 'declarations', replacement: fileURLToPath(new URL('../declarations', import.meta.url)) }, { find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }, - ] + ], + dedupe: ['@dfinity/agent'], } }); diff --git a/src/dfx/src/commands/identity/import.rs b/src/dfx/src/commands/identity/import.rs index 4a2d374979..832a4ff5d5 100644 --- a/src/dfx/src/commands/identity/import.rs +++ b/src/dfx/src/commands/identity/import.rs @@ -9,7 +9,7 @@ use std::fs; use std::path::PathBuf; use std::str::FromStr; -/// Creates a new identity from a PEM file. +/// Creates a new identity from a PEM file or seed phrase file. #[derive(Parser)] pub struct ImportOpts { /// The identity to create. diff --git a/src/dfx/src/lib/canister_info/rust.rs b/src/dfx/src/lib/canister_info/rust.rs index a3b7266c26..22ce219e04 100644 --- a/src/dfx/src/lib/canister_info/rust.rs +++ b/src/dfx/src/lib/canister_info/rust.rs @@ -64,7 +64,7 @@ impl CanisterInfoFactory for RustCanisterInfo { }; let mut candidate_targets = package_info.targets.iter().filter(|x| { x.crate_types.iter().any(|c| { - (c == "cdylib" && x.name == crate_name.replace('-', "_")) + (c == "cdylib" && x.name.replace('-', "_") == crate_name.replace('-', "_")) || (c == "bin" && x.name == crate_name) }) }); diff --git a/src/dfx/src/lib/diagnosis.rs b/src/dfx/src/lib/diagnosis.rs index eb121f14ab..1eaa1b6f2c 100644 --- a/src/dfx/src/lib/diagnosis.rs +++ b/src/dfx/src/lib/diagnosis.rs @@ -1,5 +1,6 @@ use crate::lib::error_code; use anyhow::Error as AnyhowError; +use dfx_core::error::root_key::FetchRootKeyError; use ic_agent::agent::{RejectCode, RejectResponse}; use ic_agent::AgentError; use ic_asset::error::{GatherAssetDescriptorsError, SyncError, UploadContentError}; @@ -55,6 +56,10 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis { } } + if local_replica_not_running(err) { + return diagnose_local_replica_not_running(); + } + if let Some(sync_error) = err.downcast_ref::() { if duplicate_asset_key_dist_and_src(sync_error) { return diagnose_duplicate_asset_key_dist_and_src(); @@ -64,6 +69,32 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis { NULL_DIAGNOSIS } +fn local_replica_not_running(err: &AnyhowError) -> bool { + let maybe_agent_error = { + if let Some(FetchRootKeyError::AgentError(agent_error)) = + err.downcast_ref::() + { + Some(agent_error) + } else { + err.downcast_ref::() + } + }; + if let Some(AgentError::TransportError(transport_error)) = maybe_agent_error { + transport_error.is_connect() + && transport_error + .url() + .and_then(|url| url.host()) + .map(|host| match host { + url::Host::Domain(domain) => domain == "localhost", + url::Host::Ipv4(ipv4_addr) => ipv4_addr.is_loopback(), + url::Host::Ipv6(ipv6_addr) => ipv6_addr.is_loopback(), + }) + .unwrap_or(false) + } else { + false + } +} + fn not_a_controller(err: &AgentError) -> bool { // Newer replicas include the error code in the reject response. if matches!( @@ -119,6 +150,17 @@ The most common way this error is solved is by running 'dfx canister update-sett ) } +fn diagnose_local_replica_not_running() -> Diagnosis { + let error_explanation = + "You are trying to connect to the local replica but dfx cannot connect to it."; + let action_suggestion = + "Target a different network or run 'dfx start' to start the local replica."; + ( + Some(error_explanation.to_string()), + Some(action_suggestion.to_string()), + ) +} + fn subnet_not_authorized() -> Diagnosis { let action_suggestion = "If you are connecting to a node directly instead of a boundary node, try using --provisional-create-canister-effective-canister-id with a canister id in the subnet's canister range. First non-root subnet: 5v3p4-iyaaa-aaaaa-qaaaa-cai, second non-root subnet: jrlun-jiaaa-aaaab-aaaaa-cai"; (None, Some(action_suggestion.to_string())) diff --git a/src/distributed/ui.wasm b/src/distributed/ui.wasm index 908ef62dc1..b56e2fb430 100644 Binary files a/src/distributed/ui.wasm and b/src/distributed/ui.wasm differ