Skip to content

Commit 05c5eb4

Browse files
lwshangclaudeIDX GitHub Automation
authored
chore: vendor dfx-core identity/network resolution, drop external dfx-core dependency (#10640)
## Why `ic-sns-cli` and `ic-sns-testing` depended on the external [`dfx-core`](https://crates.io/crates/dfx-core) crate. `dfx-core` re-exports `ic-agent` in its public API, so its **major version is bumped in lockstep with `ic-agent`** — every `ic-agent` upgrade in this repo requires a matching `dfx-core` release. Once the [`dfinity/sdk`](https://github.com/dfinity/sdk) repo is archived, those `dfx-core` releases can no longer be cut, which would block this monorepo from upgrading `ic-agent` at all. It also drags in a pile of outdated transitive deps (`bip32 0.4`, `tiny-bip39`, `sec1 0.3`, `handlebars`, `byte-unit`, …) that other teams have hit while trying to upgrade their own dependencies. SNS only uses a small, self-contained slice of `dfx-core`: resolving a dfx identity + network into an `ic_agent::Agent`, and resolving an identity name to a principal. This PR vendors just that slice as a new internal crate and removes the external `dfx-core` dependency. This direction was approved by the NNS team, who own `ic-sns-cli`: <!-- TODO: NNS discussion link -->. ## What - New crate **`rs/sns/dfx-core-vendored`** — a trimmed, in-tree subset of `dfx-core 0.4.0` exposing two functions: - `get_agent(network, identity) -> Agent` - `get_identity_principal(name) -> Principal` - `ic-sns-cli` and `ic-sns-testing` rewired onto it; the `dfx-core` dependency removed from both crates and from the workspace (`Cargo.toml`, `bazel/rust.MODULE.bazel`, lockfiles). - The keyring/encrypted-PEM deps `dfx-core` used to pull in (`aes-gcm`, `argon2`, `dialoguer`, `keyring`, `security-framework`) become direct deps of the new crate; the unused ones (`bip32 0.4`, `tiny-bip39`, `sec1 0.3`, `handlebars`, `byte-unit`, `dunce`, `tar`, `flate2`, …) drop out of the graph. ## Behaviour No user-facing change. dfx identities are untouched (`~/.config/dfx/identity/…`) and the SNS CLI behaves as before for the supported cases: - **Identities:** plaintext PEM, password-encrypted PEM, OS keyring, and HSM — all still load exactly as dfx loads them. - **Networks:** `ic` (mainnet), `local` (shared or project, honouring a running replica's `webserver-port`), and explicit IC HTTP endpoint URLs (e.g. `sns-testing --network http://127.0.0.1:8080`). Not vendored (unused by SNS): identity creation/rename/removal/export, wallets, the extension manager, playground networks, and general `dfx.json`/`networks.json` parsing beyond the `local` bind. `IdentityManager::new` no longer auto-creates a default identity for `InitializeIdentity::Allow` (that is dfx's job), but SNS only ever calls it with `Disallow`, so there is no behavioural change in practice. See `rs/sns/dfx-core-vendored/README.md` for how to verify the subset against upstream and the maintenance expectations (the crate is treated as frozen vendored code — the only expected changes are the mechanical adjustments to track future `ic-agent` majors, which is the whole reason it exists). ## Reviewing the vendored crate The crate is derived from **`dfx-core 0.4.0`** (which itself depends on `ic-agent 0.47`, this workspace's version). Most modules are copied at the **same relative path** as upstream and trimmed; two files (`network.rs`, `lib.rs`) are reimplementations. To confirm the copied modules really are a subset: ```sh diff -ur sdk/src/dfx-core/src ic/rs/sns/dfx-core-vendored/src ``` That diff is ~2,530 lines, but the review surface is tiny: | | Lines | Notes | | --- | --- | --- | | Deletions (trimmed-out code) | ~1,865 | Skim-only | | `Only in …` notices for dropped files | 34 | ~60 dropped files/dirs, collapsed to one line each | | **Additions** | **~190** | The only lines to actually read | Of the ~190 additions: - **~150 are the two reimplementations** — `lib.rs` (+112) and `network.rs` (~300 lines, which shows only as `Only in …/vendored: network.rs`, so review it directly). These need normal code review; there is no upstream counterpart to diff against. - **~39 across all 19 copied modules**, and none of it is rustfmt noise: ~25 shortened `use` statements (each verifiably a subset of upstream's imports, since imports are dropped alongside the code that used them), 9 doc-comment lines (two `Unlike upstream …` notes), and 5 code lines for the edits those notes describe (`_initialize_identity` ignored, the `NoIdentityConfigurationFound` early-return, and `password_prompt` dropping its `PromptMode` argument). (Don't run `diff -urN`: it expands every dropped file into a full deletion, ballooning to ~11,000 lines for no benefit.) ## Testing - `cargo build` + `cargo clippy` (`-D warnings`, per `ci/scripts/rust-lint.sh`) on `dfx-core-vendored`, `ic-sns-cli`, `ic-sns-testing`. - `bazel build` of the new crate and all `//rs/sns/cli/...` + `//rs/sns/testing/...` targets. - `bazel test`: - `//rs/sns/cli:sns_test`, `//rs/sns/cli:sns_doctest`, `//rs/sns/testing:sns_testing_test` - `//rs/sns/testing:sns_testing_ci` (full PocketIC NNS + SNS lifecycle) - `//rs/nervous_system/integration_tests:sns_extension_test` - After merging the latest `master`, the crate compiles cleanly against the upgraded `ic-agent` / `ic-identity-hsm 0.47`, with no `dfx-core` release required — a live confirmation of the decoupling. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: IDX GitHub Automation <infra+github-automation@dfinity.org>
1 parent cff5e90 commit 05c5eb4

35 files changed

Lines changed: 1816 additions & 2294 deletions

Cargo.Bazel.json.lock

Lines changed: 106 additions & 1566 deletions
Large diffs are not rendered by default.

Cargo.Bazel.toml.lock

Lines changed: 66 additions & 329 deletions
Large diffs are not rendered by default.

Cargo.lock

Lines changed: 89 additions & 353 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ members = [
416416
"rs/rust_canisters/xrc_mock",
417417
"rs/sns/audit",
418418
"rs/sns/cli",
419+
"rs/sns/dfx-core-vendored",
419420
"rs/sns/governance",
420421
"rs/sns/governance/api",
421422
"rs/sns/governance/api_helpers",
@@ -643,7 +644,6 @@ curve25519-dalek = { version = "4.1.3", features = [
643644
der = { version = "0.7", default-features = false, features = ["derive"] }
644645
derive-new = "0.7.0"
645646
devicemapper = "0.34"
646-
dfx-core = { version = "0.4.0" }
647647
ed25519-dalek = { version = "2.2.0", features = [
648648
"std",
649649
"zeroize",

bazel/rust.MODULE.bazel

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ crate.spec(
6464
package = "aide",
6565
version = "^0.14.2",
6666
)
67+
crate.spec(
68+
features = [
69+
"std",
70+
],
71+
package = "aes-gcm",
72+
version = "^0.10.3",
73+
)
74+
crate.spec(
75+
features = [
76+
"std",
77+
],
78+
package = "argon2",
79+
version = "^0.4",
80+
)
6781
crate.spec(
6882
package = "arbitrary",
6983
version = "^1.3.2",
@@ -393,8 +407,12 @@ crate.spec(
393407
version = "0.34",
394408
)
395409
crate.spec(
396-
package = "dfx-core",
397-
version = "^0.4.0",
410+
package = "dialoguer",
411+
version = "^0.11.0",
412+
)
413+
crate.spec(
414+
package = "directories-next",
415+
version = "^2.0.0",
398416
)
399417
crate.spec(
400418
package = "dyn-clone",
@@ -903,6 +921,17 @@ crate.spec(
903921
package = "k256",
904922
version = "^0.13.4",
905923
)
924+
crate.spec(
925+
features = [
926+
"apple-native",
927+
"windows-native",
928+
"linux-native",
929+
"sync-secret-service",
930+
"vendored",
931+
],
932+
package = "keyring",
933+
version = "^3",
934+
)
906935
crate.spec(
907936
package = "lazy_static",
908937
version = "^1.4.0",
@@ -1472,6 +1501,10 @@ crate.spec(
14721501
package = "secp256k1",
14731502
version = "^0.22",
14741503
)
1504+
crate.spec(
1505+
package = "security-framework",
1506+
version = "^3",
1507+
)
14751508
crate.spec(
14761509
features = [
14771510
"serde",

rs/sns/cli/BUILD.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ rust_library(
2929
"//rs/nns/constants",
3030
"//rs/nns/governance/api",
3131
"//rs/nns/sns-wasm",
32+
"//rs/sns/dfx-core-vendored",
3233
"//rs/sns/governance",
3334
"//rs/sns/governance/api",
3435
"//rs/sns/init",
@@ -39,7 +40,6 @@ rust_library(
3940
"@crate_index//:base64",
4041
"@crate_index//:candid",
4142
"@crate_index//:clap",
42-
"@crate_index//:dfx-core",
4343
"@crate_index//:futures",
4444
"@crate_index//:hex",
4545
"@crate_index//:ic-agent",
@@ -78,6 +78,7 @@ rust_binary(
7878
"//rs/nns/constants",
7979
"//rs/nns/governance/api",
8080
"//rs/nns/sns-wasm",
81+
"//rs/sns/dfx-core-vendored",
8182
"//rs/sns/governance",
8283
"//rs/sns/governance/api",
8384
"//rs/sns/init",
@@ -88,7 +89,6 @@ rust_binary(
8889
"@crate_index//:base64",
8990
"@crate_index//:candid",
9091
"@crate_index//:clap",
91-
"@crate_index//:dfx-core",
9292
"@crate_index//:futures",
9393
"@crate_index//:hex",
9494
"@crate_index//:ic-agent",
@@ -144,6 +144,7 @@ rust_test(
144144
"//rs/nns/constants",
145145
"//rs/nns/governance/api",
146146
"//rs/nns/sns-wasm",
147+
"//rs/sns/dfx-core-vendored",
147148
"//rs/sns/governance",
148149
"//rs/sns/governance/api",
149150
"//rs/sns/init",
@@ -155,7 +156,6 @@ rust_test(
155156
"@crate_index//:base64",
156157
"@crate_index//:candid",
157158
"@crate_index//:clap",
158-
"@crate_index//:dfx-core",
159159
"@crate_index//:futures",
160160
"@crate_index//:hex",
161161
"@crate_index//:ic-agent",

rs/sns/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ base64 = { workspace = true }
1919
candid = { workspace = true }
2020
candid-utils = { path = "../../nervous_system/candid_utils" }
2121
clap = { workspace = true }
22-
dfx-core = { workspace = true }
22+
dfx-core-vendored = { path = "../dfx-core-vendored" }
2323
futures = { workspace = true }
2424
hex = { workspace = true }
2525
ic-agent = { workspace = true }

rs/sns/cli/src/utils.rs

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use anyhow::Context;
22
use anyhow::Result;
3-
use dfx_core::interface::{builder::IdentityPicker, dfx::DfxInterface};
43
use futures::{StreamExt, stream};
54
use ic_agent::Agent;
65
use ic_nervous_system_agent::sns::Sns;
@@ -10,34 +9,13 @@ use serde::{Deserialize, Serialize};
109
/// Gets an agent for a given network and identity. This is similar to the code DFX uses internally to get an agent.
1110
/// If no identity is provided, it will use the identity currently selected in the DFX CLI.
1211
pub async fn get_agent(network_name: &str, identity: Option<String>) -> Result<Agent> {
13-
let interface = dfx_interface(network_name, identity)
12+
dfx_core_vendored::get_agent(network_name, identity.clone())
1413
.await
15-
.context("Failed to get dfx interface")?;
16-
Ok(interface.agent().clone())
17-
}
18-
19-
/// Gets a dfx interface for a given network and identity. This is similar to the code DFX uses internally to build the interface.
20-
/// So this function allows the DFX SNS Extension to use the same interface as DFX itself.
21-
/// If no identity is provided, it will use the identity currently selected in the DFX CLI.
22-
pub async fn dfx_interface(network_name: &str, identity: Option<String>) -> Result<DfxInterface> {
23-
let interface_builder = {
24-
let identity = identity
25-
.clone()
26-
.map(IdentityPicker::Named)
27-
.unwrap_or(IdentityPicker::Selected);
28-
DfxInterface::builder()
29-
.with_identity(identity)
30-
.with_network_named(network_name)
31-
};
32-
let interface = interface_builder.build().await.context(format!(
33-
"Failed to build dfx interface with network `{network_name}` and identity `{identity:?}`"
34-
))?;
35-
if !interface.network_descriptor().is_ic {
36-
interface.agent().fetch_root_key().await.context(format!(
37-
"Failed to fetch root key from network `{network_name}`."
38-
))?;
39-
}
40-
Ok(interface)
14+
.with_context(|| {
15+
format!(
16+
"Failed to build agent for network `{network_name}` and identity `{identity:?}`"
17+
)
18+
})
4119
}
4220

4321
#[derive(Debug, Serialize, Deserialize)]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
load("@rules_rust//rust:defs.bzl", "rust_library")
2+
3+
package(default_visibility = [
4+
"//rs/sns/cli:__pkg__",
5+
"//rs/sns/testing:__pkg__",
6+
])
7+
8+
rust_library(
9+
name = "dfx-core-vendored",
10+
srcs = glob(["src/**/*.rs"]),
11+
crate_name = "dfx_core_vendored",
12+
version = "0.4.0",
13+
deps = [
14+
# Keep sorted.
15+
"@crate_index//:aes-gcm",
16+
"@crate_index//:argon2",
17+
"@crate_index//:candid",
18+
"@crate_index//:dialoguer",
19+
"@crate_index//:directories-next",
20+
"@crate_index//:hex",
21+
"@crate_index//:ic-agent",
22+
"@crate_index//:ic-identity-hsm",
23+
"@crate_index//:keyring",
24+
"@crate_index//:lazy_static",
25+
"@crate_index//:reqwest",
26+
"@crate_index//:serde",
27+
"@crate_index//:serde_json",
28+
"@crate_index//:slog",
29+
"@crate_index//:thiserror",
30+
"@crate_index//:url",
31+
] + select({
32+
"@platforms//os:osx": [
33+
"@crate_index//:security-framework",
34+
],
35+
"//conditions:default": [],
36+
}),
37+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[package]
2+
name = "dfx-core-vendored"
3+
# Tracks the upstream `dfx-core` release this crate was derived from, not the
4+
# workspace version, so the copied modules can be diffed against that release.
5+
version = "0.4.0"
6+
authors.workspace = true
7+
edition.workspace = true
8+
description.workspace = true
9+
documentation.workspace = true
10+
11+
[dependencies]
12+
aes-gcm = { version = "0.10.3", features = ["std"] }
13+
argon2 = { version = "0.4", features = ["std"] }
14+
candid = { workspace = true }
15+
dialoguer = { version = "0.11.0" }
16+
directories-next = { version = "2.0.0" }
17+
hex = { workspace = true }
18+
ic-agent = { workspace = true }
19+
lazy_static = { workspace = true }
20+
ic-identity-hsm = { workspace = true }
21+
keyring = { version = "3", features = [
22+
"apple-native",
23+
"windows-native",
24+
"linux-native",
25+
"sync-secret-service",
26+
"vendored",
27+
] }
28+
reqwest = { workspace = true }
29+
serde = { workspace = true }
30+
serde_json = { workspace = true }
31+
slog = { workspace = true }
32+
thiserror = { workspace = true }
33+
url = { workspace = true }
34+
35+
[target.'cfg(target_os = "macos")'.dependencies]
36+
security-framework = { version = "3" }

0 commit comments

Comments
 (0)