Skip to content

Commit bd5850e

Browse files
authored
feat: Candid export (internet-computer-protocol#3)
* feat: export candid Signed-off-by: benjizhai <[email protected]> * feat: export candid Signed-off-by: benjizhai <[email protected]> * feat: export candid Signed-off-by: benjizhai <[email protected]> * feat: export candid Signed-off-by: benjizhai <[email protected]> * feat: export candid Signed-off-by: benjizhai <[email protected]> * feat: export candid Signed-off-by: benjizhai <[email protected]> --------- Signed-off-by: benjizhai <[email protected]>
1 parent 8a97e65 commit bd5850e

File tree

8 files changed

+60
-40
lines changed

8 files changed

+60
-40
lines changed

Cargo.lock

Lines changed: 34 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rustic"
3-
version = "0.1.5"
3+
version = "0.1.6"
44
authors = ["Benji Zhai"]
55
edition = "2021"
66
rust-version = "1.65.0"
@@ -18,8 +18,8 @@ categories = ["cryptography::cryptocurrencies", "finance", "wasm"]
1818
[dependencies]
1919
candid = { version = "0.9", features = ["parser"] }
2020
ciborium = "0.2"
21-
ic-cdk = "0.10"
22-
ic-cdk-macros = "0.7"
21+
ic-cdk = "0.11"
22+
ic-cdk-macros = "0.8"
2323
ic-stable-structures = "0.5"
2424
num-traits = "0.2"
2525
serde = "1.0"
@@ -30,6 +30,7 @@ rustic-macros = "0.1"
3030
default = ["access-roles", "reentrancy", "lifecycle"]
3131
access = []
3232
access-roles = ["access"]
33+
export-candid = []
3334
lifecycle = []
3435
logging = []
3536
pausable = ["access"]

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ pub fn post_upgrade () {
5656
}
5757
```
5858

59+
## Export Candid
60+
The `export-candid` allows candid export using the mechanism introduced in `ic-cdk` [v0.11](https://github.com/dfinity/cdk-rs/blob/main/src/ic-cdk/CHANGELOG.md#0110---2023-09-18). However due to how this mechanism works, the candid needs to be exported twice, once in your application and once from the Rustic library.
61+
62+
To export the candid from Rustic, use Rustic with the `export-candid` feature, and comment out `ic_cdk::export_candid!()` in your main canister to avoid conflicts. Then generate wasm once, and use [`candid-extractor`](https://github.com/dfinity/cdk-rs/tree/main/src/candid-extractor) to extract the candid.
63+
64+
Then to export candid from your main application, disable the `export-candid` feature, add `ic_cdk::export_candid!()` to your main canister, compile and extract candid again.
65+
66+
Manually combine the two candid files to get the final candid for your canister.
67+
5968
## Lifecycle
6069
When using the `lifecycle` feature (enabled by default), in the post-upgrade hook of the new canister, the `lifecycle_on_upgrade` method is called via calling `rustic::rustic_post_upgrade`. For the semver you'll need to specify whether you want a major/minor/patchlevel version bump. If the stable memory layout changed (make sure you test compatibility as this is not checked by rustic), bump the stable memory version. If you bump the major version then the minor/patchlevel are ignored and will be set to start from 0.
6170

src/access_control.rs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::memory_map::*;
3333
use crate::testing::*;
3434
use crate::types::*;
3535
use crate::utils::*;
36-
use candid::{candid_method, CandidType, Principal};
36+
use candid::{CandidType, Principal};
3737
use ic_cdk_macros::{query, update};
3838
use ic_stable_structures::{DefaultMemoryImpl, StableBTreeMap, StableCell};
3939
use rustic_macros::modifiers;
@@ -104,7 +104,6 @@ pub fn only_owner() -> Result<(), String> {
104104
}
105105

106106
/// Checks if a principal is the owner.
107-
#[candid_method(query)]
108107
#[query]
109108
pub fn is_owner(owner: Principal) -> bool {
110109
ACCESS_CONTROL.with(|c| {
@@ -120,7 +119,6 @@ pub fn is_owner(owner: Principal) -> bool {
120119
/// The ownership is not affected until the new owners calls [`accept_ownership`],
121120
/// at which point ownership would be transfered from the original owner to the new owner.
122121
/// If the `new_owner` is set to `None`, then any pending ownership transfer is cancelled.
123-
#[candid_method(update)]
124122
#[update]
125123
#[modifiers("only_owner")]
126124
pub fn transfer_ownership(new_owner: Option<Principal>) {
@@ -147,7 +145,6 @@ pub fn transfer_ownership(new_owner: Option<Principal>) {
147145
///
148146
/// The use of this function is discouraged, as there is no recourse if the wrong principal is specified.
149147
/// This function is useful in cases where the accepting principal cannot call [`accept_ownership`].
150-
#[candid_method(update)]
151148
#[update]
152149
#[modifiers("only_owner")]
153150
pub fn transfer_ownership_immediate(new_owner: Option<Principal>) {
@@ -171,7 +168,6 @@ pub fn transfer_ownership_immediate(new_owner: Option<Principal>) {
171168
}
172169

173170
/// Accepts ownership transfer. The caller must be the pending owner.
174-
#[candid_method(update)]
175171
#[update]
176172
pub fn accept_ownership() {
177173
ACCESS_CONTROL.with(|c| {
@@ -194,15 +190,13 @@ pub fn accept_ownership() {
194190
}
195191

196192
/// Query method to get the current owner.
197-
#[candid_method(query)]
198193
#[query]
199194
pub fn owner() -> Option<Principal> {
200195
#[allow(clippy::unwrap_used)] // unwrap desired
201196
ACCESS_CONTROL.with(|c| c.borrow().get().0.clone().unwrap().owner)
202197
}
203198

204199
/// Query method to get the current pending owner.
205-
#[candid_method(query)]
206200
#[query]
207201
pub fn pending_owner() -> Option<Principal> {
208202
#[allow(clippy::unwrap_used)] // unwrap desired
@@ -231,15 +225,13 @@ pub fn only_admin() -> Result<(), String> {
231225
}
232226

233227
/// Checks if a principal is an admin.
234-
#[candid_method(query)]
235228
#[query]
236229
pub fn is_admin(admin: Principal) -> bool {
237230
#[allow(clippy::unwrap_used)] // unwrap desired
238231
ACCESS_CONTROL.with(|c| c.borrow().get().0.clone().unwrap().admins.contains(&admin))
239232
}
240233

241234
/// Grants admin to a new Principal. Must be called by the `owner`.
242-
#[candid_method(update)]
243235
#[update]
244236
#[modifiers("only_owner")]
245237
pub fn grant_admin(new_admin: Principal) {
@@ -261,7 +253,6 @@ pub fn grant_admin(new_admin: Principal) {
261253
}
262254

263255
/// Revokes admin from a Principal. Must be called by the `owner`.
264-
#[candid_method(update)]
265256
#[update]
266257
#[modifiers("only_owner")]
267258
pub fn revoke_admin(admin: Principal) {
@@ -276,7 +267,6 @@ pub fn revoke_admin(admin: Principal) {
276267
}
277268

278269
/// Revokes admin from the caller. Must be called by the admin itself.
279-
#[candid_method(update)]
280270
#[update]
281271
#[modifiers("only_admin")]
282272
pub fn renounce_admin() {
@@ -327,7 +317,6 @@ fn is_role_admin(role_flag: u32, role: u8) -> bool {
327317
/// but the current caller does not have the permission to grant the role,
328318
/// the return value for that role is `false`.
329319
#[cfg(feature = "access-roles")]
330-
#[candid_method(update)]
331320
#[update]
332321
pub fn grant_roles(roles: Vec<u8>, principal: Principal) -> Vec<bool> {
333322
// caller authentication in arithmetics
@@ -362,7 +351,6 @@ pub fn grant_roles(roles: Vec<u8>, principal: Principal) -> Vec<bool> {
362351
/// but the current caller does not have the permission to revoke the role,
363352
/// the return value for that role is `false`.
364353
#[cfg(feature = "access-roles")]
365-
#[candid_method(update)]
366354
#[update]
367355
pub fn revoke_roles(roles: Vec<u8>, principal: Principal) -> Vec<bool> {
368356
// caller authentication arithmetics
@@ -389,7 +377,6 @@ pub fn revoke_roles(roles: Vec<u8>, principal: Principal) -> Vec<bool> {
389377

390378
/// Returns the bitflag of roles granted to a principal.
391379
#[cfg(feature = "access-roles")]
392-
#[candid_method(query)]
393380
#[query]
394381
pub fn get_user_roles(principal: Principal) -> u32 {
395382
ACCESS_ROLES.with(|c| {
@@ -401,7 +388,6 @@ pub fn get_user_roles(principal: Principal) -> u32 {
401388
/// Checks whether a principal has a certain role.
402389
/// Returns a boolean indicating whether the principal has the role.
403390
#[cfg(feature = "access-roles")]
404-
#[candid_method(query)]
405391
#[query]
406392
pub fn user_has_role(role: u8, principal: Principal) -> bool {
407393
ACCESS_ROLES.with(|c| {
@@ -429,7 +415,6 @@ pub fn has_role(role: u8) -> Result<(), String> {
429415
/// Checks whether a principal has all of the specified roles.
430416
/// Returns a boolean indicating whether the principal has all of the roles.
431417
#[cfg(feature = "access-roles")]
432-
#[candid_method(query)]
433418
#[query]
434419
pub fn user_has_roles_all(roles: Vec<u8>, principal: Principal) -> bool {
435420
ACCESS_ROLES.with(|c| {
@@ -457,7 +442,6 @@ pub fn has_roles_all(roles: Vec<u8>) -> Result<(), String> {
457442
/// Checks whether a principal has any of the specified roles.
458443
/// Returns a boolean indicating whether the principal has any of the roles.
459444
#[cfg(feature = "access-roles")]
460-
#[candid_method(query)]
461445
#[query]
462446
pub fn user_has_roles_any(roles: Vec<u8>, principal: Principal) -> bool {
463447
ACCESS_ROLES.with(|c| {
@@ -484,7 +468,6 @@ pub fn has_roles_any(roles: Vec<u8>) -> Result<(), String> {
484468

485469
/// Sets role admins for a role. Must be called by admins.
486470
#[cfg(feature = "access-roles")]
487-
#[candid_method(update)]
488471
#[update]
489472
#[modifiers("only_admin")]
490473
pub fn set_role_admins(role: u8, admins: Vec<u8>) {
@@ -503,7 +486,6 @@ pub fn set_role_admins(role: u8, admins: Vec<u8>) {
503486

504487
/// Revokes role admins for a role. Must be called by admins.
505488
#[cfg(feature = "access-roles")]
506-
#[candid_method(update)]
507489
#[update]
508490
#[modifiers("only_admin")]
509491
pub fn revoke_role_admins(role: u8, admins: Vec<u8>) {

src/global_flags.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::memory_map::*;
22
use crate::types::*;
3-
use candid::{candid_method, CandidType};
3+
use candid::CandidType;
44
use ic_cdk_macros::query;
55
use ic_stable_structures::{DefaultMemoryImpl, StableCell};
66
use std::cell::RefCell;
@@ -40,7 +40,6 @@ pub(crate) fn global_flags_init() {
4040

4141
/// Returns the `RUSTIC_USER_PAGE_END` constant used during setup.
4242
/// This value is set through a environment variable and shall remain constant across versions.
43-
#[candid_method(query)]
4443
#[query]
4544
pub async fn get_config_user_page_end() -> u64 {
4645
GLOBAL_FLAGS.with(|gf| {

src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// Re-export the macros
44
#[doc(inline)]
55
pub use rustic_macros::*;
6-
use utils::canister_caller;
76

87
pub mod access_control;
98
mod global_flags;
@@ -17,6 +16,12 @@ pub mod testing;
1716
pub mod types;
1817
pub mod utils;
1918

19+
#[cfg(feature = "lifecycle")]
20+
use crate::lifecycle::CanisterLifecycle;
21+
use crate::utils::canister_caller;
22+
23+
use candid::Principal;
24+
2025
/// Initializes the Rustic module. Needs to be called in the init hook of every canister.
2126
/// # Example
2227
/// ```rust
@@ -59,3 +64,6 @@ pub fn rustic_post_upgrade(
5964
#[cfg(feature = "lifecycle")]
6065
crate::lifecycle::lifecycle_on_upgrade(stable_memory_bump, major_bump, minor_bump);
6166
}
67+
68+
#[cfg(feature = "export-candid")]
69+
ic_cdk::export_candid!();

src/lifecycle.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#![cfg(feature = "lifecycle")]
22
//! Canister Lifecycle Management
3-
//!
3+
//!
44
55
use crate::memory_map::*;
66
#[cfg(test)]
77
use crate::testing::*;
88
use crate::types::*;
99
use crate::utils::*;
10-
use candid::{candid_method, CandidType};
10+
use candid::CandidType;
1111
use ic_cdk_macros::query;
1212
use ic_stable_structures::{DefaultMemoryImpl, StableCell};
1313
use std::cell::RefCell;
@@ -89,15 +89,13 @@ pub(crate) fn lifecycle_on_upgrade(stable_memory_bump: bool, major_bump: bool, m
8989

9090
/// Returns the current version of the canister.
9191
#[query]
92-
#[candid_method(query)]
9392
pub fn get_version() -> CanisterLifecycle {
9493
#[allow(clippy::unwrap_used)] // safe unwrap
9594
CANISTER_LIFECYCLE.with(|l| l.borrow().get().0.clone().unwrap())
9695
}
9796

9897
/// Returns the current version of the canister as a string.
9998
#[query]
100-
#[candid_method(query)]
10199
pub fn get_version_text() -> String {
102100
#[allow(clippy::unwrap_used)] // safe unwrap
103101
CANISTER_LIFECYCLE.with(|l| l.borrow().get().0.clone().unwrap().to_string())

src/pausable.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use crate::global_flags::*;
1515
#[cfg(test)]
1616
use crate::testing::*;
1717
use crate::types::*;
18-
use candid::candid_method;
1918
use ic_cdk_macros::{query, update};
2019
use rustic_macros::modifiers;
2120

@@ -42,15 +41,13 @@ pub fn when_paused() -> Result<(), String> {
4241
}
4342

4443
/// Query method to get the current pause status.
45-
#[candid_method(query)]
4644
#[query]
4745
pub fn is_paused() -> bool {
4846
#[allow(clippy::unwrap_used)] // unwrap desired
4947
GLOBAL_FLAGS.with(|f| f.borrow().get().0.clone().unwrap().paused)
5048
}
5149

5250
/// Pauses the canister. Can only be called by admins.
53-
#[candid_method(update)]
5451
#[update]
5552
#[modifiers("only_admin")]
5653
pub fn pause() {
@@ -65,7 +62,6 @@ pub fn pause() {
6562
}
6663

6764
/// Resumes the canister. Can only be called by admins.
68-
#[candid_method(update)]
6965
#[update]
7066
#[modifiers("only_admin")]
7167
pub fn resume() {

0 commit comments

Comments
 (0)