Skip to content

Commit

Permalink
feat: root management canister mod with new bindings (#538)
Browse files Browse the repository at this point in the history
  • Loading branch information
lwshang authored Dec 18, 2024
1 parent d98a6ff commit 0ab2864
Show file tree
Hide file tree
Showing 15 changed files with 2,378 additions and 302 deletions.
2 changes: 1 addition & 1 deletion e2e-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ publish = false
candid.workspace = true
cargo_metadata = "0.18"
escargot = { version = "0.5.7", features = ["print"] }
ic-cdk.workspace = true
ic-cdk = { workspace = true, features = ["transform-closure"] }
ic-cdk-timers.workspace = true
lazy_static = "1.4.0"
serde_bytes.workspace = true
Expand Down
52 changes: 23 additions & 29 deletions e2e-tests/src/bin/canister_info.rs
Original file line number Diff line number Diff line change
@@ -1,82 +1,76 @@
use candid::Principal;
use ic_cdk::api::management_canister::main::{
use ic_cdk::management_canister::{
canister_info, create_canister, install_code, uninstall_code, update_settings,
CanisterIdRecord, CanisterInfoRequest, CanisterInfoResponse,
CanisterInfoArgs, CanisterInfoResult,
CanisterInstallMode::{Install, Reinstall, Upgrade},
CanisterSettings, CreateCanisterArgument, InstallCodeArgument, UpdateSettingsArgument,
CanisterSettings, CreateCanisterArgs, InstallCodeArgs, UninstallCodeArgs, UpdateSettingsArgs,
};

#[ic_cdk::update]
async fn info(canister_id: Principal) -> CanisterInfoResponse {
let request = CanisterInfoRequest {
async fn info(canister_id: Principal) -> CanisterInfoResult {
let request = CanisterInfoArgs {
canister_id,
num_requested_changes: Some(20),
};
canister_info(request).await.unwrap().0
canister_info(request).await.unwrap()
}

#[ic_cdk::update]
async fn canister_lifecycle() -> Principal {
let canister_id = create_canister(CreateCanisterArgument { settings: None }, 1_000_000_000_000)
let canister_id = create_canister(CreateCanisterArgs { settings: None }, 1_000_000_000_000)
.await
.unwrap()
.0;
install_code(InstallCodeArgument {
.canister_id;
install_code(InstallCodeArgs {
mode: Install,
arg: vec![],
wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00],
canister_id: canister_id.canister_id,
})
.await
.unwrap();
uninstall_code(CanisterIdRecord {
canister_id: canister_id.canister_id,
canister_id,
})
.await
.unwrap();
install_code(InstallCodeArgument {
uninstall_code(UninstallCodeArgs { canister_id })
.await
.unwrap();
install_code(InstallCodeArgs {
mode: Install,
arg: vec![],
wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00],
canister_id: canister_id.canister_id,
canister_id,
})
.await
.unwrap();
install_code(InstallCodeArgument {
install_code(InstallCodeArgs {
mode: Reinstall,
arg: vec![],
wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00],
canister_id: canister_id.canister_id,
canister_id,
})
.await
.unwrap();
install_code(InstallCodeArgument {
install_code(InstallCodeArgs {
mode: Upgrade(None),
arg: vec![],
wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00],
canister_id: canister_id.canister_id,
canister_id,
})
.await
.unwrap();
update_settings(UpdateSettingsArgument {
update_settings(UpdateSettingsArgs {
settings: CanisterSettings {
controllers: Some(vec![
ic_cdk::id(),
canister_id.canister_id,
Principal::anonymous(),
]),
controllers: Some(vec![ic_cdk::id(), canister_id, Principal::anonymous()]),
compute_allocation: None,
memory_allocation: None,
freezing_threshold: None,
reserved_cycles_limit: None,
log_visibility: None,
wasm_memory_limit: None,
},
canister_id: canister_id.canister_id,
canister_id,
})
.await
.unwrap();
canister_id.canister_id
canister_id
}

fn main() {}
21 changes: 10 additions & 11 deletions e2e-tests/src/bin/chunk.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,41 @@
use candid::Principal;
use ic_cdk::api::management_canister::main::{
use ic_cdk::management_canister::{
clear_chunk_store, create_canister, install_chunked_code, stored_chunks, upload_chunk,
CanisterInstallMode, ChunkHash, ClearChunkStoreArgument, CreateCanisterArgument,
InstallChunkedCodeArgument, StoredChunksArgument, UploadChunkArgument,
CanisterInstallMode, ChunkHash, ClearChunkStoreArgs, CreateCanisterArgs,
InstallChunkedCodeArgs, StoredChunksArgs, UploadChunkArgs,
};
use ic_cdk::update;

#[update]
async fn call_create_canister() -> Principal {
let arg = CreateCanisterArgument::default();
let arg = CreateCanisterArgs::default();

create_canister(arg, 1_000_000_000_000u128)
.await
.unwrap()
.0
.canister_id
}

#[update]
async fn call_upload_chunk(canister_id: Principal, chunk: Vec<u8>) -> Vec<u8> {
let arg = UploadChunkArgument {
let arg = UploadChunkArgs {
canister_id,
chunk: chunk.to_vec(),
};

upload_chunk(arg).await.unwrap().0.hash
upload_chunk(arg).await.unwrap().hash
}

#[update]
async fn call_stored_chunks(canister_id: Principal) -> Vec<Vec<u8>> {
let arg = StoredChunksArgument { canister_id };
let hashes = stored_chunks(arg).await.unwrap().0;
let arg = StoredChunksArgs { canister_id };
let hashes = stored_chunks(arg).await.unwrap();
hashes.into_iter().map(|v| v.hash).collect()
}

#[update]
async fn call_clear_chunk_store(canister_id: Principal) {
let arg = ClearChunkStoreArgument { canister_id };
let arg = ClearChunkStoreArgs { canister_id };
clear_chunk_store(arg).await.unwrap();
}

Expand All @@ -50,7 +49,7 @@ async fn call_install_chunked_code(
.iter()
.map(|v| ChunkHash { hash: v.clone() })
.collect();
let arg = InstallChunkedCodeArgument {
let arg = InstallChunkedCodeArgs {
mode: CanisterInstallMode::Install,
target_canister: canister_id,
store_canister: None,
Expand Down
152 changes: 152 additions & 0 deletions e2e-tests/src/bin/http_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use ic_cdk::management_canister::{
http_request, http_request_with_closure, HttpHeader, HttpMethod, HttpRequestArgs,
HttpRequestResult, TransformArgs, TransformContext,
};
use ic_cdk::{query, update};

/// The formula to calculate the cost of a request.
fn cycles_cost(args: &HttpRequestArgs) -> u128 {
const N: u128 = 13;
let request_bytes_len = (args.url.len()
+ args
.headers
.iter()
.map(|h| h.name.len() + h.value.len())
.sum::<usize>()
+ args.body.as_ref().map(|b| b.len()).unwrap_or(0)
+ args
.transform
.as_ref()
.map(|t| t.context.len() + t.function.0.method.len())
.unwrap_or(0)) as u128;
let response_bytes_len = args.max_response_bytes.unwrap_or(2_000_000) as u128;
(3_000_000 + 60_000 * N) * N + 400 * N * request_bytes_len + 800 * N * response_bytes_len
}

/// All fields are Some except transform.
#[update]
async fn get_without_transform() {
let args = HttpRequestArgs {
url: "https://example.com".to_string(),
method: HttpMethod::GET,
headers: vec![HttpHeader {
name: "request_header_name".to_string(),
value: "request_header_value".to_string(),
}],
body: Some(vec![1]),
max_response_bytes: Some(100_000),
transform: None,
};
let cycles = cycles_cost(&args);
let res = http_request(args, cycles).await.unwrap();
assert_eq!(res.status, 200u32);
assert_eq!(
res.headers,
vec![HttpHeader {
name: "response_header_name".to_string(),
value: "response_header_value".to_string(),
}]
);
assert_eq!(res.body, vec![42]);
}

/// Method is POST.
#[update]
async fn post() {
let args = HttpRequestArgs {
url: "https://example.com".to_string(),
method: HttpMethod::POST,
..Default::default()
};
let cycles = cycles_cost(&args);
http_request(args, cycles).await.unwrap();
}

/// Method is HEAD.
#[update]
async fn head() {
let args = HttpRequestArgs {
url: "https://example.com".to_string(),
method: HttpMethod::HEAD,
..Default::default()
};
let cycles = cycles_cost(&args);
http_request(args, cycles).await.unwrap();
}

/// The standard way to define a transform function.
///
/// It is a query endpoint that takes a TransformArgs and returns an HttpRequestResult.
#[query]
fn transform(args: TransformArgs) -> HttpRequestResult {
let mut body = args.response.body;
body.push(args.context[0]);
HttpRequestResult {
status: args.response.status,
headers: args.response.headers,
body,
}
}

/// Set the transform field with the name of the transform query endpoint.
#[update]
async fn get_with_transform() {
let args = HttpRequestArgs {
url: "https://example.com".to_string(),
method: HttpMethod::GET,
transform: Some(TransformContext::from_name(
"transform".to_string(),
vec![42],
)),
..Default::default()
};
let cycles = cycles_cost(&args);
let res = http_request(args, cycles).await.unwrap();
assert_eq!(res.status, 200u32);
assert_eq!(
res.headers,
vec![HttpHeader {
name: "response_header_name".to_string(),
value: "response_header_value".to_string(),
}]
);
// The first 42 is from the response body, the second 42 is from the transform context.
assert_eq!(res.body, vec![42, 42]);
}

/// Set the transform field with a closure.
#[update]
async fn get_with_transform_closure() {
let transform = |args: HttpRequestResult| {
let mut body = args.body;
body.push(42);
HttpRequestResult {
status: args.status,
headers: args.headers,
body,
}
};
let args = HttpRequestArgs {
url: "https://example.com".to_string(),
method: HttpMethod::GET,
transform: None,
..Default::default()
};
// The transform closure takes 40 bytes.
let cycles = cycles_cost(&args) + 40 * 400 * 13;
let res = http_request_with_closure(args.clone(), cycles, transform)
.await
.unwrap();
assert_eq!(res.status, 200u32);
assert_eq!(
res.headers,
vec![HttpHeader {
name: "response_header_name".to_string(),
value: "response_header_value".to_string(),
}]
);
// The first 42 is from the response body, the second 42 is from the transform closure.
assert_eq!(res.body, vec![42, 42]);
}

fn main() {}
Loading

0 comments on commit 0ab2864

Please sign in to comment.