@@ -48,12 +48,14 @@ struct Cli {
4848 /// List of subnets to create. `--subnet=nns` is always implied. Defaults to `--subnet=application`.
4949 #[ arg( long, value_enum, action = ArgAction :: Append ) ]
5050 subnet : Vec < SubnetKind > ,
51- /// Addresses of bitcoind nodes to connect to. Implies `--subnet=bitcoin`.
51+ /// Addresses of bitcoind nodes to connect to (e.g. 127.0.0.1:18444 or bitcoind:18444).
52+ /// Implies `--subnet=bitcoin`.
5253 #[ arg( long, action = ArgAction :: Append ) ]
53- bitcoind_addr : Vec < SocketAddr > ,
54- /// Addresses of dogecoind nodes to connect to. Implies `--subnet=bitcoin`.
54+ bitcoind_addr : Vec < String > ,
55+ /// Addresses of dogecoind nodes to connect to (e.g. 127.0.0.1:22556 or dogecoind:22556).
56+ /// Implies `--subnet=bitcoin`.
5557 #[ arg( long, action = ArgAction :: Append ) ]
56- dogecoind_addr : Vec < SocketAddr > ,
58+ dogecoind_addr : Vec < String > ,
5759 /// Installs the Internet Identity canister.
5860 #[ arg( long) ]
5961 ii : bool ,
@@ -234,14 +236,19 @@ async fn main() -> anyhow::Result<()> {
234236 }
235237 }
236238 pic = pic. with_nns_subnet ( ) ;
239+ // --bitcoind-addr and --dogecoind-addr imply --subnet=bitcoin
240+ if !bitcoind_addr. is_empty ( ) || !dogecoind_addr. is_empty ( ) {
241+ pic = pic. with_bitcoin_subnet ( ) ;
242+ }
237243 let mut features = IcpFeatures {
238244 cycles_minting : Some ( IcpFeaturesConfig :: DefaultConfig ) ,
239245 icp_token : Some ( IcpFeaturesConfig :: DefaultConfig ) ,
240246 cycles_token : Some ( IcpFeaturesConfig :: DefaultConfig ) ,
241247 registry : Some ( IcpFeaturesConfig :: DefaultConfig ) ,
242248 ..<_ >:: default ( )
243249 } ;
244- if nns || ii {
250+ // II subnet provides threshold signature keys (tECDSA) needed for Bitcoin/Dogecoin signing
251+ if nns || ii || !bitcoind_addr. is_empty ( ) || !dogecoind_addr. is_empty ( ) {
245252 pic = pic. with_ii_subnet ( ) ;
246253 features. ii = Some ( IcpFeaturesConfig :: DefaultConfig ) ;
247254 }
@@ -252,12 +259,24 @@ async fn main() -> anyhow::Result<()> {
252259 features. sns = Some ( IcpFeaturesConfig :: DefaultConfig ) ;
253260 features. canister_migration = Some ( IcpFeaturesConfig :: DefaultConfig ) ;
254261 }
262+ if !bitcoind_addr. is_empty ( ) {
263+ features. bitcoin = Some ( IcpFeaturesConfig :: DefaultConfig ) ;
264+ }
265+ if !dogecoind_addr. is_empty ( ) {
266+ features. dogecoin = Some ( IcpFeaturesConfig :: DefaultConfig ) ;
267+ }
255268 pic = pic. with_icp_features ( features) ;
256269 if !bitcoind_addr. is_empty ( ) {
257- pic = pic. with_bitcoind_addrs ( bitcoind_addr) ;
270+ let addrs = resolve_addrs ( & bitcoind_addr)
271+ . await
272+ . context ( "failed to resolve --bitcoind-addr" ) ?;
273+ pic = pic. with_bitcoind_addrs ( addrs) ;
258274 }
259275 if !dogecoind_addr. is_empty ( ) {
260- pic = pic. with_dogecoind_addrs ( dogecoind_addr) ;
276+ let addrs = resolve_addrs ( & dogecoind_addr)
277+ . await
278+ . context ( "failed to resolve --dogecoind-addr" ) ?;
279+ pic = pic. with_dogecoind_addrs ( addrs) ;
261280 }
262281 let pic = pic. build_async ( ) . await ;
263282 // pocket-ic crate doesn't currently support setting artificial delay via builder
@@ -336,6 +355,20 @@ async fn main() -> anyhow::Result<()> {
336355 Ok ( ( ) )
337356}
338357
358+ /// Resolves a list of address strings (hostname:port or ip:port) to socket addresses.
359+ async fn resolve_addrs ( addrs : & [ String ] ) -> anyhow:: Result < Vec < SocketAddr > > {
360+ let mut resolved = Vec :: with_capacity ( addrs. len ( ) ) ;
361+ for addr in addrs {
362+ let socket_addr = tokio:: net:: lookup_host ( addr)
363+ . await
364+ . with_context ( || format ! ( "failed to resolve address '{addr}'" ) ) ?
365+ . next ( )
366+ . with_context ( || format ! ( "no addresses found for '{addr}'" ) ) ?;
367+ resolved. push ( socket_addr) ;
368+ }
369+ Ok ( resolved)
370+ }
371+
339372fn get_errorchecked_args ( ) -> Cli {
340373 let mut cli = Cli :: parse ( ) ;
341374 let mut command = Cli :: command ( ) ;
0 commit comments