A Closer Look at Software Canisters, an Evolution of Smart Contracts

Canisters are smart contracts that scale — interoperable compute units designed for internet-scale services.

DFINITY
The Internet Computer Review

--

By Johan Granström, Director of Engineering, DFINITY

The Internet Computer —the world’s first web-speed, internet-scale public blockchain — enables a seamless software ecosystem where different apps and programs communicate and use each other’s APIs. This is made possible by canister smart contracts, computational units that consist of both code and data. A canister can be deployed on the Internet Computer and accessed over the internet.

Software canisters underpin an open and collaborative internet, and are a key concept for the Internet Computer. When hearing about canisters for the first time, people with different backgrounds make different associations:

  • An Ethereum developer may think about smart contracts.
  • A Ph.D. student may think about the actor model.
  • A system engineer may think about processes, as in an operating system.
  • An expert in virtual machines may think about WebAssembly modules.

Though each of these comparisons is correct, they are also incomplete. But taken altogether, they paint a complete picture.

Canisters as smart contracts

A canister is like a smart contract because its execution is governed by a secure protocol: the Internet Computer Protocol (ICP). A canister is therefore tamperproof, meaning that its state can only be modified through messages included in the blockchain, which is governed by the protocol.

Additionally, because the execution of canister code is fully deterministic, a canister’s state can be audited in a cryptographically secure way by inspecting the messages in the blockchain.

A canister has all of the capabilities of a traditional smart contract. In contrast to smart contracts, however, canisters have performance characteristics that makes it possible to use them to build software services that scale.

Canisters as actors

Now, let us take a step back and think about canisters from a more abstract point of view. From this perspective, a canister is much like an actor in the actor model pioneered by computer scientist Carl Hewitt and other contributors. The actor model is a mathematical model of concurrent computation, where, in response to a message, an actor can modify its local or private state, send messages, and create more actors.

A canister is like an actor in many respects. For example, a canister has:

  • a private state that can only be modified by the canister itself;
  • a single thread of execution, so it does not need lock-based synchronization;
  • the ability to communicate with other canisters through asynchronous messages; and
  • the ability to create new canisters.

An important difference between traditional actors and canisters is that canisters on the Internet Computer have bidirectional message passing. Messages are divided into requests and responses, where requests can be replied to and the Internet Computer keeps track of the callback for responses.

In actor terminology, each actor has a mailing address that is used to receive messages. A canister also has a mailing address, which happens to look similar to an IPv6 address.

A single canister has only one thread of execution for updates, but the Internet Computer executes a potentially massive number of canisters in parallel. This is how the Internet Computer overcomes the performance limitations of some early platforms for smart contracts. In addition, we make a distinction between requests that need to update the state of a canister, and queries, which cannot modify the state of a canister.

While a canister’s update throughput is limited by the blockchain and the single thread of execution, a canister can serve hundreds of queries concurrently, achieving throughput in the order of thousands of queries per second, and latency measured in milliseconds.

To complete this picture, it has to be added that end users also participate as actors in the model, at least to some extent. This means that browsers and mobile apps can directly perform update and query operations on canisters.

The Motoko programming language, developed by the DFINITY Foundation, is inspired by the actor model.

Canisters as processes

A canister is much like a process in an operating system like Linux, MacOS, or Windows. The operating system keeps track of valid memory ranges for a process, while a canister has a boundary on its linear memory that’s enforced by the Internet Computer.

The operating system scheduler wakes up a process when there is work to be done, while the Internet Computer schedules the execution of canisters.

The operating system maintains state on behalf of a process, like open file descriptors and the parent process. Similarly, the Internet Computer maintains state on behalf of a canister, but instead of things like file descriptors, it keeps track of the canister’s balances of tokens and cycles, outstanding calls, permissions, and more.

Just as a process cannot directly modify its table of file descriptors, a canister cannot directly modify its balances of tokens.

The operating system provides functionality to processes that allow them to perform special operations, like manipulating files and communicating with peripheral devices. Similarly, the Internet Computer provides APIs to canisters so that they can:

  • make payments;
  • call out to other canisters;
  • create and manage canisters;
  • manage permissions; and
  • get the system time (a non-trivial function in a distributed system).

A unique feature of the Internet Computer is that it provides access to secure randomness. In the future, canisters will also be able to sign Bitcoin and Ethereum contracts through such APIs.

Behind the scenes, the biggest difference between a process and a canister is that a canister is replicated over all nodes in a subnetwork.

When a process malfunctions it crashes, but when a canister malfunctions, caused by a trap in WebAssembly, it does not crash. Instead its state is rolled back to what it was before the current message started executing, so that the canister can continue executing new messages.

Of course, this is of little help if the canister crashes on all of the messages it receives, but it is a very useful safeguard against accidentally missed cases in the logic of a canister.

In fact, a canister cannot terminate in the same way a process can terminate — because there is no exit() or abort() system call. A canister can only be removed from the Internet Computer by its controller through an administrative command. The controller of a canister is a user or another canister that is allowed to perform administrative commands, such as removing or updating the canister. A canister controlling another canister is a key ingredient when building autonomous or self-governing services on the Internet Computer.

Canisters as WebAssembly module instances

A canister is much like a WebAssembly module instance. This is more than just an analogy — this is how canisters are actually implemented on the Internet Computer.

Technically, the code part of a canister is a WebAssembly module that imports the System API; that is, the functionality provided to the canister by the Internet Computer. Moreover, a canister can export an API of its own, into which other canisters can call.

According to the WebAssembly specification: “A module instance is the dynamic representation of a module, complete with its own state and execution stack.” Therefore, a canister is a WebAssembly module instance, not just a WebAssembly module — an important distinction.

Canisters use orthogonal persistence to make it seem like the module instance lives forever, rendering databases or file IO obsolete. To persistently store a variable, developers can just write the variable to memory. The operation of persisting the data is completely transparent to the developer, and orthogonal in the sense that the developer doesn’t have to do anything special to persist the data.

All “writes” to a canister’s linear memory are tracked, for two reasons. First, so that failed computations or WebAssembly traps can be rolled back. Next, if one replica in a subnetwork crashes due to unforeseen events such as power outages, when the replica comes back online, it will request modified pages from other replicas, so it can resume operation.

This is how the Internet Computer maintains the illusion of providing WebAssembly module instances with indefinite lifetime.

Embracing WebAssembly affords the Internet Computer several benefits:

  • Canisters can be written in any language that can compile to WebAssembly (e.g. Motoko, Rust), and canisters written in different languages are fully interoperable.
  • WebAssembly is deterministic, except for a few edge cases that are easy to rule out.
  • WebAssembly has formal semantics. Over a longer time horizon, we expect to see end-to-end formally verified WebAssembly execution environments, for additional security.

Moreover, we will evolve with the WebAssembly specification, adding support for new features as they become mature enough. For example, it is on our roadmap to support multiple modules in a single canister.

_________________________________________________________________

Software canisters are the building blocks of the Internet Computer. Internet scale services will be implemented by many collaborating canisters. A service with a billion users could require thousands of canisters just to store user data.

But this is not a problem. The canister abstraction is designed to scale; both vertically, by becoming more powerful over time, but mainly through horizontal scaling — or scaling out, using a large number of canisters that collaborate to implement a single service.

The Internet Computer drastically simplifies the experience of writing and deploying code. Get started building on the Internet Computer using canisters by clicking here.

Start building at sdk.dfinity.org and join our developer community at forum.dfinity.org.

--

--

DFINITY
The Internet Computer Review

The Internet Computer is a revolutionary blockchain that hosts unlimited data and computation on-chain. Build scalable Web3 dapps, DeFi, games, and more.