Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: root management canister mod with new bindings #538

Merged
merged 27 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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