This starter is a collection of helpful patterns, tooling and best practices for developing canisters in Rust. You can read more below.
- dfx 0.24.3
- node 22.12.0
- npm 10.9.0
pocket-icbinary present in/usr/local/bin/pocket-ic(make sure it's executable)
The canister's lifecycle methods are managed in src/backend/src/lifecycle.rs. The init method in main.rs is called when the canister is created, and the post_upgrade method in main.rs is called after the canister is upgraded. Currently they both share the same implementation, that is when upgrading the canister you have to provide the same arguments as when creating the canister.
The application has a state that is stored in the canister. The state is a struct that is defined in src/backend/src/state.rs. The state state is lost when the canister is being upgraded. For data that should be persisted, use refer to the Storage section.
Data that is supposed to survive canister upgrades is defined in src/backend/src/storage.rs. The data is stored in the canister's stable memory and is not lost when the canister is being upgraded. This is faciliated by using the ic-stable-structures crate released by DFINITY.
There are two levels of logging in the application:
- INFO: General information about the application
- DEBUG: Detailed information about the application
To emit a log message, use the following syntax:
log!(INFO, "Received a request to fetch logs");
log!(DEBUG, "Received a request to fetch logs");Logs in query calls are not persisted.
You can access canister logs via http requests to the canister's /logs endpoint. It accepts the following query parameters:
priority: The log level to filter by. Possible values areinfoanddebug.time: Accepts a timestamp in nanoseconds. Only logs after this timestamp will be returned.sort: The order in which logs are returned. Possible values areascanddesc. Default isascwhentimeis not provided, anddescotherwise.
Note that logs are not persisted and are lost when the canister is being upgraded. You also can't log when the execution traps, as by design the state changes are rolled back. For this you can rely on the canister logging feature provided by the protocol by simply using println! exposed by the ic_cdk, note that here you only have one level of logging and 4KB of log storage. Read more here and in the example here.
There are different metrics exposed via http requests to the canister's /metrics endpoint. You can modify them in src/backend/src/metrics.rs
The application has a dashboard that can be accessed via http requests to the canisters the /dashboard endpoint. It currently only exposes the way the user is greeted when calling greet. You can modify the askama dashboard template in src/backend/dashboard.rs and the corresponding HTML in src/backend/templates/dashboard.html.
We use a StableLog to persist all greeting in stable memory. Usually this is used to store state changing events that should survive canister upgrades. They can be used to restore the canisters state that lives on the heap after an upgrade. You can learn more about the reasoning for this approach here. It can also be used as an audit trail for the canister. In our case we just replay the event logs in the post_upgrade to restore a hashmap that keeps the count of greetings per name greeted.
canbench is a tool for benchmarking canisters on the Internet Computer. The config can be found in canbench.yml.
To encode the deploy arguments in candid use the didc tool:
didc encode '(variant { InitArg = record { greeting = "moin" } })' -d src/backend/backend.did -t '(Arg)'Edit rust-analyzer.cargo.features in your .vscode/settings.json to enable the canbench feature for the rust-analyzer so canbench blocks are analyzed.
Run canbench --persist to persist the current benchmark results, then canbench after code changes to compare the current benchmark results with the persisted ones.
There are some example integration tests leveraging pocket-ic in src/backend/tests. You can run them with cargo test.
Simple automatic fuzz testing for Internet Computer Protocol (ICP) canisters, configuration is defined in cuzz.json. Read more here.
The canisters candid file is generated by leveraging the ic_cdk::export_candid!() macro and candid-extractor. You can read more about this approach here.