11use candid:: { Decode , Encode , Nat , Principal } ;
2- use ic_agent:: { Agent , AgentError } ;
2+ use ic_agent:: {
3+ Agent , AgentError ,
4+ agent:: { Subnet , SubnetType } ,
5+ } ;
36use icp_canister_interfaces:: {
47 cycles_ledger:: {
5- CYCLES_LEDGER_PRINCIPAL , CanisterSettingsArg , CreateCanisterArgs , CreateCanisterResponse ,
6- CreationArgs , SubnetSelectionArg ,
8+ CYCLES_LEDGER_PRINCIPAL , CreateCanisterArgs , CreateCanisterResponse , CreationArgs ,
9+ SubnetSelectionArg ,
710 } ,
811 cycles_minting_canister:: CYCLES_MINTING_CANISTER_PRINCIPAL ,
9- registry:: { GetSubnetForCanisterRequest , GetSubnetForCanisterResult , REGISTRY_PRINCIPAL } ,
12+ management_canister:: {
13+ CanisterSettingsArg , MgmtCreateCanisterArgs , MgmtCreateCanisterResponse ,
14+ } ,
1015} ;
1116use rand:: seq:: IndexedRandom ;
12- use snafu:: { ResultExt , Snafu } ;
17+ use snafu:: { OptionExt , ResultExt , Snafu } ;
1318use std:: sync:: Arc ;
1419use tokio:: sync:: OnceCell ;
1520
@@ -91,13 +96,35 @@ impl CreateOperation {
9196 pub async fn create (
9297 & self ,
9398 settings : & CanisterSettingsArg ,
99+ ) -> Result < Principal , CreateOperationError > {
100+ let selected_subnet = self
101+ . get_subnet ( )
102+ . await
103+ . map_err ( |e| CreateOperationError :: SubnetResolution { message : e } ) ?;
104+ let subnet_info = self
105+ . inner
106+ . agent
107+ . get_subnet_by_id ( & selected_subnet)
108+ . await
109+ . context ( GetSubnetSnafu ) ?;
110+ let cid = if let Some ( SubnetType :: Unknown ( kind) ) = subnet_info. subnet_type ( )
111+ && kind == "cloud_engine"
112+ {
113+ self . create_mgmt ( settings, & subnet_info) . await ?
114+ } else {
115+ self . create_ledger ( settings, selected_subnet) . await ?
116+ } ;
117+ Ok ( cid)
118+ }
119+
120+ async fn create_ledger (
121+ & self ,
122+ settings : & CanisterSettingsArg ,
123+ selected_subnet : Principal ,
94124 ) -> Result < Principal , CreateOperationError > {
95125 let creation_args = CreationArgs {
96126 subnet_selection : Some ( SubnetSelectionArg :: Subnet {
97- subnet : self
98- . get_subnet ( )
99- . await
100- . map_err ( |e| CreateOperationError :: SubnetResolution { message : e } ) ?,
127+ subnet : selected_subnet,
101128 } ) ,
102129 settings : Some ( settings. clone ( ) ) ,
103130 } ;
@@ -128,10 +155,40 @@ impl CreateOperation {
128155 . fail ( ) ;
129156 }
130157 } ;
131-
132158 Ok ( cid)
133159 }
134160
161+ async fn create_mgmt (
162+ & self ,
163+ settings : & CanisterSettingsArg ,
164+ selected_subnet : & Subnet ,
165+ ) -> Result < Principal , CreateOperationError > {
166+ let arg = MgmtCreateCanisterArgs {
167+ settings : Some ( settings. clone ( ) ) ,
168+ sender_canister_version : None ,
169+ } ;
170+
171+ // Call management canister create_canister
172+ let resp = self
173+ . inner
174+ . agent
175+ . update ( & Principal :: management_canister ( ) , "create_canister" )
176+ . with_arg ( Encode ! ( & arg) . context ( CandidEncodeSnafu ) ?)
177+ . with_effective_canister_id (
178+ * selected_subnet
179+ . iter_canister_ranges ( )
180+ . next ( )
181+ . context ( CreateCanisterSnafu {
182+ message : "subnet did not contain canister ranges" ,
183+ } ) ?
184+ . start ( ) ,
185+ )
186+ . await
187+ . context ( AgentSnafu ) ?;
188+ let resp = Decode ! ( & resp, MgmtCreateCanisterResponse ) . context ( CandidDecodeSnafu ) ?;
189+ Ok ( resp. canister_id )
190+ }
191+
135192 /// 1. If a subnet is explicitly provided, use it
136193 /// 2. If no canisters exist yet, pick a random available subnet
137194 /// 3. If canisters exist, use the same subnet as the first existing canister
@@ -148,10 +205,13 @@ impl CreateOperation {
148205 }
149206
150207 if let Some ( canister) = self . inner . existing_canisters . first ( ) {
151- let subnet = get_canister_subnet ( & self . inner . agent , * canister)
208+ let subnet = & self
209+ . inner
210+ . agent
211+ . get_subnet_by_canister ( canister)
152212 . await
153213 . map_err ( |e| e. to_string ( ) ) ?;
154- Ok ( subnet)
214+ Ok ( subnet. id ( ) )
155215 } else {
156216 // If no canisters exist, pick a random available subnet
157217 let subnets = get_available_subnets ( & self . inner . agent )
@@ -170,31 +230,6 @@ impl CreateOperation {
170230 }
171231}
172232
173- async fn get_canister_subnet (
174- agent : & Agent ,
175- canister : Principal ,
176- ) -> Result < Principal , CreateOperationError > {
177- let args = & GetSubnetForCanisterRequest {
178- principal : Some ( canister) ,
179- } ;
180-
181- let bs = agent
182- . query ( & REGISTRY_PRINCIPAL , "get_subnet_for_canister" )
183- . with_arg ( Encode ! ( args) . context ( CandidEncodeSnafu ) ?)
184- . call ( )
185- . await
186- . context ( GetSubnetSnafu ) ?;
187-
188- let resp = Decode ! ( & bs, GetSubnetForCanisterResult ) . context ( CandidDecodeSnafu ) ?;
189-
190- let out = resp
191- . map_err ( |err| CreateOperationError :: Registry { message : err } ) ?
192- . subnet_id
193- . ok_or ( CreateOperationError :: MissingSubnetId ) ?;
194-
195- Ok ( out)
196- }
197-
198233async fn get_available_subnets ( agent : & Agent ) -> Result < Vec < Principal > , CreateOperationError > {
199234 let bs = agent
200235 . query ( & CYCLES_MINTING_CANISTER_PRINCIPAL , "get_default_subnets" )
0 commit comments