Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • nodes/rust/duniter-v2s
  • llaq/lc-core-substrate
  • pini-gh/duniter-v2s
  • vincentux/duniter-v2s
  • mildred/duniter-v2s
  • d0p1/duniter-v2s
  • bgallois/duniter-v2s
  • Nicolas80/duniter-v2s
8 results
Select Git revision
Loading items
Show changes
Showing
with 577 additions and 245 deletions
# Weights benchmarking
## What is the reference machine?
For now (09/2022), it's a `Raspberry Pi 4 Model B - 4GB` with an SSD connected via USB3.
To cross-compile the benchmarks binary for armv7:
```
./scripts/cross-build-arm.sh --features runtime-benchmarks
```
The cross compiled binary is generated here: `target/armv7-unknown-linux-gnueabihf/release/duniter`
## How to benchmarks weights of a Call/Hook/Pallet
1. Create the benchmarking tests. See commit f5f2ae969ac592ba9957b0e40e18d6e4b0048473 for a
complete real example.
2. Run the benchmark test on your local machine:
```
cargo test -p <pallet> --features runtime-benchmarks
```
3. If the benchmark tests compiles and pass, compile the binary with benchmarks on your local
machine:
```
cargo build --release --features runtime-benchmarks
```
4. Run the benchmarks on your local machine (to test if it work with a real runtime). See 0d1232cd0d8b5809e1586b48376f8952cebc0d27 for a complete real example. The command is:
```
duniter benchmark pallet --chain=CHAINSPEC --steps=50 --repeat=20 --pallet=<pallet> --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/common/src/weights/
```
5. Use the generated file content to create the `WeightInfo` trait and the `()` dummy implementation in `pallets/<pallet>/src/weights.rs`. Then use the `WeightInfo` trait in the real code of the pallet. See 62dcc17f2c0b922e883fbc6337a9e7da97fc3218 for a complete real example.
6. Redo steps `3.` and `4.` on the reference machine.
7. Use the `runtime/common/src/weights/pallet_<pallet>.rs` generated on the reference machine in the runtimes configuration. See af62a3b9cfc42d6653b3a957836f58540c18e65a for a complete real example.
Note 1: Use relevant chainspec for the benchmarks in place of `CHAINSPEC`, for example `--chain=dev`.
Note 2: If the reference machine does not support wasmtime, you should replace `--wasm-execution=compiled`
by `--wasm-execution=interpreted-i-know-what-i-do`.
## Generate base block benchmarking
1. Build binary for reference machine and copy it on reference machine.
2. Run base block benchmarks command:
```
duniter benchmark overhead --chain=dev --execution=wasm --wasm-execution=compiled --weight-path=./runtime/common/src/weights/ --warmup=10 --repeat=100
```
3. Commit changes and open an MR.
## Generate storage benchmarking
1. Build binary for reference machine and copy it on reference machine.
2. Copy a DB on reference machine (on ssd), example: `scp -r -P 37015 tmp/t1 pi@192.168.1.188:/mnt/ssd1/duniter-v2s/`
3. Run storage benchmarks command, example:
```
duniter benchmark storage -d=/mnt/ssd1/duniter-v2s/t1 --chain=gdev --mul=2 --weight-path=. --state-version=1
```
4. Copy the generated file `paritydb_weights.rs` in the codebase in folder `runtime/common/src/weights/`.
5. Commit changes and open an MR.
## How to Write Benchmarks
### Calls
Ensure that any extrinsic call is benchmarked using the most computationally intensive path, i.e., the worst-case scenario.
### Hooks
Benchmark each hook to determine the weight consumed by it; hence, it is essential to benchmark all possible paths.
### Handlers and Internal Functions
When designing handlers and internal functions, it is advisable to avoid having them return weight for the following reasons:
1. **Simplified Benchmarking**: Writing benchmarks for hooks or calls where handlers and internal functions are utilized becomes more straightforward.
2. **Reduced Benchmarking Complexity**: By directly measuring execution and overhead in a single pass, the number of benchmarks is minimized.
3. **Enhanced Readability**: Understanding that weight accounting occurs at the outermost level improves the overall readability of the code.
One notable exception is the internal functions called in hooks like `on_idle` or `on_initialize` that can be easier to benchmark separately when the hook contains numerous branching.
# How to Build Duniter-V2S Debian Package
Compile packages for native integration for Debian-based systems.
## With Docker (on any system)
1. Install Docker and Docker Buildx.
2. Use the `scripts/build-deb.sh` script.
3. The `.deb` packages will be located in the `target/debian` folder.
## Without Docker (on a Debian-based system)
1. Install the necessary dependencies:
```sh
sudo apt-get install -y clang cmake protobuf-compiler libssl-dev
```
2. Compile the project:
```sh
cargo build --release
```
3. Install `cargo-deb`:
```sh
cargo install cargo-deb
```
4. Build the Duniter node `.deb` package:
```sh
cargo deb --no-build -p duniter
```
5. The `.deb` package will be located in the `target/debian` folder.
# How to build duniter-v2s for arm
Cross-compile Duniter to arm (e.g. Raspberry Pi).
## With Docker
1. Create a docker image that contains the build environment
```bash
docker build -t duniter-v2s-arm-builder -f docker/cross-arm.Dockerfile .
```
2. Use this docker image to cross-compile duniter-v2s for armv7
```bash
./scripts/cross-build-arm.sh
```
then, get the final binary at `target/armv7-unknown-linux-gnueabihf/release/duniter`.
## Without Docker
**Warning**: armv7 (default for Raspberry Pi) is **not** supported. Linux on RPi can be easily switched to aarch64, please search how to do so on the Internet.
This produces a musl build: the resulting executable is static, hence more portable than a dynamic one. It will be compatible with systems older than the compilation host.
```bash
# Install the tools
rustup target add aarch64-unknown-linux-musl --toolchain nightly-2023-08-23-x86_64-unknown-linux-musl
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install musl-dev:arm64 musl-tools g++-aarch64-linux-gnu gcc-aarch64-linux-gnu
# Cross-compile
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc cargo build --target=aarch64-unknown-linux-musl --release
```
# How to Build the Duniter RPM Package
1. Install dependencies:
```sh
# Fedora
sudo dnf install clang cmake protobuf-compiler openssl-devel
```
2. Compile the project:
```sh
cargo build --release
```
3. Install `cargo-generate-rpm`:
```sh
cargo install cargo-generate-rpm
```
4. Build the package:
```sh
cargo generate-rpm -p node
```
5. The `.rpm` package will be located in the `target/generate-rpm` folder.
# Autocompletion # Autocompletion
One can generate autocompletion for its favorite shell using the following option: You can generate autocompletion for your favorite shell using the following option:
```sh ```sh
cargo run --release -- completion --generator <GENERATOR> cargo run -- completion --generator <GENERATOR>
``` ```
Where `GENERATOR` can be any of `bash`, `elvish`, `fish`, `powershell` and `zsh`. Where `GENERATOR` can be any of `bash`, `elvish`, `fish`, `powershell` and `zsh`.
...@@ -14,7 +14,7 @@ First, get the completion file in a known place: ...@@ -14,7 +14,7 @@ First, get the completion file in a known place:
```sh ```sh
mkdir -p ~/.local/share/duniter mkdir -p ~/.local/share/duniter
cargo run --release -- completion --generator bash > ~/.local/share/duniter/completion.bash cargo run -- completion --generator bash > ~/.local/share/duniter/completion.bash
``` ```
You can now manually source the file when needed: You can now manually source the file when needed:
...@@ -30,3 +30,17 @@ Or you can automatically source it at `bash` startup by adding this to your `~/. ...@@ -30,3 +30,17 @@ Or you can automatically source it at `bash` startup by adding this to your `~/.
``` ```
You can now enjoy semantic completion of the `./target/release/duniter` command using `<Tab>` key. You can now enjoy semantic completion of the `./target/release/duniter` command using `<Tab>` key.
## Zsh
Zsh equivalent
```sh
# make directory to store completion
mkdir -p ~/.zsh/completion
# write the completion script
cargo run -- completion --generator zsh > ~/.zsh/completion/_duniter.zsh
# add the following lines to your ~/.zshrc
fpath+=(~/.zsh/completion)
compinit # might slow down terminal startup
```
# Distance rule evaluation
The [distance rule](https://duniter.org/blog/duniter-deep-dive-wot/) is computationally too heavy to be handled by the runtime. Therefore it is computed offchain using the distance oracle.
Distance evaluation is operated on a voluntary basis by individual smiths. Since evaluators can lie or make errors, the result considered for applying the distance rule is the median of results published by the different evaluators.
## Running distance evaluation
Any smith member authoring blocks can run a distance evaluation oracle. It is better to have a machine more powerful than the reference machine.
Create a service from this command line, run by the same user as Duniter, on the same system:
/absolute/path/to/duniter distance-oracle --interval <duration>
The duration is the number of seconds between two evaluations. It should be less than the duration of a distance evaluation period. If it is equal, your node may not have the time to evaluate distance.
The oracle communicates with Duniter using its RPC API and using temporary files. Without additional (unsupported) configuration, both must run on the same filesystem. The node also needs to be forging blocks for the evaluations to be published.
### Additional Duniter configuration
Duniter should keep states at least one distance evaluation period old. If this is more than the default 256 and your node is not already an archive (`--state-pruning archive`), use the option `--state-pruning <blocks>`.
## Weights and Fees Calculation
### Introduction
Transaction weights and fees ensure network efficiency, fairness, and security in Substrate-based blockchains. These concepts are designed to manage resource allocation and incentivize proper usage of the blockchain infrastructure.
### Transaction Weights
Transaction weight measures the computational resources required to process a transaction. It is determined by factors such as the complexity of the transaction logic and the amount of data involved. Transactions with higher weights consume more resources and thus contribute to the overall load on the network.
### Transaction Fees
Transaction fees in Substrate-based blockchains are crucial for efficiently managing network resources and sustaining economic viability. They regulate resource allocation by ensuring transactions consuming more computational resources incur higher fees, discouraging spam, and promoting fair use of network capacity.
The fees are computed as follows:
`fee = base_fee + weight2fee * fee_multiplier + length2fee + tip`
## Fees in Duniter
### Fees Implementation Details
Implementing a zero-fee chain in Duniter involves configuring the blockchain to waive transaction fees when the current block weight is below a specified target. This approach aims to promote accessibility and encourage participation by eliminating fees during periods of lower network activity.
However, transaction fees are applied when the block weight or length surpasses the defined targets to ensure network security and stability during increased usage. Additionally, leveraging the fee multiplier mechanism helps deter potential prolonged network attacks by dynamically adjusting fee levels based on previous network conditions.
Duniter members benefit from the quota system, which refunds transaction fees during high network activity periods.
Fees are computed as follows:
* If `current_weight < 0.25 * max_weight` and `current_length < 0.25 * max_length` and `fee_multiplier = 1`, ie. normal load:
`fee = 0`
* If `current_weight > 0.25 * max_weight` or `current_length > 0.25 * max_length` or `fee_multiplier > 1`, ie. heavy usage (approximately more than 135 transactions per second):
`fee = `5cĞ1 + extrinsic_weight * (5cĞ1/base_extrinsic_weight)* fee_multiplier + extrinsic_length/100 + tip`
The multiplier is updated as follows:
* If `current_weight > 0.25 * max_weight` or `current_length > 0.25 * max_length`:
`Min(fee_multiplier += 1, 10)`
* If `current_weight < 0.25 * max_weight` and `current_length < 0.25 * max_length`:
`Max(fee_multiplier -= 1, 1)`
# Debian Setup Instructions
## Mirror Node
1. Download the Duniter .deb file.
2. Install the package: `dpkg -i duniter_vx.y.z.deb`.
3. Change the default configuration (at least the node name) by modifying `/etc/duniter/env_file`.
4. Start the service: `sudo systemctl start duniter-mirror.service`.
5. Enable the service at startup: `sudo systemctl enable duniter-mirror.service`.
## Smith Node
1. Download the Duniter .deb file.
2. Install the package: `dpkg -i duniter_vx.y.z.deb`.
3. Change the default configuration (at least the node name) by modifying `/etc/duniter/env_file`.
4. Create network keys using the same base path as in the config file: `duniter2 key generate-node-key --base-path <YOUR_BASE_PATH> --chain <YOUR_CHAIN>`.
5. Start the service: `sudo systemctl start duniter-smith.service`.
6. Enable the service at startup: `sudo systemctl enable duniter-smith.service`.
## Distance Oracle
A Smith node needs to be installed.
1. Change the default configuration by modifying `/etc/duniter/env_file`.
2. Start the service: `sudo systemctl start distance-oracle.service`.
3. Enable the service at startup: `sudo systemctl enable distance-oracle.service`.
# How to deploy a permanent mirror node on ĞDev network
## Publish a node
### Duniter part
- Add this docker-compose on your server :
[docker/compose/gdev-mirror.docker-compose.yml](https://git.duniter.org/nodes/rust/duniter-v2s/-/blob/master/docker/compose/gdev-mirror.docker-compose.yml)
- Edit lines 26: `"/dns/SERVER_DOMAIN/tcp/30333/p2p/PEER_ID"`
with your domain name an the PEER_ID you get using te command in comment.
- If you have write access errors run in docker-compose.yml folder : `chmod o+rwX -R .`
- `docker-compose up -d` to start your node
### Reverse-proxy part (with Nginx)
In `/etc/nginx/sites-enabled/gdev.YOUR_DOMAIN` put (you can probably do simpler) :
```
server {
server_name gdev.YOUR_DOMAIN.fr;
listen 443 ssl http2;
ssl_certificate /etc/nginx/ssl/YOUR_DOMAIN.cert;
ssl_certificate_key /etc/nginx/ssl/YOUR_DOMAIN.key;
root /nowhere;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_read_timeout 90;
location /http {
proxy_pass http://localhost:9933;
proxy_http_version 1.1;
}
location /ws {
proxy_pass http://localhost:9944;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_read_timeout 1200s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
```
and replace `YOUR_DOMAIN` by your domain each time.
- [generate your ssl certificates](https://github.com/acmesh-official/acme.sh) with let's encrypt
if you don't already have a wildcard certificate.
- `service nginx reload`
Your node is now online as a mirror node. It's fully capable for wallet use.
To go further, read [How to become a (black)smith](./smith.md)
## Upgrade your node with minimal interruption
1. Modify docker image tag on your compose file
2. Run `docker compose pull`, this will pull the new image.
3. Run `docker compose up -d --remove-orphans`, this will recreate the container
4. Verify that your node restarted well `docker compose logs duniter-rpc`
5. Remove the old image `docker images rmi duniter/duniter-v2s:OLD_TAG`
# How to become a (black)smith
## Publish a node
### Duniter part
- Add this docker-compose on your server :
[docker/compose/gdev-validator.docker-compose.yml](https://git.duniter.org/nodes/rust/duniter-v2s/-/blob/master/docker/compose/gdev-validator.docker-compose.yml)
- Edit lines 26 and 51 : `/ip4/SERVER_IP/tcp/30333/p2p/PEER_ID`
with your IP or domain an the PEER_ID you get using te command in comment.
Note: duniter-rpc PEER_ID and duniter-validator PEER_ID isn't the same.
- If you have write access errors run in docker-compose.yml folder : `chmod o+rwX -R .`
- `docker-compose up -d` to start your node
### Reverse-proxy part (with Nginx)
In `/etc/nginx/sites-enabled/gdev.YOUR_DOMAIN` put (you can probably do simpler) :
```
server {
server_name gdev.YOUR_DOMAIN.fr;
listen 443 ssl http2;
ssl_certificate /etc/nginx/ssl/YOUR_DOMAIN.cert;
ssl_certificate_key /etc/nginx/ssl/YOUR_DOMAIN.key;
root /nowhere;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_read_timeout 90;
location /http {
proxy_pass http://localhost:9933;
proxy_http_version 1.1;
}
location /ws {
proxy_pass http://localhost:9944;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_read_timeout 1200s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
```
and replace `YOUR_DOMAIN` by your domain each time.
- [generate your ssl certificates](https://github.com/acmesh-official/acme.sh) with let's encrypt
if you don't already have a wildcard certificate.
- `service nginx reload`
Your node is now online as a mirror node. It's fully capable for wallet use.
## Join the Smith WoT
- add polkadot webextension to be able to authentificate with your account.
- Go to [any node with polkadotjs ui](https://gdev.1000i100.fr/dev-ui/?rpc=wss://gdev.1000i100.fr/ws)
- Ask to join Smith WoT (you need to already be in the main WoT)
- developer > extrinsics > YOUR_SMITH_ACCOUNT > smithMembership > requestMemberShip(metadata)
- add your p2p endpoint (optional)
- add your session key (follow point 1 to 4 from Validate blocks > Generate and publish your session key)
- Send the query
- Await smith certification : developer > extrinsics > CERTIFIER_SMITH_ACCOUNT > smithCert > addCert(receiver)
When you have at least 3 certifications, your'in !
## Validate blocks (blacksmith work)
- Generate and publish your session keys
1. create an ssh bridge from your desktop/laptop to your server : `ssh -L 9945:localhost:9945 SSH_USER@YOUR_SERVER`
2. In your browser go to [polkadotjs : ws://localhost:9945](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2Flocalhost%3A9945#/explorer)
3. In the UI : developer > appel RPC > author > rotateKey() and run
4. copy the result in clipboard
5. In the UI : developer > extrinsics > YOUR_SMITH_ACCOUNT > authorityMembers > setSessionKeys(keys) then copy your session keys and run the query.
6. **wait 48h to verify you keep sync**
- Join
- In the UI : developer > extrinsics > YOUR_SMITH_ACCOUNT > authorityMembers > goOnline()
If you're not able to monitor, reboot, act on your node, goOffline() to avoid penality to the blockchain and to you.
## Upgrade your node with minimal interruption
1. Modify docker image tag on your compose file
2. Run `docker compose pull`, this will pull the new image.
3. Run `docker compose up -d --remove-orphans`, this will recreate the container
4. Verify that your node restarted well `docker compose logs duniter-validator`
5. Remove the old image `docker images rmi duniter/duniter-v2s:OLD_TAG`
[package] [package]
authors = ['Axiom-Team Developers <https://axiom-team.fr>'] authors.workspace = true
description = 'duniter end2end tests.' description = "duniter end2end tests"
edition = '2018' edition.workspace = true
homepage = 'https://substrate.dev' homepage.workspace = true
license = 'AGPL-3.0' license.workspace = true
name = 'duniter-end2end-tests' name = "duniter-end2end-tests"
repository = 'https://git.duniter.org/nodes/rust/duniter-v2s' repository.workspace = true
version = '3.0.0' version.workspace = true
[dev-dependencies]
async-trait = "0.1"
clap = { version = "3.0", features = ["derive"] }
ctrlc = "3.2.2"
cucumber = "0.11"
env_logger = "0.9.0"
notify = "4.0"
parity-scale-codec = "2.3.1"
portpicker = "0.1.1"
serde_json = "1.0.64"
sp-keyring = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-02" }
subxt = { git = 'https://github.com/librelois/subxt.git', branch = 'duniter-monthly-2022-02' }
tokio = { version = "1.15.0", features = ["macros"] }
[[test]] [[test]]
name = "cucumber_tests" name = "cucumber_tests"
harness = false # allows Cucumber to print output instead of libtest harness = false # allows Cucumber to print output instead of libtest
[features]
default = ["std"]
std = [
"anyhow/std",
"codec/std",
"distance-oracle/std",
"hex/std",
"serde_json/std",
"sp-core/std",
"sp-runtime/std",
]
standalone = ["distance-oracle/standalone"]
try-runtime = ["distance-oracle/try-runtime", "sp-runtime/try-runtime"]
runtime-benchmarks = []
[dev-dependencies]
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive", "cargo"] }
codec = { workspace = true }
ctrlc = { workspace = true }
cucumber = { workspace = true, features = ["macros"] }
distance-oracle = { workspace = true }
env_logger = { workspace = true }
hex = { workspace = true }
notify = { workspace = true }
notify-debouncer-mini = { workspace = true }
portpicker = { workspace = true }
serde_json = { workspace = true }
sp-core = { workspace = true }
sp-keyring = { workspace = true }
sp-runtime = { workspace = true }
subxt = { workspace = true, features = [
"native",
"jsonrpsee",
] }
tokio = { workspace = true, features = ["macros", "time", "rt-multi-thread"] }
# Duniter-v2s integration tests # Duniter-v2s end2end tests
## cucumber functionnal tests ## Cucumber functional tests
We use [cucumber] to be able to describe test scenarios in human language. We use [cucumber] to be able to describe test scenarios in human language.
...@@ -20,7 +20,7 @@ Feature: Balance transfer ...@@ -20,7 +20,7 @@ Feature: Balance transfer
Then dave should have 5 ĞD Then dave should have 5 ĞD
``` ```
### create a new functional test ### Create a new functional test
To create a new test scenario, simply create a new file with a name of your choice in the To create a new test scenario, simply create a new file with a name of your choice in the
`/cucumber-features` folder and give it the extension `.feature`. `/cucumber-features` folder and give it the extension `.feature`.
...@@ -47,44 +47,16 @@ Feature: My awesome feature ...@@ -47,44 +47,16 @@ Feature: My awesome feature
Then We should observe that Then We should observe that
``` ```
### Test users ### Steps
6 test users are provided:
- alice
- bob
- charlie
- dave
- eve
- ferdie
### genesis state Each scenario is a list of steps. In our context (blockchain), only the `When` and `Then` steps make sense, `Given` being the genesis.
Each scenario bootstraps its own blockchain with its own genesis state.
By default, all scenarios use the same configuration for the genesis, which is located in the file
`/cucumber-genesis/default.json`.
You can define a custom genesis state for each scenario with the tag `@genesis.confName`.
The genesis configuration must then be defined in a json file located at
`/cucumber-genesis/confName.json`.
You can also define a custom genesis at the feature level, all the scenarios of this feature will
then inherit the genesis configuration.
### Currency amounts
Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are not supported.
If you need more precision, you can express amounts in cents of ĞD (write `cĞD`), or in thousandths
of UD (write `mUD`).
#### When #### When
List of possible actions: List of possible actions:
- transfer: `alice send 5 ĞD to bob` - transfer: `alice sends 5 ĞD to bob`
- transfer_ud: `alice send 3 UD to bob` - transfer_ud: `alice sends 3 UD to bob`
- transfer_all: `alice sends all her ĞDs to bob` - transfer_all: `alice sends all her ĞDs to bob`
#### Then #### Then
...@@ -95,36 +67,80 @@ List of possible actions: ...@@ -95,36 +67,80 @@ List of possible actions:
Example: `alice should have 10 ĞD` Example: `alice should have 10 ĞD`
### Universal dividend creation
#### Then
- Check the current UD amount - Check the current UD amount
Usage: `Current UD amount should be {amount}.{cents} ĞD` Usage: `Current UD amount should be {amount}.{cents} ĞD`
Example: `Current UD amount should be 10.00 ĞD` Example: `Current UD amount should be 10.00 ĞD`
- Check the monetary mass - Check the monetary mass
Usage: `Monetary mass should be {amount}.{cents} ĞD` Usage: `Monetary mass should be {amount}.{cents} ĞD`
Example: `Monetary mass should be 30.00 ĞD` Example: `Monetary mass should be 30.00 ĞD`
### Test users
8 test users are provided derived from the same [dev mnemonic](https://docs.substrate.io/v3/getting-started/glossary/#dev-phrase)
```
bottom drive obey lake curtain smoke basket hold race lonely fit walk
```
with the derivation path `//Alice`, `//Bob`...
- alice `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY`
- bob `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`
- charlie `5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y`
- dave `5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy`
- eve `5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw`
- ferdie `5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL`
- one `5Fxune7f71ZbpP2FoY3mhYcmM596Erhv1gRue4nsPwkxMR4n`
- two `5CUjxa4wVKMj3FqKdqAUf7zcEMr4MYAjXeWmUf44B41neLmJ`
### Currency amounts
Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are not supported.
If you need more precision, you can express amounts in cents of ĞD (write `cĞD`), or in thousandths
of UD (write `mUD`).
### Genesis state
Each scenario bootstraps its own blockchain with its own genesis state.
By default, all scenarios use the same configuration for the genesis, which is located in the file
`/cucumber-genesis/default.json`.
You can define a custom genesis state for each scenario with the tag `@genesis.confName`.
The genesis configuration must then be defined in a json file located at
`/cucumber-genesis/confName.json`.
You can also define a custom genesis at the feature level, all the scenarios of this feature will
then inherit the genesis configuration.
### ignoreErrors
For some scenarios, you may need to perform an action (When) that fails voluntarily, in this case you must add the tag @ignoreErrors to your scenario, otherwise it will be considered as failed
### Run cucumber functional tests ### Run cucumber functional tests
The cucumber tests use the last debug binary in your `target` folder. Make sure this binary corresponds to the executable you want to test by running `cargo build --features constant-fees` before.
To run the cucumber tests, you will need to have the rust toolchain installed locally. To run the cucumber tests, you will need to have the rust toolchain installed locally.
To run all the scenarios (there are many) use the command: `cargo cucumber` To run all the scenarios (there are many) use the command: `cargo cucumber`
You can filter the `.feature` files to run with the option `i`, for instante: You can filter the `.feature` files to run with the option `i`, for instance:
``` ```
cargo cucumber -i monetary* cargo cucumber -i 'monetary*'
``` ```
Will only run `.feature` files that start with `"monetary"`. will only run `.feature` files that start with `"monetary"`.
The features will be tested in parallel and logs files will be written in the `end2end-tests` folder.
If you get an `Error: Timeout`, look at the logs to understand why Duniter did not launch successfully. You can also set the environment variable `DUNITER_END2END_TESTS_SPAWN_NODE_TIMEOUT` to increase the timeout for node spawn.
### Contribute to the code that runs the tests ### Contribute to the code that runs the tests
...@@ -152,6 +168,19 @@ subxt metadata -f bytes > resources/metadata.scale ...@@ -152,6 +168,19 @@ subxt metadata -f bytes > resources/metadata.scale
If you don't have subxt, install it: `cargo install subxt-cli` If you don't have subxt, install it: `cargo install subxt-cli`
### Debug
Cucumber uses carriage returns to pretty-print.
Hence you may need to append an additional newline to your debug printings.
This commandline can be used to enable more debugging:
```bash
RUST_LOG=debug cargo cucumber -i distance_fail.feature -vvv --show-output
```
You can use the `log::debug` macro if the line `env_logger::init()` is uncommented in `cucumber_tests.rs`.
[BDD]: https://en.wikipedia.org/wiki/Behavior-driven_development [BDD]: https://en.wikipedia.org/wiki/Behavior-driven_development
[cucumber]: https://cucumber.io/ [cucumber]: https://cucumber.io/
[Cucumber Rust Book]: https://cucumber-rs.github.io/cucumber/current/writing/index.html [Cucumber Rust Book]: https://cucumber-rs.github.io/cucumber/current/writing/index.html
......
Feature: Account creation
Scenario: Create a new account with enough funds
When alice sends 5 ĞD to dave
Then dave should have 5 ĞD
When 1 block later
"""
The blockchain did not automatically withdraw account creation tax (3 ĞD) because this feature has been removed
"""
Then dave should have 5 ĞD
Scenario: Create a new account without enough funds then retry with enough funds
When alice sends 2 ĞD to eve
Then eve should have 2 ĞD
When 1 block later
"""
The blockchain did not automatically destroy Eve account for Eve not having enough funds to pay the new account tax
Because this feature has been removed
"""
Then eve should have 2 ĞD
When alice send 5 ĞD to eve
Then eve should have 7 ĞD
When 1 block later
"""
The blockchain did not automatically withdraw account creation tax (3 ĞD) because this feature has been removed
"""
Then eve should have 7 ĞD
@ignoreErrors
Scenario: Create a new account without any funds
Then eve should have 0 ĞD
# Alice is treasury funder for 1 ĞD
Then alice should have 9 ĞD
When eve send 0 ĞD to alice
Then alice should have 9 ĞD
When alice send 5 ĞD to eve
Then eve should have 5 ĞD
When 1 block later
"""
The blockchain did not automatically withdraw account creation tax (3 ĞD) because this feature has been removed
"""
Then eve should have 5 ĞD
Feature: Balance transfer
Scenario: If alice sends 5 ĞD to Dave, Dave will get 5 ĞD
Given alice have 10 ĞD
When alice send 5 ĞD to dave
Then dave should have 5 ĞD
@genesis.wot
Feature: Certification
Scenario: Dave certifies Alice
When 2 blocks later
When dave certifies alice
Then alice should be certified by dave
@genesis.bad_distance
Feature: Distance fail
#
# WoT:
#
# H<->G<->E<->D<->C-->B
# ^ ^ ^ ^
# \ / \ /
# v v v v
# I A
#
# Every member is referee. Referee count = 8; 80% = 6.4
# Certs from Alice and Bob do not ensure the fulfilling of the distance rule
# because the newcomer would reach only 6 members up to G.
Scenario: an unvalidated member fails the distance rule
Then treasury should contain 1 ĞD
When alice sends 7 ĞD to ferdie
Then alice should have 0 ĞD reserved
Then alice should have 199 cĞD
When bob sends 750 cĞD to ferdie
When 15 block later
When alice creates identity for ferdie
Then ferdie identity should be unconfirmed
Then ferdie should be certified by alice
When ferdie confirms his identity with pseudo "ferdie"
Then ferdie identity should be unvalidated
When 3 block later
When bob certifies ferdie
Then ferdie should be certified by bob
Then ferdie should have 0 ĞD reserved
# 700 + 750 - 2(one transaction fee, not a member yet)
Then ferdie should have 1448 cĞD
When ferdie requests distance evaluation
Then ferdie should have 10 ĞD reserved
# 1448 - 1000 - 2(one transaction fee, not a member yet)
Then ferdie should have 446 cĞD
When 7 blocks later
Then treasury should contain 105 cĞD
When alice runs distance oracle
When 7 blocks later
Then ferdie should be certified by alice
Then ferdie should be certified by bob
# The distance rule is failed
Then ferdie identity should be unvalidated
# Ferdie got his reserve slashed
Then ferdie should have 0 ĞD reserved
Then ferdie should have 446 cĞD
# Slashed amount is transfered to treasury
Then treasury should contain 1105 cĞD
Feature: Identity creation
Scenario: alice invites a new member to join the web of trust
# 6 ĞD covers:
# - existential deposit (1 ĞD)
# - transaction fees (below 1 ĞD)
When alice sends 7 ĞD to dave
# Alice is treasury funder for 1 ĞD => 10-1-7 = 2 (minus TODO fees)
Then alice should have 0 ĞD reserved
Then alice should have 199 cĞD
When bob sends 750 cĞD to dave
When charlie sends 6 ĞD to eve
# alice last certification is counted from block zero
# then next cert can be done after cert_period, which is 15
When 15 block later
When alice creates identity for dave
Then dave identity should be unconfirmed
Then dave should be certified by alice
When dave confirms his identity with pseudo "dave"
Then dave identity should be unvalidated
When 3 block later
When bob certifies dave
When charlie certifies dave
Then dave should be certified by bob
Then dave should be certified by charlie
Then dave should have 0 ĞD reserved
# 700 + 750 - 2(one transaction fee, not a member yet)
Then dave should have 1448 cĞD
When dave requests distance evaluation
Then dave should have 10 ĞD reserved
# 1448 - 1000 - 2(one transaction fee, not a member yet)
Then dave should have 446 cĞD
When 7 blocks later
When alice runs distance oracle
When 7 blocks later
Then dave identity should be member
Then dave should have 0 ĞD reserved
Then dave should have 1446 cĞD
Feature: Balance transfer Feature: Monetary mass
Scenario: After 10 blocks, the monetary mass should be 60 ĞD Scenario: After 10 blocks, the monetary mass should be 60 ĞD
Then Monetary mass should be 30.00 ĞD Then Monetary mass should be 30.00 ĞD
Then Current UD amount should be 10.00 ĞD Then Current UD amount should be 10.00 ĞD
When 10 blocks later When 15 blocks later
Then Monetary mass should be 60.00 ĞD Then Monetary mass should be 60.00 ĞD
When 10 blocks later When 10 blocks later
Then Monetary mass should be 90.00 ĞD Then Monetary mass should be 90.00 ĞD
......
Feature: Oneshot account
Scenario: Simple oneshot consumption
When charlie sends 7 ĞD to dave
# Cover the oneshot calls fees
When alice sends 7 ĞD to oneshot dave
# Alice is treasury funder for 1 ĞD, and member so fees are refunded
Then alice should have 2 ĞD
Then dave should have oneshot 7 ĞD
When oneshot dave consumes into account bob
Then dave should have oneshot 0 ĞD
Then bob should have 1698 cĞD
Then bob should have oneshot 0 ĞD
Scenario: Double oneshot consumption
When charlie sends 7 ĞD to dave
Then charlie should have 299 cĞD
# Cover the oneshot calls fees
When alice sends 7 ĞD to oneshot dave
# Alice is treasury funder for 1 ĞD, and member so fees are refunded
Then alice should have 2 ĞD
Then dave should have oneshot 7 ĞD
When oneshot dave consumes 4 ĞD into account bob and the rest into oneshot charlie
Then dave should have oneshot 0 ĞD
Then bob should have 14 ĞD
Then bob should have oneshot 0 ĞD
Then charlie should have 299 cĞD
Then charlie should have oneshot 298 cĞD
@genesis.default @genesis.default
Feature: Balance transfer all Feature: Balance transfer all
Scenario: If alice sends all her ĞDs to Dave, Dave will get 8 ĞD Scenario: If bob sends all his ĞDs to Dave
When alice sends all her ĞDs to dave When bob sends all his ĞDs to dave
""" """
Alice is a smith member, as such she is not allowed to empty her account completely, Bob is a member, as such he is not allowed to empty his account completely,
if she tries to do so, the existence deposit (2 ĞD) must remain. if he tries to do so, the existence deposit (1 ĞD) must remain.
Bob is a member, transaction fees are refunded for him
101 = existential deposit (100) + fees refunded using quota (001)
""" """
Then alice should have 2 ĞD Then bob should have 101 cĞD
Then dave should have 8 ĞD """
10 ĞD (initial Bob balance) - 1 ĞD (Existential deposit) - 0.02 ĞD (transaction fees)
"""
Then dave should have 898 cĞD
# TODO check that the missing cent went to treasury