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!: config canister endpoint decoding quota #465

Merged
merged 9 commits into from
Mar 1, 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
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = ["src/*", "library/*", "e2e-tests"]
resolver = "2"

[workspace.package]
authors = ["DFINITY Stiftung <sdk@dfinity.org>"]
Expand All @@ -23,8 +24,8 @@ ic0 = { path = "src/ic0", version = "0.21.1" }
ic-cdk = { path = "src/ic-cdk", version = "0.12.1" }
ic-cdk-timers = { path = "src/ic-cdk-timers", version = "0.6.0" }

candid = "0.10"
candid_parser = "0.1.0"
candid = "0.10.4"
candid_parser = "0.1.4"
futures = "0.3"
hex = "0.4"
quote = "1"
Expand All @@ -33,3 +34,4 @@ serde_bytes = "0.11"
sha2 = "0.10"
slotmap = "1"
syn = "1"

2 changes: 1 addition & 1 deletion examples/counter/src/counter_rs/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn init() {
OWNER.with(|owner| owner.set(ic_cdk::api::caller()));
}

#[update]
#[update(debug = true, decoding_quota = 50, skipping_quota = 0)]
fn inc() {
ic_cdk::println!("{:?}", OWNER.with(|owner| owner.get()));
COUNTER.with(|counter| *counter.borrow_mut() += 1u64);
Expand Down
91 changes: 82 additions & 9 deletions src/ic-cdk-macros/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ struct ExportAttributes {
pub composite: bool,
#[serde(default)]
pub hidden: bool,
#[serde(default)]
pub decoding_quota: Option<usize>,
#[serde(default = "default_skipping_quota")]
pub skipping_quota: Option<usize>,
#[serde(default)]
pub debug: bool,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -56,6 +62,10 @@ impl std::fmt::Display for MethodType {
}
}

fn default_skipping_quota() -> Option<usize> {
Some(10_000)
}

fn get_args(method: MethodType, signature: &Signature) -> Result<Vec<(Ident, Box<Type>)>, Error> {
// We only need the tuple of arguments, not their types. Magic of type inference.
let mut args = vec![];
Expand Down Expand Up @@ -176,7 +186,29 @@ fn dfn_macro(
let arg_decode = if method.is_lifecycle() && arg_count == 0 {
quote! {}
} else {
quote! { let ( #( #arg_tuple, )* ) = ic_cdk::api::call::arg_data(); }
let decoding_quota = if let Some(n) = attrs.decoding_quota {
quote! { Some(#n) }
} else {
quote! { None }
};
let skipping_quota = if let Some(n) = attrs.skipping_quota {
quote! { Some(#n) }
} else {
quote! { None }
};
let debug = if attrs.debug {
quote! { true }
} else {
quote! { false }
};
let config = quote! {
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: #decoding_quota,
skipping_quota: #skipping_quota,
debug: #debug,
}
};
quote! { let ( #( #arg_tuple, )* ) = ic_cdk::api::call::arg_data(#config); }
};

let guard = if let Some(guard_name) = attrs.guard {
Expand Down Expand Up @@ -298,7 +330,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let () = ic_cdk::api::call::arg_data();
let () = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query();
ic_cdk::api::call::reply(())
});
Expand All @@ -314,7 +352,6 @@ mod test {
_ => panic!("not a function"),
};
}

#[test]
fn ic_query_return_one_value() {
let generated = ic_query(
Expand All @@ -335,7 +372,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let () = ic_cdk::api::call::arg_data();
let () = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query();
ic_cdk::api::call::reply((result,))
});
Expand Down Expand Up @@ -372,7 +415,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let () = ic_cdk::api::call::arg_data();
let () = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query();
ic_cdk::api::call::reply(result)
});
Expand Down Expand Up @@ -409,7 +458,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let (a, ) = ic_cdk::api::call::arg_data();
let (a, ) = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query(a);
ic_cdk::api::call::reply(())
});
Expand Down Expand Up @@ -446,7 +501,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let (a, b, ) = ic_cdk::api::call::arg_data();
let (a, b, ) = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query(a, b);
ic_cdk::api::call::reply(())
});
Expand Down Expand Up @@ -483,7 +544,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let (a, b, ) = ic_cdk::api::call::arg_data();
let (a, b, ) = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query(a, b);
ic_cdk::api::call::reply((result,))
});
Expand Down Expand Up @@ -520,7 +587,13 @@ mod test {
fn #fn_name() {
ic_cdk::setup();
ic_cdk::spawn(async {
let () = ic_cdk::api::call::arg_data();
let () = ic_cdk::api::call::arg_data(
ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: None,
skipping_quota: Some(10000usize),
debug: false,
}
);
let result = query();
ic_cdk::api::call::reply(())
});
Expand Down
7 changes: 6 additions & 1 deletion src/ic-cdk-timers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ extern "C" fn timer_executor() {
if ic_cdk::api::caller() != ic_cdk::api::id() {
ic_cdk::trap("This function is internal to ic-cdk and should not be called externally.");
}
let (task_id,) = ic_cdk::api::call::arg_data();
let config = ic_cdk::api::call::ArgDecoderConfig {
decoding_quota: Some(10_000),
skipping_quota: Some(100),
mraszyk marked this conversation as resolved.
Show resolved Hide resolved
debug: false,
};
let (task_id,) = ic_cdk::api::call::arg_data(config);
let task_id = TimerId(KeyData::from_ffi(task_id));
// We can't be holding `TASKS` when we call the function, because it may want to schedule more tasks.
// Instead, we swap the task out in order to call it, and then either swap it back in, or remove it.
Expand Down
10 changes: 10 additions & 0 deletions src/ic-cdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add `is_recovering_from_trap` function for implementing trap cleanup logic
- Allow setting decoding quota for canister endpoints and inter-canister calls.
* When defining canister endpoints, we add the following attributes: `#[update(decoding_quota = 10000, skipping_quota = 100, debug = true)]`
- `skipping_quota` limits the amount of work allowed for skipping unneeded data on the wire. If this attributes is not present, we set a default quota of `10_000`. This affects ALL existing canisters, and is mainly used to improve canister throughput. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_skipping_quota) to understand the skipping cost.
- `decoding_quota` limits the total amount of work the deserializer can perform. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_decoding_quota) to understand the cost model.
- `debug = true` prints the instruction count and the decoding/skipping cost to the replica log, after a successful deserialization. The decoding/skipping cost is logged only when you have already set a quota in the attributes. The debug mode is useful to determine the right quotas above. Developers can send a few large payloads to the debugging endpoint and know the actual decoding cost.
* When making inter-canister calls, we have a new function `call_with_config` to config the same decoding quotas described above. It's strongly recommended to use `call_with_config` when calling third-party untrusted canisters.
chenyan-dfinity marked this conversation as resolved.
Show resolved Hide resolved

### Changed

- `ic_cdk::api::call::arg_data` takes `ArgDecoderConfig` as argument.

## [0.12.1] - 2024-01-12

Expand Down
Loading