A new FRAME-based Substrate node, ready for hacking 🚀
Follow these steps to prepare a local Substrate development environment 🛠️
Install all the required dependencies with a single command (be patient, this can take up to 30 minutes).
curl https://getsubstrate.io -sSf | bash -s -- --fast
If the command above doesn't work, the dependencies can also be installed manually.
sudo apt install -y cmake pkg-config libssl-dev git gcc build-essential git clang libclang-dev
curl https://sh.rustup.rs -sSf | sh
rustup default stable
rustup toolchain install nightly-2021-01-20
rustup target add wasm32-unknown-unknown --toolchain nightly-2021-01-20
rustup update
The current rust nightly version is fixed to the nightly build: nightly-2021-01-20
to ensure that the project always builds and the tests work.
This is especially the case for the GitHub Actions pipelines which otherwise update their rust versions everytime they are triggered.
In order to upgrade the nightly version, the following commands need to be run:
rustup check # check if there is a new version
rustup update # updates the rust version
Find manual setup instructions at the Substrate Developer Hub.
#### MacOS M1 Setup Extra Steps
You will need to perform a few actions manually in order for the project to compile on a M1 Mac.
- make sure the top-level
Cargo.toml
is adjusted for the patching ofrust-rocksdb
- patch
rust-rocksdb
manually, by cloning and overwritting it: https://github.com/hdevalence/rust-rocksdb - patch
wasmtime-runtime/src/traphandlers.rs:169
-> change(*cx.uc_mcontext).__ss.__rip as *const u8
to(*cx.uc_mcontext).__ss.__pc as *const u8
locally. The file is located here:/Users/<your-name>/.cargo/registry/src/github.com-<some-hash>/wasmtime-runtime-0.19.0/src/traphandlers.rs
Once the development environment is set up, build the node template. This command will build the Wasm and native code:
WASM_BUILD_TOOLCHAIN=nightly-2021-01-20 cargo build --release
The following section describes how to build the provotum-mixnet docker image and how to use the Github container registry.
To use the images built and hosted on the Github container registry (ghcr.io), you need to be logged in.
Expose a Github personal access token with package:read
rights and expose it:
- locally as an ENV variable
export CR_PAT=***
- as a Github secret such that it can be used inside a Github Action:
${{ secrets.CR_PAT }}
echo $CR_PAT | docker login ghcr.io -u $GITHUB_USER --password-stdin
The command needs to be execute from the parent folder of: /node
and /crypto
(in this case called: provotum-mixnet
) since both folders are required inside the Docker context during the build.
~/.../provotum-mixnet: DOCKER_BUILDKIT=1 docker build . -f ./node/Dockerfile --tag provotum-dev .
Once the project has been published and is publicy available. The path requirement for the crypto
crate inside node/pallets/mixnet/Cargo.toml
can be replaced with a reference to the Github project in which the crypto
crate is hosted.
To build the project such that it can run on different underlying architectures (e.g., amd64
and arm64
), the necessary cross-compilation and emulation libraries need to be installed.
On Linux (Ubuntu 20.04), the following needs to be installed:
sudo apt install qemu binfmt-support qemu-user-static
The Docker image can then be built using Docker Buildx.
~/.../provotum-mixnet: DOCKER_BUILDKIT=1 docker buildx build --platform "linux/amd64,linux/arm64" -f ./node/Dockerfile --tag provotum-docker .
Note. This is not recommend because it takes a 1h+ to build the arm64
image on Linux. I recommend to build the images natively whenever possible.
A Github Action workflow exists to build the provotum
docker container and push it to the Github container registry. Have a look at the .github/workflows/build.yml
for more details.
The GitHub-hosted runner can only build for the following architectures: Windows, MacOS, Linux
Run the following command to execute all tests.
cargo +nightly-2021-01-20 test
Run the following command to execute the test of the pallet-mixnet
crate:
cargo +nightly-2021-01-20 test -p pallet-mixnet --features runtime-benchmarks
Navigate into the folder: node/node
and run the following command to check that all benchmarks are working correctly. Note: This executes the tests.
cargo +nightly-2021-01-20 test -p pallet-mixnet --features runtime-benchmarks
Build the provotum node including the benchmark feature by running the following command:
cargo +nightly-2021-01-20 build --features runtime-benchmarks
Note: To produce results that closely resemble the production environment, make sure to use the flag --release
. Please, be aware that this will increase the compilation time. Also, the binary will end up inside ./target/release
and not ./target/debug
.
Navigate back to the folder: node
.
- To list all existing commands of the
pallet-mixnet
crate run the following command:
./target/debug/provotum benchmark --chain dev --pallet "pallet_mixnet" --extrinsic "*" --repeat 0
- To perform all benchmarks run the following command. Note: The number of times each benchmark is executed can be changed via
--repeat
.
./target/debug/provotum benchmark --chain dev --pallet "pallet_mixnet" --extrinsic "*" --repeat 10
Purge any existing dev chain state:
./target/release/provotum purge-chain --dev
Start a dev chain:
./target/release/provotum --dev
Or, start a dev chain with detailed logging:
RUST_LOG=debug RUST_BACKTRACE=1 ./target/release/provotum -lruntime=debug --dev
To start a multi-node local test network, the docker-compose.yml
file can be used.
docker-compose up
This starts a three-node local test network with:
- Alice, as voting-authority (cannot author blocks, but is the voting admin)
- Bob and Charlie, as sealers and PoA-authorities (can author blocks)
There are two possible network modes:
- bridge all containers run in a separate docker network (e.g., 172.31.0.0/16, Alice on 172.31.0.2, Bob on 172.31.0.3, and so on...)
- host all containers are exposed on the local network (e.g., 127.0.0.1, Alice on 127.0.0.1:9944, Bob on 127.0.0.1:9945, and so on...)
Note: If you want to curl
one of the conatiners from the local network and the containers are running in bridge mode, it won't work. You need to either execute the curl
from inside of one of the containers OR alterantively switch to the host network.
Switching networks can be done by commenting out the respective network block in the docker-compose.yml
:
- to activate host network mode:
alice: container_name: alice image: ghcr.io/meck93/provotum-mixnet:latest command: --chain=local --name Alice --base-path /tmp/alice --port 30333 --ws-port 9944 --rpc-port 9933 network_mode: host # network_mode: bridge # ports: # - 9944:9944 # (host_port:container_port) # - 30333:30333 # - 9933:9933
- to activate bridge network mode:
alice: container_name: alice image: ghcr.io/meck93/provotum-mixnet:latest command: --chain=local --name Alice --base-path /tmp/alice --port 30333 --ws-port 9944 --rpc-port 9933 # network_mode: host network_mode: bridge ports: - 9944:9944 # (host_port:container_port) - 30333:30333 - 9933:9933
A Substrate project such as this consists of a number of components that are spread across a few directories.
A blockchain node is an application that allows users to participate in a blockchain network. Substrate-based blockchain nodes expose a number of capabilities:
- Networking: Substrate nodes use the
libp2p
networking stack to allow the nodes in the network to communicate with one another. - Consensus: Blockchains must have a way to come to consensus on the state of the network. Substrate makes it possible to supply custom consensus engines and also ships with several consensus mechanisms that have been built on top of Web3 Foundation research.
- RPC Server: A remote procedure call (RPC) server is used to interact with Substrate nodes.
There are several files in the node
directory - take special note of the following:
chain_spec.rs
: A chain specification is a source code file that defines a Substrate chain's initial (genesis) state. Chain specifications are useful for development and testing, and critical when architecting the launch of a production chain. Take note of thedevelopment_config
andtestnet_genesis
functions, which are used to define the genesis state for the local development chain configuration. These functions identify some well-known accounts and use them to configure the blockchain's initial state.service.rs
: This file defines the node implementation. Take note of the libraries that this file imports and the names of the functions it invokes. In particular, there are references to consensus-related topics, such as the longest chain rule, the Aura block authoring mechanism and the GRANDPA finality gadget.
After the node has been built, refer to the embedded documentation to learn more about the capabilities and configuration parameters that it exposes:
./target/release/provotum --help
In Substrate, the terms "runtime" and "state transition function" are analogous - they refer to the core logic of the blockchain that is responsible for validating blocks and executing the state changes they define. The Substrate project in this repository uses the FRAME framework to construct a blockchain runtime. FRAME allows runtime developers to declare domain-specific logic in modules called "pallets". At the heart of FRAME is a helpful macro language that makes it easy to create pallets and flexibly compose them to create blockchains that can address a variety of needs.
Review the FRAME runtime implementation included in this template and note the following:
- This file configures several pallets to include in the runtime. Each pallet configuration is
defined by a code block that begins with
impl $PALLET_NAME::Trait for Runtime
. - The pallets are composed into a single runtime by way of the
construct_runtime!
macro, which is part of the core FRAME Support library.
The runtime in this project is constructed using many FRAME pallets that ship with the
core Substrate repository and a
template pallet that is defined in the pallets
directory.
A FRAME pallet is compromised of a number of blockchain primitives:
- Storage: FRAME defines a rich set of powerful storage abstractions that makes it easy to use Substrate's efficient key-value database to manage the evolving state of a blockchain.
- Dispatchables: FRAME pallets define special types of functions that can be invoked (dispatched) from outside of the runtime in order to update its state.
- Events: Substrate uses events to notify users of important changes in the runtime.
- Errors: When a dispatchable fails, it returns an error.
- Trait: The
Trait
configuration interface is used to define the types and parameters upon which a FRAME pallet depends.