Skip to content

Commit

Permalink
Merge pull request #3368 from dfinity/update-maintain-docs
Browse files Browse the repository at this point in the history
Add other language examples to Maintain docs
  • Loading branch information
jessiemongeon1 authored Aug 16, 2024
2 parents f34c4ec + 48c17e1 commit e14bfe6
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 194 deletions.
4 changes: 2 additions & 2 deletions docs/developer-docs/smart-contracts/maintain/control.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ A <GlossaryTooltip>canister</GlossaryTooltip> is managed by a list of controller

## Setting the controllers of a canister

The controllers of a canister are set when the canister is created. If no explicit controller settings are specified, the default controller is the principal that created the canister. If the canister is created with `dfx` then the default controller list contains the developer identity.
The controllers of a canister are set when the canister is created. If no explicit controller settings are specified, the default controller is the principal that created the canister. If the canister is created with `dfx` then the default controller list contains the current developer identity.

## Update the controllers of a canister

Expand Down Expand Up @@ -78,4 +78,4 @@ Developers can also add the NNS root canister as a controller of their canister,

### No controller

A canister has no controller and is immutable. On ICP this model is often called a blackholed canister. This is the model used by smart contracts on Ethereum.
A canister has no controller and is immutable. On ICP, this model is often called a blackholed canister. This is the model used by smart contracts on Ethereum.
15 changes: 8 additions & 7 deletions docs/developer-docs/smart-contracts/maintain/delete.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { GlossaryTooltip } from "/src/components/Tooltip/GlossaryTooltip";

## Overview

If you want to permanently delete a specific <GlossaryTooltip>canister</GlossaryTooltip> or all canisters for a specific project on a given deployment (either local, or remote), you can do so by running the command:
If you want to permanently delete a specific <GlossaryTooltip>canister</GlossaryTooltip> or all canisters for a specific project on a given deployment (either local or remote), you can do so by running the command:

```
dfx canister delete <canister-name>
dfx canister delete <canister-name> // Delete local canister
dfx canister delete <canister-name> --network=ic // Delete canister on mainnet
```

Deleting a canister removes the canister identifier, code, and state. Before you can delete a canister, however, you must first stop the canister to clear any pending message requests or replies.
Expand All @@ -28,18 +29,18 @@ To delete all canisters for a project:

- #### Step 2: Start the local canister execution environment, if necessary.

In most cases, this step is only necessary if you are running the canisters locally.
In most cases, this step is only necessary if you are running the canisters locally.

If you were deleting canisters to run on a remote execution environment, e.g. ICP blockchain, you would include the `--network` command-line option to perform tasks on the environment specified under this parameter.
If you were deleting canisters to run on a remote execution environment, e.g. ICP blockchain, you would include the `--network` command-line option to perform tasks on the environment specified under this parameter.

- #### Step 3: Check the status of the project canisters running on the local canister execution environment by running the following command:

dfx canister status --all
dfx canister status --all

- #### Step 4: Stop all of the project canisters by running the following command:

dfx canister stop --all
dfx canister stop --all

- #### Step 5: Delete all of the project canisters by running the following command:

dfx canister delete --all
dfx canister delete --all
231 changes: 53 additions & 178 deletions docs/developer-docs/smart-contracts/maintain/history.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
keywords: [beginner, tutorial, maintain canisters, history, canister history]
---

import TabItem from "@theme/TabItem";
import useBaseUrl from "@docusaurus/useBaseUrl";
import { AdornedTabs } from "/src/components/Tabs/AdornedTabs";
import { AdornedTab } from "/src/components/Tabs/AdornedTab";
import { BetaChip } from "/src/components/Chip/BetaChip"
import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow";
import { GlossaryTooltip } from "/src/components/Tooltip/GlossaryTooltip";

Expand Down Expand Up @@ -32,9 +37,28 @@ Note that canister history is only available for changes that have happened sinc

You can read more about this feature on the [developer forum](https://forum.dfinity.org/t/canister-history-on-the-ic/21745).

## Canister history example
## Getting a canister's history

The following is an example of a Rust canister that displays the canister history feature and its functionality. This code includes in-line comments that document the different features and functions relating to canister history.
To get a canister's history, make a call to the IC management canister's `canister_info` method:

<AdornedTabs groupId="language">
<TabItem value="motoko" label="Motoko" default>

```motoko
actor {
let IC =
actor "aaaaa-aa" : actor {
canister_info : { canister_id : Principal } ->
async ();
};
};
```

</TabItem>

<TabItem value="rust" label="Rust">

```rust
use candid::{CandidType, Principal};
Expand All @@ -56,188 +80,39 @@ async fn info(canister_id: Principal) -> CanisterInfoResponse {
};
canister_info(request).await.unwrap().0
}
```

/// Returns all (reflexive and transitive) controllers of a canister characterized by a given principal
/// by implementing BFS over the controllers.
#[ic_cdk::update]
async fn reflexive_transitive_controllers(canister_id: Principal) -> Vec<Principal> {
let mut ctrls = vec![canister_id];
let mut queue = vec![canister_id];
while !queue.is_empty() {
let cur = queue.pop().unwrap();
// check if the principal characterizes a canister by determining if it is an opaque principal
if cur.as_slice().last() == Some(&0x01) {
let info = info(cur).await;
for c in info.controllers {
if !ctrls.contains(&c) {
ctrls.push(c);
queue.push(c);
}
}
}
}
ctrls
}
</TabItem>

/// Specifies a canister snapshot by providing a Unix timestamp in nanoseconds
/// or a canister version.
#[derive(CandidType, Deserialize, Clone)]
pub enum CanisterSnapshot {
#[serde(rename = "at_timestamp")]
AtTimestamp(u64),
#[serde(rename = "at_version")]
AtVersion(u64),
}
<AdornedTab value={"typescript"} label="TypeScript" endAdornment={<BetaChip />}>

/// Maps the latest canister change of the canister characterized by a given principal before or at the given `CanisterSnapshot`
/// and returns an optional integer characterizing the maximum clock skew (in nanoseconds)
/// between the subnet hosting the canister and the given `CanisterSnapshot`
/// (i.e., if this integer is `None`, then no assumptions on clock skew are needed).
/// Returns `None` if no change to map can be determined due to unavailability of canister changes in canister history
/// or due to ambiguity between canister changes with the same timestamp.
/// Traps if a canister_info call is rejected (in particular, if the given principal does not characterize a canister).
async fn map_canister_change<T>(
canister_id: Principal,
canister_deployment: CanisterSnapshot,
f: impl Fn(&CanisterChange) -> Option<T>,
) -> Option<(T, Option<u64>)> {
let info = info(canister_id).await;
let mut map_change = None;
let mut skew = None;
for c in info.recent_changes {
if let Some(x) = f(&c) {
match &canister_deployment {
CanisterSnapshot::AtTimestamp(t) => {
if *t == c.timestamp_nanos {
return None;
}
if *t >= c.timestamp_nanos {
map_change = Some(x);
}
let d = if *t >= c.timestamp_nanos {
*t - c.timestamp_nanos
} else {
c.timestamp_nanos - *t
};
skew = Some(std::cmp::min(d, skew.unwrap_or(d)));
}
CanisterSnapshot::AtVersion(v) => {
if *v >= c.canister_version {
map_change = Some(x);
} else {
break;
}
}
};
}
}
map_change.map(|x| (x, skew))
}
```typescript
import { call, IDL, Principal, query, update } from 'azle';
import {
CanisterInfoArgs,
CanisterInfoResult,
} from 'azle/canisters/management';

/// Returns the controllers of the canister characterized by a given principal and at the given `CanisterSnapshot`
/// and an optional integer characterizing the maximum clock skew (in nanoseconds)
/// between the subnet hosting the canister and the given `CanisterSnapshot`
/// (i.e., if this integer is `None`, then no assumptions on clock skew are needed).
/// Returns `None` if the controllers cannot be determined due to unavailability of canister changes in canister history
/// or due to ambiguity between canister changes with the same timestamp.
/// Traps if a canister_info call is rejected (in particular, if the given principal does not characterize a canister).
#[ic_cdk::update]
async fn canister_controllers(
canister_id: Principal,
canister_deployment: CanisterSnapshot,
) -> Option<(Vec<Principal>, Option<u64>)> {
map_canister_change(canister_id, canister_deployment, |c| match &c.details {
Creation(creation) => Some(creation.controllers.clone()),
ControllersChange(ctrls) => Some(ctrls.controllers.clone()),
_ => None,
})
.await
}
type State = {
createdCanisterId: Principal;
};

/// Returns the module hash of the canister characterized by a given principal and at the given `CanisterSnapshot`
/// and an optional integer characterizing the maximum clock skew (in nanoseconds)
/// between the subnet hosting the canister and the given `CanisterSnapshot`
/// (i.e., if this integer is `None`, then no assumptions on clock skew are needed).
/// Returns `None` if the module hash cannot be determined due to unavailability of canister changes in canister history
/// or due to ambiguity between canister changes with the same timestamp.
/// Traps if a canister_info call is rejected (in particular, if the given principal does not characterize a canister).
#[ic_cdk::update]
async fn canister_module_hash(
canister_id: Principal,
canister_deployment: CanisterSnapshot,
) -> Option<(Option<Vec<u8>>, Option<u64>)> {
map_canister_change(canister_id, canister_deployment, |c| match &c.details {
Creation(_) => Some(None),
CodeUninstall => Some(None),
CodeDeployment(code_deployment) => Some(Some(code_deployment.module_hash.clone())),
_ => None,
})
.await
}
let state: State = {
createdCanisterId: Principal.fromText('aaaaa-aa')
};

/// Returns the deployment chain of the canister characterized by a given principal and at the given `CanisterSnapshot`:
/// a list of canister changes starting with the change resulting in the canister deployment characterized by the given principal and at the given `CanisterSnapshot`
/// and with each subsequent change resulting in the canister deployment triggering the previous change,
/// and an optional integer characterizing the maximum clock skew (in nanoseconds)
/// between the subnets hosting the canisters from the deployment chain and the given `CanisterSnapshot`
/// (i.e., if this integer is `None`, then no assumptions on clock skew are needed).
/// The deployment chain stops if a canister change in the deployment chain cannot be determined (due to unavailability of canister changes in canister history
/// or due to ambiguity between canister changes with the same timestamp)
/// or upon encountering a change from a user principal or upon encountering a loop among canisters from the deployment chain.
/// Traps if a canister_info call is rejected (in particular, if the given principal does not characterize a canister).
#[ic_cdk::update]
async fn canister_deployment_chain(
canister_id: Principal,
canister_deployment: CanisterSnapshot,
) -> (Vec<CanisterChange>, Option<u64>) {
let mut current_canister_id = canister_id;
let mut current_canister_deployment = canister_deployment;
let mut visited_canister_ids = vec![]; // canister IDs of canisters from the deployment chain
let mut deployment_chain = vec![];
let mut skew = None;
loop {
visited_canister_ids.push(current_canister_id);
match map_canister_change(
current_canister_id,
current_canister_deployment.clone(),
|c| match &c.details {
CodeDeployment(_) => Some(c.clone()),
_ => None,
},
)
.await
{
Some((c, s)) => {
let mut done = false;
match &c.origin {
FromUser(_) => {
done = true;
}
FromCanister(o) => {
if visited_canister_ids.contains(&o.canister_id) {
done = true;
} else {
current_canister_id = o.canister_id;
current_canister_deployment = match o.canister_version {
None => CanisterSnapshot::AtTimestamp(c.timestamp_nanos),
Some(v) => CanisterSnapshot::AtVersion(v),
};
}
}
};
deployment_chain.push(c);
skew = s
.map(|dt| Some(std::cmp::min(dt, skew.unwrap_or(dt))))
.unwrap_or(skew);
if done {
break;
}
}
None => {
break;
}
};
export default class {
@update([CanisterInfoArgs], CanisterInfoResult)
async getCanisterInfo(args: CanisterInfoArgs): Promise<CanisterInfoResult> {
return await call('aaaaa-aa', 'canister_info', {
paramIdlTypes: [CanisterInfoArgs],
returnIdlType: CanisterInfoResult,
args: [args]
});
}
(deployment_chain, skew)
}

```

</AdornedTab>
</AdornedTabs>
4 changes: 3 additions & 1 deletion docs/developer-docs/smart-contracts/maintain/logs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ Non-replicated calls currently are supported for logging.

## Using `dfx`

A canister's logs can be viewed using the [`dfx canister logs`](/docs/current/developer-docs/developer-tools/cli-tools/cli-reference/dfx-canister/#dfx-canister-logs) command. As an example, consider a Rust canister with the following code:
A canister's logs can be viewed using the [`dfx canister logs`](/docs/current/developer-docs/developer-tools/cli-tools/cli-reference/dfx-canister/#dfx-canister-logs) command.

As an example, consider a Rust canister with the following code:

```rust
use std::cell::RefCell;
Expand Down
10 changes: 5 additions & 5 deletions docs/developer-docs/smart-contracts/maintain/resource-limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ The limits depend on the message type as shown in the following table.
| Message resource limits | Constraint |
| ------------------------------------------------------------------------------------ | ----------- |
| Message queue limit, messages per <GlossaryTooltip>canister</GlossaryTooltip> | 500 |
| Maximum ingress message payload | 2MB |
| Maximum cross-net inter-canister message payload | 2MB |
| Maximum same-<GlossaryTooltip>subnet</GlossaryTooltip> inter-canister request payload (may be deprecated at some point) | 10MB |
| Maximum response size (replicated execution) | 2MB |
| Maximum response size (non-replicated execution, i.e. in query calls) | 3MB |
| Maximum ingress message payload | 2MiB |
| Maximum cross-net inter-canister message payload | 2MiB |
| Maximum same-<GlossaryTooltip>subnet</GlossaryTooltip> inter-canister request payload (may be deprecated at some point) | 10MiB |
| Maximum response size (replicated execution) | 2MiB |
| Maximum response size (non-replicated execution, i.e. in query calls) | 3MiB |

| Instruction resource limits | Constraint |
| ------------------------------------------------------------------------------------ | ----------- |
Expand Down
Loading

0 comments on commit e14bfe6

Please sign in to comment.