Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cd9c9cd
feat: managed compose network with bitcoin
marc0olo Feb 6, 2026
80652a6
feat: add bitcoind/dogecoind-addr config and unify launcher settings …
marc0olo Feb 6, 2026
b84539e
docs: document unified launcher settings and args overlap for Docker …
marc0olo Feb 6, 2026
d233bfd
fix: formatting
marc0olo Feb 6, 2026
eeb2cc2
refactor: remove launcher settings from Docker image variant
marc0olo Feb 9, 2026
61a22fe
chore: remove redundant tests and comments
marc0olo Feb 9, 2026
f659d13
Merge branch 'main' into marc0olo/bitcoin-support-and-docs
marc0olo Feb 9, 2026
038ef70
Merge branch 'main' into marc0olo/bitcoin-support-and-docs
lwshang Feb 10, 2026
3d3f1d3
Merge remote-tracking branch 'origin/main' into marc0olo/bitcoin-supp…
marc0olo Feb 12, 2026
b57b4df
Merge remote-tracking branch 'origin/main' into marc0olo/bitcoin-supp…
marc0olo Feb 16, 2026
6a03025
docs: add changelog entry for bitcoind/dogecoind network support
marc0olo Feb 16, 2026
9a28fd8
Merge branch 'main' into marc0olo/bitcoin-support-and-docs
marc0olo Feb 17, 2026
0e2d852
Merge branch 'main' into marc0olo/bitcoin-support-and-docs
marc0olo Feb 19, 2026
f5cac3a
Merge branch 'main' into marc0olo/bitcoin-support-and-docs
raymondk Feb 19, 2026
db5646d
Merge remote-tracking branch 'origin/main' into marc0olo/bitcoin-supp…
marc0olo Feb 23, 2026
37d6185
fix: add missing field to Gateway constructors after merge
marc0olo Feb 23, 2026
9906188
Merge remote-tracking branch 'origin/main' into marc0olo/bitcoin-supp…
marc0olo Feb 24, 2026
1b48913
Merge remote-tracking branch 'origin/main' into marc0olo/bitcoin-supp…
marc0olo Feb 25, 2026
c2a5320
fix: add `0.0.0.0` to docker address translation and expose `extra-ho…
marc0olo Feb 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

* feat: Add `bitcoind-addr` and `dogecoind-addr` options for managed networks to connect to Bitcoin and Dogecoin nodes
* feat: Init/call arg files now support raw binary without conversion to hex
* feat!: Remove argument type inference in init/call args in commands and manifest. Args are always assumed Candid, new parameters allow specifying other formats like hex, and alternate parameters are used to specify loading from a file.
* feat: Network gateway now supports a `domains` key
Expand Down
8 changes: 8 additions & 0 deletions crates/icp/src/context/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,8 @@ async fn test_get_agent_defaults_inside_project_with_default_local() {
ii: false,
nns: false,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})),
},
Expand Down Expand Up @@ -677,6 +679,8 @@ async fn test_get_agent_defaults_with_overridden_local_network() {
ii: false,
nns: false,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})),
},
Expand Down Expand Up @@ -747,6 +751,8 @@ async fn test_get_agent_defaults_with_overridden_local_environment() {
ii: false,
nns: false,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})),
},
Expand All @@ -767,6 +773,8 @@ async fn test_get_agent_defaults_with_overridden_local_environment() {
ii: false,
nns: false,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})),
},
Expand Down
4 changes: 4 additions & 0 deletions crates/icp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ impl MockProjectLoader {
ii: false,
nns: false,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})),
},
Expand All @@ -446,6 +448,8 @@ impl MockProjectLoader {
ii: false,
nns: false,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})),
},
Expand Down
103 changes: 103 additions & 0 deletions crates/icp/src/manifest/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub enum ManagedMode {
status_dir: Option<String>,
/// Bind mounts to add to the container in the format relative_host_path:container_path[:options]
mounts: Option<Vec<String>>,
/// Extra hosts entries for Docker networking (e.g. "host.docker.internal:host-gateway")
extra_hosts: Option<Vec<String>>,
},
Launcher {
/// HTTP gateway configuration
Expand All @@ -68,6 +70,10 @@ pub enum ManagedMode {
nns: Option<bool>,
/// Configure the list of subnets (one application subnet by default)
subnets: Option<Vec<SubnetKind>>,
/// Bitcoin P2P node addresses to connect to (e.g. "127.0.0.1:18444")
bitcoind_addr: Option<Vec<String>>,
/// Dogecoin P2P node addresses to connect to
dogecoind_addr: Option<Vec<String>>,
/// The version of icp-cli-network-launcher to use. Defaults to the latest released version. Launcher versions correspond to published PocketIC or IC-OS releases.
version: Option<String>,
},
Expand All @@ -81,6 +87,8 @@ impl Default for ManagedMode {
ii: None,
nns: None,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
}
}
Expand Down Expand Up @@ -264,6 +272,8 @@ mod tests {
ii: None,
nns: None,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})
})
Expand Down Expand Up @@ -293,6 +303,8 @@ mod tests {
ii: None,
nns: None,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})
})
Expand Down Expand Up @@ -323,6 +335,97 @@ mod tests {
ii: None,
nns: None,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
})
})
},
);
}

#[test]
fn managed_network_with_dogecoind_addr() {
assert_eq!(
validate_network_yaml(indoc! {r#"
name: my-network
mode: managed
dogecoind-addr:
- "127.0.0.1:22556"
"#}),
NetworkManifest {
name: "my-network".to_string(),
configuration: Mode::Managed(Managed {
mode: Box::new(ManagedMode::Launcher {
gateway: None,
artificial_delay_ms: None,
ii: None,
nns: None,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: Some(vec!["127.0.0.1:22556".to_string()]),
version: None,
})
})
},
);
}

#[test]
fn managed_docker_network_with_extra_hosts() {
assert_eq!(
validate_network_yaml(indoc! {r#"
name: my-network
mode: managed
image: ghcr.io/dfinity/icp-cli-network-launcher
port-mapping:
- "8000:4943"
extra-hosts:
- "host.docker.internal:host-gateway"
"#}),
NetworkManifest {
name: "my-network".to_string(),
configuration: Mode::Managed(Managed {
mode: Box::new(ManagedMode::Image {
image: "ghcr.io/dfinity/icp-cli-network-launcher".to_string(),
port_mapping: vec!["8000:4943".to_string()],
rm_on_exit: None,
args: None,
entrypoint: None,
environment: None,
volumes: None,
platform: None,
user: None,
shm_size: None,
status_dir: None,
mounts: None,
extra_hosts: Some(vec!["host.docker.internal:host-gateway".to_string()]),
})
})
},
);
}

#[test]
fn managed_network_with_bitcoind_addr() {
assert_eq!(
validate_network_yaml(indoc! {r#"
name: my-network
mode: managed
bitcoind-addr:
- "127.0.0.1:18444"
"#}),
NetworkManifest {
name: "my-network".to_string(),
configuration: Mode::Managed(Managed {
mode: Box::new(ManagedMode::Launcher {
gateway: None,
artificial_delay_ms: None,
ii: None,
nns: None,
subnets: None,
bitcoind_addr: Some(vec!["127.0.0.1:18444".to_string()]),
dogecoind_addr: None,
version: None,
})
})
Expand Down
2 changes: 2 additions & 0 deletions crates/icp/src/manifest/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ mod tests {
ii: None,
nns: None,
subnets: None,
bitcoind_addr: None,
dogecoind_addr: None,
version: None,
}),
}),
Expand Down
55 changes: 55 additions & 0 deletions crates/icp/src/network/managed/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub struct ManagedImageOptions {
pub status_dir: String,
/// Parsed mounts (excluding the status directory mount, which is added at runtime).
pub mounts: Vec<Mount>,
/// Extra hosts entries for Docker networking (e.g. "host.docker.internal:host-gateway").
pub extra_hosts: Vec<String>,
}

impl ManagedImageOptions {
Expand Down Expand Up @@ -160,6 +162,7 @@ impl TryFrom<&ManagedImageConfig> for ManagedImageOptions {
shm_size: config.shm_size,
status_dir: config.status_dir.clone(),
mounts,
extra_hosts: config.extra_hosts.clone(),
})
}
}
Expand Down Expand Up @@ -187,6 +190,52 @@ pub enum ManagedImageConversionError {
WslPathConvert { source: WslPathConversionError },
}

/// Translates a host:port address for use inside a Docker container.
/// Replaces `127.0.0.1`, `0.0.0.0`, `localhost`, and `::1` with `host.docker.internal`
/// so the container can reach services running on the host machine.
pub(super) fn translate_addr_for_docker(addr: &str) -> String {
if let Some((host, port)) = addr.rsplit_once(':') {
let translated = match host {
"127.0.0.1" | "0.0.0.0" | "localhost" | "::1" => "host.docker.internal",
_ => host,
};
format!("{translated}:{port}")
} else {
addr.to_string()
}
}

/// Returns extra_hosts entries needed for Docker to resolve `host.docker.internal`.
/// On Linux, Docker Engine does not provide `host.docker.internal` by default,
/// so we add `host.docker.internal:host-gateway` when any addresses reference localhost.
pub(super) fn docker_extra_hosts_for_addrs(addrs: &[String]) -> Vec<String> {
let needs_host_gateway = addrs.iter().any(|addr| {
addr.rsplit_once(':')
.map(|(host, _)| matches!(host, "127.0.0.1" | "0.0.0.0" | "localhost" | "::1"))
.unwrap_or(false)
});
if needs_host_gateway {
vec!["host.docker.internal:host-gateway".to_string()]
} else {
vec![]
}
}

/// Translates localhost addresses in `--bitcoind-addr=` and `--dogecoind-addr=` flags
/// for use inside a Docker container.
pub(super) fn translate_launcher_args_for_docker(args: Vec<String>) -> Vec<String> {
args.into_iter()
.map(|arg| {
for prefix in ["--bitcoind-addr=", "--dogecoind-addr="] {
if let Some(addr) = arg.strip_prefix(prefix) {
return format!("{prefix}{}", translate_addr_for_docker(addr));
}
}
arg
})
.collect()
}

pub async fn spawn_docker_launcher(
options: &ManagedImageOptions,
) -> Result<
Expand All @@ -211,6 +260,7 @@ pub async fn spawn_docker_launcher(
shm_size,
status_dir,
mounts,
extra_hosts,
} = options;

// Create status tmpdir and convert path for WSL2 if needed
Expand Down Expand Up @@ -320,6 +370,11 @@ pub async fn spawn_docker_launcher(
mounts: Some(all_mounts),
binds: Some(volumes.clone()),
shm_size: *shm_size,
extra_hosts: if extra_hosts.is_empty() {
None
} else {
Some(extra_hosts.clone())
},
..<_>::default()
}),
..<_>::default()
Expand Down
12 changes: 12 additions & 0 deletions crates/icp/src/network/managed/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ pub fn launcher_settings_flags(config: &ManagedLauncherConfig) -> Vec<String> {
ii,
nns,
subnets,
bitcoind_addr,
dogecoind_addr,
} = config;
let mut flags = vec![];
if *ii {
Expand All @@ -191,6 +193,16 @@ pub fn launcher_settings_flags(config: &ManagedLauncherConfig) -> Vec<String> {
flags.push(format!("--subnet={subnet}"));
}
}
if let Some(addrs) = &bitcoind_addr {
for addr in addrs {
flags.push(format!("--bitcoind-addr={addr}"));
}
}
if let Some(addrs) = &dogecoind_addr {
for addr in addrs {
flags.push(format!("--dogecoind-addr={addr}"));
}
}
for domain in &gateway.domains {
flags.push(format!("--domain={domain}"));
}
Expand Down
Loading
Loading