-
Notifications
You must be signed in to change notification settings - Fork 510
Adapt the inter-canister call recommendations to anonymous refunds feature #6144
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
Draft
oggy-dfin
wants to merge
3
commits into
master
Choose a base branch
from
oggy/bounded-wait-anonymous-refunds
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,10 @@ This example will demonstrate how to call an example counter canister, which pro | |||||
| 3. Setting the counter to an arbitrary value. | ||||||
| 4. Setting the counter to an arbitrary value while returning the previous value. | ||||||
|
|
||||||
| :::note | ||||||
| The Internet Computer supports two types of inter-canister calls: bounded- and unbounded-wait calls. This tutorial shows both, but in general, we recommend to use bounded-wait calls wherever possible for scalability and robustness reasons. | ||||||
| ::: | ||||||
|
|
||||||
| ## Prerequisites | ||||||
|
|
||||||
| To follow along, clone the [Git examples repository](https://github.com/dfinity/examples). The code for this tutorial is in the `rust/inter-canister-calls` directory, where you can find the completed code in the `caller` package. Note that this example uses the alpha version of the Rust CDK. | ||||||
|
|
@@ -177,42 +181,40 @@ The Rust CDK provides helpful functions such as `is_clean_reject` and `is_immedi | |||||
| To summarize, bounded-wait calls will time out after a while, but you might not know whether the callee executed the call or not. Use them for reads or endpoints that are idempotent or provide a way to query their results. | ||||||
| ::: | ||||||
|
|
||||||
| ## Attaching cycles: canister signatures | ||||||
| ## Attaching cycles: depositing cycles | ||||||
|
|
||||||
| A canister can attach [cycles](/docs/building-apps/getting-started/tokens-and-cycles) (Internet Computer "gas") to any call that they make, transferring cycles from the caller's to the callee's cycle balance. The callee must explicitly [accept](https://docs.rs/ic-cdk/latest/ic_cdk/api/call/fn.msg_cycles_accept128.html) such cycles; non-accepted cycles are refunded to the caller. | ||||||
|
|
||||||
| Cycle transfers are generally either used to pay for the callee's costs of processing the call or to move and store cycles as assets. An example where they are used to pay for call processing costs is the IC [threshold signature feature](/docs/building-apps/network-features/signatures/t-ecdsa), which allows a canister to hold a cryptographic key and sign messages with it. An example of cycles being used as assets is the [cycles ledger](/docs/defi/token-ledgers/cycles-ledger/). | ||||||
| Moreover, the management canister provides a special method called `deposit_cycles`, which takes a target canister as a parameter. When called, `deposit_cycles` simply accepts all the attached cycles and deposits them to the target canister's balance. | ||||||
|
|
||||||
| Below is a threshold signature example that shows how to attach cycles to a call: | ||||||
| Below is an example that shows how to attach cycles to a `deposit_cycles` call. | ||||||
|
|
||||||
| ```rust reference | ||||||
| https://github.com/dfinity/examples/blob/945e77d5f7f91dd1d8357b4b7cbc3a11708e8803/rust/inter-canister-calls/src/caller/src/lib.rs#L138-L187 | ||||||
| https://github.com/dfinity/examples/blob/1d91ffa675699a78f0f31603f13d2e8d7149480c/rust/inter-canister-calls/src/caller/src/lib.rs#L139-L153 | ||||||
| ``` | ||||||
|
|
||||||
| Let's test this by calling the `sign_message` method. The exact signature in your response will differ, as it will based on a secret key from your local development environment. | ||||||
| Let's test this by instructing `send_cycles` to send two billion of the caller's available cycles to the counter. | ||||||
|
|
||||||
| ```bash | ||||||
| dfx canister call caller sign_message '("Some text to be signed")' | ||||||
| ( | ||||||
| variant { | ||||||
| Ok = "394b7250ab7a088238fae3af9b60fec521584ac8c874b2ca72e6b950cda509452f307006693c342636afb5b2cb7a395106cc04365bd499c0064f5842a0bdbe2f" | ||||||
| }, | ||||||
| ) | ||||||
| % dfx canister status counter | grep Balance | ||||||
| Balance: 4_560_238_047_511 Cycles | ||||||
| % dfx canister call caller send_cycles "( principal \"`dfx canister id counter`\", 2_000_000_000: nat64 )" | ||||||
| (variant { Ok }) | ||||||
| % dfx canister status counter | grep Balance | ||||||
| Balance: 4_562_236_725_265 Cycles | ||||||
| ``` | ||||||
|
|
||||||
| Cycles can be attached to both bounded and unbounded wait messages. For unbounded wait messages, cycles that are not consumed by the callee are guaranteed to be refunded to the caller. As the example notes, refunds do not happen for bounded-wait calls that result in an error with the `SysUnknown` reject code, which is the code issued when the system decides to stop waiting for a response. | ||||||
| The balances you see will obviously differ. As you see, here the counter's balance has increased by a little less than 2 billion; the difference is used up by the running costs of the counter, which are continually charged while the canister is active. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "counter canister"?
Suggested change
|
||||||
|
|
||||||
| However, this is usually acceptable for API calls that charge for cycles, since the amount charged is usually low (10 billion cycles for signatures with the test key). For transferring larger amounts of cycles, switch to using unbounded wait calls. See the section on [inter-canister calls](/docs/references/async-code) for more details. | ||||||
| Cycles can be attached to both bounded and unbounded wait calls. In case of an error, where the call doesn't make it to the callee, the cycles will eventually be refunded. For bounded-wait calls, this may happen asynchronously, after the error is returned. | ||||||
|
|
||||||
| :::info | ||||||
| To summarize, you can transfer cycles to the callee by attaching them to a bounded- or unbounded-wait call. Bounded-wait calls may drop the attached cycles, so avoid using them for large cycle amounts. | ||||||
| ::: | ||||||
| In general, bounded-wait calls are sufficient and recommended for cycle deposits. However, since there isn't a bulletproof way to check whether `deposit_cycles` succeeded or not in case of a `SYS_UNKNOWN`, applications which need to do error handling on `deposit_cycles` calls (for examples, DEXes which trade wrapped cycles) may want to use unbounded-wait calls instead. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: "which" -> "that"
Suggested change
|
||||||
|
|
||||||
| ## Further reading | ||||||
|
|
||||||
| More details on how inter-canister calls execute and how the different call types work is provided in the documentation on [inter-canister calls and async code](/docs/references/async-code). Also consult the documentation on [properties of call execution](/docs/references/message-execution-properties) and [security best practices](/docs/building-apps/security/security-best-practices/inter-canister-calls). To allow your callers to handle call errors robustly, follow the [best practice](/docs/building-apps/best-practices/idempotency) document on retries and idempotency. | ||||||
|
|
||||||
| As noted in the above examples, an `update` method can always call any method of any other canister. In cases where you only need to call query methods on other canisters, and if you are sure that these canisters are on the same [subnet](https://learn.internetcomputer.org/hc/en-us/articles/34209955782420-Subnet-Creation) as your canister, you can also use [composite query calls](/docs/building-apps/interact-with-canisters/query-calls) methods instead of update methods. | ||||||
|
|
||||||
| For a real-life example of how to handle errors when calling canisters, see the [ICRC-1 examples](/docs/defi/token-ledgers/usage/icrc1_ledger_usage) | ||||||
| For a real-life example of how to handle errors when calling canisters, see the [ICRC-1 examples](/docs/defi/token-ledgers/usage/icrc1_ledger_usage). The [`ic-call-chaos`](https://crates.io/crates/ic-call-chaos) library helps test the correctness of error handling in your code. The [`ic-call-retry`](https://crates.io/crates/ic-call-retry) library provides retries on errors. The [`ic-safe-upgrades`](https://crates.io/crates/ic-safe-upgrades) library implements error handling when trying to upgrade canisters from other canisters. | ||||||
|
|
||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: A "parameter" is a part of the function's signature (e.g.
target: CanisterId); the value passed in a function call is an "argument". I believe the latter is more appropriate here, as this description is from the point of view of the dev / client.