Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ For more information about the philosophy behind the library, see [Roman's tutor

## Tutorials

[Schema Upgrades](./docs/schema-upgrades.md)
[Schema Upgrades](./docs/src/schema-upgrades.md)

## How it Works

Expand Down Expand Up @@ -164,4 +164,4 @@ cargo +nightly fuzz list

# To run a target
cargo +nightly fuzz run <TARGET_NAME>
```
```
7 changes: 7 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Summary

- [Introduction](./introduction.md)
- [Design Principles](./design-principles.md)
- [Available Data Structures](./available-data-structures.md)

- [Schema Upgrades](./schema-upgrades.md)
9 changes: 9 additions & 0 deletions docs/src/available-data-structures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Available Data Structures

The library provides several stable data structures:

* **BTreeMap**: A key-value store that maintains keys in sorted order
* **Vec**: A growable array
* **Log**: An append-only list of variable-size entries
* **Cell**: A serializable value
* **MinHeap**: A priority queue
21 changes: 21 additions & 0 deletions docs/src/design-principles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Design Principles

The library is built on several key principles:

* **Radical simplicity**: Each data structure follows the most straightforward design that solves the problem at hand.
This makes the code easier to understand, debug, and maintain.

* **Backward compatibility**: Upgrading the library version must preserve the data.
All data structures have a metadata section with the layout version, ensuring that new versions can read data written by old versions.

* **No [`pre_upgrade` hooks](https://internetcomputer.org/docs/references/ic-interface-spec#system-api-upgrades)**: A bug in the `pre_upgrade` hook can make your canister non-upgradable.
The best way to avoid this issue is not to have a `pre_upgrade` hook at all.

* **Limited blast radius**: If a single data structure has a bug, it should not corrupt the contents of other data structures.
This isolation helps prevent cascading failures.

* **No reallocation**: Moving large amounts of data is expensive and can lead to prohibitively high cycle consumption.
All data structures must manage their memory without costly moves.

* **Compatibility with multi-memory WebAssembly**: The design should work when canisters have multiple stable memories.
This ensures future compatibility with upcoming IC features.
34 changes: 34 additions & 0 deletions docs/src/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Introduction

## Background

Smart contracts on the [Internet Computer](https://internetcomputer.org) are referred to as [canisters](https://learn.internetcomputer.org/hc/en-us/articles/34210839162004-Canister-Smart-Contracts).

Canisters, compared to traditional smart contracts, have some unique properties including:

**Mutability**: A canister can have a set of controllers, and controllers are able to upgrade the code of the canister (e.g., to add new features, fix bugs, etc.)

**Scale**: Canisters have access to hundreds of gigabytes of memory and ample amounts of compute, allowing developers to build fully functioning dapps without relying on external cloud providers.

### The Challenge of Upgrades

When upgrading a canister, the canister's code is replaced with the new code.
In Rust, the new version of the code is not guaranteed to understand the memory layout established by the previous version.
This is because Rust's memory layout can change between different versions of the code, making it unsafe to directly access the old memory layout.
Therefore, by default, when a canister is upgraded and a new module is installed, the canister's main memory is wiped.

To persist state, the Internet Computer provides canisters with an additional memory called _stable memory_.
The conventional approach to canister state persistence follows these steps:

1. Serialize and store the state of the canister just before the upgrade using the `pre_upgrade` hook.
2. Install the new Wasm module of the canister (and wipe out the canister's main memory).
3. Deserialize the data that was stored in stable memory in step 1 using the `post_upgrade` hook.

This approach is easy to implement and works well for relatively small datasets.
Unfortunately, it does not scale well and can render a canister non-upgradable.

### The Solution: Stable Structures

Rather than using standard Rust data structures, which store their data in the canister's main memory, you can use stable structures.
Stable structures are designed to use stable memory as the primary storage, allowing them to grow to gigabytes in size without the need for `pre_upgrade`/`post_upgrade` hooks.
This is the key characteristic that distinguishes stable structures from Rust's standard data structures.
File renamed without changes.