I have Rust code that dynamically creates canister by importing the wasm as a byte array and then creating canisters on demand. The import and creation code looks like this
For the canister wasm to be created I need to either call dfx build or dfx deploy. The problem is they require the canister_id to be provisioned beforehand otherwise I get an error that looks like this
Error: Failed to determine id for canister āindividual_user_templateā.
Caused by: Failed to determine id for canister āindividual_user_templateā.
Cannot find canister id. Please issue ādfx canister create individual_user_templateā.
Can dfx build be decoupled from the canister ID provision step and just build the resulting WASM without requiring an already present canister. Otherwise I will have to spend cycles and provision an unnecessary canister.
cargo build -p hello_backend --target wasm32-unknown-unknown produces wasm directly. Just note that if you want to compile canister IDs into the wasm somehow this will probably run into some problems.
The problem with this approach is that dfx does some minification and gzipping that this wonāt do. The type: "rust" specified in the dfx.json is much more ergonomic and having to do all the build steps ourself is error prone and counter productive.
It would be much more ergonomic to just be able to ask dfx to build without needing to get the canister_id in valid cases such as this.
For dynamically deployed canisters, does dfx need to know the canister IDs? It could just build the canister and call it a day, no? Are there drawbacks to this that Iām not seeing?
Ah, I see. We have a todo item that would expose the optimization through a dfx command, but it just hasnāt been prioritized so far. For the moment itās still possible to use ic-cdk-optimizer to shrink your wasm. cargo install ic-cdk-optimizer will install it. It wonāt be around forever since itās been deprecated, but for now it still performs the exact same optimization dfx would do otherwise.
Honestly, so far we havenāt really considered dynamically deployed canisters. And you are right, the id is not necessary in quite a few cases, so I agree that restriction could probably be lifted. (But Iāll double-check with those that know the build system in a bit more detail.)
The biggest drawback I can come up with (not claiming this is a show-stopping issue) is that in some cases compilation could run for a while until the error appears even though it couldāve been detected much earlier if no canister IDs are present.
Potential workaround for now: Create a script that does the following
I remember reading earlier that dfx also gzips the canister after ic-cdk-optimizer has been run. Not sure how to do that step manually.
Finally, the compiler output tells me that thereās another toolchain called binaryen thatās run on the resulting wasm which reduces size further. Again not sure how to do that manually
These are what my optimization steps look like:
I believe ic-cdk-optimizer alone wouldnāt give me those results
Looking through the code I donāt think dfx already zips up the wasm, but I may be wrong. You can just gzip canister.wasm though and put the resulting .gz file either as your canisterās wasm in dfx.json or manually upload it with dfx canister install --wasm canister.wasm.gz (May require the 0.12.0 beta).
binaryen is run with both dfx (which uses the ic-wasm crate) and ic-cdk-optimizer (just grep for binaryen), so you can use either to minimize your wasm (or to read the source if you want to do it yourself).
Got it. I ended up using a hack for now. By setting the canister ID in the canister_ids.json to aaaa-aa. I remember reading that since that ID corresponds to the management canister, itās ignored by dfx but the build passes.
I believe the drawback you suggested would mostly not be relevant for folks running dfx build, because most folks whose build expect the canister ID to be present would be using dfx deploy instead of dfx build. I believe dfx build should be decoupled for that reason.
Consider this thread a feature request if you decide to add this to your roadmap
So, I just learned that ic-wasm can be used as a CLI tool! You can install it with cargo install ic-wasm. This would mean that you can ditch ic-cdk-optimizer for good. See ic-wasm -h for the available commands, the one youāre most likely looking for is shrink.
Thanks for that info. Starred that repo. Will try. To be honest, if dfx build just worked without needing canister ID provisioning, that would be ideal. But will bookmark ic-wasm in case I need to do manual canister creation/optimization in the future
I was looking through the dfx source code to understand exactly what steps the dfx build runs through. Specifically, I am looking at this file but I donāt quite understand what it does.
If I were to use the ic-wasm CLI to mimic what dfx build does, what would the steps look like for a Rust canister?
Iām happy to provide as many details as you want. Iāll just start with a relatively high-level overview so I donāt spend too much time on stuff youāre not interested in. Let me know what you want to know more about.
L45-L60: Figure out from CLI options and dfx.json settings which canisters are supposed to be built, plus figure out for which canistersā canister ID and candid interface have to be available for the build commands
L62-L76: enforce that canisters have to be created before the canisters are built. Iād love to lift that entirely, but thereās some issues with that. Not sure if you were involved in the last discussions around thatā¦
What you are probably most interested in is the Rust specialisation of the CanisterBuilder trait: RustBuilder. This contains all the Rust-specific compilation details. Build step here, shrink step refers to this method.
Hey, just wanted to mention that if you need to create a canister that can self spawn itself, the pattern of storing the wasm binary at build time can quickly get out of hand (self importing gets almost as weird as time traveling, once you re-build your project). Iāve found a better pattern to handle such cases, check it out and consider this as an alternative.
Instead of importing the bytes directly, you define somewhere a wasm variable of type Vec<u8>
I think thatās doing the same twice. If DFX_NETWORK is set and no --network is provided then it should act as if --network was set to DFX_NETWORK
Yes, these lines set the envvars so that the right canister ids are available. The pool contains the right IDs for the network. But Iāve never looked at that in detail, so Iād have to investigate the details too
Thank you. Just to confirm, when running cargo build as shown above, I donāt need to specify the DFX_NETWORK env var. Itāll automatically get set to āicā?
Assuming you run dfx with --network ic yes, in that case you donāt have to manually set DFX_NETWORK. If you want to run everything on your own, then youāll have to set it manually.
How I arrived at this (mostly future reference for myself):
This is our (apparently lacking) documentation for environment variables which doesnāt mention DFX_NETWORK.
BUT in the default webpack config taken from dfx new there is this line: