Guard functions should not be allowed in init, pre_upgrade, and post_upgrade

I think we’ve discovered a bug in the Rust CDK. Currently ic_cdk_macros allows guard functions on init, pre_upgrade, and post_upgrade. But the expanded code for the guard function macro results in a call to ic0.msg_reject, which is not allowed from canister_init, canister_post_upgrade, or canister_pre_upgrade according to the current interface spec. We also see this error in our CI tests for guard functions in Azle.

Here’s the expanded code for a guard function:

#[export_name = "canister_pre_upgrade"]
fn pre_upgrade_4_() {
    ic_cdk::setup();
    let r: Result<(), String> = _cdk_user_defined_preventUpgrades();
    if let Err(e) = r {
        ic_cdk::api::call::reject(&e);
        return;
    }
    ic_cdk::spawn(async {
        let result = pre_upgrade();
    });
}

And here’s where the interface spec describes where ic0.msg_reject can be called from:

ic0.msg_reject : (src : i32, size : i32) → (); // U Q CQ Ry Rt CRy CRt

Seems like the Rust CDK is incorrect in allowing guard functions for init, pre_upgrade and post_upgrade.

4 Likes

Thanks for reporting the issue.

ic-cdk-macros v0.7.1 was published to fix it.

Now, only #[update] and #[query] can take guard function.

2 Likes