Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c3c0db7bf |
1252
Cargo.lock
generated
67
Cargo.toml
@ -17,7 +17,7 @@ homepage.workspace = true
|
|||||||
[workspace.package]
|
[workspace.package]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
authors = ["571nky", "57r37ch", "f4750"]
|
authors = ["571nky", "57r37ch", "f4750"]
|
||||||
version = "0.9.3"
|
version = "0.7.180"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
homepage = "https://ghostchain.io"
|
homepage = "https://ghostchain.io"
|
||||||
repository = "https://git.ghostchain.io/ghostchain/ghost-node"
|
repository = "https://git.ghostchain.io/ghostchain/ghost-node"
|
||||||
@ -67,21 +67,17 @@ serial_test = { version = "2.0.0" }
|
|||||||
color-eyre = { version = "0.6.1" }
|
color-eyre = { version = "0.6.1" }
|
||||||
bs58 = { version = "0.5.0" }
|
bs58 = { version = "0.5.0" }
|
||||||
prometheus-parse = { version = "0.2.2" }
|
prometheus-parse = { version = "0.2.2" }
|
||||||
k256 = { version = "0.13.0", default-features = false }
|
|
||||||
rustc-hex = { version = "2.1.0", default-features = false }
|
rustc-hex = { version = "2.1.0", default-features = false }
|
||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
num-traits = { version = "0.2.17", default-features = false }
|
|
||||||
libsecp256k1 = { version = "0.7", default-features = false }
|
libsecp256k1 = { version = "0.7", default-features = false }
|
||||||
bip39 = { package = "parity-bip39", version = "2.0.1" }
|
bip39 = { package = "parity-bip39", version = "2.0.1" }
|
||||||
sha3 = { version = "0.10", default-features = false }
|
sha3 = { version = "0.10", default-features = false }
|
||||||
sha2 = { version = "0.10.7", default-features = false }
|
|
||||||
serde = { version = "1.0.197", default-features = false }
|
serde = { version = "1.0.197", default-features = false }
|
||||||
serde_derive = { version = "1.0.117", default-features = false }
|
serde_derive = { version = "1.0.117", default-features = false }
|
||||||
serde_json = { version = "1.0.114", default-features = false }
|
serde_json = { version = "1.0.114", default-features = false }
|
||||||
blake2-rfc = { version = "0.2.18", default-features = false }
|
blake2-rfc = { version = "0.2.18", default-features = false }
|
||||||
impl-serde = { version = "0.4.0", default-features = false }
|
impl-serde = { version = "0.4.0", default-features = false }
|
||||||
hex = { version = "0.4.3", default-features = false }
|
hex = { version = "0.4.3", default-features = false }
|
||||||
strum = { version = "0.28.0", default-features = false }
|
|
||||||
metered = { package = "prioritized-metered-channel", version = "0.6.1", default-features = false }
|
metered = { package = "prioritized-metered-channel", version = "0.6.1", default-features = false }
|
||||||
|
|
||||||
scale-value = { version = "0.16.0" }
|
scale-value = { version = "0.16.0" }
|
||||||
@ -89,13 +85,6 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features =
|
|||||||
scale-info = { version = "2.5.0", default-features = false }
|
scale-info = { version = "2.5.0", default-features = false }
|
||||||
subxt = { version = "0.37.0", default-features = false }
|
subxt = { version = "0.37.0", default-features = false }
|
||||||
|
|
||||||
frost-secp256k1-tr = { version = "2.2.0", default-features = false, features = ["serialization"] }
|
|
||||||
frost-ed25519 = { version = "2.2.0", default-features = false, features = ["serialization"] }
|
|
||||||
chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["alloc"] }
|
|
||||||
frost-core = { version = "2.2.0", default-features = false }
|
|
||||||
rand_chacha = { version = "0.3.0", default-features = false }
|
|
||||||
hkdf = { version = "0.12.4", default-features = false }
|
|
||||||
|
|
||||||
# Frames
|
# Frames
|
||||||
frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
@ -151,7 +140,6 @@ pallet-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk
|
|||||||
pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-bags-list = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-bags-list = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-bags-list-remote-tests = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
|
||||||
pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-child-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-child-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
@ -166,6 +154,8 @@ pallet-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag =
|
|||||||
pallet-identity = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-identity = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-indices = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-indices = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-multisig = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-multisig = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
|
pallet-nomination-pools = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
|
pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-offences = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-offences = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-preimage = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-preimage = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-proxy = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-proxy = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
@ -193,6 +183,8 @@ generate-bags = { git = "https://github.com/paritytech/polkadot-sdk.git", tag =
|
|||||||
pallet-election-provider-support-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-election-provider-support-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-offences-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-offences-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
|
pallet-nomination-pools-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
|
pallet-bags-list-remote-tests = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0", default-features = false }
|
||||||
|
|
||||||
# Substrate Client dependencies
|
# Substrate Client dependencies
|
||||||
sc-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0" }
|
sc-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.12.0" }
|
||||||
@ -234,22 +226,14 @@ ghost-metrics = { path = "metrics", default-features = false }
|
|||||||
ghost-rpc = { path = "rpc", default-features = false }
|
ghost-rpc = { path = "rpc", default-features = false }
|
||||||
service = { package = "ghost-service", path = "service", default-features = false }
|
service = { package = "ghost-service", path = "service", default-features = false }
|
||||||
ghost-traits = { path = "pallets/traits", default-features = false }
|
ghost-traits = { path = "pallets/traits", default-features = false }
|
||||||
ghost-helpers = { path = "pallets/helpers", default-features = false }
|
|
||||||
ghost-networks = { path = "pallets/networks", default-features = false }
|
ghost-networks = { path = "pallets/networks", default-features = false }
|
||||||
ghost-governance = { path = "pallets/governance", default-features = false }
|
ghost-claims = { path = "pallets/claims", default-features = false }
|
||||||
ghost-weaver = { path = "pallets/weaver", default-features = false }
|
ghost-slow-clap = { path = "pallets/slow-clap", default-features = false }
|
||||||
ghost-exodus = { path = "pallets/exodus", default-features = false }
|
|
||||||
ghost-sudo = { path = "pallets/sudo", default-features = false }
|
|
||||||
ghost-nomination-pools = { path = "pallets/pools", default-features = false }
|
|
||||||
ghost-nomination-pools-runtime-api = { path = "pallets/pools/runtime-api", default-features = false }
|
|
||||||
ghost-nomination-pools-benchmarking = { path = "pallets/pools/benchmarking", default-features = false }
|
|
||||||
ghost-nomination-pools-fuzzer = { path = "pallets/pools/benchmarking", default-features = false }
|
|
||||||
|
|
||||||
ghost-runtime-constants = { package = "ghost-runtime-constants", path = "runtime/ghost/constants", default-features = false }
|
ghost-runtime-constants = { package = "ghost-runtime-constants", path = "runtime/ghost/constants", default-features = false }
|
||||||
casper-runtime = { path = "runtime/casper", default-features = false }
|
casper-runtime = { path = "runtime/casper", default-features = false }
|
||||||
casper-runtime-constants = { package = "casper-runtime-constants", path = "runtime/casper/constants", default-features = false }
|
casper-runtime-constants = { package = "casper-runtime-constants", path = "runtime/casper/constants", default-features = false }
|
||||||
primitives = { package = "ghost-core-primitives", path = "core-primitives", default-features = false }
|
primitives = { package = "ghost-core-primitives", path = "core-primitives", default-features = false }
|
||||||
runtime-common = { package = "runtime-common", path = "runtime/common", default-features = false }
|
runtime-common = { package = "ghost-runtime-common", path = "runtime/common", default-features = false }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
color-eyre = { workspace = true }
|
color-eyre = { workspace = true }
|
||||||
@ -274,29 +258,24 @@ substrate-build-script-utils = { workspace = true }
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"core-primitives",
|
"core-primitives",
|
||||||
"cli",
|
"cli",
|
||||||
"rpc",
|
"rpc",
|
||||||
"service",
|
"service",
|
||||||
"metrics",
|
"metrics",
|
||||||
"client/cli",
|
"client/cli",
|
||||||
"primitives/machine",
|
"primitives/machine",
|
||||||
"runtime/common",
|
"runtime/common",
|
||||||
"runtime/casper",
|
"runtime/casper",
|
||||||
"runtime/casper/constants",
|
"runtime/casper/constants",
|
||||||
"pallets/networks",
|
"pallets/networks",
|
||||||
"pallets/governance",
|
"pallets/claims",
|
||||||
"pallets/pools",
|
"pallets/slow-clap",
|
||||||
"pallets/sudo",
|
"utils/bags-list",
|
||||||
"pallets/exodus",
|
"utils/chain-spec-builder",
|
||||||
"pallets/exodus/runtime-api",
|
"utils/generate-bags",
|
||||||
"pallets/helpers",
|
|
||||||
"pallets/weaver",
|
|
||||||
"utils/bags-list",
|
|
||||||
"utils/chain-spec-builder",
|
|
||||||
"utils/generate-bags",
|
|
||||||
"utils/ghostkey",
|
"utils/ghostkey",
|
||||||
"utils/staking-miner",
|
"utils/staking-miner",
|
||||||
]
|
]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
@ -344,12 +323,9 @@ crypto-mac = { opt-level = 3 }
|
|||||||
curve25519-dalek = { opt-level = 3 }
|
curve25519-dalek = { opt-level = 3 }
|
||||||
ed25519-dalek = { opt-level = 3 }
|
ed25519-dalek = { opt-level = 3 }
|
||||||
flate2 = { opt-level = 3 }
|
flate2 = { opt-level = 3 }
|
||||||
frost-core = { opt-level = 3 }
|
|
||||||
frost-secp256k1-tr = { opt-level = 3 }
|
|
||||||
futures-channel = { opt-level = 3 }
|
futures-channel = { opt-level = 3 }
|
||||||
hash-db = { opt-level = 3 }
|
hash-db = { opt-level = 3 }
|
||||||
hashbrown = { opt-level = 3 }
|
hashbrown = { opt-level = 3 }
|
||||||
hkdf = { opt-level = 3 }
|
|
||||||
hmac = { opt-level = 3 }
|
hmac = { opt-level = 3 }
|
||||||
httparse = { opt-level = 3 }
|
httparse = { opt-level = 3 }
|
||||||
integer-sqrt = { opt-level = 3 }
|
integer-sqrt = { opt-level = 3 }
|
||||||
@ -365,7 +341,6 @@ parking_lot = { opt-level = 3 }
|
|||||||
parking_lot_core = { opt-level = 3 }
|
parking_lot_core = { opt-level = 3 }
|
||||||
percent-encoding = { opt-level = 3 }
|
percent-encoding = { opt-level = 3 }
|
||||||
primitive-types = { opt-level = 3 }
|
primitive-types = { opt-level = 3 }
|
||||||
rand_chacha = { opt-level = 3 }
|
|
||||||
ring = { opt-level = 3 }
|
ring = { opt-level = 3 }
|
||||||
rustls = { opt-level = 3 }
|
rustls = { opt-level = 3 }
|
||||||
sha2 = { opt-level = 3 }
|
sha2 = { opt-level = 3 }
|
||||||
|
|||||||
0
LICENSE.md
Normal file → Executable file
462
README.md
Normal file → Executable file
@ -1,461 +1 @@
|
|||||||
# Notes
|
TODO
|
||||||
|
|
||||||
Arrows will mark the places where your input is needed
|
|
||||||
|
|
||||||
# Update system
|
|
||||||
|
|
||||||
Update Your System: Before installing any new software,
|
|
||||||
it's a good practice to update your system's package list and
|
|
||||||
upgrade the existing packages to their latest versions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt upgrade
|
|
||||||
```
|
|
||||||
|
|
||||||
# Firewall
|
|
||||||
|
|
||||||
Install Ubuntu firewall
|
|
||||||
```bash
|
|
||||||
sudo apt install ufw
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to connect to your node-machine, allow connection to the SSH port
|
|
||||||
```bash
|
|
||||||
sudo ufw allow ssh
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
Enable firewall, confirm the operation
|
|
||||||
```bash
|
|
||||||
sudo ufw enable
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
Check firewall status. You should see `Status: active`
|
|
||||||
```bash
|
|
||||||
sudo ufw numbered
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
# SSH
|
|
||||||
## Install SSH
|
|
||||||
|
|
||||||
Once you have installed the proper Operating System on you machine
|
|
||||||
we recommend that you enable SSH. Most people have a Daily Driver machine.
|
|
||||||
GHOST requires a separate machine. Adding an extra machine to your life could be cumbersome.
|
|
||||||
Chances are you had to use an external monitor to install Ubuntu on GHOST Node,
|
|
||||||
the same screen regularly used by the Daily Driver machine.
|
|
||||||
SSH enables you to manage your GHOST Nodes remotely from the comfort of your Daily Driver.
|
|
||||||
|
|
||||||
Install OpenSSH Server: With your system updated, the next step is to install
|
|
||||||
the OpenSSH server package. This package contains the necessary software to run
|
|
||||||
an SSH server.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install openssh-server
|
|
||||||
```
|
|
||||||
|
|
||||||
Start SSH Service using _systemctl_.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl start ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable SSH Service to Start on Boot: To ensure that the SSH service automatically
|
|
||||||
starts after a reboot, you need to enable it using _systemctl_.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
To check the status of the SSH service, run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl status ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
You should be seeing something like this:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Connect by SSH
|
|
||||||
|
|
||||||
With SSH enabled and the firewall configured, you can now connect
|
|
||||||
to your node machine from another computer using SSH.
|
|
||||||
You have to know your username and your local network ip
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh [username]@[your_server_ip_OR_hostname]
|
|
||||||
```
|
|
||||||
|
|
||||||
# Install GHOST Application
|
|
||||||
## Allow Ports
|
|
||||||
Allow `port 30333` on GHOST Node:
|
|
||||||
```bash
|
|
||||||
sudo ufw allow 30333
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
To ensure that the ports are allowed, run the following command:
|
|
||||||
```bash
|
|
||||||
sudo ufw numbered
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
## Install Substrate Libraries
|
|
||||||
Click [here](https://docs.substrate.io/install/linux/) for a detailed Rust guide if you want to dive deep into the documentation. Otherwise let's proceed.
|
|
||||||
|
|
||||||
### Install Dependencies
|
|
||||||
Check the documentation for your operating system for information about
|
|
||||||
the packages that are installed and how to download and install
|
|
||||||
any additional packages you might need. For example,
|
|
||||||
if you use Debian/Ubuntu, you can use the Advanced Packaging Tool (apt)
|
|
||||||
to install packages:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install -y build-essential clang curl git make libssl-dev protobuf-compiler llvm libudev-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rust Install
|
|
||||||
|
|
||||||
Download the rustup installation program and use it to install Rust by running the following command:
|
|
||||||
```bash
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Press Enter for using default options:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Update your current shell to include Cargo by running the following command:
|
|
||||||
```bash
|
|
||||||
source $HOME/.cargo/env
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify your installation by running the following command:
|
|
||||||
```bash
|
|
||||||
rustc --version
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see something like this:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Configure the Rust toolchain to default to the latest stable version by running the following commands:
|
|
||||||
```bash
|
|
||||||
rustup default stable
|
|
||||||
```
|
|
||||||
```bash
|
|
||||||
rustup update
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rustup target add wasm32-unknown-unknown --toolchain stable
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify the configuration of your development environment by running
|
|
||||||
the following command:
|
|
||||||
```bash
|
|
||||||
rustup show
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see something like this:
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
Now run:
|
|
||||||
```bash
|
|
||||||
rustup component add rust-src --toolchain stable
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install GHOST
|
|
||||||
|
|
||||||
Check if Git is already installed:
|
|
||||||
```bash
|
|
||||||
git --version
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
Make a GHOST Directory and go to it:
|
|
||||||
```bash
|
|
||||||
mkdir ~/ghost && cd ~/ghost
|
|
||||||
```
|
|
||||||
|
|
||||||
Clone GHOST Node Git:
|
|
||||||
```bash
|
|
||||||
git clone https://git.ghostchain.io/ghostchain/ghost-node.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Go to ghost-node directory:
|
|
||||||
```bash
|
|
||||||
cd ghost-node
|
|
||||||
```
|
|
||||||
|
|
||||||
Compile the node template by running the following command:
|
|
||||||
```bash
|
|
||||||
./scripts/starter.sh --release
|
|
||||||
```
|
|
||||||
|
|
||||||
The build can take up to 20 minutes depending on the specifications of your machine.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
# Boot Node Qualification
|
|
||||||
|
|
||||||
## Boot Node Qualification Test
|
|
||||||
|
|
||||||
Becoming a Boot Node on GHOST Chain is an absolute MUST if we want the network
|
|
||||||
to be sufficiently decentralized. However, due to certain ISP limitations
|
|
||||||
some countries make it being a Boot Node easier than others.
|
|
||||||
Hence, the GHOST Dev team came up with a Boot Node Qualification Test.
|
|
||||||
|
|
||||||
First we should install TRACEROUTE:
|
|
||||||
```bash
|
|
||||||
sudo apt install traceroute
|
|
||||||
```
|
|
||||||
|
|
||||||
Then we must determine the public IP for your GHOST Node Machine.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -4 icanhazip.com
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
Then type the following command replacing `<YOUR_IP_FROM_PREVIOUS_STEP>` with an actual IP address from previous step:
|
|
||||||
```bash
|
|
||||||
traceroute <YOUR_IP_FROM_PREVIOUS_STEP>
|
|
||||||
```
|
|
||||||
|
|
||||||
If your GHOST Node is **not behind a NAT** your terminal window should look something like this:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
If your GHOST Node is **behind NAT** your terminal window should look something like this:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
If your GHOST Node is NOT behind the NAT then your node can be a Boot Node.
|
|
||||||
If your GHOST Node is behind the NAT then your node CANNOT be a Boot Node
|
|
||||||
and you will have to use some kind of proxy to rectify the situation.
|
|
||||||
|
|
||||||
Afterwards, post the screenshot of your terminal window in the Whales group.
|
|
||||||
Feel free to blur out your IP.
|
|
||||||
|
|
||||||
## Setting Proper Firewall Rules
|
|
||||||
|
|
||||||
You must enable `port 30333` on your router
|
|
||||||
|
|
||||||
Based on prior instructions you may have configured your firewall in way that is
|
|
||||||
no longer needed. For the purpose of GHOST Chain you should only
|
|
||||||
open `port 30333` and close other ports.
|
|
||||||
|
|
||||||
### Checking Firewall
|
|
||||||
|
|
||||||
Check the ports that are opened on your firewall:
|
|
||||||
```bash
|
|
||||||
sudo ufw numbered
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
If `port 9945` is opened then close it:
|
|
||||||
```bash
|
|
||||||
sudo ufw deny 9945
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuring Router
|
|
||||||
|
|
||||||
Enable port forwarding for port 30333 on your router. Be mindful that different networks and routers have different ways of setting this up. It is best to search for portforwarding instruction for the specific router model.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Checking Ports
|
|
||||||
Simulate a broadcasting node by running a dummy GHOST Node launch command:
|
|
||||||
```bash
|
|
||||||
./target/release/ghost --port=30333 --rpc-port=9945 --chain=dev --node-key=0000000000000000000000000000000000000000000000000000000000000001 --base-path=/tmp/alice --alice --validator --unsafe-rpc-external --no-telemetry --state-pruning=archive
|
|
||||||
```
|
|
||||||
|
|
||||||
To check ports go to [Port Checker Website](https://dnschecker.org/port-scanner.php) and check following ports:
|
|
||||||
```
|
|
||||||
30333, 9945
|
|
||||||
```
|
|
||||||
|
|
||||||
Only `port 30333` should be opened.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Press _CTRL+C_ to stop the node.
|
|
||||||
|
|
||||||
# Launching GHOST TestNet
|
|
||||||
|
|
||||||
Switch to main GIT branch:
|
|
||||||
```bash
|
|
||||||
git switch main
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure `ghost-node` is up to date:
|
|
||||||
```bash
|
|
||||||
git pull origin main
|
|
||||||
```
|
|
||||||
|
|
||||||
## Generating keys
|
|
||||||
Create ghost configuration directory:
|
|
||||||
```bash
|
|
||||||
sudo mkdir /etc/ghost
|
|
||||||
```
|
|
||||||
|
|
||||||
Give the user permission to the directory:
|
|
||||||
```bash
|
|
||||||
sudo chown $(whoami) /etc/ghost
|
|
||||||
```
|
|
||||||
|
|
||||||
To generate the node key use the following command:
|
|
||||||
```bash
|
|
||||||
./target/release/ghost key generate-node-key --bin --file=/etc/ghost/node-key
|
|
||||||
```
|
|
||||||
|
|
||||||
Feel free to backup the file on a separate storage device.
|
|
||||||
|
|
||||||
## Build and start the ghost-node
|
|
||||||
|
|
||||||
Recompile `ghost-node` using `starter.sh` and `--release` flag.
|
|
||||||
Make `ghost-node` service being able to be started by default user
|
|
||||||
using `--make-global`:
|
|
||||||
```bash
|
|
||||||
./scripts/starter.sh --release --make-global
|
|
||||||
```
|
|
||||||
|
|
||||||
We need to recompile so type y and press Enter to proceed:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Recompiling will take some time!
|
|
||||||
|
|
||||||
The script needs higher permissions to write the ghost-node startup file, so it may ask for your user's password
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Check the hash of the build:
|
|
||||||
```bash
|
|
||||||
sha256sum /etc/ghost/casper.json
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see:
|
|
||||||
```
|
|
||||||
4645c7d39a64424d823f659d408070f8c90426117e7af147b0360697b2db7f4f /etc/ghost/casper.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Create running `ghost-node` service that starts on system boot using `--unit-file` flag.
|
|
||||||
And we must set up the `ghost-node` launch command by setting arguments using
|
|
||||||
`--set-arguments` flag:
|
|
||||||
```bash
|
|
||||||
./scripts/starter.sh --unit-file --set-arguments
|
|
||||||
```
|
|
||||||
|
|
||||||
Only change the defaults if you are advanced otherwise press _Enter_ for
|
|
||||||
the following prompts.
|
|
||||||
|
|
||||||
If this is your first node simply press _Enter_ to proceed with the default.
|
|
||||||
If this is your second node you should type a different port here,
|
|
||||||
for example `30334`, and then you should open this port on your firewall
|
|
||||||
and create a dedicated port forwarding rule on your router as specified
|
|
||||||
in the Testing Connectivity Part:
|
|
||||||
```
|
|
||||||
specify p2p protocol TCP port (default: 30333): 30334
|
|
||||||
```
|
|
||||||
|
|
||||||
To choose default options press Enter here:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
If you want to validate from this node press Enter:
|
|
||||||
```
|
|
||||||
disable validator mode? [y/N]: y
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
Press _Enter_ for reject enabling Prometheus:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
For the following prompt:
|
|
||||||
```
|
|
||||||
list of bootnodes if any:
|
|
||||||
```
|
|
||||||
|
|
||||||
Paste one of available **Boot Node** addresses.
|
|
||||||
|
|
||||||
Official **Boot Node** addresses:
|
|
||||||
```
|
|
||||||
/dns/bootnode69.chain.ghostchain.io/tcp/30334/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf
|
|
||||||
```
|
|
||||||
```
|
|
||||||
/dns/bootnode70.chain.ghostchain.io/tcp/30333/p2p/12D3KooWP3h5dSdqcpvsCr7fp1jyfqDj291QDZ68a4gY6VQ8T1nW
|
|
||||||
```
|
|
||||||
|
|
||||||
A full list of available **Boot Nodes** can be found on [the guide page](https://blog.ghostchain.io/launching-ghost-testnet-3-1/#Boot_Node).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Press _Enter_:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Changing `unit-file` name is optional, otherwise press _Enter_:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Type `y` and press _Enter_ for create dedicated user for running `ghost-node`:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**DO NOT start and DO NOT enable** `ghost-node.service` (press _Enter_):
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Full Node:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Validator Node:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Start `ghost-node`:
|
|
||||||
```bash
|
|
||||||
sudo systemctl start ghost-node
|
|
||||||
```
|
|
||||||
|
|
||||||
Wait ~60 seconds!
|
|
||||||
|
|
||||||
Check node is started:
|
|
||||||
```bash
|
|
||||||
sudo systemctl status ghost-node
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
For exit press _CTRL+C_
|
|
||||||
|
|
||||||
Restart `ghost-node`:
|
|
||||||
```bash
|
|
||||||
sudo systemctl restart ghost-node
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable `ghost-node`:
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable ghost-node
|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
To see the logs produced by your `ghost-node`:
|
|
||||||
```bash
|
|
||||||
journalctl -f -u ghost-node
|
|
||||||
```
|
|
||||||
|
|
||||||
To exit press _CTRL+C_.
|
|
||||||
|
|
||||||
Congratulations! You have an operational GHOST Node.
|
|
||||||
|
|||||||
14
cli/Cargo.toml
Normal file → Executable file
@ -52,14 +52,14 @@ substrate-build-script-utils = { workspace = true }
|
|||||||
default = ["cli", "db", "full-node"]
|
default = ["cli", "db", "full-node"]
|
||||||
db = ["service/db"]
|
db = ["service/db"]
|
||||||
cli = [
|
cli = [
|
||||||
"clap",
|
"clap",
|
||||||
"frame-benchmarking-cli",
|
"frame-benchmarking-cli",
|
||||||
"sc-cli",
|
"sc-cli",
|
||||||
"sc-service",
|
"sc-service",
|
||||||
"sc-tracing",
|
"sc-tracing",
|
||||||
"service",
|
"service",
|
||||||
"ghost-client-cli",
|
"ghost-client-cli",
|
||||||
"ghost-machine-primitives",
|
"ghost-machine-primitives",
|
||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
"frame-benchmarking-cli?/runtime-benchmarks",
|
"frame-benchmarking-cli?/runtime-benchmarks",
|
||||||
|
|||||||
2
cli/build.rs
Normal file → Executable file
@ -1,4 +1,4 @@
|
|||||||
fn main() {
|
fn main () {
|
||||||
if let Ok(profile) = std::env::var("PROFILE") {
|
if let Ok(profile) = std::env::var("PROFILE") {
|
||||||
println!("cargo:rustc-cfg=build_type=\"{}\"", profile);
|
println!("cargo:rustc-cfg=build_type=\"{}\"", profile);
|
||||||
}
|
}
|
||||||
|
|||||||
3
cli/src/cli.rs
Normal file → Executable file
@ -55,7 +55,6 @@ pub enum Subcommand {
|
|||||||
/// Revert the chain to a previous state.
|
/// Revert the chain to a previous state.
|
||||||
Revert(sc_cli::RevertCmd),
|
Revert(sc_cli::RevertCmd),
|
||||||
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
|
||||||
/// The custom benchmark subcommmand benchmarking runtime pallets.
|
/// The custom benchmark subcommmand benchmarking runtime pallets.
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
|
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
|
||||||
@ -85,7 +84,7 @@ pub struct RunCmd {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub no_hardware_benchmarks: bool,
|
pub no_hardware_benchmarks: bool,
|
||||||
|
|
||||||
/// Enable the block authoring backoff that is triggered when finality is
|
/// Enable the block authoring backoff that is triggered when finality is
|
||||||
/// lagging.
|
/// lagging.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub force_authoring_backoff: bool,
|
pub force_authoring_backoff: bool,
|
||||||
|
|||||||
208
cli/src/command.rs
Normal file → Executable file
@ -1,10 +1,14 @@
|
|||||||
use crate::cli::{Cli, Subcommand};
|
use crate::cli::{Cli, Subcommand};
|
||||||
|
use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory};
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
use sc_cli::SubstrateCli;
|
use sc_cli::SubstrateCli;
|
||||||
use service::{self, IdentifyVariant};
|
use service::{
|
||||||
|
self, IdentifyVariant,
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
benchmarking::{
|
||||||
use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory};
|
benchmark_inherent_data, RemarkBuilder, TransferKeepAliveBuilder
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use keyring::Sr25519Keyring;
|
||||||
|
|
||||||
pub use crate::{error::Error, service::BlockId};
|
pub use crate::{error::Error, service::BlockId};
|
||||||
#[cfg(feature = "pyroscope")]
|
#[cfg(feature = "pyroscope")]
|
||||||
@ -64,35 +68,28 @@ impl SubstrateCli for Cli {
|
|||||||
#[cfg(feature = "casper-native")]
|
#[cfg(feature = "casper-native")]
|
||||||
"casper" => Box::new(service::chain_spec::casper_config()?),
|
"casper" => Box::new(service::chain_spec::casper_config()?),
|
||||||
#[cfg(feature = "casper-native")]
|
#[cfg(feature = "casper-native")]
|
||||||
"casper-dev" | "casper-development" => {
|
"casper-dev" | "dev" | "development" => Box::new(service::chain_spec::casper_development_config()?),
|
||||||
Box::new(service::chain_spec::casper_development_config()?)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "casper-native")]
|
#[cfg(feature = "casper-native")]
|
||||||
"casper-local" => {
|
"casper-local" | "local" => Box::new(service::chain_spec::casper_local_testnet_config()?),
|
||||||
Box::new(service::chain_spec::casper_local_testnet_config()?)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "casper-native")]
|
#[cfg(feature = "casper-native")]
|
||||||
"casper-staging" | "staging" => {
|
"casper-staging" | "staging" => Box::new(service::chain_spec::casper_staging_testnet_config()?),
|
||||||
Box::new(service::chain_spec::casper_staging_testnet_config()?)
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "casper-native"))]
|
#[cfg(not(feature = "casper-native"))]
|
||||||
name if name.starts_with("casper-") && !name.ends_with(".json") => Err(format!(
|
name if name.starts_with("casper-") && !name.ends_with(".json") =>
|
||||||
"`{}` only supported with `casper-native` feature enabled.",
|
Err(format!("`{}` only supported with `casper-native` feature enabled.", name))?,
|
||||||
name
|
|
||||||
))?,
|
|
||||||
#[cfg(feature = "casper-native")]
|
#[cfg(feature = "casper-native")]
|
||||||
path => {
|
path => {
|
||||||
let path = std::path::PathBuf::from(path);
|
let path = std::path::PathBuf::from(path);
|
||||||
|
|
||||||
let chain_spec = Box::new(service::GenericChainSpec::from_json_file(path.clone())?)
|
let chain_spec = Box::new(
|
||||||
as Box<dyn service::ChainSpec>;
|
service::GenericChainSpec::from_json_file(path.clone())?
|
||||||
|
) as Box<dyn service::ChainSpec>;
|
||||||
|
|
||||||
if chain_spec.is_casper() {
|
if chain_spec.is_casper() {
|
||||||
Box::new(service::CasperChainSpec::from_json_file(path)?)
|
Box::new(service::CasperChainSpec::from_json_file(path)?)
|
||||||
} else {
|
} else {
|
||||||
chain_spec
|
chain_spec
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +109,10 @@ fn set_ss58_version(spec: &Box<dyn service::ChainSpec>) {
|
|||||||
sp_core::crypto::set_default_ss58_version(ss58_version);
|
sp_core::crypto::set_default_ss58_version(ss58_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_node_inner<F>(cli: Cli, logger_hook: F) -> Result<()>
|
fn run_node_inner<F>(
|
||||||
|
cli: Cli,
|
||||||
|
logger_hook: F,
|
||||||
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
|
F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
|
||||||
{
|
{
|
||||||
@ -123,16 +123,12 @@ where
|
|||||||
|
|
||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
runner.run_node_until_exit(move |mut config| async move {
|
runner.run_node_until_exit(move |config| async move {
|
||||||
config.offchain_worker.enabled = true;
|
|
||||||
config.offchain_worker.indexing_enabled = true;
|
|
||||||
|
|
||||||
let hwbench = (!cli.run.no_hardware_benchmarks)
|
let hwbench = (!cli.run.no_hardware_benchmarks)
|
||||||
.then_some(config.database.path().map(|database_path| {
|
.then_some(config.database.path().map(|database_path| {
|
||||||
let _ = std::fs::create_dir_all(&database_path);
|
let _ = std::fs::create_dir_all(&database_path);
|
||||||
sc_sysinfo::gather_hwbench(Some(database_path))
|
sc_sysinfo::gather_hwbench(Some(database_path))
|
||||||
}))
|
})).flatten();
|
||||||
.flatten();
|
|
||||||
|
|
||||||
let database_source = config.database.clone();
|
let database_source = config.database.clone();
|
||||||
let task_manager = service::build_full(
|
let task_manager = service::build_full(
|
||||||
@ -142,8 +138,7 @@ where
|
|||||||
telemetry_worker_handle: None,
|
telemetry_worker_handle: None,
|
||||||
hwbench,
|
hwbench,
|
||||||
},
|
},
|
||||||
)
|
).map(|full| full.task_manager)?;
|
||||||
.map(|full| full.task_manager)?;
|
|
||||||
|
|
||||||
if let Some(path) = database_source.path() {
|
if let Some(path) = database_source.path() {
|
||||||
sc_storage_monitor::StorageMonitorService::try_spawn(
|
sc_storage_monitor::StorageMonitorService::try_spawn(
|
||||||
@ -172,9 +167,7 @@ pub fn run() -> Result<()> {
|
|||||||
let agent = pyro::Pyroscope::builder(
|
let agent = pyro::Pyroscope::builder(
|
||||||
"http://".to_owned() + address.to_string().as_str(),
|
"http://".to_owned() + address.to_string().as_str(),
|
||||||
"ghost".to_owned(),
|
"ghost".to_owned(),
|
||||||
)
|
).backend(pprof_backend(PprofConfig::new().sample_rate(113))).build()?;
|
||||||
.backend(pprof_backend(PprofConfig::new().sample_rate(113)))
|
|
||||||
.build()?;
|
|
||||||
|
|
||||||
Some(agent.start()?)
|
Some(agent.start()?)
|
||||||
} else {
|
} else {
|
||||||
@ -183,7 +176,7 @@ pub fn run() -> Result<()> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "pyroscope"))]
|
#[cfg(not(feature = "pyroscope"))]
|
||||||
if cli.run.pyroscope_server.is_some() {
|
if cli.run.pyroscope_server.is_some() {
|
||||||
return Err(Error::PyroscopeNotCompiledIn);
|
return Err(Error::PyroscopeNotCompiledIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
match &cli.subcommand {
|
match &cli.subcommand {
|
||||||
@ -191,7 +184,7 @@ pub fn run() -> Result<()> {
|
|||||||
Some(Subcommand::BuildSpec(cmd)) => {
|
Some(Subcommand::BuildSpec(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
Ok(runner.sync_run(|config| cmd.run(config.chain_spec, config.network))?)
|
Ok(runner.sync_run(|config| cmd.run(config.chain_spec, config.network))?)
|
||||||
}
|
},
|
||||||
Some(Subcommand::CheckBlock(cmd)) => {
|
Some(Subcommand::CheckBlock(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
let chain_spec = &runner.config().chain_spec;
|
let chain_spec = &runner.config().chain_spec;
|
||||||
@ -199,13 +192,11 @@ pub fn run() -> Result<()> {
|
|||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
runner.async_run(|mut config| {
|
runner.async_run(|mut config| {
|
||||||
let (client, _, import_queue, task_manager) = service::new_chain_ops(&mut config)?;
|
let (client, _, import_queue, task_manager) =
|
||||||
Ok((
|
service::new_chain_ops(&mut config)?;
|
||||||
cmd.run(client, import_queue).map_err(Error::SubstrateCli),
|
Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager))
|
||||||
task_manager,
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
Some(Subcommand::ExportBlocks(cmd)) => {
|
Some(Subcommand::ExportBlocks(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
let chain_spec = &runner.config().chain_spec;
|
let chain_spec = &runner.config().chain_spec;
|
||||||
@ -213,14 +204,11 @@ pub fn run() -> Result<()> {
|
|||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
Ok(runner.async_run(|mut config| {
|
Ok(runner.async_run(|mut config| {
|
||||||
let (client, _, _, task_manager) = service::new_chain_ops(&mut config)?;
|
let ( client, _, _, task_manager ) =
|
||||||
Ok((
|
service::new_chain_ops(&mut config)?;
|
||||||
cmd.run(client, config.database)
|
Ok((cmd.run(client, config.database).map_err(Error::SubstrateCli), task_manager))
|
||||||
.map_err(Error::SubstrateCli),
|
|
||||||
task_manager,
|
|
||||||
))
|
|
||||||
})?)
|
})?)
|
||||||
}
|
},
|
||||||
Some(Subcommand::ExportState(cmd)) => {
|
Some(Subcommand::ExportState(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
let chain_spec = &runner.config().chain_spec;
|
let chain_spec = &runner.config().chain_spec;
|
||||||
@ -228,14 +216,11 @@ pub fn run() -> Result<()> {
|
|||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
Ok(runner.async_run(|mut config| {
|
Ok(runner.async_run(|mut config| {
|
||||||
let (client, _, _, task_manager) = service::new_chain_ops(&mut config)?;
|
let ( client, _, _, task_manager ) =
|
||||||
Ok((
|
service::new_chain_ops(&mut config)?;
|
||||||
cmd.run(client, config.chain_spec)
|
Ok((cmd.run(client, config.chain_spec).map_err(Error::SubstrateCli), task_manager))
|
||||||
.map_err(Error::SubstrateCli),
|
|
||||||
task_manager,
|
|
||||||
))
|
|
||||||
})?)
|
})?)
|
||||||
}
|
},
|
||||||
Some(Subcommand::ImportBlocks(cmd)) => {
|
Some(Subcommand::ImportBlocks(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
let chain_spec = &runner.config().chain_spec;
|
let chain_spec = &runner.config().chain_spec;
|
||||||
@ -243,17 +228,15 @@ pub fn run() -> Result<()> {
|
|||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
Ok(runner.async_run(|mut config| {
|
Ok(runner.async_run(|mut config| {
|
||||||
let (client, _, import_queue, task_manager) = service::new_chain_ops(&mut config)?;
|
let ( client, _, import_queue, task_manager ) =
|
||||||
Ok((
|
service::new_chain_ops(&mut config)?;
|
||||||
cmd.run(client, import_queue).map_err(Error::SubstrateCli),
|
Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager))
|
||||||
task_manager,
|
|
||||||
))
|
|
||||||
})?)
|
})?)
|
||||||
}
|
},
|
||||||
Some(Subcommand::PurgeChain(cmd)) => {
|
Some(Subcommand::PurgeChain(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
Ok(runner.sync_run(|config| cmd.run(config.database))?)
|
Ok(runner.sync_run(|config| cmd.run(config.database))?)
|
||||||
}
|
},
|
||||||
Some(Subcommand::Revert(cmd)) => {
|
Some(Subcommand::Revert(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
let chain_spec = &runner.config().chain_spec;
|
let chain_spec = &runner.config().chain_spec;
|
||||||
@ -261,58 +244,68 @@ pub fn run() -> Result<()> {
|
|||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
Ok(runner.async_run(|mut config| {
|
Ok(runner.async_run(|mut config| {
|
||||||
let (client, backend, _, task_manager) = service::new_chain_ops(&mut config)?;
|
let (client, backend, _, task_manager) =
|
||||||
|
service::new_chain_ops(&mut config)?;
|
||||||
|
|
||||||
let aux_revert = Box::new(|client, backend, blocks| {
|
let aux_revert = Box::new(|client, backend, blocks| {
|
||||||
service::revert_backend(client, backend, blocks).map_err(|err| match err {
|
service::revert_backend(client, backend, blocks).map_err(|err| {
|
||||||
service::Error::Blockchain(err) => err.into(),
|
match err {
|
||||||
err => sc_cli::Error::Application(err.into()),
|
service::Error::Blockchain(err) => err.into(),
|
||||||
|
err => sc_cli::Error::Application(err.into()),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
cmd.run(client, backend, Some(aux_revert))
|
cmd.run(client, backend, Some(aux_revert)).map_err(Error::SubstrateCli),
|
||||||
.map_err(Error::SubstrateCli),
|
task_manager
|
||||||
task_manager,
|
|
||||||
))
|
))
|
||||||
})?)
|
})?)
|
||||||
},
|
},
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
|
||||||
Some(Subcommand::Benchmark(cmd)) => {
|
Some(Subcommand::Benchmark(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
|
||||||
let chain_spec = &runner.config().chain_spec;
|
let chain_spec = &runner.config().chain_spec;
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
|
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||||
|
BenchmarkCmd::Storage(_) =>
|
||||||
|
return Err(sc_cli::Error::Input(
|
||||||
|
"Compile with `--feature=runtime-benchmarks \
|
||||||
|
to enable storage benchmarks.".into()
|
||||||
|
).into()),
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| {
|
BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| {
|
||||||
let (client, backend, _, _) = service::new_chain_ops(&mut config)?;
|
let (client, backend, _, _,) =
|
||||||
|
service::new_chain_ops(&mut config)?;
|
||||||
let db = backend.expose_db();
|
let db = backend.expose_db();
|
||||||
let storage = backend.expose_storage();
|
let storage = backend.expose_storage();
|
||||||
|
|
||||||
cmd.run(config, client.clone(), db, storage)
|
cmd.run(config, client.clone(), db, storage).map_err(Error::SubstrateCli)
|
||||||
.map_err(Error::SubstrateCli)
|
|
||||||
}),
|
}),
|
||||||
BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| {
|
BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| {
|
||||||
let (client, _, _, _) = service::new_chain_ops(&mut config)?;
|
let (client, _, _, _,) =
|
||||||
|
service::new_chain_ops(&mut config)?;
|
||||||
cmd.run(client.clone()).map_err(Error::SubstrateCli)
|
cmd.run(client.clone()).map_err(Error::SubstrateCli)
|
||||||
}),
|
}),
|
||||||
// These commands are very similar and can be handled in nearly the same way
|
// These commands are very similar and can be handled in nearly the same way
|
||||||
BenchmarkCmd::Extrinsic(_) | BenchmarkCmd::Overhead(_) => {
|
BenchmarkCmd::Extrinsic(_) | BenchmarkCmd::Overhead(_) =>
|
||||||
use service::benchmarking::{benchmark_inherent_data, RemarkBuilder, TransferKeepAliveBuilder};
|
|
||||||
|
|
||||||
runner.sync_run(|mut config| {
|
runner.sync_run(|mut config| {
|
||||||
let (client, _, _, _) = service::new_chain_ops(&mut config)?;
|
let (client, _, _, _) =
|
||||||
|
service::new_chain_ops(&mut config)?;
|
||||||
|
|
||||||
let inherent_data = benchmark_inherent_data()
|
let inherent_data = benchmark_inherent_data()
|
||||||
.map_err(|e| format!("generating inherent data: {:?}", e))?;
|
.map_err(|e| format!("generating inherent data: {:?}", e))?;
|
||||||
|
|
||||||
let remark_builder =
|
let remark_builder = RemarkBuilder::new(
|
||||||
RemarkBuilder::new(client.clone(), config.chain_spec.identify_chain());
|
client.clone(),
|
||||||
|
config.chain_spec.identify_chain(),
|
||||||
|
);
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
BenchmarkCmd::Extrinsic(cmd) => {
|
BenchmarkCmd::Extrinsic(cmd) => {
|
||||||
let tka_builder = TransferKeepAliveBuilder::new(
|
let tka_builder = TransferKeepAliveBuilder::new(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
keyring::Sr25519Keyring::Alice.to_account_id(),
|
Sr25519Keyring::Alice.to_account_id(),
|
||||||
config.chain_spec.identify_chain(),
|
config.chain_spec.identify_chain(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -323,47 +316,48 @@ pub fn run() -> Result<()> {
|
|||||||
|
|
||||||
cmd.run(client.clone(), inherent_data, Vec::new(), &ext_factory)
|
cmd.run(client.clone(), inherent_data, Vec::new(), &ext_factory)
|
||||||
.map_err(Error::SubstrateCli)
|
.map_err(Error::SubstrateCli)
|
||||||
}
|
},
|
||||||
BenchmarkCmd::Overhead(cmd) => cmd
|
BenchmarkCmd::Overhead(cmd) => cmd.run(
|
||||||
.run(
|
config,
|
||||||
config,
|
client.clone(),
|
||||||
client.clone(),
|
inherent_data,
|
||||||
inherent_data,
|
Vec::new(),
|
||||||
Vec::new(),
|
&remark_builder,
|
||||||
&remark_builder,
|
).map_err(Error::SubstrateCli),
|
||||||
)
|
|
||||||
.map_err(Error::SubstrateCli),
|
|
||||||
_ => unreachable!("Ensured by the outside match; qed"),
|
_ => unreachable!("Ensured by the outside match; qed"),
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
BenchmarkCmd::Pallet(cmd) => {
|
BenchmarkCmd::Pallet(cmd) => {
|
||||||
set_ss58_version(chain_spec);
|
set_ss58_version(chain_spec);
|
||||||
|
|
||||||
runner.sync_run(|config| {
|
if cfg!(feature = "runtime-benchmarks") {
|
||||||
cmd.run_with_spec::<sp_runtime::traits::HashingFor<service::Block>, ()>(
|
runner.sync_run(|config| {
|
||||||
Some(config.chain_spec),
|
cmd.run_with_spec::<sp_runtime::traits::HashingFor<service::Block>, ()>(
|
||||||
).map_err(|e| Error::SubstrateCli(e))
|
Some(config.chain_spec),
|
||||||
})
|
).map_err(|e| Error::SubstrateCli(e))
|
||||||
}
|
})
|
||||||
|
} else {
|
||||||
|
Err(sc_cli::Error::Input(
|
||||||
|
"Benchmarking wasn't enabled when building the node. \
|
||||||
|
You can enable it with `--features=runtime-benchmarks`.".into()
|
||||||
|
).into())
|
||||||
|
}
|
||||||
|
},
|
||||||
BenchmarkCmd::Machine(cmd) => runner.sync_run(|config| {
|
BenchmarkCmd::Machine(cmd) => runner.sync_run(|config| {
|
||||||
cmd.run(
|
cmd.run(&config, ghost_machine_primitives::GHOST_NODE_REFERENCE_HARDWARE.clone())
|
||||||
&config,
|
.map_err(Error::SubstrateCli)
|
||||||
ghost_machine_primitives::GHOST_NODE_REFERENCE_HARDWARE.clone(),
|
|
||||||
)
|
|
||||||
.map_err(Error::SubstrateCli)
|
|
||||||
}),
|
}),
|
||||||
// Note: this allows to implement additional new benchmark
|
// Note: this allows to implement additional new benchmark
|
||||||
// commands.
|
// commands.
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => Err(Error::CommandNotImplemented),
|
_ => Err(Error::CommandNotImplemented),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
|
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
|
||||||
Some(Subcommand::ChainInfo(cmd)) => {
|
Some(Subcommand::ChainInfo(cmd)) => {
|
||||||
let runner = cli.create_runner(cmd)?;
|
let runner = cli.create_runner(cmd)?;
|
||||||
Ok(runner.sync_run(|config| cmd.run::<service::Block>(&config))?)
|
Ok(runner.sync_run(|config| cmd.run::<service::Block>(&config))?)
|
||||||
}
|
},
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
#[cfg(feature = "pyroscope")]
|
#[cfg(feature = "pyroscope")]
|
||||||
|
|||||||
0
cli/src/error.rs
Normal file → Executable file
4
cli/src/lib.rs
Normal file → Executable file
@ -6,7 +6,9 @@ mod command;
|
|||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
#[cfg(feature = "service")]
|
#[cfg(feature = "service")]
|
||||||
pub use service::{self, Block, CoreApi, IdentifyVariant, ProvideRuntimeApi, TFullClient};
|
pub use service::{
|
||||||
|
self, Block, CoreApi, IdentifyVariant, ProvideRuntimeApi, TFullClient,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
pub use cli::*;
|
pub use cli::*;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-client-cli"
|
name = "ghost-client-cli"
|
||||||
version = "0.1.4"
|
version = "0.1.3"
|
||||||
description = "Ghost CLI interface"
|
description = "Ghost CLI interface"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|||||||
@ -3,10 +3,13 @@
|
|||||||
use bip39::Mnemonic;
|
use bip39::Mnemonic;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use sc_cli::{with_crypto_scheme, CryptoSchemeFlag, Error, KeystoreParams, OutputTypeFlag};
|
use sc_cli::{
|
||||||
|
with_crypto_scheme, KeystoreParams, OutputTypeFlag,
|
||||||
|
CryptoSchemeFlag, Error,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::commands::utils::print_from_uri;
|
|
||||||
use crate::params::NetworkSchemeFlag;
|
use crate::params::NetworkSchemeFlag;
|
||||||
|
use crate::commands::utils::print_from_uri;
|
||||||
|
|
||||||
/// The `generate` command
|
/// The `generate` command
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
@ -14,58 +17,58 @@ use crate::params::NetworkSchemeFlag;
|
|||||||
pub struct GenerateCmd {
|
pub struct GenerateCmd {
|
||||||
/// The number of words in the phrase to generate. One of 12 (default),
|
/// The number of words in the phrase to generate. One of 12 (default),
|
||||||
/// 15, 18, 21 and 24.
|
/// 15, 18, 21 and 24.
|
||||||
#[arg(short = 'w', long, value_name = "WORDS")]
|
#[arg(short = 'w', long, value_name = "WORDS")]
|
||||||
words: Option<usize>,
|
words: Option<usize>,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub keystore_params: KeystoreParams,
|
pub keystore_params: KeystoreParams,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub network_scheme: NetworkSchemeFlag,
|
pub network_scheme: NetworkSchemeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub output_scheme: OutputTypeFlag,
|
pub output_scheme: OutputTypeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub crypto_scheme: CryptoSchemeFlag,
|
pub crypto_scheme: CryptoSchemeFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenerateCmd {
|
impl GenerateCmd {
|
||||||
/// Run the command
|
/// Run the command
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&self) -> Result<(), Error> {
|
||||||
let words = match self.words {
|
let words = match self.words {
|
||||||
Some(words_count) if [12, 15, 18, 21, 24].contains(&words_count) => Ok(words_count),
|
Some(words_count) if [12, 15, 18, 21, 24].contains(&words_count) => Ok(words_count),
|
||||||
Some(_) => Err(Error::Input(
|
Some(_) => Err(Error::Input(
|
||||||
"Invalid number of words given for phrase: must be 12/15/18/21/24".into(),
|
"Invalid number of words given for phrase: must be 12/15/18/21/24".into(),
|
||||||
)),
|
)),
|
||||||
None => Ok(12),
|
None => Ok(12),
|
||||||
}?;
|
}?;
|
||||||
let mnemonic = Mnemonic::generate(words)
|
let mnemonic = Mnemonic::generate(words)
|
||||||
.map_err(|e| Error::Input(format!("Mnemonic generation failed: {e}").into()))?;
|
.map_err(|e| Error::Input(format!("Mnemonic generation failed: {e}").into()))?;
|
||||||
let password = self.keystore_params.read_password()?;
|
let password = self.keystore_params.read_password()?;
|
||||||
let output = self.output_scheme.output_type;
|
let output = self.output_scheme.output_type;
|
||||||
|
|
||||||
let phrase = mnemonic.words().join(" ");
|
let phrase = mnemonic.words().join(" ");
|
||||||
|
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_uri(&phrase, password, self.network_scheme.network, output)
|
print_from_uri(&phrase, password, self.network_scheme.network, output)
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate() {
|
fn generate() {
|
||||||
let generate = GenerateCmd::parse_from(&["generate", "--password", "12345"]);
|
let generate = GenerateCmd::parse_from(&["generate", "--password", "12345"]);
|
||||||
assert!(generate.run().is_ok())
|
assert!(generate.run().is_ok())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,101 +1,102 @@
|
|||||||
//! Implementation of the `inspect` subcommand
|
//! Implementation of the `inspect` subcommand
|
||||||
|
|
||||||
use crate::commands::utils::{print_from_public, print_from_uri};
|
|
||||||
use crate::params::NetworkSchemeFlag;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use sc_cli::{
|
|
||||||
utils::read_uri, with_crypto_scheme, CryptoSchemeFlag, Error, KeystoreParams, OutputTypeFlag,
|
|
||||||
};
|
|
||||||
use sp_core::crypto::{ExposeSecret, SecretString, SecretUri, Ss58Codec};
|
use sp_core::crypto::{ExposeSecret, SecretString, SecretUri, Ss58Codec};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use sc_cli::{
|
||||||
|
with_crypto_scheme, KeystoreParams, OutputTypeFlag, CryptoSchemeFlag, Error,
|
||||||
|
utils::read_uri,
|
||||||
|
};
|
||||||
|
use crate::params::NetworkSchemeFlag;
|
||||||
|
use crate::commands::utils::{print_from_public, print_from_uri};
|
||||||
|
|
||||||
/// The `inspect` command
|
/// The `inspect` command
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(
|
#[command(
|
||||||
name = "inspect",
|
name = "inspect",
|
||||||
about = "Gets a public key and a SS58 address from the provided Secret URI"
|
about = "Gets a public key and a SS58 address from the provided Secret URI"
|
||||||
)]
|
)]
|
||||||
pub struct InspectKeyCmd {
|
pub struct InspectKeyCmd {
|
||||||
/// A Key URI to be inspected. May be a secret seed, secret URI
|
/// A Key URI to be inspected. May be a secret seed, secret URI
|
||||||
/// (with derivation paths and password), SS58, public URI or a hex encoded public key.
|
/// (with derivation paths and password), SS58, public URI or a hex encoded public key.
|
||||||
///
|
///
|
||||||
/// If it is a hex encoded public key, `--public` needs to be given as argument.
|
/// If it is a hex encoded public key, `--public` needs to be given as argument.
|
||||||
///
|
///
|
||||||
/// If the given value is a file, the file content will be used
|
/// If the given value is a file, the file content will be used
|
||||||
/// as URI.
|
/// as URI.
|
||||||
///
|
///
|
||||||
/// If omitted, you will be prompted for the URI.
|
/// If omitted, you will be prompted for the URI.
|
||||||
uri: Option<String>,
|
uri: Option<String>,
|
||||||
|
|
||||||
/// Is the given `uri` a hex encoded public key?
|
/// Is the given `uri` a hex encoded public key?
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
public: bool,
|
public: bool,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub keystore_params: KeystoreParams,
|
pub keystore_params: KeystoreParams,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub network_scheme: NetworkSchemeFlag,
|
pub network_scheme: NetworkSchemeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub output_scheme: OutputTypeFlag,
|
pub output_scheme: OutputTypeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub crypto_scheme: CryptoSchemeFlag,
|
pub crypto_scheme: CryptoSchemeFlag,
|
||||||
|
|
||||||
/// Expect that `--uri` has the given public key/account-id.
|
/// Expect that `--uri` has the given public key/account-id.
|
||||||
///
|
///
|
||||||
/// If `--uri` has any derivations, the public key is checked against the base `uri`, i.e. the
|
/// If `--uri` has any derivations, the public key is checked against the base `uri`, i.e. the
|
||||||
/// `uri` without any derivation applied. However, if `uri` has a password or there is one
|
/// `uri` without any derivation applied. However, if `uri` has a password or there is one
|
||||||
/// given by `--password`, it will be used to decrypt `uri` before comparing the public
|
/// given by `--password`, it will be used to decrypt `uri` before comparing the public
|
||||||
/// key/account-id.
|
/// key/account-id.
|
||||||
///
|
///
|
||||||
/// If there is no derivation in `--uri`, the public key will be checked against the public key
|
/// If there is no derivation in `--uri`, the public key will be checked against the public key
|
||||||
/// of `--uri` directly.
|
/// of `--uri` directly.
|
||||||
#[arg(long, conflicts_with = "public")]
|
#[arg(long, conflicts_with = "public")]
|
||||||
pub expect_public: Option<String>,
|
pub expect_public: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InspectKeyCmd {
|
impl InspectKeyCmd {
|
||||||
/// Run the command
|
/// Run the command
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&self) -> Result<(), Error> {
|
||||||
let uri = read_uri(self.uri.as_ref())?;
|
let uri = read_uri(self.uri.as_ref())?;
|
||||||
let password = self.keystore_params.read_password()?;
|
let password = self.keystore_params.read_password()?;
|
||||||
|
|
||||||
if self.public {
|
if self.public {
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_public(
|
print_from_public(
|
||||||
&uri,
|
&uri,
|
||||||
self.network_scheme.network,
|
self.network_scheme.network,
|
||||||
self.output_scheme.output_type,
|
self.output_scheme.output_type,
|
||||||
)
|
)
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
if let Some(ref expect_public) = self.expect_public {
|
if let Some(ref expect_public) = self.expect_public {
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
expect_public_from_phrase(expect_public, &uri, password.as_ref())
|
expect_public_from_phrase(expect_public, &uri, password.as_ref())
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_uri(
|
print_from_uri(
|
||||||
&uri,
|
&uri,
|
||||||
password,
|
password,
|
||||||
self.network_scheme.network,
|
self.network_scheme.network,
|
||||||
self.output_scheme.output_type,
|
self.output_scheme.output_type,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that `expect_public` is the public key of `suri`.
|
/// Checks that `expect_public` is the public key of `suri`.
|
||||||
@ -105,156 +106,148 @@ impl InspectKeyCmd {
|
|||||||
///
|
///
|
||||||
/// Returns an error if the public key does not match.
|
/// Returns an error if the public key does not match.
|
||||||
fn expect_public_from_phrase<Pair: sp_core::Pair>(
|
fn expect_public_from_phrase<Pair: sp_core::Pair>(
|
||||||
expect_public: &str,
|
expect_public: &str,
|
||||||
suri: &str,
|
suri: &str,
|
||||||
password: Option<&SecretString>,
|
password: Option<&SecretString>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let secret_uri = SecretUri::from_str(suri).map_err(|e| format!("{:?}", e))?;
|
let secret_uri = SecretUri::from_str(suri).map_err(|e| format!("{:?}", e))?;
|
||||||
let expected_public = if let Some(public) = expect_public.strip_prefix("0x") {
|
let expected_public = if let Some(public) = expect_public.strip_prefix("0x") {
|
||||||
let hex_public = array_bytes::hex2bytes(public)
|
let hex_public = array_bytes::hex2bytes(public)
|
||||||
.map_err(|_| format!("Invalid expected public key hex: `{}`", expect_public))?;
|
.map_err(|_| format!("Invalid expected public key hex: `{}`", expect_public))?;
|
||||||
Pair::Public::try_from(&hex_public)
|
Pair::Public::try_from(&hex_public)
|
||||||
.map_err(|_| format!("Invalid expected public key: `{}`", expect_public))?
|
.map_err(|_| format!("Invalid expected public key: `{}`", expect_public))?
|
||||||
} else {
|
} else {
|
||||||
Pair::Public::from_string_with_version(expect_public)
|
Pair::Public::from_string_with_version(expect_public)
|
||||||
.map_err(|_| format!("Invalid expected account id: `{}`", expect_public))?
|
.map_err(|_| format!("Invalid expected account id: `{}`", expect_public))?
|
||||||
.0
|
.0
|
||||||
};
|
};
|
||||||
|
|
||||||
let pair = Pair::from_string_with_seed(
|
let pair = Pair::from_string_with_seed(
|
||||||
secret_uri.phrase.expose_secret().as_str(),
|
secret_uri.phrase.expose_secret().as_str(),
|
||||||
password
|
password
|
||||||
.or_else(|| secret_uri.password.as_ref())
|
.or_else(|| secret_uri.password.as_ref())
|
||||||
.map(|p| p.expose_secret().as_str()),
|
.map(|p| p.expose_secret().as_str()),
|
||||||
)
|
)
|
||||||
.map_err(|_| format!("Invalid secret uri: {}", suri))?
|
.map_err(|_| format!("Invalid secret uri: {}", suri))?
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
if pair.public() == expected_public {
|
if pair.public() == expected_public {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Expected public ({}) key does not match.", expect_public).into())
|
Err(format!("Expected public ({}) key does not match.", expect_public).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use sp_core::crypto::{ByteArray, Pair};
|
use sp_core::crypto::{ByteArray, Pair};
|
||||||
use sp_runtime::traits::IdentifyAccount;
|
use sp_runtime::traits::IdentifyAccount;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspect() {
|
fn inspect() {
|
||||||
let words =
|
let words =
|
||||||
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
||||||
let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5";
|
let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5";
|
||||||
|
|
||||||
let inspect = InspectKeyCmd::parse_from(&["inspect-key", words, "--password", "12345"]);
|
let inspect = InspectKeyCmd::parse_from(&["inspect-key", words, "--password", "12345"]);
|
||||||
assert!(inspect.run().is_ok());
|
assert!(inspect.run().is_ok());
|
||||||
|
|
||||||
let inspect = InspectKeyCmd::parse_from(&["inspect-key", seed]);
|
let inspect = InspectKeyCmd::parse_from(&["inspect-key", seed]);
|
||||||
assert!(inspect.run().is_ok());
|
assert!(inspect.run().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspect_public_key() {
|
fn inspect_public_key() {
|
||||||
let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
||||||
|
|
||||||
let inspect = InspectKeyCmd::parse_from(&["inspect-key", "--public", public]);
|
let inspect = InspectKeyCmd::parse_from(&["inspect-key", "--public", public]);
|
||||||
assert!(inspect.run().is_ok());
|
assert!(inspect.run().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspect_with_expected_public_key() {
|
fn inspect_with_expected_public_key() {
|
||||||
let check_cmd = |seed, expected_public, success| {
|
let check_cmd = |seed, expected_public, success| {
|
||||||
let inspect = InspectKeyCmd::parse_from(&[
|
let inspect = InspectKeyCmd::parse_from(&[
|
||||||
"inspect-key",
|
"inspect-key",
|
||||||
"--expect-public",
|
"--expect-public",
|
||||||
expected_public,
|
expected_public,
|
||||||
seed,
|
seed,
|
||||||
]);
|
]);
|
||||||
let res = inspect.run();
|
let res = inspect.run();
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
} else {
|
} else {
|
||||||
assert!(res.unwrap_err().to_string().contains(&format!(
|
assert!(res.unwrap_err().to_string().contains(&format!(
|
||||||
"Expected public ({}) key does not match.",
|
"Expected public ({}) key does not match.",
|
||||||
expected_public
|
expected_public
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let seed =
|
let seed =
|
||||||
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
||||||
let invalid_public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
let invalid_public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
||||||
let valid_public = sp_core::sr25519::Pair::from_string_with_seed(seed, None)
|
let valid_public = sp_core::sr25519::Pair::from_string_with_seed(seed, None)
|
||||||
.expect("Valid")
|
.expect("Valid")
|
||||||
.0
|
.0
|
||||||
.public();
|
.public();
|
||||||
let valid_public_hex = array_bytes::bytes2hex("0x", valid_public.as_slice());
|
let valid_public_hex = array_bytes::bytes2hex("0x", valid_public.as_slice());
|
||||||
let valid_accountid = format!("{}", valid_public.into_account());
|
let valid_accountid = format!("{}", valid_public.into_account());
|
||||||
|
|
||||||
// It should fail with the invalid public key
|
// It should fail with the invalid public key
|
||||||
check_cmd(seed, invalid_public, false);
|
check_cmd(seed, invalid_public, false);
|
||||||
|
|
||||||
// It should work with the valid public key & account id
|
// It should work with the valid public key & account id
|
||||||
check_cmd(seed, &valid_public_hex, true);
|
check_cmd(seed, &valid_public_hex, true);
|
||||||
check_cmd(seed, &valid_accountid, true);
|
check_cmd(seed, &valid_accountid, true);
|
||||||
|
|
||||||
let password = "test12245";
|
let password = "test12245";
|
||||||
let seed_with_password = format!("{}///{}", seed, password);
|
let seed_with_password = format!("{}///{}", seed, password);
|
||||||
let valid_public_with_password =
|
let valid_public_with_password =
|
||||||
sp_core::sr25519::Pair::from_string_with_seed(&seed_with_password, Some(password))
|
sp_core::sr25519::Pair::from_string_with_seed(&seed_with_password, Some(password))
|
||||||
.expect("Valid")
|
.expect("Valid")
|
||||||
.0
|
.0
|
||||||
.public();
|
.public();
|
||||||
let valid_public_hex_with_password =
|
let valid_public_hex_with_password =
|
||||||
array_bytes::bytes2hex("0x", valid_public_with_password.as_slice());
|
array_bytes::bytes2hex("0x", valid_public_with_password.as_slice());
|
||||||
let valid_accountid_with_password =
|
let valid_accountid_with_password =
|
||||||
format!("{}", &valid_public_with_password.into_account());
|
format!("{}", &valid_public_with_password.into_account());
|
||||||
|
|
||||||
// Only the public key that corresponds to the seed with password should be accepted.
|
// Only the public key that corresponds to the seed with password should be accepted.
|
||||||
check_cmd(&seed_with_password, &valid_public_hex, false);
|
check_cmd(&seed_with_password, &valid_public_hex, false);
|
||||||
check_cmd(&seed_with_password, &valid_accountid, false);
|
check_cmd(&seed_with_password, &valid_accountid, false);
|
||||||
|
|
||||||
check_cmd(&seed_with_password, &valid_public_hex_with_password, true);
|
check_cmd(&seed_with_password, &valid_public_hex_with_password, true);
|
||||||
check_cmd(&seed_with_password, &valid_accountid_with_password, true);
|
check_cmd(&seed_with_password, &valid_accountid_with_password, true);
|
||||||
|
|
||||||
let seed_with_password_and_derivation = format!("{}//test//account///{}", seed, password);
|
let seed_with_password_and_derivation = format!("{}//test//account///{}", seed, password);
|
||||||
|
|
||||||
let valid_public_with_password_and_derivation =
|
let valid_public_with_password_and_derivation =
|
||||||
sp_core::sr25519::Pair::from_string_with_seed(
|
sp_core::sr25519::Pair::from_string_with_seed(
|
||||||
&seed_with_password_and_derivation,
|
&seed_with_password_and_derivation,
|
||||||
Some(password),
|
Some(password),
|
||||||
)
|
)
|
||||||
.expect("Valid")
|
.expect("Valid")
|
||||||
.0
|
.0
|
||||||
.public();
|
.public();
|
||||||
let valid_public_hex_with_password_and_derivation =
|
let valid_public_hex_with_password_and_derivation =
|
||||||
array_bytes::bytes2hex("0x", valid_public_with_password_and_derivation.as_slice());
|
array_bytes::bytes2hex("0x", valid_public_with_password_and_derivation.as_slice());
|
||||||
|
|
||||||
// They should still be valid, because we check the base secret key.
|
// They should still be valid, because we check the base secret key.
|
||||||
check_cmd(
|
check_cmd(&seed_with_password_and_derivation, &valid_public_hex_with_password, true);
|
||||||
&seed_with_password_and_derivation,
|
check_cmd(&seed_with_password_and_derivation, &valid_accountid_with_password, true);
|
||||||
&valid_public_hex_with_password,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
check_cmd(
|
|
||||||
&seed_with_password_and_derivation,
|
|
||||||
&valid_accountid_with_password,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// And these should be invalid.
|
// And these should be invalid.
|
||||||
check_cmd(&seed_with_password_and_derivation, &valid_public_hex, false);
|
check_cmd(&seed_with_password_and_derivation, &valid_public_hex, false);
|
||||||
check_cmd(&seed_with_password_and_derivation, &valid_accountid, false);
|
check_cmd(&seed_with_password_and_derivation, &valid_accountid, false);
|
||||||
|
|
||||||
// The public of the derived account should fail.
|
// The public of the derived account should fail.
|
||||||
check_cmd(
|
check_cmd(
|
||||||
&seed_with_password_and_derivation,
|
&seed_with_password_and_derivation,
|
||||||
&valid_public_hex_with_password_and_derivation,
|
&valid_public_hex_with_password_and_derivation,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +1,40 @@
|
|||||||
//! Key related CLI utilities
|
//! Key related CLI utilities
|
||||||
|
|
||||||
use super::{generate::GenerateCmd, inspect_key::InspectKeyCmd};
|
use super::{generate::GenerateCmd, inspect_key::InspectKeyCmd};
|
||||||
use sc_cli::{Error, GenerateKeyCmdCommon, InsertKeyCmd, InspectNodeKeyCmd, SubstrateCli};
|
use sc_cli::{
|
||||||
|
GenerateKeyCmdCommon, InsertKeyCmd, InspectNodeKeyCmd, Error,
|
||||||
|
SubstrateCli,
|
||||||
|
};
|
||||||
|
|
||||||
/// Key utilities for the cli.
|
/// Key utilities for the cli.
|
||||||
#[derive(Debug, clap::Subcommand)]
|
#[derive(Debug, clap::Subcommand)]
|
||||||
pub enum KeySubcommand {
|
pub enum KeySubcommand {
|
||||||
/// Generate a random node key, write it to a file or stdout and write the
|
/// Generate a random node key, write it to a file or stdout and write the
|
||||||
/// corresponding peer-id to stderr
|
/// corresponding peer-id to stderr
|
||||||
GenerateNodeKey(GenerateKeyCmdCommon),
|
GenerateNodeKey(GenerateKeyCmdCommon),
|
||||||
|
|
||||||
/// Generate a Substrate-based random account
|
/// Generate a Substrate-based random account
|
||||||
Generate(GenerateCmd),
|
Generate(GenerateCmd),
|
||||||
|
|
||||||
/// Gets a public key and a SS58 address from the provided Secret URI
|
/// Gets a public key and a SS58 address from the provided Secret URI
|
||||||
Inspect(InspectKeyCmd),
|
Inspect(InspectKeyCmd),
|
||||||
|
|
||||||
/// Load a node key from a file or stdin and print the corresponding peer-id
|
/// Load a node key from a file or stdin and print the corresponding peer-id
|
||||||
InspectNodeKey(InspectNodeKeyCmd),
|
InspectNodeKey(InspectNodeKeyCmd),
|
||||||
|
|
||||||
/// Insert a key to the keystore of a node.
|
/// Insert a key to the keystore of a node.
|
||||||
Insert(InsertKeyCmd),
|
Insert(InsertKeyCmd),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeySubcommand {
|
impl KeySubcommand {
|
||||||
/// run the key subcommands
|
/// run the key subcommands
|
||||||
pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> {
|
pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> {
|
||||||
match self {
|
match self {
|
||||||
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(),
|
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(),
|
||||||
KeySubcommand::Generate(cmd) => cmd.run(),
|
KeySubcommand::Generate(cmd) => cmd.run(),
|
||||||
KeySubcommand::Inspect(cmd) => cmd.run(),
|
KeySubcommand::Inspect(cmd) => cmd.run(),
|
||||||
KeySubcommand::Insert(cmd) => cmd.run(cli),
|
KeySubcommand::Insert(cmd) => cmd.run(cli),
|
||||||
KeySubcommand::InspectNodeKey(cmd) => cmd.run(),
|
KeySubcommand::InspectNodeKey(cmd) => cmd.run(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
mod generate;
|
|
||||||
mod inspect_key;
|
|
||||||
mod key;
|
mod key;
|
||||||
mod utils;
|
mod generate;
|
||||||
mod vanity;
|
mod vanity;
|
||||||
|
mod inspect_key;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
key::KeySubcommand, vanity::VanityCmd, inspect_key::InspectKeyCmd,
|
||||||
generate::GenerateCmd,
|
generate::GenerateCmd,
|
||||||
inspect_key::InspectKeyCmd,
|
utils::{unwrap_or_default_ss58_name, print_from_uri, print_from_public},
|
||||||
key::KeySubcommand,
|
|
||||||
utils::{print_from_public, print_from_uri, unwrap_or_default_ss58_name},
|
|
||||||
vanity::VanityCmd,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,206 +1,194 @@
|
|||||||
use sc_cli::{
|
|
||||||
utils::{PublicFor, SeedFor},
|
|
||||||
OutputType,
|
|
||||||
};
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use sc_cli::{
|
||||||
|
OutputType,
|
||||||
|
utils::{PublicFor, SeedFor},
|
||||||
|
};
|
||||||
|
use sp_runtime::{traits::IdentifyAccount, MultiSigner};
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
crypto::{
|
crypto::{
|
||||||
unwrap_or_default_ss58_version, ExposeSecret, SecretString, Ss58AddressFormat, Ss58Codec,
|
unwrap_or_default_ss58_version,
|
||||||
|
Ss58Codec, ExposeSecret, Ss58AddressFormat, SecretString,
|
||||||
},
|
},
|
||||||
hexdisplay::HexDisplay,
|
hexdisplay::HexDisplay,
|
||||||
};
|
};
|
||||||
use sp_runtime::{traits::IdentifyAccount, MultiSigner};
|
|
||||||
|
|
||||||
pub fn print_from_uri<Pair>(
|
pub fn print_from_uri<Pair>(
|
||||||
uri: &str,
|
uri: &str,
|
||||||
password: Option<SecretString>,
|
password: Option<SecretString>,
|
||||||
network_override: Option<Ss58AddressFormat>,
|
network_override: Option<Ss58AddressFormat>,
|
||||||
output: OutputType,
|
output: OutputType,
|
||||||
) where
|
) where
|
||||||
Pair: sp_core::Pair,
|
Pair: sp_core::Pair,
|
||||||
Pair::Public: Into<MultiSigner>,
|
Pair::Public: Into<MultiSigner>,
|
||||||
{
|
{
|
||||||
let password = password.as_ref().map(|s| s.expose_secret().as_str());
|
let password = password.as_ref().map(|s| s.expose_secret().as_str());
|
||||||
let network_id = unwrap_or_default_ss58_name(network_override);
|
let network_id = unwrap_or_default_ss58_name(network_override);
|
||||||
|
|
||||||
if let Ok((pair, seed)) = Pair::from_phrase(uri, password) {
|
if let Ok((pair, seed)) = Pair::from_phrase(uri, password) {
|
||||||
let public_key = pair.public();
|
let public_key = pair.public();
|
||||||
let network_override = unwrap_or_default_ss58_version(network_override);
|
let network_override = unwrap_or_default_ss58_version(network_override);
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"secretPhrase": uri,
|
"secretPhrase": uri,
|
||||||
"networkId": network_id,
|
"networkId": network_id,
|
||||||
"secretSeed": format_seed::<Pair>(seed),
|
"secretSeed": format_seed::<Pair>(seed),
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"accountId": format_account_id::<Pair>(public_key),
|
"accountId": format_account_id::<Pair>(public_key),
|
||||||
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
OutputType::Text => {
|
OutputType::Text => {
|
||||||
println!(
|
println!(
|
||||||
"Secret phrase: {}\n \
|
"Secret phrase: {}\n \
|
||||||
Network ID: {}\n \
|
Network ID: {}\n \
|
||||||
Secret seed: {}\n \
|
Secret seed: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
uri,
|
uri,
|
||||||
network_id,
|
network_id,
|
||||||
format_seed::<Pair>(seed),
|
format_seed::<Pair>(seed),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
pair.public()
|
pair.public().into().into_account().to_ss58check_with_version(network_override),
|
||||||
.into()
|
);
|
||||||
.into_account()
|
},
|
||||||
.to_ss58check_with_version(network_override),
|
}
|
||||||
);
|
} else if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password) {
|
||||||
}
|
let public_key = pair.public();
|
||||||
}
|
let network_override = unwrap_or_default_ss58_version(network_override);
|
||||||
} else if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password) {
|
|
||||||
let public_key = pair.public();
|
|
||||||
let network_override = unwrap_or_default_ss58_version(network_override);
|
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"secretKeyUri": uri,
|
"secretKeyUri": uri,
|
||||||
"networkId": network_id,
|
"networkId": network_id,
|
||||||
"secretSeed": if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
"secretSeed": if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"accountId": format_account_id::<Pair>(public_key),
|
"accountId": format_account_id::<Pair>(public_key),
|
||||||
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
OutputType::Text => {
|
OutputType::Text => {
|
||||||
println!(
|
println!(
|
||||||
"Secret Key URI `{}` is account:\n \
|
"Secret Key URI `{}` is account:\n \
|
||||||
Network ID: {}\n \
|
Network ID: {}\n \
|
||||||
Secret seed: {}\n \
|
Secret seed: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
uri,
|
uri,
|
||||||
network_id,
|
network_id,
|
||||||
if let Some(seed) = seed {
|
if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
||||||
format_seed::<Pair>(seed)
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
} else {
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
"n/a".into()
|
public_key.to_ss58check_with_version(network_override),
|
||||||
},
|
pair.public().into().into_account().to_ss58check_with_version(network_override),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
);
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
},
|
||||||
public_key.to_ss58check_with_version(network_override),
|
}
|
||||||
pair.public()
|
} else if let Ok((public_key, network)) = Pair::Public::from_string_with_version(uri) {
|
||||||
.into()
|
let network_override = network_override.unwrap_or(network);
|
||||||
.into_account()
|
|
||||||
.to_ss58check_with_version(network_override),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Ok((public_key, network)) = Pair::Public::from_string_with_version(uri) {
|
|
||||||
let network_override = network_override.unwrap_or(network);
|
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"publicKeyUri": uri,
|
"publicKeyUri": uri,
|
||||||
"networkId": String::from(network_override),
|
"networkId": String::from(network_override),
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"accountId": format_account_id::<Pair>(public_key.clone()),
|
"accountId": format_account_id::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"ss58Address": public_key.to_ss58check_with_version(network_override),
|
"ss58Address": public_key.to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
OutputType::Text => {
|
OutputType::Text => {
|
||||||
println!(
|
println!(
|
||||||
"Public Key URI `{}` is account:\n \
|
"Public Key URI `{}` is account:\n \
|
||||||
Network ID/Version: {}\n \
|
Network ID/Version: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
uri,
|
uri,
|
||||||
String::from(network_override),
|
String::from(network_override),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Invalid phrase/URI given");
|
println!("Invalid phrase/URI given");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to parse given `public` as hex encoded public key and print relevant information.
|
/// Try to parse given `public` as hex encoded public key and print relevant information.
|
||||||
pub fn print_from_public<Pair>(
|
pub fn print_from_public<Pair>(
|
||||||
public_str: &str,
|
public_str: &str,
|
||||||
network_override: Option<Ss58AddressFormat>,
|
network_override: Option<Ss58AddressFormat>,
|
||||||
output: OutputType,
|
output: OutputType,
|
||||||
) -> Result<(), sc_cli::Error>
|
) -> Result<(), sc_cli::Error>
|
||||||
where
|
where
|
||||||
Pair: sp_core::Pair,
|
Pair: sp_core::Pair,
|
||||||
Pair::Public: Into<MultiSigner>,
|
Pair::Public: Into<MultiSigner>,
|
||||||
{
|
{
|
||||||
let public = array_bytes::hex2bytes(public_str)?;
|
let public = array_bytes::hex2bytes(public_str)?;
|
||||||
|
|
||||||
let public_key = Pair::Public::try_from(&public)
|
let public_key = Pair::Public::try_from(&public)
|
||||||
.map_err(|_| "Failed to construct public key from given hex")?;
|
.map_err(|_| "Failed to construct public key from given hex")?;
|
||||||
|
|
||||||
let network_override = unwrap_or_default_ss58_version(network_override);
|
let network_override = unwrap_or_default_ss58_version(network_override);
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"networkId": String::from(network_override),
|
"networkId": String::from(network_override),
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"accountId": format_account_id::<Pair>(public_key.clone()),
|
"accountId": format_account_id::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"ss58Address": public_key.to_ss58check_with_version(network_override),
|
"ss58Address": public_key.to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
|
|
||||||
println!(
|
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
|
||||||
"{}",
|
},
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
OutputType::Text => {
|
||||||
);
|
println!(
|
||||||
}
|
"Network ID/Version: {}\n \
|
||||||
OutputType::Text => {
|
|
||||||
println!(
|
|
||||||
"Network ID/Version: {}\n \
|
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
String::from(network_override),
|
String::from(network_override),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_or_default_ss58_name(x: Option<Ss58AddressFormat>) -> String {
|
pub fn unwrap_or_default_ss58_name(x: Option<Ss58AddressFormat>) -> String {
|
||||||
@ -228,8 +216,5 @@ fn format_account_id<P: sp_core::Pair>(public_key: PublicFor<P>) -> String
|
|||||||
where
|
where
|
||||||
PublicFor<P>: Into<MultiSigner>,
|
PublicFor<P>: Into<MultiSigner>,
|
||||||
{
|
{
|
||||||
format!(
|
format!("0x{}", HexDisplay::from(&public_key.into().into_account().as_ref()))
|
||||||
"0x{}",
|
|
||||||
HexDisplay::from(&public_key.into().into_account().as_ref())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,218 +2,213 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use rand::{rngs::OsRng, RngCore};
|
||||||
use sc_cli::{utils::format_seed, with_crypto_scheme, CryptoSchemeFlag, Error, OutputTypeFlag};
|
use sp_core::crypto::{
|
||||||
use sp_core::crypto::{unwrap_or_default_ss58_version, Ss58AddressFormat, Ss58Codec};
|
unwrap_or_default_ss58_version, Ss58AddressFormat, Ss58Codec,
|
||||||
|
};
|
||||||
use sp_runtime::traits::IdentifyAccount;
|
use sp_runtime::traits::IdentifyAccount;
|
||||||
|
use sc_cli::{
|
||||||
|
with_crypto_scheme, Error, OutputTypeFlag, CryptoSchemeFlag,
|
||||||
|
utils::format_seed,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::commands::utils::print_from_uri;
|
use crate::commands::utils::print_from_uri;
|
||||||
use crate::params::NetworkSchemeFlag;
|
use crate::params::NetworkSchemeFlag;
|
||||||
|
|
||||||
/// The `vanity` command
|
/// The `vanity` command
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
#[command(
|
#[command(name = "vanity", about = "Generate a seed that provides a vanity address")]
|
||||||
name = "vanity",
|
|
||||||
about = "Generate a seed that provides a vanity address"
|
|
||||||
)]
|
|
||||||
pub struct VanityCmd {
|
pub struct VanityCmd {
|
||||||
/// Desired pattern
|
/// Desired pattern
|
||||||
#[arg(long, value_parser = assert_non_empty_string)]
|
#[arg(long, value_parser = assert_non_empty_string)]
|
||||||
pattern: String,
|
pattern: String,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
network_scheme: NetworkSchemeFlag,
|
network_scheme: NetworkSchemeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
output_scheme: OutputTypeFlag,
|
output_scheme: OutputTypeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
crypto_scheme: CryptoSchemeFlag,
|
crypto_scheme: CryptoSchemeFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VanityCmd {
|
impl VanityCmd {
|
||||||
/// Run the command
|
/// Run the command
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&self) -> Result<(), Error> {
|
||||||
let formated_seed = with_crypto_scheme!(
|
let formated_seed = with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
generate_key(
|
generate_key(
|
||||||
&self.pattern,
|
&self.pattern,
|
||||||
unwrap_or_default_ss58_version(self.network_scheme.network)
|
unwrap_or_default_ss58_version(self.network_scheme.network)
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_uri(
|
print_from_uri(
|
||||||
&formated_seed,
|
&formated_seed,
|
||||||
None,
|
None,
|
||||||
self.network_scheme.network,
|
self.network_scheme.network,
|
||||||
self.output_scheme.output_type,
|
self.output_scheme.output_type,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// genertae a key based on given pattern
|
/// genertae a key based on given pattern
|
||||||
fn generate_key<Pair>(
|
fn generate_key<Pair>(
|
||||||
desired: &str,
|
desired: &str,
|
||||||
network_override: Ss58AddressFormat,
|
network_override: Ss58AddressFormat,
|
||||||
) -> Result<String, &'static str>
|
) -> Result<String, &'static str>
|
||||||
where
|
where
|
||||||
Pair: sp_core::Pair,
|
Pair: sp_core::Pair,
|
||||||
Pair::Public: IdentifyAccount,
|
Pair::Public: IdentifyAccount,
|
||||||
<Pair::Public as IdentifyAccount>::AccountId: Ss58Codec,
|
<Pair::Public as IdentifyAccount>::AccountId: Ss58Codec,
|
||||||
{
|
{
|
||||||
println!("Generating key containing pattern '{}'", desired);
|
println!("Generating key containing pattern '{}'", desired);
|
||||||
|
|
||||||
let top = 45 + (desired.len() * 48);
|
let top = 45 + (desired.len() * 48);
|
||||||
let mut best = 0;
|
let mut best = 0;
|
||||||
let mut seed = Pair::Seed::default();
|
let mut seed = Pair::Seed::default();
|
||||||
let mut done = 0;
|
let mut done = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if done % 100000 == 0 {
|
if done % 100000 == 0 {
|
||||||
OsRng.fill_bytes(seed.as_mut());
|
OsRng.fill_bytes(seed.as_mut());
|
||||||
} else {
|
} else {
|
||||||
next_seed(seed.as_mut());
|
next_seed(seed.as_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
let p = Pair::from_seed(&seed);
|
let p = Pair::from_seed(&seed);
|
||||||
let ss58 = p
|
let ss58 = p.public().into_account().to_ss58check_with_version(network_override);
|
||||||
.public()
|
|
||||||
.into_account()
|
|
||||||
.to_ss58check_with_version(network_override);
|
|
||||||
println!("{:?}", ss58);
|
println!("{:?}", ss58);
|
||||||
let score = calculate_score(desired, &ss58);
|
let score = calculate_score(desired, &ss58);
|
||||||
if score > best || desired.len() < 2 {
|
if score > best || desired.len() < 2 {
|
||||||
best = score;
|
best = score;
|
||||||
if best >= top {
|
if best >= top {
|
||||||
println!("best: {} == top: {}", best, top);
|
println!("best: {} == top: {}", best, top);
|
||||||
return Ok(format_seed::<Pair>(seed.clone()));
|
return Ok(format_seed::<Pair>(seed.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done += 1;
|
done += 1;
|
||||||
|
|
||||||
if done % good_waypoint(done) == 0 {
|
if done % good_waypoint(done) == 0 {
|
||||||
println!("{} keys searched; best is {}/{} complete", done, best, top);
|
println!("{} keys searched; best is {}/{} complete", done, best, top);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn good_waypoint(done: u64) -> u64 {
|
fn good_waypoint(done: u64) -> u64 {
|
||||||
match done {
|
match done {
|
||||||
0..=1_000_000 => 100_000,
|
0..=1_000_000 => 100_000,
|
||||||
1_000_001..=10_000_000 => 1_000_000,
|
1_000_001..=10_000_000 => 1_000_000,
|
||||||
10_000_001..=100_000_000 => 10_000_000,
|
10_000_001..=100_000_000 => 10_000_000,
|
||||||
100_000_001.. => 100_000_000,
|
100_000_001.. => 100_000_000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_seed(seed: &mut [u8]) {
|
fn next_seed(seed: &mut [u8]) {
|
||||||
for s in seed {
|
for s in seed {
|
||||||
match s {
|
match s {
|
||||||
255 => {
|
255 => {
|
||||||
*s = 0;
|
*s = 0;
|
||||||
}
|
},
|
||||||
_ => {
|
_ => {
|
||||||
*s += 1;
|
*s += 1;
|
||||||
break;
|
break
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the score of a key based on the desired
|
/// Calculate the score of a key based on the desired
|
||||||
/// input.
|
/// input.
|
||||||
fn calculate_score(_desired: &str, key: &str) -> usize {
|
fn calculate_score(_desired: &str, key: &str) -> usize {
|
||||||
for truncate in 0.._desired.len() {
|
for truncate in 0.._desired.len() {
|
||||||
let snip_size = _desired.len() - truncate;
|
let snip_size = _desired.len() - truncate;
|
||||||
let truncated = &_desired[0..snip_size];
|
let truncated = &_desired[0..snip_size];
|
||||||
if let Some(pos) = key.find(truncated) {
|
if let Some(pos) = key.find(truncated) {
|
||||||
return (47 - pos) + (snip_size * 48);
|
return (47 - pos) + (snip_size * 48)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// checks that `pattern` is non-empty
|
/// checks that `pattern` is non-empty
|
||||||
fn assert_non_empty_string(pattern: &str) -> Result<String, &'static str> {
|
fn assert_non_empty_string(pattern: &str) -> Result<String, &'static str> {
|
||||||
if pattern.is_empty() {
|
if pattern.is_empty() {
|
||||||
Err("Pattern must not be empty")
|
Err("Pattern must not be empty")
|
||||||
} else {
|
} else {
|
||||||
Ok(pattern.to_string())
|
Ok(pattern.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
crypto::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec},
|
crypto::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec},
|
||||||
sr25519, Pair,
|
sr25519, Pair,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vanity() {
|
fn vanity() {
|
||||||
let vanity = VanityCmd::parse_from(&["vanity", "--pattern", "j"]);
|
let vanity = VanityCmd::parse_from(&["vanity", "--pattern", "j"]);
|
||||||
assert!(vanity.run().is_ok());
|
assert!(vanity.run().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generation_with_single_char() {
|
fn test_generation_with_single_char() {
|
||||||
let seed = generate_key::<sr25519::Pair>("ab", default_ss58_version()).unwrap();
|
let seed = generate_key::<sr25519::Pair>("ab", default_ss58_version()).unwrap();
|
||||||
assert!(
|
assert!(sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
||||||
sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
.unwrap()
|
||||||
.unwrap()
|
.public()
|
||||||
.public()
|
.to_ss58check()
|
||||||
.to_ss58check()
|
.contains("ab"));
|
||||||
.contains("ab")
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_key_respects_network_override() {
|
fn generate_key_respects_network_override() {
|
||||||
let seed =
|
let seed =
|
||||||
generate_key::<sr25519::Pair>("ab", Ss58AddressFormatRegistry::PolkadotAccount.into())
|
generate_key::<sr25519::Pair>("ab", Ss58AddressFormatRegistry::PolkadotAccount.into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(
|
assert!(sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
||||||
sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
.unwrap()
|
||||||
.unwrap()
|
.public()
|
||||||
.public()
|
.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())
|
||||||
.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())
|
.contains("ab"));
|
||||||
.contains("ab")
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_1_char_100() {
|
fn test_score_1_char_100() {
|
||||||
let score = calculate_score("j", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H");
|
let score = calculate_score("j", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H");
|
||||||
assert_eq!(score, 96);
|
assert_eq!(score, 96);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_100() {
|
fn test_score_100() {
|
||||||
let score = calculate_score("ghst", "sYsghstuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG");
|
let score = calculate_score("ghst", "sYsghstuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG");
|
||||||
assert_eq!(score, 246);
|
assert_eq!(score, 246);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_50_2() {
|
fn test_score_50_2() {
|
||||||
// 50% for the position + 50% for the size
|
// 50% for the position + 50% for the size
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
calculate_score("ghst", "sYsghXXuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"),
|
calculate_score("ghst", "sYsghXXuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"),
|
||||||
146
|
146
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_0() {
|
fn test_score_0() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
calculate_score("ghst", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"),
|
calculate_score("ghst", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod params;
|
pub mod params;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
pub use commands::KeySubcommand;
|
pub use commands::KeySubcommand;
|
||||||
pub use commands::VanityCmd;
|
pub use commands::VanityCmd;
|
||||||
|
|||||||
@ -21,25 +21,19 @@ pub struct InnerSs58AddressFormat(Ss58AddressFormat);
|
|||||||
impl InnerSs58AddressFormat {
|
impl InnerSs58AddressFormat {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn custom(prefix: u16) -> Self {
|
pub fn custom(prefix: u16) -> Self {
|
||||||
Self {
|
Self { 0: Ss58AddressFormat::custom(prefix) }
|
||||||
0: Ss58AddressFormat::custom(prefix),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for InnerSs58AddressFormat {
|
impl std::fmt::Display for InnerSs58AddressFormat {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(
|
write!(f, "{} network", ALL_POSSIBLE_IDS
|
||||||
f,
|
.binary_search(&u16::from(self.0))
|
||||||
"{} network",
|
.expect("always be found"))
|
||||||
ALL_POSSIBLE_IDS
|
|
||||||
.binary_search(&u16::from(self.0))
|
|
||||||
.expect("always be found")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for InnerSs58AddressFormat {
|
impl<'a>TryFrom<&'a str> for InnerSs58AddressFormat {
|
||||||
type Error = ParseError;
|
type Error = ParseError;
|
||||||
|
|
||||||
fn try_from(x: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(x: &'a str) -> Result<Self, Self::Error> {
|
||||||
@ -50,13 +44,13 @@ impl<'a> TryFrom<&'a str> for InnerSs58AddressFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_s58_prefix_address_format(x: &str) -> Result<Ss58AddressFormat, String> {
|
pub fn parse_s58_prefix_address_format(x: &str,) -> Result<Ss58AddressFormat, String> {
|
||||||
match InnerSs58AddressFormat::try_from(x) {
|
match InnerSs58AddressFormat::try_from(x) {
|
||||||
Ok(format_registry) => Ok(format_registry.0.into()),
|
Ok(format_registry) => Ok(format_registry.0.into()),
|
||||||
Err(_) => Err(format!(
|
Err(_) => Err(format!(
|
||||||
"Unable to parse variant. Known variants: {:?}",
|
"Unable to parse variant. Known variants: {:?}",
|
||||||
&ALL_POSSIBLE_NAMES
|
&ALL_POSSIBLE_NAMES
|
||||||
)),
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
core-primitives/Cargo.toml
Normal file → Executable file
@ -15,7 +15,7 @@ sp-runtime = { workspace = true }
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
]
|
]
|
||||||
|
|||||||
0
core-primitives/src/lib.rs
Normal file → Executable file
0
file_header.txt
Normal file → Executable file
BIN
images/img.png
|
Before Width: | Height: | Size: 4.7 KiB |
BIN
images/img_1.png
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 49 KiB |
BIN
images/img_2.png
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
BIN
images/img_3.png
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
BIN
images/img_5.png
|
Before Width: | Height: | Size: 4.4 KiB |
BIN
images/img_6.png
|
Before Width: | Height: | Size: 10 KiB |
BIN
images/img_7.png
|
Before Width: | Height: | Size: 15 KiB |
BIN
images/img_8.png
|
Before Width: | Height: | Size: 4.7 KiB |
BIN
images/img_9.png
|
Before Width: | Height: | Size: 22 KiB |
@ -14,7 +14,7 @@ pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Con
|
|||||||
|_logger_builder, _config| {}
|
|_logger_builder, _config| {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This module exports Prometheus types an:d defines
|
/// This module exports Prometheus types an:d defines
|
||||||
/// the [`Metrics`](metrics::Metrics) trait.
|
/// the [`Metrics`](metrics::Metrics) trait.
|
||||||
pub mod metrics {
|
pub mod metrics {
|
||||||
pub use prometheus_endpoint as prometheus;
|
pub use prometheus_endpoint as prometheus;
|
||||||
|
|||||||
@ -22,11 +22,7 @@ impl Metronome {
|
|||||||
/// Create a new metronome source with a defined cycle duration.
|
/// Create a new metronome source with a defined cycle duration.
|
||||||
pub fn new(cycle: Duration) -> Self {
|
pub fn new(cycle: Duration) -> Self {
|
||||||
let period = cycle.into();
|
let period = cycle.into();
|
||||||
Self {
|
Self { period, delay: Delay::new(period), state: MetronomeState::Snooze }
|
||||||
period,
|
|
||||||
delay: Delay::new(period),
|
|
||||||
state: MetronomeState::Snooze,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,14 +36,14 @@ impl futures::Stream for Metronome {
|
|||||||
let val = self.period;
|
let val = self.period;
|
||||||
self.delay.reset(val);
|
self.delay.reset(val);
|
||||||
self.state = MetronomeState::Snooze;
|
self.state = MetronomeState::Snooze;
|
||||||
}
|
},
|
||||||
MetronomeState::Snooze => {
|
MetronomeState::Snooze => {
|
||||||
if !Pin::new(&mut self.delay).poll(cx).is_ready() {
|
if !Pin::new(&mut self.delay).poll(cx).is_ready() {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
self.state = MetronomeState::SetAlarm;
|
self.state = MetronomeState::SetAlarm;
|
||||||
return Poll::Ready(Some(()));
|
return Poll::Ready(Some(()))
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
|
|||||||
@ -2,16 +2,19 @@
|
|||||||
|
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
use core_primitives::{
|
use core_primitives::{
|
||||||
metrics_definitions::{CounterDefinition, CounterVecDefinition, HistogramDefinition},
|
metrics_definitions::{
|
||||||
|
CounterDefinition, CounterVecDefinition, HistogramDefinition,
|
||||||
|
},
|
||||||
RuntimeMetricLabelValues, RuntimeMetricOp, RuntimeMetricUpdate,
|
RuntimeMetricLabelValues, RuntimeMetricOp, RuntimeMetricUpdate,
|
||||||
};
|
};
|
||||||
use prometheus_endpoint::{
|
use prometheus_endpoint::{
|
||||||
register, Counter, CounterVec, Histogram, HistogramOpts, Opts, PrometheusError, Registry, U64,
|
register, Counter, CounterVec, Histogram, HistogramOpts, Opts, Registry,
|
||||||
|
PrometheusError, U64,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::hash_map::HashMap,
|
collections::hash_map::HashMap,
|
||||||
sync::{Arc, Mutex, MutexGuard},
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
};
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Metrics {
|
pub struct Metrics {
|
||||||
@ -29,15 +32,13 @@ impl RuntimeMetricsProvider {
|
|||||||
|
|
||||||
pub fn register_countervec(&self, countervec: CounterVecDefinition) {
|
pub fn register_countervec(&self, countervec: CounterVecDefinition) {
|
||||||
self.with_counter_vecs_lock_held(|mut hashmap| {
|
self.with_counter_vecs_lock_held(|mut hashmap| {
|
||||||
hashmap
|
hashmap.entry(countervec.name.to_owned()).or_insert(register(
|
||||||
.entry(countervec.name.to_owned())
|
CounterVec::new(
|
||||||
.or_insert(register(
|
Opts::new(countervec.name, countervec.description),
|
||||||
CounterVec::new(
|
countervec.labels,
|
||||||
Opts::new(countervec.name, countervec.description),
|
)?,
|
||||||
countervec.labels,
|
&self.0,
|
||||||
)?,
|
)?);
|
||||||
&self.0,
|
|
||||||
)?);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -45,7 +46,10 @@ impl RuntimeMetricsProvider {
|
|||||||
pub fn register_counter(&self, counter: CounterDefinition) {
|
pub fn register_counter(&self, counter: CounterDefinition) {
|
||||||
self.with_counters_lock_held(|mut hashmap| {
|
self.with_counters_lock_held(|mut hashmap| {
|
||||||
hashmap.entry(counter.name.to_owned()).or_insert(register(
|
hashmap.entry(counter.name.to_owned()).or_insert(register(
|
||||||
Counter::new(counter.name, counter.description)?,
|
Counter::new(
|
||||||
|
counter.name,
|
||||||
|
counter.description,
|
||||||
|
)?,
|
||||||
&self.0,
|
&self.0,
|
||||||
)?);
|
)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -56,7 +60,8 @@ impl RuntimeMetricsProvider {
|
|||||||
self.with_histograms_lock_held(|mut hashmap| {
|
self.with_histograms_lock_held(|mut hashmap| {
|
||||||
hashmap.entry(hist.name.to_owned()).or_insert(register(
|
hashmap.entry(hist.name.to_owned()).or_insert(register(
|
||||||
Histogram::with_opts(
|
Histogram::with_opts(
|
||||||
HistogramOpts::new(hist.name, hist.description).buckets(hist.buckets.to_vec()),
|
HistogramOpts::new(hist.name, hist.description)
|
||||||
|
.buckets(hist.buckets.to_vec()),
|
||||||
)?,
|
)?,
|
||||||
&self.0,
|
&self.0,
|
||||||
)?);
|
)?);
|
||||||
@ -64,21 +69,23 @@ impl RuntimeMetricsProvider {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc_counter_vec_by(&self, name: &str, value: u64, labels: &RuntimeMetricsProvider) {
|
pub fn inc_counter_vec_by(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
value: u64,
|
||||||
|
labels: &RuntimeMetricsProvider,
|
||||||
|
) {
|
||||||
self.with_counter_vecs_lock_held(|mut hashmap| {
|
self.with_counter_vecs_lock_held(|mut hashmap| {
|
||||||
hashmap.entry(name.to_owned()).and_modify(|counter_vec| {
|
hashmap.entry(name.to_owned()).and_modify(|counter_vec| {
|
||||||
counter_vec
|
counter_vec.with_label_values(&labels.as_str_vec()).inc_by(value)
|
||||||
.with_label_values(&labels.as_str_vec())
|
|
||||||
.inc_by(value)
|
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc_counter_by(&self, name: &str, value: u64) {
|
pub fn inc_counter_by(&self, name: &str, value: u64) {
|
||||||
self.with_counters_lock_held(|mut hashmap| {
|
self.with_counters_lock_held(|mut hashmap| {
|
||||||
hashmap
|
hashmap.entry(name.to_owned())
|
||||||
.entry(name.to_owned())
|
|
||||||
.and_modify(|counter_vec| counter_vec.inc_by(value));
|
.and_modify(|counter_vec| counter_vec.inc_by(value));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -86,9 +93,8 @@ impl RuntimeMetricsProvider {
|
|||||||
|
|
||||||
pub fn observe_histogram(&self, name: &str, value: u128) {
|
pub fn observe_histogram(&self, name: &str, value: u128) {
|
||||||
self.with_histograms_lock_held(|mut hashmap| {
|
self.with_histograms_lock_held(|mut hashmap| {
|
||||||
hashmap
|
hashmap.entry(name.to_owned())
|
||||||
.entry(name.to_owned())
|
and_modify(|histogram| histogram.observe(value as f64 / 1_000_000_000.0));
|
||||||
.and_modify(|histogram| histogram.observe(value as f64 / 1_000_000_000.0));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -97,36 +103,21 @@ impl RuntimeMetricsProvider {
|
|||||||
where
|
where
|
||||||
F: FnOnce(MutexGuard<'_, HashMap<String, Counter<U64>>>) -> Result<(), PrometheusError>,
|
F: FnOnce(MutexGuard<'_, HashMap<String, Counter<U64>>>) -> Result<(), PrometheusError>,
|
||||||
{
|
{
|
||||||
let _ = self
|
let _ = self.1.counters.lock().map(do_something).or_else(|error| Err(error));
|
||||||
.1
|
|
||||||
.counters
|
|
||||||
.lock()
|
|
||||||
.map(do_something)
|
|
||||||
.or_else(|error| Err(error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_counter_vecs_lock_held<F>(&self, do_something: F)
|
fn with_counter_vecs_lock_held<F>(&self, do_something: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(MutexGuard<'_, HashMap<String, CounterVec<U64>>>) -> Result<(), PrometheusError>,
|
F: FnOnce(MutexGuard<'_, HashMap<String, CounterVec<U64>>>) -> Result<(), PrometheusError>,
|
||||||
{
|
{
|
||||||
let _ = self
|
let _ = self.1.counter_vecs.lock().map(do_something).or_else(|error| Err(error));
|
||||||
.1
|
|
||||||
.counter_vecs
|
|
||||||
.lock()
|
|
||||||
.map(do_something)
|
|
||||||
.or_else(|error| Err(error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_histograms_lock_held<F>(&self, do_something: F)
|
fn with_histograms_lock_held<F>(&self, do_something: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(MutexGuard<'_, HashMap<String, Histogram>>) -> Result<(), PrometheusError>,
|
F: FnOnce(MutexGuard<'_, HashMap<String, Histogram>>) -> Result<(), PrometheusError>,
|
||||||
{
|
{
|
||||||
let _ = self
|
let _ = self.1.histograms.lock().map(do_something).or_else(|error| Err(error));
|
||||||
.1
|
|
||||||
.histograms
|
|
||||||
.lock()
|
|
||||||
.map(do_something)
|
|
||||||
.or_else(|error| Err(error));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +131,7 @@ impl sc_tracing::TraceHandler for RuntimeMetricsProvider {
|
|||||||
.unwrap_or(&String::default())
|
.unwrap_or(&String::default())
|
||||||
.ne("metrics")
|
.ne("metrics")
|
||||||
{
|
{
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(update_op_bs58) = event.values.string_values.get("params") {
|
if let Some(update_op_bs58) = event.values.string_values.get("params") {
|
||||||
@ -150,7 +141,7 @@ impl sc_tracing::TraceHandler for RuntimeMetricsProvider {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
) {
|
) {
|
||||||
Ok(update_op) => self.parse_metric_update(update_op),
|
Ok(update_op) => self.parse_metric_update(update_op),
|
||||||
Err(_) => {}
|
Err(_) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,15 +150,12 @@ impl sc_tracing::TraceHandler for RuntimeMetricsProvider {
|
|||||||
impl RuntimeMetricsProvider {
|
impl RuntimeMetricsProvider {
|
||||||
fn parse_metric_update(&self, update: RuntimeMetricUpdate) {
|
fn parse_metric_update(&self, update: RuntimeMetricUpdate) {
|
||||||
match update.op {
|
match update.op {
|
||||||
RuntimeMetricOp::IncrementCounterVec(value, ref labels) => {
|
RuntimeMetricOp::IncrementCounterVec(value, ref labels) =>
|
||||||
self.inc_counter_vec_by(update.metric_name(), value, labels)
|
self.inc_counter_vec_by(update.metric_name(), value, labels),
|
||||||
}
|
RuntimeMetricOp::IncrementCounter(value) =>
|
||||||
RuntimeMetricOp::IncrementCounter(value) => {
|
self.inc_counter_by(update.metric_name(), value),
|
||||||
self.inc_counter_by(update.metric_name(), value)
|
RuntimeMetricOp::ObserveHistogram(value) =>
|
||||||
}
|
self.observe_histogram(update.metric_name(), value),
|
||||||
RuntimeMetricOp::ObserveHistogram(value) => {
|
|
||||||
self.observe_histogram(update.metric_name(), value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +178,7 @@ impl RuntimeMetricsProvider {
|
|||||||
pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration) -> () {
|
pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration) -> () {
|
||||||
|logger_builder, config| {
|
|logger_builder, config| {
|
||||||
if config.prometheus_registry().is_none() {
|
if config.prometheus_registry().is_none() {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let registry = config.prometheus_registry().cloned().unwrap();
|
let registry = config.prometheus_registry().cloned().unwrap();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use ghost_test_service::{node_config, run_validator_node, test_prometheus_config};
|
|
||||||
use hyper::{Client, Uri};
|
use hyper::{Client, Uri};
|
||||||
|
use ghost_test_service::{node_config, run_validator_node, test_prometheus_config};
|
||||||
use keyring::AccountKeyring::*;
|
use keyring::AccountKeyring::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -7,13 +7,8 @@ const DEFAULT_PROMETHEUS_PORT: u16 = 9616;
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn runtime_can_publish_metrics() {
|
async fn runtime_can_publish_metrics() {
|
||||||
let mut alice_config = node_config(
|
let mut alice_config =
|
||||||
|| {},
|
node_config(|| {}, tokio::runtime::Handle::current(), Alice, Vec::new(), true);
|
||||||
tokio::runtime::Handle::current(),
|
|
||||||
Alice,
|
|
||||||
Vec::new(),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Enable prometheus metrics for Alice.
|
// Enable prometheus metrics for Alice.
|
||||||
alice_config.prometheus_config = Some(test_prometheus_config(DEFAULT_PROMETHEUS_PORT));
|
alice_config.prometheus_config = Some(test_prometheus_config(DEFAULT_PROMETHEUS_PORT));
|
||||||
@ -31,13 +26,8 @@ async fn runtime_can_publish_metrics() {
|
|||||||
// Start validator Alice
|
// Start validator Alice
|
||||||
let alice = run_validator_node(alice_config, None);
|
let alice = run_validator_node(alice_config, None);
|
||||||
|
|
||||||
let bob_config = node_config(
|
let bob_config =
|
||||||
|| {},
|
node_config(|| {}, tokio::runtime::Handle::current(), Bob, vec![alice.addr.clone()], true);
|
||||||
tokio::runtime::Handle::current(),
|
|
||||||
Bob,
|
|
||||||
vec![alice.addr.clone()],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Start validator Bob
|
// Start validator Bob
|
||||||
let _bob = run_validator_node(bob_config, None);
|
let _bob = run_validator_node(bob_config, None);
|
||||||
@ -58,25 +48,19 @@ async fn scrape_prometheus_metrics(metrics_uri: &str) -> HashMap<String, u64> {
|
|||||||
.expect("GET request failed");
|
.expect("GET request failed");
|
||||||
|
|
||||||
let body = String::from_utf8(
|
let body = String::from_utf8(
|
||||||
hyper::body::to_bytes(res)
|
hyper::body::to_bytes(res).await.expect("can't get body as bytes").to_vec(),
|
||||||
.await
|
).expect("body is not an UTF8 string");
|
||||||
.expect("can't get body as bytes")
|
|
||||||
.to_vec(),
|
|
||||||
)
|
|
||||||
.expect("body is not an UTF8 string");
|
|
||||||
|
|
||||||
let lines: Vec<_> = body.lines().map(|s| Ok(s.to_owned())).collect();
|
let lines: Vec<_> = body.lines().map(|s| Ok(s.to_owned())).collect();
|
||||||
prometheus_parse::Scrape::parse(lines.into_iter())
|
prometheus_parse::Scrape::parse(lines.into_iter())
|
||||||
.expect("Scraper failed to parse Prometheus metrics")
|
.expect("Scraper failed to parse Prometheus metrics")
|
||||||
.samples
|
.samples
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(
|
.filter_map(|prometheus_parse::Sample { metric, value, .. }| match value {
|
||||||
|prometheus_parse::Sample { metric, value, .. }| match value {
|
prometheus_parse::Value::Counter(value) => Some((metric, value as u64)),
|
||||||
prometheus_parse::Value::Counter(value) => Some((metric, value as u64)),
|
prometheus_parse::Value::Gauge(value) => Some((metric, value as u64)),
|
||||||
prometheus_parse::Value::Gauge(value) => Some((metric, value as u64)),
|
prometheus_parse::Value::Untyped(value) => Some((metric, value as u64)),
|
||||||
prometheus_parse::Value::Untyped(value) => Some((metric, value as u64)),
|
_ => None,
|
||||||
_ => None,
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-governance"
|
name = "ghost-claims"
|
||||||
version = "0.3.7"
|
version = "0.2.2"
|
||||||
description = "Full-chain and cross-chain governance pallet with early adopter share claims"
|
description = "Ghost balance and rank claims based on EVM actions"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
@ -20,6 +20,8 @@ frame-benchmarking = { workspace = true, optional = true }
|
|||||||
frame-support = { workspace = true }
|
frame-support = { workspace = true }
|
||||||
frame-system = { workspace = true }
|
frame-system = { workspace = true }
|
||||||
|
|
||||||
|
pallet-ranked-collective = { workspace = true }
|
||||||
|
pallet-vesting = { workspace = true }
|
||||||
pallet-balances = { workspace = true }
|
pallet-balances = { workspace = true }
|
||||||
|
|
||||||
sp-core = { features = ["serde"], workspace = true }
|
sp-core = { features = ["serde"], workspace = true }
|
||||||
@ -27,47 +29,46 @@ sp-runtime = { features = ["serde"], workspace = true }
|
|||||||
sp-io = { workspace = true }
|
sp-io = { workspace = true }
|
||||||
sp-std = { workspace = true }
|
sp-std = { workspace = true }
|
||||||
|
|
||||||
ghost-traits = { workspace = true }
|
|
||||||
ghost-helpers = { workspace = true }
|
|
||||||
ghost-networks = { workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
libsecp256k1 = { workspace = true, default-features = true }
|
|
||||||
hex-literal = { workspace = true, default-features = true }
|
hex-literal = { workspace = true, default-features = true }
|
||||||
|
libsecp256k1 = { workspace = true, default-features = true }
|
||||||
|
serde_json = { workspace = true, default-features = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"frame-benchmarking?/std",
|
"frame-benchmarking?/std",
|
||||||
"serde/std",
|
"serde/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
"libsecp256k1/std",
|
"libsecp256k1/std",
|
||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"frame-system/std",
|
"frame-system/std",
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
"sp-io/std",
|
"sp-io/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
|
"pallet-ranked-collective/std",
|
||||||
|
"pallet-vesting/std",
|
||||||
"pallet-balances/std",
|
"pallet-balances/std",
|
||||||
"rustc-hex/std",
|
"rustc-hex/std",
|
||||||
"ghost-traits/std",
|
|
||||||
"ghost-helpers/std",
|
|
||||||
"ghost-networks/std",
|
|
||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
"libsecp256k1/hmac",
|
"libsecp256k1/hmac",
|
||||||
"libsecp256k1/static-context",
|
"libsecp256k1/static-context",
|
||||||
"frame-benchmarking/runtime-benchmarks",
|
"frame-benchmarking/runtime-benchmarks",
|
||||||
"frame-support/runtime-benchmarks",
|
"frame-support/runtime-benchmarks",
|
||||||
"frame-system/runtime-benchmarks",
|
"frame-system/runtime-benchmarks",
|
||||||
"pallet-balances/runtime-benchmarks",
|
"pallet-ranked-collective/runtime-benchmarks",
|
||||||
"sp-runtime/runtime-benchmarks",
|
"pallet-vesting/runtime-benchmarks",
|
||||||
"ghost-networks/runtime-benchmarks",
|
"pallet-balances/runtime-benchmarks",
|
||||||
|
"sp-runtime/runtime-benchmarks",
|
||||||
]
|
]
|
||||||
try-runtime = [
|
try-runtime = [
|
||||||
"frame-support/try-runtime",
|
"frame-support/try-runtime",
|
||||||
"frame-system/try-runtime",
|
"frame-system/try-runtime",
|
||||||
"pallet-balances/try-runtime",
|
"pallet-ranked-collective/try-runtime",
|
||||||
"sp-runtime/try-runtime",
|
"pallet-vesting/try-runtime",
|
||||||
|
"pallet-balances/try-runtime",
|
||||||
|
"sp-runtime/try-runtime",
|
||||||
]
|
]
|
||||||
56
pallets/claims/src/benchmarking.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#![cfg(feature = "runtime-benchmarks")]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use frame_benchmarking::v2::*;
|
||||||
|
|
||||||
|
#[instance_benchmarks]
|
||||||
|
mod benchmarks {
|
||||||
|
use super::*;
|
||||||
|
use pallet_ranked_collective::Pallet as Club;
|
||||||
|
use frame_support::dispatch::RawOrigin;
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn claim() {
|
||||||
|
let i = 1337u32;
|
||||||
|
let ethereum_secret_key = libsecp256k1::SecretKey::parse(
|
||||||
|
&keccak_256(&i.to_le_bytes())).unwrap();
|
||||||
|
let eth_address = crate::secp_utils::eth(ðereum_secret_key);
|
||||||
|
|
||||||
|
let balance = CurrencyOf::<T, I>::minimum_balance();
|
||||||
|
let pseudo_account: T::AccountId = Pallet::<T, I>::into_account_id(eth_address).unwrap();
|
||||||
|
let _ = CurrencyOf::<T, I>::deposit_creating(&pseudo_account, balance);
|
||||||
|
Total::<T, I>::put(balance);
|
||||||
|
|
||||||
|
let pseudo_rank = 5u16;
|
||||||
|
let _ = Club::<T, I>::do_add_member_to_rank(
|
||||||
|
pseudo_account.clone(),
|
||||||
|
pseudo_rank,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let user_account: T::AccountId = account("user", i, 0);
|
||||||
|
let signature = crate::secp_utils::sig::<T, I>(ðereum_secret_key, &user_account.encode());
|
||||||
|
|
||||||
|
let prev_balance = CurrencyOf::<T, I>::free_balance(&user_account);
|
||||||
|
let prev_rank = Club::<T, I>::rank_of(&user_account);
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
claim(RawOrigin::Signed(user_account.clone()), eth_address, signature);
|
||||||
|
|
||||||
|
assert_eq!(CurrencyOf::<T, I>::free_balance(&user_account), prev_balance + balance);
|
||||||
|
assert_eq!(CurrencyOf::<T, I>::free_balance(&pseudo_account), balance - balance);
|
||||||
|
|
||||||
|
let rank = match prev_rank {
|
||||||
|
Some(current_rank) if pseudo_rank <= current_rank => Some(current_rank),
|
||||||
|
_ => Some(pseudo_rank),
|
||||||
|
};
|
||||||
|
assert_eq!(Club::<T, I>::rank_of(&user_account), rank);
|
||||||
|
assert_eq!(Club::<T, I>::rank_of(&pseudo_account), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_benchmark_test_suite!(
|
||||||
|
Pallet,
|
||||||
|
crate::mock::new_test_ext(),
|
||||||
|
crate::mock::Test,
|
||||||
|
);
|
||||||
|
}
|
||||||
313
pallets/claims/src/lib.rs
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use frame_support::{
|
||||||
|
ensure, pallet_prelude::*,
|
||||||
|
traits::{
|
||||||
|
Currency, ExistenceRequirement, Get, RankedMembers,
|
||||||
|
RankedMembersSwapHandler, VestingSchedule,
|
||||||
|
},
|
||||||
|
DefaultNoBound,
|
||||||
|
};
|
||||||
|
use frame_system::pallet_prelude::*;
|
||||||
|
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
pub use pallet::*;
|
||||||
|
|
||||||
|
use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256};
|
||||||
|
use sp_runtime::traits::{CheckedSub, CheckedDiv, BlockNumberProvider};
|
||||||
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::{format, string::String};
|
||||||
|
|
||||||
|
pub mod weights;
|
||||||
|
pub use crate::weights::WeightInfo;
|
||||||
|
mod tests;
|
||||||
|
mod mock;
|
||||||
|
mod benchmarking;
|
||||||
|
mod secp_utils;
|
||||||
|
|
||||||
|
/// An ethereum address (i.e. 20 bytes, used to represent an Ethereum account).
|
||||||
|
///
|
||||||
|
/// This gets serialized to the 0x-prefixed hex representation.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Default, RuntimeDebug, TypeInfo)]
|
||||||
|
pub struct EthereumAddress(pub [u8; 20]);
|
||||||
|
|
||||||
|
impl Serialize for EthereumAddress {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let hex: String = rustc_hex::ToHex::to_hex(&self.0[..]);
|
||||||
|
serializer.serialize_str(&format!("0x{}", hex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for EthereumAddress {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let base_string = String::deserialize(deserializer)?;
|
||||||
|
let offset = if base_string.starts_with("0x") { 2 } else { 0 };
|
||||||
|
let s = &base_string[offset..];
|
||||||
|
if s.len() != 40 {
|
||||||
|
Err(serde::de::Error::custom(
|
||||||
|
"Bad length of Ethereum address (should be 42 including `0x`)",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
let raw: Vec<u8> = rustc_hex::FromHex::from_hex(s)
|
||||||
|
.map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?;
|
||||||
|
let mut r = Self::default();
|
||||||
|
r.0.copy_from_slice(&raw);
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Clone, TypeInfo)]
|
||||||
|
pub struct EcdsaSignature(pub [u8; 65]);
|
||||||
|
|
||||||
|
impl PartialEq for EcdsaSignature {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
&self.0[..] == &other.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sp_std::fmt::Debug for EcdsaSignature {
|
||||||
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
|
||||||
|
write!(f, "EcdsaSignature({:?})", &self.0[..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrencyOf<T, I> = <<T as Config<I>>::VestingSchedule as VestingSchedule<
|
||||||
|
<T as frame_system::Config>::AccountId
|
||||||
|
>>::Currency;
|
||||||
|
|
||||||
|
type BalanceOf<T, I> = <CurrencyOf<T, I> as Currency<
|
||||||
|
<T as frame_system::Config>::AccountId>
|
||||||
|
>::Balance;
|
||||||
|
|
||||||
|
type RankOf<T, I> = <pallet_ranked_collective::Pallet::<T, I> as RankedMembers>::Rank;
|
||||||
|
type AccountIdOf<T, I> = <pallet_ranked_collective::Pallet::<T, I> as RankedMembers>::AccountId;
|
||||||
|
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
#[pallet::without_storage_info]
|
||||||
|
pub struct Pallet<T, I = ()>(_);
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config<I: 'static = ()>: frame_system::Config + pallet_ranked_collective::Config<I> {
|
||||||
|
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||||
|
|
||||||
|
type VestingSchedule: VestingSchedule<Self::AccountId, Moment = BlockNumberFor<Self>>;
|
||||||
|
type BlockNumberProvider: BlockNumberProvider<BlockNumber = BlockNumberFor<Self>>;
|
||||||
|
type MemberSwappedHandler: RankedMembersSwapHandler<AccountIdOf<Self, I>, RankOf<Self, I>>;
|
||||||
|
|
||||||
|
#[pallet::constant]
|
||||||
|
type Prefix: Get<&'static [u8]>;
|
||||||
|
|
||||||
|
#[pallet::constant]
|
||||||
|
type MaximumWithdrawAmount: Get<BalanceOf<Self, I>>;
|
||||||
|
|
||||||
|
#[pallet::constant]
|
||||||
|
type VestingBlocks: Get<u32>;
|
||||||
|
|
||||||
|
type WeightInfo: WeightInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::event]
|
||||||
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
|
pub enum Event<T: Config<I>, I: 'static = ()> {
|
||||||
|
Claimed {
|
||||||
|
receiver: T::AccountId,
|
||||||
|
donor: T::AccountId,
|
||||||
|
amount: BalanceOf<T, I>,
|
||||||
|
rank: Option<RankOf<T, I>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::error]
|
||||||
|
pub enum Error<T, I = ()> {
|
||||||
|
InvalidEthereumSignature,
|
||||||
|
InvalidEthereumAddress,
|
||||||
|
NoBalanceToClaim,
|
||||||
|
AddressDecodingFailed,
|
||||||
|
ArithmeticError,
|
||||||
|
PotUnderflow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type Total<T: Config<I>, I: 'static = ()> = StorageValue<_, BalanceOf<T, I>, ValueQuery>;
|
||||||
|
|
||||||
|
#[pallet::genesis_config]
|
||||||
|
#[derive(DefaultNoBound)]
|
||||||
|
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
|
||||||
|
pub total: BalanceOf<T, I>,
|
||||||
|
pub members_and_ranks: Vec<(T::AccountId, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::genesis_build]
|
||||||
|
impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
|
||||||
|
fn build(&self) {
|
||||||
|
let cult_accounts: Vec<_> = self
|
||||||
|
.members_and_ranks
|
||||||
|
.iter()
|
||||||
|
.map(|(account_id, rank)| {
|
||||||
|
assert!(
|
||||||
|
pallet_ranked_collective::Pallet::<T, I>::do_add_member_to_rank(
|
||||||
|
account_id.clone(), *rank, false).is_ok(),
|
||||||
|
"error during adding and promotion"
|
||||||
|
);
|
||||||
|
account_id
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
self.members_and_ranks.len() == cult_accounts.len(),
|
||||||
|
"duplicates in `members_and_ranks`"
|
||||||
|
);
|
||||||
|
|
||||||
|
Total::<T, I>::put(self.total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||||
|
#[pallet::call_index(0)]
|
||||||
|
#[pallet::weight(<T as Config<I>>::WeightInfo::claim())]
|
||||||
|
pub fn claim(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
ethereum_address: EthereumAddress,
|
||||||
|
ethereum_signature: EcdsaSignature,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let who = ensure_signed(origin)?;
|
||||||
|
let data = who.using_encoded(to_ascii_hex);
|
||||||
|
let recovered_address = Self::recover_ethereum_address(
|
||||||
|
ðereum_signature,
|
||||||
|
&data,
|
||||||
|
).ok_or(Error::<T, I>::InvalidEthereumSignature)?;
|
||||||
|
|
||||||
|
ensure!(recovered_address == ethereum_address,
|
||||||
|
Error::<T, I>::InvalidEthereumAddress);
|
||||||
|
|
||||||
|
Self::do_claim(who, ethereum_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_ascii_hex(data: &[u8]) -> Vec<u8> {
|
||||||
|
let mut r = Vec::with_capacity(data.len() * 2);
|
||||||
|
let mut push_nibble = |n| r.push(if n < 10 { b'0' + n } else { b'a' - 10 + n });
|
||||||
|
for &b in data.iter() {
|
||||||
|
push_nibble(b / 16);
|
||||||
|
push_nibble(b % 16);
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||||
|
fn ethereum_signable_message(what: &[u8]) -> Vec<u8> {
|
||||||
|
let prefix = T::Prefix::get();
|
||||||
|
let mut l = prefix.len() + what.len();
|
||||||
|
let mut rev = Vec::new();
|
||||||
|
while l > 0 {
|
||||||
|
rev.push(b'0' + (l % 10) as u8);
|
||||||
|
l /= 10;
|
||||||
|
}
|
||||||
|
let mut v = b"\x19Ethereum Signed Message:\n".to_vec();
|
||||||
|
v.extend(rev.into_iter().rev());
|
||||||
|
v.extend_from_slice(prefix);
|
||||||
|
v.extend_from_slice(what);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recover_ethereum_address(s: &EcdsaSignature, what: &[u8]) -> Option<EthereumAddress> {
|
||||||
|
let msg = keccak_256(&Self::ethereum_signable_message(what));
|
||||||
|
let mut res = EthereumAddress::default();
|
||||||
|
res.0
|
||||||
|
.copy_from_slice(&keccak_256(&secp256k1_ecdsa_recover(&s.0, &msg).ok()?[..])[12..]);
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_account_id(address: EthereumAddress) -> Result<T::AccountId, codec::Error> {
|
||||||
|
let mut data = [0u8; 32];
|
||||||
|
data[0..4].copy_from_slice(b"evm:");
|
||||||
|
data[4..24].copy_from_slice(&address.0[..]);
|
||||||
|
|
||||||
|
let hash = sp_core::keccak_256(&data);
|
||||||
|
T::AccountId::decode(&mut &hash[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_claim(receiver: T::AccountId, ethereum_address: EthereumAddress) -> DispatchResult {
|
||||||
|
let donor = Self::into_account_id(ethereum_address).ok()
|
||||||
|
.ok_or(Error::<T, I>::AddressDecodingFailed)?;
|
||||||
|
|
||||||
|
let balance_due = CurrencyOf::<T, I>::free_balance(&donor);
|
||||||
|
ensure!(balance_due >= CurrencyOf::<T, I>::minimum_balance(),
|
||||||
|
Error::<T, I>::NoBalanceToClaim);
|
||||||
|
|
||||||
|
let new_total = Total::<T, I>::get()
|
||||||
|
.checked_sub(&balance_due)
|
||||||
|
.ok_or(Error::<T, I>::PotUnderflow)?;
|
||||||
|
|
||||||
|
CurrencyOf::<T, I>::transfer(
|
||||||
|
&donor,
|
||||||
|
&receiver,
|
||||||
|
balance_due,
|
||||||
|
ExistenceRequirement::AllowDeath,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let max_amount = T::MaximumWithdrawAmount::get();
|
||||||
|
if balance_due > max_amount {
|
||||||
|
let vesting_balance = balance_due
|
||||||
|
.checked_sub(&max_amount)
|
||||||
|
.ok_or(Error::<T, I>::ArithmeticError)?;
|
||||||
|
|
||||||
|
let vesting_blocks = T::VestingBlocks::get();
|
||||||
|
let per_block_balance = vesting_balance
|
||||||
|
.checked_div(&vesting_blocks.into())
|
||||||
|
.ok_or(Error::<T, I>::ArithmeticError)?;
|
||||||
|
|
||||||
|
T::VestingSchedule::add_vesting_schedule(
|
||||||
|
&receiver,
|
||||||
|
vesting_balance,
|
||||||
|
per_block_balance,
|
||||||
|
T::BlockNumberProvider::current_block_number(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rank = if let Some(rank) = <pallet_ranked_collective::Pallet::<T, I> as RankedMembers>::rank_of(&donor) {
|
||||||
|
pallet_ranked_collective::Pallet::<T, I>::do_remove_member_from_rank(&donor, rank)?;
|
||||||
|
let new_rank = match <pallet_ranked_collective::Pallet::<T, I> as RankedMembers>::rank_of(&receiver) {
|
||||||
|
Some(current_rank) if current_rank >= rank => current_rank,
|
||||||
|
Some(current_rank) if current_rank < rank => {
|
||||||
|
for _ in 0..rank - current_rank {
|
||||||
|
pallet_ranked_collective::Pallet::<T, I>::do_promote_member(receiver.clone(), None, false)?;
|
||||||
|
}
|
||||||
|
rank
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
pallet_ranked_collective::Pallet::<T, I>::do_add_member_to_rank(receiver.clone(), rank, false)?;
|
||||||
|
rank
|
||||||
|
},
|
||||||
|
};
|
||||||
|
<T as pallet::Config<I>>::MemberSwappedHandler::swapped(&donor, &receiver, new_rank);
|
||||||
|
Some(new_rank)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Total::<T, I>::put(new_total);
|
||||||
|
Self::deposit_event(Event::<T, I>::Claimed {
|
||||||
|
receiver,
|
||||||
|
donor,
|
||||||
|
amount: balance_due,
|
||||||
|
rank,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
221
pallets/claims/src/mock.rs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use crate as ghost_claims;
|
||||||
|
use frame_support::{
|
||||||
|
parameter_types, derive_impl,
|
||||||
|
traits::{PollStatus, Polling, WithdrawReasons, ConstU16, ConstU64},
|
||||||
|
};
|
||||||
|
use frame_system::EnsureRootWithSuccess;
|
||||||
|
use sp_runtime::{BuildStorage, traits::Convert};
|
||||||
|
pub use pallet_ranked_collective::{TallyOf, Rank};
|
||||||
|
|
||||||
|
pub mod eth_keys {
|
||||||
|
use crate::{
|
||||||
|
mock::Test,
|
||||||
|
EcdsaSignature, EthereumAddress,
|
||||||
|
};
|
||||||
|
use hex_literal::hex;
|
||||||
|
use codec::Encode;
|
||||||
|
|
||||||
|
pub fn total_claims() -> u64 { 10 + 100 + 1000 }
|
||||||
|
pub fn alice_account_id() -> <Test as frame_system::Config>::AccountId { 69 }
|
||||||
|
pub fn bob_account_id() -> <Test as frame_system::Config>::AccountId { 1337 }
|
||||||
|
|
||||||
|
pub fn first_eth_public_known() -> EthereumAddress { EthereumAddress(hex!("1A69d2D5568D1878023EeB121a73d33B9116A760")) }
|
||||||
|
pub fn second_eth_public_known() -> EthereumAddress { EthereumAddress(hex!("2f86cfBED3fbc1eCf2989B9aE5fc019a837A9C12")) }
|
||||||
|
pub fn third_eth_public_known() -> EthereumAddress { EthereumAddress(hex!("e83f67361Ac74D42A48E2DAfb6706eb047D8218D")) }
|
||||||
|
pub fn fourth_eth_public_known() -> EthereumAddress { EthereumAddress(hex!("827ee4ad9b259b6fa1390ed60921508c78befd63")) }
|
||||||
|
|
||||||
|
fn first_eth_private_key() -> libsecp256k1::SecretKey { libsecp256k1::SecretKey::parse(&hex!("01c928771aea942a1e7ac06adf2b73dfbc9a25d9eaa516e3673116af7f345198")).unwrap() }
|
||||||
|
fn second_eth_private_key() -> libsecp256k1::SecretKey { libsecp256k1::SecretKey::parse(&hex!("b19a435901872f817185f7234a1484eae837613f9d10cf21927a23c2d8cb9139")).unwrap() }
|
||||||
|
fn third_eth_private_key() -> libsecp256k1::SecretKey { libsecp256k1::SecretKey::parse(&hex!("d3baf57b74d65719b2dc33f5a464176022d0cc5edbca002234229f3e733875fc")).unwrap() }
|
||||||
|
fn fourth_eth_private_key() -> libsecp256k1::SecretKey { libsecp256k1::SecretKey::parse(&hex!("c4683d566436af6b58b4a59c8f501319226e85b21869bf93d5eeb4596d4791d4")).unwrap() }
|
||||||
|
fn wrong_eth_private_key() -> libsecp256k1::SecretKey { libsecp256k1::SecretKey::parse(&hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")).unwrap() }
|
||||||
|
|
||||||
|
pub fn first_eth_public_key() -> EthereumAddress { crate::secp_utils::eth(&first_eth_private_key()) }
|
||||||
|
pub fn second_eth_public_key() -> EthereumAddress { crate::secp_utils::eth(&second_eth_private_key()) }
|
||||||
|
pub fn third_eth_public_key() -> EthereumAddress { crate::secp_utils::eth(&third_eth_private_key()) }
|
||||||
|
pub fn fourth_eth_public_key() -> EthereumAddress { crate::secp_utils::eth(&fourth_eth_private_key()) }
|
||||||
|
|
||||||
|
pub fn first_account_id() -> <Test as frame_system::Config>::AccountId { crate::secp_utils::into_account_id::<Test, ()>(first_eth_public_key()) }
|
||||||
|
pub fn second_account_id() -> <Test as frame_system::Config>::AccountId { crate::secp_utils::into_account_id::<Test, ()>(second_eth_public_key()) }
|
||||||
|
pub fn third_account_id() -> <Test as frame_system::Config>::AccountId { crate::secp_utils::into_account_id::<Test, ()>(third_eth_public_key()) }
|
||||||
|
pub fn fourth_account_id() -> <Test as frame_system::Config>::AccountId { crate::secp_utils::into_account_id::<Test, ()>(fourth_eth_public_key()) }
|
||||||
|
|
||||||
|
pub fn first_signature() -> EcdsaSignature { crate::secp_utils::sig::<Test, ()>(&first_eth_private_key(), &alice_account_id().encode()) }
|
||||||
|
pub fn second_signature() -> EcdsaSignature { crate::secp_utils::sig::<Test, ()>(&second_eth_private_key(), &alice_account_id().encode()) }
|
||||||
|
pub fn third_signature() -> EcdsaSignature { crate::secp_utils::sig::<Test, ()>(&third_eth_private_key(), &alice_account_id().encode()) }
|
||||||
|
pub fn fourth_signature() -> EcdsaSignature { crate::secp_utils::sig::<Test, ()>(&fourth_eth_private_key(), &bob_account_id().encode()) }
|
||||||
|
pub fn wrong_signature() -> EcdsaSignature { crate::secp_utils::sig::<Test, ()>(&wrong_eth_private_key(), &alice_account_id().encode()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Test {
|
||||||
|
type RuntimeOrigin = RuntimeOrigin;
|
||||||
|
type RuntimeCall = RuntimeCall;
|
||||||
|
type Block = Block;
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
type AccountData = pallet_balances::AccountData<u64>;
|
||||||
|
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||||
|
impl pallet_balances::Config for Test {
|
||||||
|
type AccountStore = System;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const MinVestedTransfer: u64 = 1;
|
||||||
|
pub UnvestedFundsAllowedWithdrawReasons: WithdrawReasons =
|
||||||
|
WithdrawReasons::except(WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl pallet_vesting::Config for Test {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
type Currency = Balances;
|
||||||
|
type BlockNumberToBalance = sp_runtime::traits::ConvertInto;
|
||||||
|
type MinVestedTransfer = MinVestedTransfer;
|
||||||
|
type WeightInfo = ();
|
||||||
|
type UnvestedFundsAllowedWithdrawReasons =
|
||||||
|
UnvestedFundsAllowedWithdrawReasons;
|
||||||
|
|
||||||
|
type BlockNumberProvider = System;
|
||||||
|
const MAX_VESTING_SCHEDULES: u32 = 28;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub MinRankOfClassDelta: Rank = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MinRankOfClass<Delta>(sp_std::marker::PhantomData<Delta>);
|
||||||
|
impl<Delta: Get<Rank>> Convert<Class, Rank> for MinRankOfClass<Delta> {
|
||||||
|
fn convert(a: Class) -> Rank {
|
||||||
|
a.saturating_sub(Delta::get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TestPolls;
|
||||||
|
impl Polling<TallyOf<Test>> for TestPolls {
|
||||||
|
type Index = u8;
|
||||||
|
type Votes = u32;
|
||||||
|
type Moment = u64;
|
||||||
|
type Class = Class;
|
||||||
|
|
||||||
|
fn classes() -> Vec<Self::Class> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_ongoing(_index: u8) -> Option<(TallyOf<Test>, Self::Class)> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access_poll<R>(
|
||||||
|
_index: Self::Index,
|
||||||
|
_f: impl FnOnce(PollStatus<&mut TallyOf<Test>, Self::Moment, Self::Class>) -> R,
|
||||||
|
) -> R {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_access_poll<R>(
|
||||||
|
_index: Self::Index,
|
||||||
|
_f: impl FnOnce(
|
||||||
|
PollStatus<&mut TallyOf<Test>, Self::Moment, Self::Class>,
|
||||||
|
) -> Result<R, DispatchError>,
|
||||||
|
) -> Result<R, DispatchError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
fn create_ongoing(_class: Self::Class) -> Result<Self::Index, ()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
fn end_ongoing(_index: Self::Index, _approved: bool) -> Result<(), ()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl pallet_ranked_collective::Config for Test {
|
||||||
|
type WeightInfo = ();
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
|
||||||
|
type AddOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
|
||||||
|
type RemoveOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
|
||||||
|
type PromoteOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
|
||||||
|
type DemoteOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
|
||||||
|
type ExchangeOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
|
||||||
|
|
||||||
|
type Polls = TestPolls;
|
||||||
|
type MemberSwappedHandler = ();
|
||||||
|
type MinRankOfClass = MinRankOfClass<MinRankOfClassDelta>;
|
||||||
|
type VoteWeight = pallet_ranked_collective::Geometric;
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
type BenchmarkSetup = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub Prefix: &'static [u8] = b"AccountId:";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config for Test {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
type VestingSchedule = Vesting;
|
||||||
|
type BlockNumberProvider = System;
|
||||||
|
type MemberSwappedHandler = ();
|
||||||
|
|
||||||
|
type Prefix = Prefix;
|
||||||
|
type MaximumWithdrawAmount = ConstU64<200>;
|
||||||
|
type VestingBlocks = ConstU32<80>;
|
||||||
|
|
||||||
|
type WeightInfo = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
type Block = frame_system::mocking::MockBlock<Test>;
|
||||||
|
type Class = Rank;
|
||||||
|
|
||||||
|
frame_support::construct_runtime!(
|
||||||
|
pub enum Test
|
||||||
|
{
|
||||||
|
System: frame_system,
|
||||||
|
Balances: pallet_balances,
|
||||||
|
Vesting: pallet_vesting,
|
||||||
|
Club: pallet_ranked_collective,
|
||||||
|
Claims: ghost_claims,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||||
|
let mut t = frame_system::GenesisConfig::<Test>::default()
|
||||||
|
.build_storage()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
pallet_balances::GenesisConfig::<Test> {
|
||||||
|
balances: vec![
|
||||||
|
(crate::mock::eth_keys::first_account_id(), 10),
|
||||||
|
(crate::mock::eth_keys::second_account_id(), 100),
|
||||||
|
(crate::mock::eth_keys::third_account_id(), 1000),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
.assimilate_storage(&mut t)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ghost_claims::GenesisConfig::<Test> {
|
||||||
|
total: crate::mock::eth_keys::total_claims(),
|
||||||
|
members_and_ranks: vec![
|
||||||
|
(crate::mock::eth_keys::second_account_id(), 1),
|
||||||
|
(crate::mock::eth_keys::third_account_id(), 3),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
.assimilate_storage(&mut t)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut ext = sp_io::TestExternalities::new(t);
|
||||||
|
ext.execute_with(|| {
|
||||||
|
System::set_block_number(1);
|
||||||
|
});
|
||||||
|
ext
|
||||||
|
}
|
||||||
37
pallets/claims/src/secp_utils.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#![cfg(any(test, feature = "runtime-benchmarks"))]
|
||||||
|
|
||||||
|
use crate::{keccak_256, Config, EcdsaSignature, EthereumAddress};
|
||||||
|
|
||||||
|
pub fn public(secret: &libsecp256k1::SecretKey) -> libsecp256k1::PublicKey {
|
||||||
|
libsecp256k1::PublicKey::from_secret_key(secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eth(secret: &libsecp256k1::SecretKey) -> EthereumAddress {
|
||||||
|
let mut res = EthereumAddress::default();
|
||||||
|
res.0.copy_from_slice(&keccak_256(&public(secret).serialize()[1..65])[12..]);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn into_account_id<T: Config<I>, I: 'static>(address: EthereumAddress) -> T::AccountId {
|
||||||
|
super::Pallet::<T, I>::into_account_id(address).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sig<T: Config<I>, I: 'static>(
|
||||||
|
secret: &libsecp256k1::SecretKey,
|
||||||
|
what: &[u8],
|
||||||
|
) -> EcdsaSignature {
|
||||||
|
let msg = keccak_256(&super::Pallet::<T, I>::ethereum_signable_message(
|
||||||
|
&crate::to_ascii_hex(what)[..],
|
||||||
|
));
|
||||||
|
|
||||||
|
let (sig, recovery_id) = libsecp256k1::sign(
|
||||||
|
&libsecp256k1::Message::parse(&msg),
|
||||||
|
secret,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut r = [0u8; 65];
|
||||||
|
r[0..64].copy_from_slice(&sig.serialize()[..]);
|
||||||
|
r[64] = recovery_id.serialize();
|
||||||
|
EcdsaSignature(r)
|
||||||
|
}
|
||||||
274
pallets/claims/src/tests.rs
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use mock::{
|
||||||
|
new_test_ext, ghost_claims,
|
||||||
|
Test, System, Balances, Club, Vesting, Claims, RuntimeOrigin, RuntimeEvent,
|
||||||
|
eth_keys::{
|
||||||
|
alice_account_id, total_claims, first_eth_public_known,
|
||||||
|
first_eth_public_key, first_account_id, first_signature,
|
||||||
|
second_eth_public_known, second_eth_public_key, second_account_id,
|
||||||
|
second_signature, third_eth_public_known, third_eth_public_key,
|
||||||
|
third_account_id, third_signature, fourth_eth_public_known,
|
||||||
|
fourth_eth_public_key, fourth_account_id, fourth_signature,
|
||||||
|
wrong_signature, bob_account_id,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use hex_literal::hex;
|
||||||
|
use frame_support::{assert_err, assert_ok};
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_works() {
|
||||||
|
let x = EthereumAddress(hex!["0123456789abcdef0123456789abcdef01234567"]);
|
||||||
|
let y = serde_json::to_string(&x).unwrap();
|
||||||
|
assert_eq!(y, "\"0x0123456789abcdef0123456789abcdef01234567\"");
|
||||||
|
let z: EthereumAddress = serde_json::from_str(&y).unwrap();
|
||||||
|
assert_eq!(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn known_eth_public_accounts_are_correct() {
|
||||||
|
assert_eq!(first_eth_public_key(), first_eth_public_known());
|
||||||
|
assert_eq!(second_eth_public_key(), second_eth_public_known());
|
||||||
|
assert_eq!(third_eth_public_key(), third_eth_public_known());
|
||||||
|
assert_eq!(fourth_eth_public_key(), fourth_eth_public_known());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_setup_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 10);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 100);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 1000);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&first_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&second_account_id()), Some(1));
|
||||||
|
assert_eq!(Club::rank_of(&third_account_id()), Some(3));
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), None);
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn small_claiming_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
first_eth_public_key(),
|
||||||
|
first_signature()));
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 10);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 100);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 1000);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&first_account_id()), None);
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), None);
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims() - 10);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn medium_claiming_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
second_eth_public_key(),
|
||||||
|
second_signature(),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 100);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 10);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 1000);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), Some(1));
|
||||||
|
assert_eq!(Club::rank_of(&second_account_id()), None);
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), None);
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims() - 100);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_claiming_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
third_eth_public_key(),
|
||||||
|
third_signature(),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 200);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 10);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 100);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 0);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), Some(3));
|
||||||
|
assert_eq!(Club::rank_of(&third_account_id()), None);
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), Some(800));
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims() - 1000);
|
||||||
|
assert_ok!(Balances::transfer_allow_death(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
bob_account_id(),
|
||||||
|
200));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_accounts_claiming_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
first_eth_public_key(),
|
||||||
|
first_signature(),
|
||||||
|
));
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
second_eth_public_key(),
|
||||||
|
second_signature(),
|
||||||
|
));
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
third_eth_public_key(),
|
||||||
|
third_signature(),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 310);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 0);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), Some(3));
|
||||||
|
assert_eq!(Club::rank_of(&third_account_id()), None);
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), Some(800));
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), 0);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_accounts_reverese_claiming_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
third_eth_public_key(),
|
||||||
|
third_signature(),
|
||||||
|
));
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
second_eth_public_key(),
|
||||||
|
second_signature(),
|
||||||
|
));
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
first_eth_public_key(),
|
||||||
|
first_signature(),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 310);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 0);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), Some(3));
|
||||||
|
assert_eq!(Club::rank_of(&third_account_id()), None);
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), Some(800));
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), 0);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cannot_claim_with_bad_signature() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_err!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
first_eth_public_key(),
|
||||||
|
wrong_signature()),
|
||||||
|
crate::Error::<Test>::InvalidEthereumAddress);
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 10);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 100);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 1000);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&first_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&second_account_id()), Some(1));
|
||||||
|
assert_eq!(Club::rank_of(&third_account_id()), Some(3));
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), None);
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cannot_claim_with_wrong_address() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_err!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(bob_account_id()),
|
||||||
|
first_eth_public_key(),
|
||||||
|
first_signature()),
|
||||||
|
crate::Error::<Test>::InvalidEthereumAddress);
|
||||||
|
|
||||||
|
assert_eq!(Balances::usable_balance(&bob_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&alice_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&first_account_id()), 10);
|
||||||
|
assert_eq!(Balances::usable_balance(&second_account_id()), 100);
|
||||||
|
assert_eq!(Balances::usable_balance(&third_account_id()), 1000);
|
||||||
|
|
||||||
|
assert_eq!(Club::rank_of(&alice_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&first_account_id()), None);
|
||||||
|
assert_eq!(Club::rank_of(&second_account_id()), Some(1));
|
||||||
|
assert_eq!(Club::rank_of(&third_account_id()), Some(3));
|
||||||
|
|
||||||
|
assert_eq!(Vesting::vesting_balance(&alice_account_id()), None);
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cannot_claim_nothing() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_err!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(bob_account_id()),
|
||||||
|
fourth_eth_public_key(),
|
||||||
|
fourth_signature()),
|
||||||
|
crate::Error::<Test>::NoBalanceToClaim);
|
||||||
|
assert_eq!(Balances::usable_balance(&bob_account_id()), 0);
|
||||||
|
assert_eq!(Balances::usable_balance(&fourth_account_id()), 0);
|
||||||
|
assert_eq!(Vesting::vesting_balance(&bob_account_id()), None);
|
||||||
|
assert_eq!(ghost_claims::Total::<Test, ()>::get(), total_claims());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn event_emitted_during_claim() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let account = third_account_id();
|
||||||
|
let amount = Balances::usable_balance(&account);
|
||||||
|
let rank = Club::rank_of(&account);
|
||||||
|
|
||||||
|
System::reset_events();
|
||||||
|
assert_eq!(System::event_count(), 0);
|
||||||
|
assert_ok!(Claims::claim(
|
||||||
|
RuntimeOrigin::signed(alice_account_id()),
|
||||||
|
third_eth_public_key(),
|
||||||
|
third_signature(),
|
||||||
|
));
|
||||||
|
System::assert_has_event(RuntimeEvent::Claims(
|
||||||
|
crate::Event::Claimed {
|
||||||
|
receiver: alice_account_id(),
|
||||||
|
donor: account,
|
||||||
|
amount,
|
||||||
|
rank,
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
}
|
||||||
9
pallets/claims/src/weights.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use frame_support::weights::Weight;
|
||||||
|
|
||||||
|
pub trait WeightInfo {
|
||||||
|
fn claim() -> Weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightInfo for () {
|
||||||
|
fn claim() -> Weight { Weight::zero() }
|
||||||
|
}
|
||||||
@ -1,108 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ghost-exodus"
|
|
||||||
version = "0.0.11"
|
|
||||||
description = "Threshold signature generation with DKG included"
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log = { workspace = true }
|
|
||||||
hex = { workspace = true }
|
|
||||||
codec = { workspace = true, features = ["max-encoded-len"] }
|
|
||||||
scale-info = { workspace = true, features = ["derive"] }
|
|
||||||
strum = { workspace = true, features = ["derive"] }
|
|
||||||
num-traits = { workspace = true }
|
|
||||||
|
|
||||||
frame-benchmarking = { workspace = true, optional = true }
|
|
||||||
frame-support = { workspace = true }
|
|
||||||
frame-system = { workspace = true }
|
|
||||||
|
|
||||||
sp-arithmetic = { workspace = true }
|
|
||||||
sp-application-crypto = { workspace = true }
|
|
||||||
sp-runtime = { workspace = true }
|
|
||||||
sp-staking = { workspace = true }
|
|
||||||
sp-core = { workspace = true }
|
|
||||||
sp-std = { workspace = true }
|
|
||||||
sp-io = { workspace = true }
|
|
||||||
|
|
||||||
frost-secp256k1-tr = { workspace = true }
|
|
||||||
frost-ed25519 = { workspace = true }
|
|
||||||
frost-core = { workspace = true }
|
|
||||||
rand_chacha = { workspace = true }
|
|
||||||
hkdf = { workspace = true }
|
|
||||||
chacha20poly1305 = { workspace = true }
|
|
||||||
sha2 = { workspace = true }
|
|
||||||
k256 = { workspace = true }
|
|
||||||
parking_lot = { workspace = true }
|
|
||||||
|
|
||||||
ghost-traits = { workspace = true }
|
|
||||||
ghost-helpers = { workspace = true }
|
|
||||||
|
|
||||||
pallet-balances = { workspace = true }
|
|
||||||
pallet-session = { workspace = true }
|
|
||||||
pallet-staking = { workspace = true }
|
|
||||||
ghost-networks = { workspace = true }
|
|
||||||
pallet-staking-reward-curve = { workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
sp-keystore = { workspace = true }
|
|
||||||
frame-executive = { workspace = true }
|
|
||||||
rand = { workspace = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = [
|
|
||||||
"frame-benchmarking?/std",
|
|
||||||
"scale-info/std",
|
|
||||||
"strum/std",
|
|
||||||
"log/std",
|
|
||||||
"hex/std",
|
|
||||||
"k256/std",
|
|
||||||
"num-traits/std",
|
|
||||||
"frame-support/std",
|
|
||||||
"frame-system/std",
|
|
||||||
"sha2/std",
|
|
||||||
"hkdf/std",
|
|
||||||
"chacha20poly1305/std",
|
|
||||||
"rand_chacha/std",
|
|
||||||
"sp-arithmetic/std",
|
|
||||||
"sp-application-crypto/std",
|
|
||||||
"sp-runtime/std",
|
|
||||||
"sp-staking/std",
|
|
||||||
"sp-core/std",
|
|
||||||
"sp-std/std",
|
|
||||||
"sp-io/std",
|
|
||||||
"frost-secp256k1-tr/std",
|
|
||||||
"frost-ed25519/std",
|
|
||||||
"frost-core/std",
|
|
||||||
"ghost-traits/std",
|
|
||||||
"ghost-helpers/std",
|
|
||||||
"pallet-balances/std",
|
|
||||||
"pallet-session/std",
|
|
||||||
"pallet-staking/std",
|
|
||||||
"ghost-networks/std",
|
|
||||||
"frame-executive/std",
|
|
||||||
"sp-keystore/std",
|
|
||||||
]
|
|
||||||
runtime-benchmarks = [
|
|
||||||
"frame-benchmarking/runtime-benchmarks",
|
|
||||||
"frame-support/runtime-benchmarks",
|
|
||||||
"frame-system/runtime-benchmarks",
|
|
||||||
"sp-runtime/runtime-benchmarks",
|
|
||||||
"sp-staking/runtime-benchmarks",
|
|
||||||
"pallet-balances/runtime-benchmarks",
|
|
||||||
"pallet-staking/runtime-benchmarks",
|
|
||||||
"ghost-networks/runtime-benchmarks",
|
|
||||||
]
|
|
||||||
try-runtime = [
|
|
||||||
"frame-support/try-runtime",
|
|
||||||
"frame-system/try-runtime",
|
|
||||||
"sp-runtime/try-runtime",
|
|
||||||
"pallet-balances/try-runtime",
|
|
||||||
"pallet-session/try-runtime",
|
|
||||||
"frame-executive/try-runtime",
|
|
||||||
"ghost-networks/try-runtime",
|
|
||||||
]
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ghost-exodus-runtime-api"
|
|
||||||
version = "0.0.0"
|
|
||||||
description = "RPC runtime API for ghost EXODUS pallet"
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
codec = { workspace = true, features = ["derive"] }
|
|
||||||
sp-api = { workspace = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = ["codec/std", "sp-api/std"]
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
sp_api::decl_runtime_apis! {
|
|
||||||
pub trait ExodusApi<NetworkId> {
|
|
||||||
fn verifying_key(network_id: NetworkId) -> Vec<u8>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,853 +0,0 @@
|
|||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use frame_benchmarking::v2::*;
|
|
||||||
use frame_system::RawOrigin;
|
|
||||||
|
|
||||||
use ghost_helpers::networks::NetworkDataBuilder;
|
|
||||||
|
|
||||||
fn prepare_pallet<T: Config>(authorities_len: usize) -> (
|
|
||||||
PendingDkgAuthorities<ParticipantsBitmap<T>, BlockNumberFor<T>>,
|
|
||||||
NetworkCurve,
|
|
||||||
T::AuthorityId,
|
|
||||||
AuthIndex
|
|
||||||
) {
|
|
||||||
let network_curve = NetworkCurve::Secp256k1;
|
|
||||||
let authority = T::AuthorityId::generate_pair(None);
|
|
||||||
|
|
||||||
let authorities = vec![authority.clone(); authorities_len];
|
|
||||||
let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone())
|
|
||||||
.expect("more than the maximum number of keys provided");
|
|
||||||
|
|
||||||
QualificationAuthorities::<T>::insert(network_curve, bounded_authorities);
|
|
||||||
|
|
||||||
let network_id = NetworkIdOf::<T>::default();
|
|
||||||
let network_data = NetworkDataBuilder::default()
|
|
||||||
.with_network_type(NetworkType::Evm)
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
T::NetworkDataHandler::register(network_id, network_data)
|
|
||||||
.expect("network should be valid for insertion");
|
|
||||||
|
|
||||||
let mut dkg_state = PendingDkgAuthorities::default();
|
|
||||||
dkg_state.new_dkg(authorities_len);
|
|
||||||
|
|
||||||
(dkg_state, network_curve, authority, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmarks(where T: Config)]
|
|
||||||
mod benchmarks {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_round0_package() -> Result<(), BenchmarkError> {
|
|
||||||
let dummy_hash = ExodusHash::repeat_byte(69);
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(1);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let round0_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(0)
|
|
||||||
.with_dkg_round0(dummy_hash);
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round0(round0_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let expected_hash = Round0Packages::<T>::get(&network_curve, authority_index);
|
|
||||||
assert_eq!(expected_hash, dummy_hash);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_round1_package(
|
|
||||||
c: Linear<4, { T::MaxAuthorities::get() }>
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let max_signers = c as AuthIndex;
|
|
||||||
let min_signers = get_byzantium_quorum_threshold(max_signers);
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
|
|
||||||
dkg_state.insert_index(authority_index);
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let mut rng = Pallet::<T>::get_offchain_rng(authority_index);
|
|
||||||
|
|
||||||
let (_, public_package) = network_curve.dkg_part1(
|
|
||||||
authority_index,
|
|
||||||
max_signers,
|
|
||||||
min_signers,
|
|
||||||
&mut rng,
|
|
||||||
).expect("DKG part1 should work");
|
|
||||||
|
|
||||||
let package_hash = ExodusHash::from(blake2_256(&public_package));
|
|
||||||
Round0Packages::<T>::insert(network_curve, authority_index, package_hash);
|
|
||||||
|
|
||||||
let round1_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(0)
|
|
||||||
.with_dkg_round1(public_package)
|
|
||||||
.expect("Round1 package should be valid");
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round1(round1_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let participants = Round1Packages::<T>::get(&network_curve);
|
|
||||||
assert!(participants.contains(authority_index));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_encrypted_round2_packages(
|
|
||||||
c: Linear<1, { T::MaxAuthorities::get() / 8 }>
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let max_signers = (c * 8) as AuthIndex;
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
dkg_state.insert_index(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
let padded_participants = dkg_state
|
|
||||||
.highest_bit::<usize>()
|
|
||||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let bitmask_len = padded_participants.div_ceil(8);
|
|
||||||
let blob_len = round2_encrypted_package_size(
|
|
||||||
padded_participants,
|
|
||||||
network_curve.scalar_bytes_len(),
|
|
||||||
network_curve.header_bytes_len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut bitmask = vec![0u8; bitmask_len];
|
|
||||||
let blob = vec![0u8; blob_len];
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
if i != authority_index {
|
|
||||||
let byte_index = (i / 8) as usize;
|
|
||||||
let bit_index = (i % 8) as u8;
|
|
||||||
bitmask[byte_index] |= 1 << bit_index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let encryption_bundle = BundledPackages { bitmask, blob };
|
|
||||||
|
|
||||||
let round2_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_dkg_round2(encryption_bundle)
|
|
||||||
.expect("Round2 package should be valid");
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round2(round2_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let participants = Round2Packages::<T>::get(&network_curve);
|
|
||||||
assert!(participants.contains(authority_index));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_round3_complaints(
|
|
||||||
c: Linear<1, { T::MaxAuthorities::get() / 8 }>
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let max_signers = (c * 8) as AuthIndex;
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
dkg_state.insert_index(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let bitvec_len = (max_signers + 7) / 8;
|
|
||||||
let mut accused_indexes = vec![0u8; bitvec_len as usize];
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
if i != authority_index {
|
|
||||||
let byte_index = (i / 8) as usize;
|
|
||||||
let bit_index = (i % 8) as u8;
|
|
||||||
accused_indexes[byte_index] |= 1 << bit_index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let round3_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_dkg_round3(accused_indexes)
|
|
||||||
.expect("Round3 package should be valid");
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round3(round3_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let complaints = Complaints::<T>::get(&network_curve);
|
|
||||||
let my_complaints = complaints.get(&authority_index)
|
|
||||||
.expect("Comlpaints should be registered");
|
|
||||||
|
|
||||||
assert_eq!(my_complaints.count_ones::<AuthIndex>(), max_signers - 1);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_round4_justifications(
|
|
||||||
c: Linear<1, { T::MaxAuthorities::get() / 8 }>
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let max_signers = (c * 8) as AuthIndex;
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
dkg_state.insert_index(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
let padded_participants = dkg_state
|
|
||||||
.highest_bit::<usize>()
|
|
||||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let bitmask_len = padded_participants.div_ceil(8);
|
|
||||||
let blob_len = round2_package_size(
|
|
||||||
padded_participants,
|
|
||||||
network_curve.scalar_bytes_len(),
|
|
||||||
network_curve.header_bytes_len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut bitmask = vec![0u8; bitmask_len];
|
|
||||||
let blob = vec![0u8; blob_len];
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
if i != authority_index {
|
|
||||||
let byte_index = (i / 8) as usize;
|
|
||||||
let bit_index = (i % 8) as u8;
|
|
||||||
bitmask[byte_index] |= 1 << bit_index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let justification_bundle = BundledPackages { bitmask, blob };
|
|
||||||
|
|
||||||
let round4_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_dkg_round4(justification_bundle)
|
|
||||||
.expect("Round4 package should be valid");
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round4(round4_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let justifications = Justifications::<T>::get(&network_curve);
|
|
||||||
let my_justifications = justifications.get(&authority_index)
|
|
||||||
.expect("Justifications should be registered");
|
|
||||||
|
|
||||||
assert_eq!(my_justifications.count_ones::<AuthIndex>(), max_signers - 1);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_round5_verifying_package() -> Result<(), BenchmarkError> {
|
|
||||||
let max_signers = 4 as AuthIndex;
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
dkg_state.insert_index(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
let dkg_index = dkg_state.get_dkg_index();
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let dummy_merkle_root = ExodusHash::repeat_byte(69);
|
|
||||||
let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()];
|
|
||||||
|
|
||||||
let round5_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_dkg_round5(dummy_verifying_key.clone(), dummy_merkle_root)
|
|
||||||
.expect("Round5 package should be valid");
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round5(round5_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let bounded_verifying_key =
|
|
||||||
BoundedVec::<u8, ConstU32< { ELEMENT_MAX_BYTES }>>::try_from(
|
|
||||||
dummy_verifying_key,
|
|
||||||
).expect("Bounded verifying key should be correct");
|
|
||||||
|
|
||||||
let metadata_hashes = [
|
|
||||||
SubstrateBlake2Hasher::hash(dummy_merkle_root.as_ref()),
|
|
||||||
SubstrateBlake2Hasher::hash(bounded_verifying_key.as_ref()),
|
|
||||||
];
|
|
||||||
let verifying_hash = sequential_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
|
||||||
|
|
||||||
let verification_key = (network_curve, dkg_index, verifying_hash);
|
|
||||||
let participants = Verifications::<T>::get(verification_key);
|
|
||||||
assert!(participants.contains(authority_index));
|
|
||||||
|
|
||||||
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, dkg_index);
|
|
||||||
assert!(consensus.participants.contains(authority_index));
|
|
||||||
assert_eq!(consensus.participants.count_ones::<AuthIndex>(), 1);
|
|
||||||
assert_eq!(consensus.merkle_root, dummy_merkle_root);
|
|
||||||
assert_eq!(consensus.element_bytes, bounded_verifying_key);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_round6_public_share_package(
|
|
||||||
c: Linear<1, { T::MaxAuthorities::get().next_power_of_two().trailing_zeros() }>
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let computed_max_signers = (1u32 << c) as AuthIndex;
|
|
||||||
let constant_max_signers = T::MaxAuthorities::get() as AuthIndex;
|
|
||||||
|
|
||||||
let max_signers = sp_std::cmp::min(computed_max_signers, constant_max_signers);
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
dkg_state.next_phase();
|
|
||||||
|
|
||||||
(0..max_signers).for_each(|i| {
|
|
||||||
dkg_state.insert_index(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
let dkg_index = dkg_state.get_dkg_index();
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
|
|
||||||
let dummy_value = vec![69u8; network_curve.element_bytes_len()];
|
|
||||||
|
|
||||||
let (dummy_merkle_root, dummy_verifying_share, dummy_merkle_proof) =
|
|
||||||
with_ciphersuite!(network_curve, |f| {
|
|
||||||
let mut dummy_values = BTreeMap::new();
|
|
||||||
|
|
||||||
for i in 0..max_signers {
|
|
||||||
let non_zero_index = i.saturating_add(1);
|
|
||||||
let identifier: frost_core::Identifier<Ciphersuite> =
|
|
||||||
frost_core::Identifier::<Ciphersuite>::try_from(non_zero_index)
|
|
||||||
.expect("Identifier should be created");
|
|
||||||
|
|
||||||
dummy_values.insert(identifier, dummy_value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let merkle_tree =
|
|
||||||
NetworkCurve::build_merkle_tree(
|
|
||||||
&dummy_values,
|
|
||||||
|value: &Vec<u8>| Ok(value.clone()),
|
|
||||||
).expect("Merkle tree for verifying shares should be valid");
|
|
||||||
|
|
||||||
let merkle_root = merkle_tree.last().copied().unwrap();
|
|
||||||
|
|
||||||
let (verifying_share, merkle_proof) = NetworkCurve::generate_merkle_proof_from_tree(
|
|
||||||
&dummy_values,
|
|
||||||
&merkle_tree,
|
|
||||||
authority_index,
|
|
||||||
|value: &Vec<u8>| Ok(value.clone())
|
|
||||||
).expect("Merkle tree proof should be valid");
|
|
||||||
|
|
||||||
(merkle_root, verifying_share, merkle_proof)
|
|
||||||
});
|
|
||||||
|
|
||||||
VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |consensus| {
|
|
||||||
consensus.participants.insert(authority_index);
|
|
||||||
consensus.merkle_root = dummy_merkle_root;
|
|
||||||
});
|
|
||||||
|
|
||||||
let round6_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_dkg_round6(dummy_verifying_share.clone(), dummy_merkle_proof)
|
|
||||||
.expect("Round6 package should be valid");
|
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round6(round6_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&dkg_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, dkg_package, signature);
|
|
||||||
|
|
||||||
let participants = VerifyingSharesParticipants::<T>::get(&network_curve, dkg_index);
|
|
||||||
assert!(participants.contains(authority_index));
|
|
||||||
|
|
||||||
let bundled_verifying_share =
|
|
||||||
BoundedVec::<u8, ConstU32<{ ELEMENT_MAX_BYTES }>>::try_from(
|
|
||||||
dummy_verifying_share,
|
|
||||||
).expect("Verifying share should be bundled");
|
|
||||||
|
|
||||||
let verifying_share_key = (network_curve, dkg_index, authority_index);
|
|
||||||
assert_eq!(
|
|
||||||
VerifyingShares::<T>::get(verifying_share_key).unwrap(),
|
|
||||||
bundled_verifying_share,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_nonce_commitment() -> Result<(), BenchmarkError> {
|
|
||||||
let network_id: NetworkIdOf<T> = 69_420u64.unique_saturated_into();
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(1);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if !dkg_state.is_dkg_pending() { break; }
|
|
||||||
dkg_state.next_phase();
|
|
||||||
}
|
|
||||||
|
|
||||||
dkg_state.insert_index(authority_index);
|
|
||||||
let exodus_session = CurrentExodus::<T>::get();
|
|
||||||
|
|
||||||
let dummy_address = EvmAddress::repeat_byte(69);
|
|
||||||
let dummy_nonce = vec![0u8; network_curve.element_bytes_len()];
|
|
||||||
|
|
||||||
let create_bond = GovernanceAction::Create {
|
|
||||||
market: [69, 420, 1337],
|
|
||||||
terms: [420, 1337],
|
|
||||||
token: dummy_address,
|
|
||||||
intervals: [34, 35],
|
|
||||||
booleans: [true, true],
|
|
||||||
};
|
|
||||||
let request = ExodusRequest::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance(
|
|
||||||
exodus_session,
|
|
||||||
network_id,
|
|
||||||
dummy_address,
|
|
||||||
create_bond,
|
|
||||||
);
|
|
||||||
|
|
||||||
let authorities = QualificationAuthorities::<T>::get(&network_curve);
|
|
||||||
let active_dkg: ActivatedState<T> = (&dkg_state).into();
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
|
|
||||||
ActiveAuthorities::<T>::insert(&network_curve, authorities);
|
|
||||||
ExodusRequests::<T>::insert(network_curve, exodus_session, request);
|
|
||||||
|
|
||||||
let nonce_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_exodus_nonce_commitment(
|
|
||||||
exodus_session,
|
|
||||||
dummy_nonce.clone(),
|
|
||||||
dummy_nonce.clone(),
|
|
||||||
)
|
|
||||||
.expect("Nonce commitment package should be valid");
|
|
||||||
|
|
||||||
let exodus_package = ExodusPackage::NonceCommitment(nonce_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&exodus_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, exodus_package, signature);
|
|
||||||
|
|
||||||
let roast_session = RoastSessions::<T>::get(&exodus_session, authority_index)
|
|
||||||
.expect("ROAST session should exist after nonce commitment");
|
|
||||||
|
|
||||||
let roast_state = RoastSessionStates::<T>::get(&exodus_session, roast_session);
|
|
||||||
assert!(roast_state.nonce_committers.contains(authority_index));
|
|
||||||
assert!(!roast_state.group_committers.contains(authority_index));
|
|
||||||
assert!(!roast_state.partial_signers.contains(authority_index));
|
|
||||||
|
|
||||||
let registered_nonces = NonceCommitments::<T>::get(
|
|
||||||
(exodus_session, roast_session),
|
|
||||||
authority_index,
|
|
||||||
).expect("Nonces should be registered during nonce commitment");
|
|
||||||
|
|
||||||
assert_eq!(registered_nonces.hiding, dummy_nonce.clone());
|
|
||||||
assert_eq!(registered_nonces.binding, dummy_nonce);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_group_commitment() -> Result<(), BenchmarkError> {
|
|
||||||
let network_id: NetworkIdOf<T> = 69_420u64.unique_saturated_into();
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(1);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if !dkg_state.is_dkg_pending() { break; }
|
|
||||||
dkg_state.next_phase();
|
|
||||||
}
|
|
||||||
|
|
||||||
dkg_state.insert_index(authority_index);
|
|
||||||
let exodus_session = CurrentExodus::<T>::get();
|
|
||||||
|
|
||||||
let dummy_bytes = EvmBytes32::repeat_byte(69);
|
|
||||||
let dummy_address = EvmAddress::repeat_byte(69);
|
|
||||||
let dummy_merkle_root = ExodusHash::repeat_byte(69);
|
|
||||||
let dummy_group_commitment = vec![0u8; network_curve.element_bytes_len()];
|
|
||||||
|
|
||||||
let create_bond = GovernanceAction::Create {
|
|
||||||
market: [69, 420, 1337],
|
|
||||||
terms: [420, 1337],
|
|
||||||
token: dummy_address,
|
|
||||||
intervals: [34, 35],
|
|
||||||
booleans: [true, true],
|
|
||||||
};
|
|
||||||
let request = ExodusRequest::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance(
|
|
||||||
exodus_session,
|
|
||||||
network_id,
|
|
||||||
dummy_address,
|
|
||||||
create_bond,
|
|
||||||
);
|
|
||||||
|
|
||||||
let authorities = QualificationAuthorities::<T>::get(&network_curve);
|
|
||||||
let active_dkg: ActivatedState<T> = (&dkg_state).into();
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
|
|
||||||
ActiveAuthorities::<T>::insert(&network_curve, authorities);
|
|
||||||
ExodusRequests::<T>::insert(network_curve, exodus_session, request);
|
|
||||||
|
|
||||||
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
|
|
||||||
roast_state.status = RoastStatus::GroupCommitments;
|
|
||||||
roast_state.nonce_committers.insert(authority_index);
|
|
||||||
|
|
||||||
let roast_session = 3;
|
|
||||||
RoastSessions::<T>::insert(&exodus_session, authority_index, roast_session);
|
|
||||||
RoastSessionStates::<T>::insert(exodus_session, roast_session, roast_state);
|
|
||||||
|
|
||||||
let group_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_exodus_group_commitment(
|
|
||||||
exodus_session,
|
|
||||||
dummy_group_commitment.clone(),
|
|
||||||
dummy_bytes,
|
|
||||||
)
|
|
||||||
.expect("Group commitment package should be valid");
|
|
||||||
|
|
||||||
let exodus_package = ExodusPackage::GroupCommitment(group_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&exodus_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, exodus_package, signature);
|
|
||||||
|
|
||||||
let bounded_group_commitment =
|
|
||||||
BoundedVec::<u8, ConstU32<{ ELEMENT_MAX_BYTES }>>::try_from(
|
|
||||||
dummy_group_commitment,
|
|
||||||
).expect("Group commtiment should be valid");
|
|
||||||
|
|
||||||
let roast_state = RoastSessionStates::<T>::get(&exodus_session, roast_session);
|
|
||||||
assert!(roast_state.nonce_committers.contains(authority_index));
|
|
||||||
assert!(roast_state.group_committers.contains(authority_index));
|
|
||||||
|
|
||||||
let consensus = GroupCommitmentsConsensus::<T>::get(exodus_session, roast_session);
|
|
||||||
assert!(consensus.participants.contains(authority_index));
|
|
||||||
assert_eq!(&consensus.element_bytes, &bounded_group_commitment);
|
|
||||||
assert_eq!(consensus.merkle_root, dummy_merkle_root);
|
|
||||||
|
|
||||||
let group_commitment_key = (exodus_session, roast_session, dummy_merkle_root, bounded_group_commitment);
|
|
||||||
let participants = GroupCommitters::<T>::get(group_commitment_key);
|
|
||||||
assert!(participants.contains(authority_index));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_signature_share() -> Result<(), BenchmarkError> {
|
|
||||||
let max_signers = T::MaxAuthorities::get();
|
|
||||||
let threshold = get_byzantium_quorum_threshold(max_signers);
|
|
||||||
let network_id: NetworkIdOf<T> = 69_420u64.unique_saturated_into();
|
|
||||||
|
|
||||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
|
||||||
prepare_pallet::<T>(max_signers as usize);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if !dkg_state.is_dkg_pending() { break; }
|
|
||||||
dkg_state.next_phase();
|
|
||||||
}
|
|
||||||
|
|
||||||
dkg_state.insert_index(authority_index);
|
|
||||||
let dkg_index = dkg_state.get_dkg_index();
|
|
||||||
let exodus_session = CurrentExodus::<T>::get();
|
|
||||||
|
|
||||||
let mut dummy_element = vec![0u8; network_curve.element_bytes_len()];
|
|
||||||
dummy_element[0] = 0x02;
|
|
||||||
let g_x_coords = [
|
|
||||||
0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac,
|
|
||||||
0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07,
|
|
||||||
0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
|
|
||||||
0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98
|
|
||||||
];
|
|
||||||
dummy_element[1..33].copy_from_slice(&g_x_coords);
|
|
||||||
|
|
||||||
let dummy_address = EvmAddress::repeat_byte(69);
|
|
||||||
let create_bond = GovernanceAction::Create {
|
|
||||||
market: [69, 420, 1337],
|
|
||||||
terms: [420, 1337],
|
|
||||||
token: dummy_address,
|
|
||||||
intervals: [34, 35],
|
|
||||||
booleans: [true, true],
|
|
||||||
};
|
|
||||||
let request = ExodusRequest::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance(
|
|
||||||
exodus_session,
|
|
||||||
network_id,
|
|
||||||
dummy_address,
|
|
||||||
create_bond,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut dummy_scalar = vec![0u8; network_curve.scalar_bytes_len()];
|
|
||||||
dummy_scalar[network_curve.scalar_bytes_len() - 1] = 1;
|
|
||||||
|
|
||||||
let dummy_signature_share = dummy_scalar.clone();
|
|
||||||
let dummy_binding_factor = dummy_scalar.clone();
|
|
||||||
let dummy_group_commitment = dummy_element.clone();
|
|
||||||
|
|
||||||
let (dummy_merkle_root, dummy_merkle_proof) =
|
|
||||||
with_ciphersuite!(network_curve, |f| {
|
|
||||||
let mut dummy_values = BTreeMap::new();
|
|
||||||
|
|
||||||
for i in 0..threshold {
|
|
||||||
let non_zero_index = (i as AuthIndex).saturating_add(1);
|
|
||||||
let identifier: frost_core::Identifier<Ciphersuite> =
|
|
||||||
frost_core::Identifier::<Ciphersuite>::try_from(non_zero_index)
|
|
||||||
.expect("Identifier should be created");
|
|
||||||
|
|
||||||
dummy_values.insert(identifier, dummy_binding_factor.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let merkle_tree =
|
|
||||||
NetworkCurve::build_merkle_tree(
|
|
||||||
&dummy_values,
|
|
||||||
|value: &Vec<u8>| Ok(value.clone()),
|
|
||||||
).expect("Merkle tree for binding factor should be valid");
|
|
||||||
|
|
||||||
let merkle_root = merkle_tree.last().copied().unwrap();
|
|
||||||
|
|
||||||
let (_, merkle_proof) =
|
|
||||||
NetworkCurve::generate_merkle_proof_from_tree(
|
|
||||||
&dummy_values,
|
|
||||||
&merkle_tree,
|
|
||||||
authority_index,
|
|
||||||
|value: &Vec<u8>| Ok(value.clone())
|
|
||||||
).expect("Merkle tree proof should be valid");
|
|
||||||
|
|
||||||
(merkle_root, merkle_proof)
|
|
||||||
});
|
|
||||||
|
|
||||||
let authorities = QualificationAuthorities::<T>::get(&network_curve);
|
|
||||||
let active_dkg: ActivatedState<T> = (&dkg_state).into();
|
|
||||||
let active_dkg_index = active_dkg.get_dkg_index();
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
|
||||||
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
|
|
||||||
ActiveAuthorities::<T>::insert(&network_curve, authorities);
|
|
||||||
ExodusRequests::<T>::insert(network_curve, exodus_session, request);
|
|
||||||
|
|
||||||
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
|
|
||||||
roast_state.status = RoastStatus::PartialSignatures;
|
|
||||||
roast_state.nonce_committers.insert(authority_index);
|
|
||||||
roast_state.group_committers.insert(authority_index);
|
|
||||||
|
|
||||||
(0..threshold).for_each(|i| {
|
|
||||||
if i as AuthIndex == authority_index { return; }
|
|
||||||
roast_state.partial_signers.insert(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
let roast_session = 3;
|
|
||||||
RoastSessions::<T>::insert(&exodus_session, authority_index, roast_session);
|
|
||||||
RoastSessionStates::<T>::insert(exodus_session, roast_session, roast_state);
|
|
||||||
|
|
||||||
let bounded_group_commitment = BoundedVec::try_from(dummy_group_commitment).unwrap();
|
|
||||||
let dummy_verifying_key = BoundedVec::try_from(dummy_element.clone()).unwrap();
|
|
||||||
|
|
||||||
let dummy_verifying_share = BoundedVec::try_from(dummy_element.clone()).unwrap();
|
|
||||||
let dummy_nonce = ExodusSeparatedNonce::new(
|
|
||||||
BoundedVec::try_from(dummy_element.clone()).unwrap(),
|
|
||||||
BoundedVec::try_from(dummy_element.clone()).unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
ActiveVerifyingKey::<T>::insert(&network_curve, dummy_verifying_key);
|
|
||||||
VerifyingShares::<T>::insert((&network_curve, active_dkg_index, authority_index), dummy_verifying_share);
|
|
||||||
NonceCommitments::<T>::insert((&exodus_session, &roast_session), authority_index, dummy_nonce);
|
|
||||||
|
|
||||||
GroupCommitmentsConsensus::<T>::mutate(&exodus_session, &roast_session, |consensus| {
|
|
||||||
consensus.element_bytes = bounded_group_commitment.clone();
|
|
||||||
consensus.merkle_root = dummy_merkle_root;
|
|
||||||
(0..threshold).for_each(|i| {
|
|
||||||
consensus.participants.insert(i);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let share_package = PackageContext::default()
|
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
|
||||||
.with_authority_index(authority_index)
|
|
||||||
.with_exodus_signature_share(
|
|
||||||
exodus_session,
|
|
||||||
dummy_signature_share.clone(),
|
|
||||||
dummy_merkle_proof,
|
|
||||||
dummy_binding_factor
|
|
||||||
)
|
|
||||||
.expect("Signature share package should be valid");
|
|
||||||
|
|
||||||
let exodus_package = ExodusPackage::SignatureShare(share_package);
|
|
||||||
|
|
||||||
let signature = authority
|
|
||||||
.sign(&exodus_package.encode())
|
|
||||||
.expect("Signature should be created");
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(RawOrigin::None, exodus_package, signature);
|
|
||||||
|
|
||||||
let maybe_roast_session = RoastSessions::<T>::get(&exodus_session, authority_index);
|
|
||||||
assert!(maybe_roast_session.is_none());
|
|
||||||
|
|
||||||
let scalar_exists = SignatureScalars::<T>::contains_key(&exodus_session, &roast_session);
|
|
||||||
let consensus_exists = GroupCommitmentsConsensus::<T>::contains_key(&exodus_session, &roast_session);
|
|
||||||
let committers_exists = GroupCommitters::<T>::contains_key((
|
|
||||||
&exodus_session,
|
|
||||||
&roast_session,
|
|
||||||
&dummy_merkle_root,
|
|
||||||
&bounded_group_commitment,
|
|
||||||
));
|
|
||||||
|
|
||||||
assert!(!scalar_exists);
|
|
||||||
assert!(!consensus_exists);
|
|
||||||
assert!(!committers_exists);
|
|
||||||
|
|
||||||
assert!(!ExodusRequests::<T>::contains_key(&network_curve, &exodus_session));
|
|
||||||
assert!(ExodusSignedGovernance::<T>::contains_key((&exodus_session, network_id), dkg_index));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn register_evm_bridge_out_exodus() -> Result<(), BenchmarkError> {
|
|
||||||
let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
|
|
||||||
let (_, network_curve, _, _) = prepare_pallet::<T>(1usize);
|
|
||||||
|
|
||||||
let network_id = NetworkIdOf::<T>::default();
|
|
||||||
let amount: BalanceOf<T> = 10_000u32.into();
|
|
||||||
let bounty = Perbill::from_percent(5);
|
|
||||||
let receiver = EvmAddress::from([69u8; 20]);
|
|
||||||
|
|
||||||
QualificationDkgState::<T>::mutate(&network_curve, |state| {
|
|
||||||
loop {
|
|
||||||
if state.is_dkg_finalized() { break; }
|
|
||||||
state.next_phase();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let total_existential = T::Currency::minimum_balance();
|
|
||||||
let initial_balance = total_existential
|
|
||||||
.saturating_mul(5u32.into())
|
|
||||||
.saturating_add(amount);
|
|
||||||
|
|
||||||
let _ = T::Currency::make_free_balance_be(&caller, initial_balance);
|
|
||||||
let _ = T::NetworkDataHandler::register_incoming(&network_id, amount).unwrap();
|
|
||||||
|
|
||||||
let current_exodus_session = CurrentExodus::<T>::get();
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
_(
|
|
||||||
RawOrigin::Signed(caller.clone()),
|
|
||||||
network_id,
|
|
||||||
amount,
|
|
||||||
bounty,
|
|
||||||
receiver,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(ExodusRequests::<T>::contains_key(&network_curve, current_exodus_session));
|
|
||||||
assert_eq!(CurrentExodus::<T>::get(), current_exodus_session + 1);
|
|
||||||
assert_eq!(T::Currency::free_balance(&caller), initial_balance - amount);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_benchmark_test_suite!(
|
|
||||||
Pallet,
|
|
||||||
crate::mock::new_test_ext_benchmarks(),
|
|
||||||
crate::mock::Runtime
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
use ghost_helpers::networks::NetworkCurve;
|
|
||||||
|
|
||||||
use crate::AuthIndex;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum ExodusError {
|
|
||||||
InvalidParticipantId,
|
|
||||||
DeserializationError,
|
|
||||||
SerializationError,
|
|
||||||
InvalidMinSigners(AuthIndex, AuthIndex),
|
|
||||||
InvalidMaxSigners(AuthIndex),
|
|
||||||
DKGNotSupported(NetworkCurve),
|
|
||||||
IncorrectNumberOfCoefficients,
|
|
||||||
IncorrectNumberOfCommitments,
|
|
||||||
IncorrectNumberOfPackages,
|
|
||||||
IncorrectPackage,
|
|
||||||
PackageNotFound,
|
|
||||||
InvalidSecretShare,
|
|
||||||
InvalidProofOfKnowledge,
|
|
||||||
MissingCommitment,
|
|
||||||
IncorrectCommitment,
|
|
||||||
UnknownIdentifier,
|
|
||||||
IdentityCommitment,
|
|
||||||
DuplicatedIdentifier,
|
|
||||||
IncorrectNumberOfIdentifiers,
|
|
||||||
InvalidSignature,
|
|
||||||
InvalidSignatureShare,
|
|
||||||
InvalidNonceCommitments,
|
|
||||||
EncryptionFailed,
|
|
||||||
DecryptionFailed,
|
|
||||||
HKDFFailed,
|
|
||||||
InvalidNonceLength,
|
|
||||||
MalformedSigningKey,
|
|
||||||
NoStoredNetworks,
|
|
||||||
OffchainTimeoutPeriod,
|
|
||||||
BadSignature(AuthIndex),
|
|
||||||
TransactionSubmissionFailed(AuthIndex),
|
|
||||||
UnknownReceiverIndex(AuthIndex),
|
|
||||||
CouldNotConstructMessage(AuthIndex),
|
|
||||||
NothingToWrite,
|
|
||||||
UnknownPrefix,
|
|
||||||
UnexpectedRound,
|
|
||||||
InvalidMerkleProof,
|
|
||||||
InvalidIdentityElement,
|
|
||||||
NoActiveAuthorities,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::fmt::Debug for ExodusError {
|
|
||||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
||||||
match *self {
|
|
||||||
ExodusError::InvalidParticipantId => write!(fmt, "Participant index could not be parsed to identifier."),
|
|
||||||
ExodusError::DeserializationError => write!(fmt, "Error during deserializing value."),
|
|
||||||
ExodusError::SerializationError => write!(fmt, "Error serializing value."),
|
|
||||||
ExodusError::InvalidMinSigners(min, max) => write!(fmt, "Number of min_signers ({min}) must be at least 2 and not larger than max_signers ({max})."),
|
|
||||||
ExodusError::InvalidMaxSigners(max_signers) => write!(fmt, "Number of max_signers must be at least 2, {max_signers} given."),
|
|
||||||
ExodusError::DKGNotSupported(ref curve) => write!(fmt, "The ciphersuite {curve:?} does not support DKG."),
|
|
||||||
ExodusError::IncorrectNumberOfCoefficients => write!(fmt, "Incorrect number of coefficients; should be equal to min signers."),
|
|
||||||
ExodusError::IncorrectNumberOfCommitments => write!(fmt, "Incorrect number of commitments; should be equal to min signers."),
|
|
||||||
ExodusError::InvalidNonceCommitments => write!(fmt, "Hiding nonce commitments indexes should match binding nonce commitments."),
|
|
||||||
ExodusError::InvalidProofOfKnowledge => write!(fmt, "The proof of knowledge is not valid."),
|
|
||||||
ExodusError::IncorrectNumberOfPackages => write!(fmt, "Incorrect number of packages; should be equal to max signers."),
|
|
||||||
ExodusError::IncorrectPackage => write!(fmt, "The incorrect package was specified."),
|
|
||||||
ExodusError::PackageNotFound => write!(fmt, "Round 1 package not found for Round 2 participant."),
|
|
||||||
ExodusError::InvalidSecretShare => write!(fmt, "Invalid secret share."),
|
|
||||||
ExodusError::MissingCommitment => write!(fmt, "The Signing Package must contain the participant's Commitments."),
|
|
||||||
ExodusError::IncorrectCommitment => write!(fmt, "The participant's commitment is incorrect."),
|
|
||||||
ExodusError::UnknownIdentifier => write!(fmt, "Unknown identifier."),
|
|
||||||
ExodusError::IdentityCommitment => write!(fmt, "Commitment equals the identity."),
|
|
||||||
ExodusError::DuplicatedIdentifier => write!(fmt, "Duplicated identifier."),
|
|
||||||
ExodusError::IncorrectNumberOfIdentifiers => write!(fmt, "Incorrect number of identifiers."),
|
|
||||||
ExodusError::InvalidSignature => write!(fmt, "Invalid signature."),
|
|
||||||
ExodusError::InvalidSignatureShare => write!(fmt, "Invalid signature share."),
|
|
||||||
ExodusError::EncryptionFailed => write!(fmt, "Encryption failed."),
|
|
||||||
ExodusError::DecryptionFailed => write!(fmt, "Decryption failed."),
|
|
||||||
ExodusError::HKDFFailed => write!(fmt, "HKDF failed."),
|
|
||||||
ExodusError::InvalidNonceLength => write!(fmt, "Encryption nonce has invalid length."),
|
|
||||||
ExodusError::MalformedSigningKey => write!(fmt, "Malformed signing key encoding."),
|
|
||||||
ExodusError::NoStoredNetworks => write!(fmt, "No stored networks to be worked on."),
|
|
||||||
ExodusError::OffchainTimeoutPeriod => write!(fmt, "Offchain request should be in-flight."),
|
|
||||||
ExodusError::BadSignature(auth_index) => write!(fmt, "Signature could not be created from {auth_index}."),
|
|
||||||
ExodusError::TransactionSubmissionFailed(auth_index) => write!(fmt, "Could not submit transaction from {auth_index}."),
|
|
||||||
ExodusError::UnknownReceiverIndex(receiver_index) => write!(fmt, "Unknown receiver index {receiver_index} found."),
|
|
||||||
ExodusError::CouldNotConstructMessage(auth_index) => write!(fmt, "Could not construct message during OCW from {auth_index}."),
|
|
||||||
ExodusError::NothingToWrite => write!(fmt, "Nothing to write into local storage."),
|
|
||||||
ExodusError::UnknownPrefix => write!(fmt, "Unknown prefix used for local storage."),
|
|
||||||
ExodusError::UnexpectedRound => write!(fmt, "Unexpected round provided."),
|
|
||||||
ExodusError::InvalidMerkleProof => write!(fmt, "Invalid merkle proof provided."),
|
|
||||||
ExodusError::InvalidIdentityElement => write!(fmt, "Invalid identity element provided."),
|
|
||||||
ExodusError::NoActiveAuthorities => write!(fmt, "No active authorities for signing exodus transactions."),
|
|
||||||
ExodusError::Unknown => write!(fmt, "Unknown error."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
use crate::{AuthIndex, ExodusError, NetworkCurve};
|
|
||||||
|
|
||||||
use ghost_traits::exodus::IdentifierConverter;
|
|
||||||
|
|
||||||
impl IdentifierConverter<AuthIndex, ExodusError> for NetworkCurve {
|
|
||||||
fn convert_identifier_to_index(
|
|
||||||
identifier_bytes: &[u8],
|
|
||||||
) -> Result<AuthIndex, ExodusError> {
|
|
||||||
const SIZE: usize = core::mem::size_of::<AuthIndex>();
|
|
||||||
|
|
||||||
let start = identifier_bytes.len().checked_sub(SIZE)
|
|
||||||
.ok_or(ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let bytes_array = identifier_bytes.get(start..)
|
|
||||||
.and_then(|slice| slice.try_into().ok())
|
|
||||||
.ok_or(ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
AuthIndex::from_be_bytes(bytes_array)
|
|
||||||
.checked_sub(1)
|
|
||||||
.ok_or(ExodusError::InvalidParticipantId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn non_zero_index(index: AuthIndex) -> Result<AuthIndex, ExodusError> {
|
|
||||||
index.checked_add(1).ok_or(ExodusError::InvalidParticipantId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,343 +0,0 @@
|
|||||||
use sp_std::{collections::btree_map::BTreeMap, vec::Vec, result::Result};
|
|
||||||
use rand_chacha::rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use ghost_traits::exodus::{
|
|
||||||
DistributedKeyGeneration, IdentifierConverter, MerkleTreeBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{AuthIndex, ExodusError, NetworkCurve};
|
|
||||||
|
|
||||||
impl DistributedKeyGeneration<AuthIndex, ExodusError> for NetworkCurve {
|
|
||||||
type RoundPackages = BTreeMap<AuthIndex, Vec<u8>>;
|
|
||||||
|
|
||||||
type Part1Result = Result<(Vec<u8>, Vec<u8>), ExodusError>;
|
|
||||||
type Part2Result = Result<(Vec<u8>, BTreeMap<AuthIndex, Vec<u8>>), ExodusError>;
|
|
||||||
type Part3Result = Result<
|
|
||||||
(
|
|
||||||
Vec<u8>, Vec<u8>,
|
|
||||||
Vec<<Self as MerkleTreeBuilder<AuthIndex, ExodusError>>::Hash>,
|
|
||||||
<Self as MerkleTreeBuilder<AuthIndex, ExodusError>>::Hash,
|
|
||||||
),
|
|
||||||
ExodusError
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn dkg_narrow_round_max_signers(
|
|
||||||
&self,
|
|
||||||
secret_package_bytes: &[u8],
|
|
||||||
new_max_signers: AuthIndex,
|
|
||||||
round_number: u8,
|
|
||||||
) -> Result<Vec<u8>, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
macro_rules! process_secret_package {
|
|
||||||
(
|
|
||||||
$package_type:ty,
|
|
||||||
$field1:ident : $action1:tt,
|
|
||||||
$field2:ident : $action2:tt,
|
|
||||||
) => {{
|
|
||||||
let secret_package = <$package_type>::deserialize(secret_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let min_signers = *secret_package.min_signers();
|
|
||||||
|
|
||||||
frost_core::keys::validate_num_of_signers(min_signers, new_max_signers)
|
|
||||||
.map_err(|err| match err {
|
|
||||||
f::Error::InvalidMinSigners => {
|
|
||||||
ExodusError::InvalidMinSigners(min_signers, new_max_signers)
|
|
||||||
},
|
|
||||||
f::Error::InvalidMaxSigners => {
|
|
||||||
ExodusError::InvalidMaxSigners(new_max_signers)
|
|
||||||
},
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
macro_rules! apply_action {
|
|
||||||
($field:ident, clone) => { secret_package.$field().clone() };
|
|
||||||
($field:ident, value) => { secret_package.$field() };
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_secret_package = <$package_type>::new(
|
|
||||||
secret_package.identifier().clone(),
|
|
||||||
apply_action!($field1, $action1),
|
|
||||||
apply_action!($field2, $action2),
|
|
||||||
min_signers,
|
|
||||||
new_max_signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
new_secret_package.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
match round_number {
|
|
||||||
1 => process_secret_package!(
|
|
||||||
f::keys::dkg::round1::SecretPackage,
|
|
||||||
coefficients: value,
|
|
||||||
commitment: clone,
|
|
||||||
),
|
|
||||||
2 => process_secret_package!(
|
|
||||||
f::keys::dkg::round2::SecretPackage,
|
|
||||||
commitment: clone,
|
|
||||||
secret_share: value,
|
|
||||||
),
|
|
||||||
_ => Err(ExodusError::UnexpectedRound),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dkg_verify_proof_of_knowledge(
|
|
||||||
&self,
|
|
||||||
index: AuthIndex,
|
|
||||||
round1_package_bytes: &[u8],
|
|
||||||
) -> Result<AuthIndex, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let round1_package = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let commitment = round1_package.commitment();
|
|
||||||
|
|
||||||
frost_core::keys::dkg::verify_proof_of_knowledge(
|
|
||||||
identifier,
|
|
||||||
&commitment,
|
|
||||||
&round1_package.proof_of_knowledge(),
|
|
||||||
).map_err(|_| ExodusError::InvalidProofOfKnowledge)?;
|
|
||||||
|
|
||||||
let coefficients = commitment.coefficients();
|
|
||||||
let coefficients_len = coefficients.len() as AuthIndex;
|
|
||||||
|
|
||||||
Ok(coefficients_len)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dkg_part1<R: RngCore + CryptoRng>(
|
|
||||||
&self,
|
|
||||||
index: AuthIndex,
|
|
||||||
max_signers: AuthIndex,
|
|
||||||
min_signers: AuthIndex,
|
|
||||||
mut rng: R,
|
|
||||||
) -> Self::Part1Result {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let (round1_secret_package, round1_package) = f::keys::dkg::part1(
|
|
||||||
identifier,
|
|
||||||
max_signers,
|
|
||||||
min_signers,
|
|
||||||
&mut rng,
|
|
||||||
).map_err(|err| {
|
|
||||||
match err {
|
|
||||||
f::Error::InvalidMinSigners => ExodusError::InvalidMinSigners(min_signers, max_signers),
|
|
||||||
f::Error::InvalidMaxSigners => ExodusError::InvalidMaxSigners(max_signers),
|
|
||||||
f::Error::DKGNotSupported => ExodusError::DKGNotSupported(*self),
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let round1_secret_package_bytes = round1_secret_package.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let round1_package_bytes = round1_package.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
Ok((round1_secret_package_bytes, round1_package_bytes))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dkg_part2(
|
|
||||||
&self,
|
|
||||||
round1_secret_package_bytes: &[u8],
|
|
||||||
round1_packages_bytes: &Self::RoundPackages,
|
|
||||||
) -> Self::Part2Result {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
|
|
||||||
type InnerField = <InnerGroup as frost_core::Group>::Field;
|
|
||||||
|
|
||||||
let round1_secret_package = f::keys::dkg::round1::SecretPackage::deserialize(round1_secret_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let round1_packages = round1_packages_bytes
|
|
||||||
.iter()
|
|
||||||
.map(|(&index, round1_package_bytes)| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let round1_package = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok((identifier, round1_package))
|
|
||||||
})
|
|
||||||
.collect::<Result<BTreeMap<f::Identifier, f::keys::dkg::round1::Package>, ExodusError>>()?;
|
|
||||||
|
|
||||||
let (round2_secret_package, round2_packages) = f::keys::dkg::part2(
|
|
||||||
round1_secret_package,
|
|
||||||
&round1_packages,
|
|
||||||
).map_err(|err| match err {
|
|
||||||
f::Error::IncorrectNumberOfCommitments => ExodusError::IncorrectNumberOfCommitments,
|
|
||||||
f::Error::IncorrectNumberOfPackages => ExodusError::IncorrectNumberOfPackages,
|
|
||||||
f::Error::InvalidProofOfKnowledge { .. } => ExodusError::InvalidProofOfKnowledge,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let round2_secret_package_bytes = round2_secret_package.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let round2_packages_bytes = round2_packages
|
|
||||||
.iter()
|
|
||||||
.map(|(id, round2_package)| {
|
|
||||||
let id_scalar = id.to_scalar();
|
|
||||||
let id_bytes = <InnerField as frost_core::Field>::serialize(&id_scalar);
|
|
||||||
|
|
||||||
let index = Self::convert_identifier_to_index(id_bytes.as_ref())?;
|
|
||||||
let round2_package_bytes = round2_package.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
Ok((index, round2_package_bytes))
|
|
||||||
})
|
|
||||||
.collect::<Result<BTreeMap<AuthIndex, Vec<u8>>, ExodusError>>()?;
|
|
||||||
|
|
||||||
Ok((round2_secret_package_bytes, round2_packages_bytes))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dkg_verify_private_package(
|
|
||||||
&self,
|
|
||||||
index: AuthIndex,
|
|
||||||
round1_package_bytes: &[u8],
|
|
||||||
round2_package_bytes: &[u8],
|
|
||||||
) -> Result<(), ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let commitment = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
|
|
||||||
.map(|round1_package| round1_package.commitment().clone())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let secret_share = f::keys::dkg::round2::Package::deserialize(round2_package_bytes)
|
|
||||||
.map(|round2_package| {
|
|
||||||
let signing_share = round2_package.signing_share().clone();
|
|
||||||
f::keys::SecretShare::new(identifier, signing_share, commitment)
|
|
||||||
})
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let _ = secret_share.verify().map_err(|_| ExodusError::InvalidSecretShare)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dkg_part3(
|
|
||||||
&self,
|
|
||||||
authority_index: AuthIndex,
|
|
||||||
round2_secret_package_bytes: &[u8],
|
|
||||||
round1_packages_bytes: &Self::RoundPackages,
|
|
||||||
round2_packages_bytes: &Self::RoundPackages,
|
|
||||||
) -> Self::Part3Result {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let round2_secret_package = f::keys::dkg::round2::SecretPackage::deserialize(round2_secret_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let round1_packages = round1_packages_bytes
|
|
||||||
.iter()
|
|
||||||
.map(|(&index, round1_package_bytes)| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let round1_package = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok((identifier, round1_package))
|
|
||||||
})
|
|
||||||
.collect::<Result<BTreeMap<f::Identifier, f::keys::dkg::round1::Package>, ExodusError>>()?;
|
|
||||||
|
|
||||||
let round2_packages = round2_packages_bytes
|
|
||||||
.iter()
|
|
||||||
.map(|(&index, round2_package_bytes)| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let round2_package = f::keys::dkg::round2::Package::deserialize(round2_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok((identifier, round2_package))
|
|
||||||
})
|
|
||||||
.collect::<Result<BTreeMap<f::Identifier, f::keys::dkg::round2::Package>, ExodusError>>()?;
|
|
||||||
|
|
||||||
let (key_package, pubkey_package) = f::keys::dkg::part3(
|
|
||||||
&round2_secret_package,
|
|
||||||
&round1_packages,
|
|
||||||
&round2_packages,
|
|
||||||
).map_err(|err| match err {
|
|
||||||
f::Error::IncorrectNumberOfPackages => ExodusError::IncorrectNumberOfPackages,
|
|
||||||
f::Error::IncorrectPackage => ExodusError::IncorrectPackage,
|
|
||||||
f::Error::PackageNotFound => ExodusError::PackageNotFound,
|
|
||||||
f::Error::InvalidSecretShare { .. } => ExodusError::InvalidSecretShare,
|
|
||||||
f::Error::IncorrectNumberOfCommitments => ExodusError::IncorrectNumberOfCommitments,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let key_package = key_package.normalize_package();
|
|
||||||
let pubkey_package = pubkey_package.normalize_package();
|
|
||||||
|
|
||||||
let key_package_bytes = key_package.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let verifying_shares_merkle_tree =
|
|
||||||
Self::build_merkle_tree(
|
|
||||||
pubkey_package.verifying_shares(),
|
|
||||||
|verifying_share| verifying_share.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (_, merkle_proof) =
|
|
||||||
Self::generate_merkle_proof_from_tree(
|
|
||||||
pubkey_package.verifying_shares(),
|
|
||||||
&verifying_shares_merkle_tree,
|
|
||||||
authority_index,
|
|
||||||
|verifying_share| verifying_share.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let merkle_root = verifying_shares_merkle_tree
|
|
||||||
.last()
|
|
||||||
.copied()
|
|
||||||
.ok_or(ExodusError::IncorrectNumberOfPackages)?;
|
|
||||||
|
|
||||||
let verifying_key_bytes = pubkey_package.verifying_key()
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
key_package_bytes,
|
|
||||||
verifying_key_bytes,
|
|
||||||
merkle_proof,
|
|
||||||
merkle_root,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dkg_derive_verifying_share(
|
|
||||||
&self,
|
|
||||||
secret_key_package_bytes: &[u8],
|
|
||||||
) -> Result<Vec<u8>, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let secret_key_package =
|
|
||||||
f::keys::KeyPackage::deserialize(secret_key_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
secret_key_package
|
|
||||||
.verifying_share()
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
use sp_std::{
|
|
||||||
vec::Vec,
|
|
||||||
result::Result,
|
|
||||||
marker::PhantomData,
|
|
||||||
};
|
|
||||||
use rand_chacha::rand_core::{CryptoRng, RngCore};
|
|
||||||
use ghost_traits::exodus::EllipticCurveDiffieHellman;
|
|
||||||
|
|
||||||
use hkdf::Hkdf as KeyDerivationFunction;
|
|
||||||
use sha2::Sha256 as Hashing;
|
|
||||||
|
|
||||||
use chacha20poly1305::{
|
|
||||||
aead::{self, Aead, KeyInit},
|
|
||||||
Key as EncryptionKey,
|
|
||||||
Nonce as EncryptionNonce,
|
|
||||||
ChaCha20Poly1305 as EncryptionCipher,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::pallet::Config;
|
|
||||||
use crate::{AuthIndex, EncryptionData, ExodusError, NetworkCurve, DkgIndex};
|
|
||||||
|
|
||||||
impl<T: Config> EllipticCurveDiffieHellman<AuthIndex, EncryptionCipher, EncryptionData<T>, DkgIndex, ExodusError> for NetworkCurve {
|
|
||||||
fn ecdh_encrypt_package<R: RngCore + CryptoRng>(
|
|
||||||
&self,
|
|
||||||
cipher: &EncryptionCipher,
|
|
||||||
aad: &[u8],
|
|
||||||
msg: &[u8],
|
|
||||||
mut rng: R,
|
|
||||||
) -> Result<EncryptionData<T>, ExodusError> {
|
|
||||||
let payload = aead::Payload { msg, aad };
|
|
||||||
|
|
||||||
let mut nonce_bytes = [0u8; crate::ENCRYPTION_NONCE_MAX_BYTES as usize];
|
|
||||||
rng.fill_bytes(&mut nonce_bytes);
|
|
||||||
let nonce = EncryptionNonce::from_slice(&nonce_bytes);
|
|
||||||
|
|
||||||
let ciphertext = cipher.encrypt(&nonce, payload)
|
|
||||||
.map_err(|_| ExodusError::EncryptionFailed)?;
|
|
||||||
|
|
||||||
EncryptionData::try_new(ciphertext, nonce.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ecdh_decrypt_package(
|
|
||||||
&self,
|
|
||||||
cipher: &EncryptionCipher,
|
|
||||||
encrypted_data: &EncryptionData<T>,
|
|
||||||
aad: &[u8],
|
|
||||||
) -> Result<Vec<u8>, ExodusError> {
|
|
||||||
let nonce = EncryptionNonce::from_iter(
|
|
||||||
encrypted_data.nonce.iter().cloned()
|
|
||||||
);
|
|
||||||
let payload = aead::Payload { msg: &encrypted_data.ciphertext, aad };
|
|
||||||
cipher.decrypt(&nonce, payload).map_err(|_| ExodusError::DecryptionFailed)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ecdh_prepare_additional_info(
|
|
||||||
&self,
|
|
||||||
sender_index: AuthIndex,
|
|
||||||
receiver_index: AuthIndex,
|
|
||||||
dkg_index: DkgIndex,
|
|
||||||
_marker: PhantomData<EncryptionData<T>>,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let mut additional_info = match self {
|
|
||||||
NetworkCurve::Secp256k1 => b"EXODUS-SECP256K1-DKG-V1".to_vec(),
|
|
||||||
NetworkCurve::Ed25519 => b"EXODUS-ED25519-DKG-V1".to_vec(),
|
|
||||||
};
|
|
||||||
|
|
||||||
additional_info.extend_from_slice(&sender_index.to_be_bytes());
|
|
||||||
additional_info.extend_from_slice(&receiver_index.to_be_bytes());
|
|
||||||
additional_info.extend_from_slice(&dkg_index.to_be_bytes());
|
|
||||||
|
|
||||||
additional_info
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ecdh_get_cipher_from_keys(
|
|
||||||
&self,
|
|
||||||
public: &[u8],
|
|
||||||
secret: &[u8],
|
|
||||||
additional_info: &[u8],
|
|
||||||
_marker: PhantomData<EncryptionData<T>>,
|
|
||||||
) -> Result<EncryptionCipher, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let secret_package = f::keys::dkg::round1::SecretPackage::deserialize(secret)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
let public_package = f::keys::dkg::round1::Package::deserialize(public)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let coefficients = secret_package.coefficients();
|
|
||||||
let local_sk = coefficients.first()
|
|
||||||
.ok_or(ExodusError::IncorrectNumberOfCoefficients)?;
|
|
||||||
let remote_pk = public_package.commitment().coefficients().first()
|
|
||||||
.ok_or(ExodusError::MissingCommitment)?.value();
|
|
||||||
|
|
||||||
let shared_secret_bytes = f::VerifyingKey::new(remote_pk * local_sk)
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let local_pk: f::VerifyingKey = f::SigningKey::from_scalar(*local_sk)
|
|
||||||
.map_err(|_| ExodusError::MalformedSigningKey)?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let local_pk_bytes = local_pk.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
let remote_pk_bytes = f::VerifyingKey::new(remote_pk)
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let mut keys = [local_pk_bytes, remote_pk_bytes];
|
|
||||||
keys.sort();
|
|
||||||
|
|
||||||
let salt = keys.concat();
|
|
||||||
let hk = KeyDerivationFunction::<Hashing>::new(Some(&salt), &shared_secret_bytes);
|
|
||||||
|
|
||||||
let mut encryption_key = EncryptionKey::default();
|
|
||||||
hk.expand(additional_info, &mut encryption_key)
|
|
||||||
.map_err(|_| ExodusError::HKDFFailed)?;
|
|
||||||
|
|
||||||
let cipher = EncryptionCipher::new_from_slice(&encryption_key)
|
|
||||||
.expect("could not happen; length is already correct qed");
|
|
||||||
|
|
||||||
Ok(cipher)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,446 +0,0 @@
|
|||||||
use ghost_traits::exodus::{
|
|
||||||
IdentifierConverter, MerkleTreeBuilder,
|
|
||||||
FlexibleRoundOptimizedSchnorrThresholdSignature,
|
|
||||||
};
|
|
||||||
use sp_std::{
|
|
||||||
vec::Vec, result::Result,
|
|
||||||
collections::{btree_set::BTreeSet, btree_map::BTreeMap},
|
|
||||||
};
|
|
||||||
|
|
||||||
use rand_chacha::rand_core::{CryptoRng, RngCore};
|
|
||||||
use ghost_helpers::networks::NetworkCurve;
|
|
||||||
use crate::{AuthIndex, ExodusHash, ExodusError};
|
|
||||||
|
|
||||||
impl FlexibleRoundOptimizedSchnorrThresholdSignature<AuthIndex, ExodusError> for NetworkCurve {
|
|
||||||
type Packages<'a> = BTreeMap<AuthIndex, &'a [u8]>;
|
|
||||||
type NoncePackages<'a> = BTreeMap<AuthIndex, (&'a [u8], &'a [u8])>;
|
|
||||||
|
|
||||||
fn header_bytes_len(&self) -> usize { 5 }
|
|
||||||
|
|
||||||
fn signature_bytes_len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
NetworkCurve::Secp256k1 => 64,
|
|
||||||
NetworkCurve::Ed25519 => 64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn element_bytes_len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
NetworkCurve::Secp256k1 => 33,
|
|
||||||
NetworkCurve::Ed25519 => 32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scalar_bytes_len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
NetworkCurve::Secp256k1 => 32,
|
|
||||||
NetworkCurve::Ed25519 => 32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_signature_share(
|
|
||||||
&self,
|
|
||||||
index: AuthIndex,
|
|
||||||
participants_iter: impl Iterator<Item = AuthIndex>,
|
|
||||||
signature_share_bytes: &[u8],
|
|
||||||
binding_factor_bytes: &[u8],
|
|
||||||
nonce_hiding_bytes: &[u8],
|
|
||||||
nonce_binding_bytes: &[u8],
|
|
||||||
group_commitment_bytes: &[u8],
|
|
||||||
verifying_share_bytes: &[u8],
|
|
||||||
verifying_key_bytes: &[u8],
|
|
||||||
message: &[u8],
|
|
||||||
) -> Result<(), ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
|
|
||||||
type InnerField = <InnerGroup as frost_core::Group>::Field;
|
|
||||||
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let identifier_set = participants_iter
|
|
||||||
.filter_map(|auth_index| {
|
|
||||||
let non_zero_index = Self::non_zero_index(auth_index).ok()?;
|
|
||||||
non_zero_index.try_into().ok()
|
|
||||||
})
|
|
||||||
.collect::<BTreeSet<f::Identifier>>();
|
|
||||||
|
|
||||||
let lambda_i =
|
|
||||||
frost_core::compute_lagrange_coefficient(&identifier_set, None, identifier)
|
|
||||||
.map_err(|err| match err {
|
|
||||||
f::Error::IncorrectNumberOfIdentifiers => ExodusError::IncorrectNumberOfIdentifiers,
|
|
||||||
f::Error::UnknownIdentifier => ExodusError::UnknownIdentifier,
|
|
||||||
f::Error::DuplicatedIdentifier => ExodusError::DuplicatedIdentifier,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let signature_share =
|
|
||||||
f::round2::SignatureShare::deserialize(signature_share_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let group_commitment_serialization =
|
|
||||||
<InnerGroup as frost_core::Group>::Serialization::try_from(group_commitment_bytes.as_ref())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let mut group_commitment_element =
|
|
||||||
<InnerGroup as frost_core::Group>::deserialize(&group_commitment_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_factor_serialization =
|
|
||||||
<InnerField as frost_core::Field>::Serialization::try_from(binding_factor_bytes.as_ref())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_factor_scalar =
|
|
||||||
<InnerField as frost_core::Field>::deserialize(&binding_factor_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let verifying_share =
|
|
||||||
f::keys::VerifyingShare::deserialize(verifying_share_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let verifying_key = f::VerifyingKey::deserialize(verifying_key_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let hiding_element_serialization =
|
|
||||||
<InnerGroup as frost_core::Group>::Serialization::try_from(nonce_hiding_bytes.as_ref())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let hiding_element =
|
|
||||||
<InnerGroup as frost_core::Group>::deserialize(&hiding_element_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_element_serialization =
|
|
||||||
<InnerGroup as frost_core::Group>::Serialization::try_from(nonce_binding_bytes.as_ref())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_element =
|
|
||||||
<InnerGroup as frost_core::Group>::deserialize(&binding_element_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let mut r_i_element = hiding_element + (binding_element * binding_factor_scalar);
|
|
||||||
|
|
||||||
let group_commitment_wrapper =
|
|
||||||
frost_core::GroupCommitment::<Ciphersuite>::from_element(group_commitment_element);
|
|
||||||
|
|
||||||
if !group_commitment_wrapper.is_normalized() {
|
|
||||||
r_i_element = -r_i_element;
|
|
||||||
group_commitment_element = -group_commitment_element;
|
|
||||||
}
|
|
||||||
|
|
||||||
let challenge = <Ciphersuite as frost_core::Ciphersuite>::challenge(
|
|
||||||
&group_commitment_element,
|
|
||||||
&verifying_key,
|
|
||||||
message,
|
|
||||||
).map_err(|err| match err {
|
|
||||||
f::Error::SerializationError => ExodusError::SerializationError,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let commitment_share =
|
|
||||||
frost_core::round1::GroupCommitmentShare::from_element(r_i_element);
|
|
||||||
|
|
||||||
signature_share.verify(
|
|
||||||
identifier,
|
|
||||||
&commitment_share,
|
|
||||||
&verifying_share,
|
|
||||||
lambda_i,
|
|
||||||
&challenge,
|
|
||||||
).map_err(|err| match err {
|
|
||||||
f::Error::InvalidSignatureShare { .. } => {
|
|
||||||
ExodusError::InvalidSignatureShare
|
|
||||||
},
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accumulate_signature_scalar(
|
|
||||||
&self,
|
|
||||||
signature_scalar_bytes: &[u8],
|
|
||||||
signature_share_bytes: &[u8],
|
|
||||||
) -> Result<Vec<u8>, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
|
|
||||||
type InnerField = <InnerGroup as frost_core::Group>::Field;
|
|
||||||
|
|
||||||
let signature_scalar_serialization =
|
|
||||||
<InnerField as frost_core::Field>::Serialization::try_from(signature_scalar_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let mut signature_scalar =
|
|
||||||
<InnerField as frost_core::Field>::deserialize(&signature_scalar_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let signature_share_serialization =
|
|
||||||
<InnerField as frost_core::Field>::Serialization::try_from(signature_share_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let signature_share =
|
|
||||||
<InnerField as frost_core::Field>::deserialize(&signature_share_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
signature_scalar += signature_share;
|
|
||||||
|
|
||||||
Ok(<InnerField as frost_core::Field>::serialize(&signature_scalar).to_vec())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_signature_valid(
|
|
||||||
&self,
|
|
||||||
verifying_key_bytes: &[u8],
|
|
||||||
r_element_bytes: &[u8],
|
|
||||||
z_scalar_bytes: &[u8],
|
|
||||||
message: &[u8],
|
|
||||||
) -> Result<(), ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
|
|
||||||
type InnerField = <InnerGroup as frost_core::Group>::Field;
|
|
||||||
|
|
||||||
let verifying_key = f::VerifyingKey::deserialize(verifying_key_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let r_element_serialization =
|
|
||||||
<InnerGroup as frost_core::Group>::Serialization::try_from(r_element_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let r_element =
|
|
||||||
<InnerGroup as frost_core::Group>::deserialize(&r_element_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let z_scalar_serialization =
|
|
||||||
<InnerField as frost_core::Field>::Serialization::try_from(z_scalar_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let z_scalar =
|
|
||||||
<InnerField as frost_core::Field>::deserialize(&z_scalar_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let signature = f::Signature::new(r_element, z_scalar);
|
|
||||||
|
|
||||||
verifying_key.verify(message, &signature)
|
|
||||||
.map_err(|_| ExodusError::InvalidSignature)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_nonce_commitment<R: CryptoRng + RngCore>(
|
|
||||||
&self,
|
|
||||||
secret_key_package_bytes: &[u8],
|
|
||||||
mut rng: R
|
|
||||||
) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let signing_share =
|
|
||||||
f::keys::KeyPackage::deserialize(secret_key_package_bytes)
|
|
||||||
.map(|key_package| *key_package.signing_share())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let (signing_nonce, signing_commitment) =
|
|
||||||
f::round1::commit(&signing_share, &mut rng);
|
|
||||||
|
|
||||||
let signing_nonce_bytes = signing_nonce.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let hiding_nonce_bytes = signing_commitment.hiding()
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let binding_nonce_bytes = signing_commitment.binding()
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
Ok((signing_nonce_bytes, hiding_nonce_bytes, binding_nonce_bytes))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_group_commitment(
|
|
||||||
&self,
|
|
||||||
authority_index: AuthIndex,
|
|
||||||
nonce_commitments_bytes: &Self::NoncePackages<'_>,
|
|
||||||
verifying_key_bytes: &[u8],
|
|
||||||
message: &[u8],
|
|
||||||
) -> Result<(Vec<u8>, Vec<u8>, Vec<ExodusHash>, ExodusHash), ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
|
|
||||||
type InnerField = <InnerGroup as frost_core::Group>::Field;
|
|
||||||
|
|
||||||
let verifying_key = f::VerifyingKey::deserialize(verifying_key_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let signing_commitments = nonce_commitments_bytes
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(&index, &(hiding_bytes, binding_bytes))| {
|
|
||||||
let non_zero_index = Self::non_zero_index(index).ok()?;
|
|
||||||
let identifier = non_zero_index.try_into().ok()?;
|
|
||||||
|
|
||||||
let hiding_serialization = <InnerGroup as frost_core::Group>::Serialization::try_from(hiding_bytes.as_ref()).ok()?;
|
|
||||||
let hiding_nonce = f::round1::NonceCommitment::deserialize(&hiding_serialization).ok()?;
|
|
||||||
|
|
||||||
let binding_serialization = <InnerGroup as frost_core::Group>::Serialization::try_from((*binding_bytes).as_ref()).ok()?;
|
|
||||||
let binding_nonce = f::round1::NonceCommitment::deserialize(&binding_serialization).ok()?;
|
|
||||||
|
|
||||||
let commitments = f::round1::SigningCommitments::new(hiding_nonce, binding_nonce);
|
|
||||||
|
|
||||||
Some((identifier, commitments))
|
|
||||||
})
|
|
||||||
.collect::<BTreeMap<_, _>>();
|
|
||||||
|
|
||||||
if nonce_commitments_bytes.len() != signing_commitments.len() {
|
|
||||||
return Err(ExodusError::InvalidParticipantId);
|
|
||||||
}
|
|
||||||
|
|
||||||
let signing_package = f::SigningPackage::new(signing_commitments, message);
|
|
||||||
|
|
||||||
let binding_factors_list_wrapped =
|
|
||||||
frost_core::compute_binding_factor_list::<Ciphersuite>(&signing_package, &verifying_key, &[])
|
|
||||||
.map_err(|err| match err {
|
|
||||||
f::Error::SerializationError => ExodusError::SerializationError,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let group_commitment =
|
|
||||||
frost_core::compute_group_commitment::<Ciphersuite>(&signing_package, &binding_factors_list_wrapped)
|
|
||||||
.map_err(|err| match err {
|
|
||||||
f::Error::IdentityCommitment => ExodusError::IdentityCommitment,
|
|
||||||
f::Error::UnknownIdentifier => ExodusError::UnknownIdentifier,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let binding_factors_list = nonce_commitments_bytes
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(&index, _)| {
|
|
||||||
let non_zero_index = Self::non_zero_index(index).ok()?;
|
|
||||||
let identifier = non_zero_index.try_into().ok()?;
|
|
||||||
|
|
||||||
let binding_factor = binding_factors_list_wrapped
|
|
||||||
.get(&identifier)
|
|
||||||
.map(|binding_factor| {
|
|
||||||
let fixed_bytes =
|
|
||||||
<InnerField as frost_core::Field>::Serialization::try_from(
|
|
||||||
binding_factor.serialize().as_slice()
|
|
||||||
).ok()?;
|
|
||||||
<InnerField as frost_core::Field>::deserialize(&fixed_bytes).ok()
|
|
||||||
})
|
|
||||||
.flatten()?;
|
|
||||||
|
|
||||||
Some((identifier, binding_factor))
|
|
||||||
})
|
|
||||||
.collect::<BTreeMap<_, _>>();
|
|
||||||
|
|
||||||
if nonce_commitments_bytes.len() != binding_factors_list.len() {
|
|
||||||
return Err(ExodusError::InvalidParticipantId);
|
|
||||||
}
|
|
||||||
|
|
||||||
let binding_factor_tree = Self::build_merkle_tree(
|
|
||||||
&binding_factors_list,
|
|
||||||
|binding_factor| {
|
|
||||||
let serialized = <InnerField as frost_core::Field>::serialize(binding_factor);
|
|
||||||
Ok(serialized.as_ref().to_vec())
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (self_binding_factor_bytes, binding_factors_proof) =
|
|
||||||
Self::generate_merkle_proof_from_tree(
|
|
||||||
&binding_factors_list,
|
|
||||||
&binding_factor_tree,
|
|
||||||
authority_index,
|
|
||||||
|binding_factor| {
|
|
||||||
let serialized = <InnerField as frost_core::Field>::serialize(binding_factor);
|
|
||||||
Ok(serialized.as_ref().to_vec())
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let group_commitment_bytes =
|
|
||||||
<InnerGroup as frost_core::Group>::serialize(&group_commitment.to_element())
|
|
||||||
.map(|serialization| serialization.to_vec())
|
|
||||||
.map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let binding_factors_root = binding_factor_tree
|
|
||||||
.last()
|
|
||||||
.cloned()
|
|
||||||
.ok_or(ExodusError::IncorrectNumberOfPackages)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
group_commitment_bytes,
|
|
||||||
self_binding_factor_bytes,
|
|
||||||
binding_factors_proof,
|
|
||||||
binding_factors_root,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_signing_package(
|
|
||||||
&self,
|
|
||||||
nonce_commitments_bytes: &Self::NoncePackages<'_>,
|
|
||||||
message: &[u8],
|
|
||||||
) -> Result<Vec<u8>, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
type InnerGroup = <Ciphersuite as frost_core::Ciphersuite>::Group;
|
|
||||||
|
|
||||||
let signing_commitments = nonce_commitments_bytes
|
|
||||||
.iter()
|
|
||||||
.map(|(&index, &(hiding_bytes, binding_bytes))| {
|
|
||||||
let identifier = Self::non_zero_index(index)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let hiding_serialization =
|
|
||||||
<InnerGroup as frost_core::Group>::Serialization::try_from(hiding_bytes.as_ref())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let hiding_nonce =
|
|
||||||
f::round1::NonceCommitment::deserialize(&hiding_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_serialization =
|
|
||||||
<InnerGroup as frost_core::Group>::Serialization::try_from((*binding_bytes).as_ref())
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_nonce =
|
|
||||||
f::round1::NonceCommitment::deserialize(&binding_serialization)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let commitments = f::round1::SigningCommitments::new(hiding_nonce, binding_nonce);
|
|
||||||
|
|
||||||
Ok((identifier, commitments))
|
|
||||||
})
|
|
||||||
.collect::<Result<BTreeMap<f::Identifier, f::round1::SigningCommitments>, ExodusError>>()?;
|
|
||||||
|
|
||||||
f::SigningPackage::new(signing_commitments, message)
|
|
||||||
.serialize()
|
|
||||||
.map_err(|_| ExodusError::SerializationError)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn partial_sign_message(
|
|
||||||
&self,
|
|
||||||
signing_package_bytes: &[u8],
|
|
||||||
signer_nonces_bytes: &[u8],
|
|
||||||
secret_share_bytes: &[u8],
|
|
||||||
) -> Result<Vec<u8>, ExodusError> {
|
|
||||||
with_ciphersuite!(self, |f| {
|
|
||||||
let signing_package = f::SigningPackage::deserialize(signing_package_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let signer_nonces = f::round1::SigningNonces::deserialize(signer_nonces_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let secret_share = f::keys::KeyPackage::deserialize(secret_share_bytes)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let signature_shares =
|
|
||||||
f::round2::sign(&signing_package, &signer_nonces, &secret_share)
|
|
||||||
.map_err(|err| match err {
|
|
||||||
f::Error::IncorrectNumberOfCommitments => ExodusError::IncorrectNumberOfCommitments,
|
|
||||||
f::Error::MissingCommitment => ExodusError::MissingCommitment,
|
|
||||||
f::Error::IncorrectCommitment => ExodusError::IncorrectCommitment,
|
|
||||||
f::Error::SerializationError => ExodusError::SerializationError,
|
|
||||||
f::Error::DuplicatedIdentifier => ExodusError::DuplicatedIdentifier,
|
|
||||||
f::Error::UnknownIdentifier => ExodusError::UnknownIdentifier,
|
|
||||||
f::Error::IncorrectNumberOfIdentifiers => ExodusError::IncorrectNumberOfIdentifiers,
|
|
||||||
_ => ExodusError::Unknown,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(signature_shares.serialize())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
use sp_std::{collections::btree_map::BTreeMap, vec::Vec, result::Result};
|
|
||||||
|
|
||||||
use frost_core::{Ciphersuite, Group, Identifier};
|
|
||||||
use ghost_helpers::SubstrateBlake2Hasher;
|
|
||||||
use ghost_traits::{
|
|
||||||
exodus::{MerkleTreeBuilder, IdentifierConverter},
|
|
||||||
hashing::GhostHasher,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{AuthIndex, ExodusError, NetworkCurve};
|
|
||||||
|
|
||||||
impl MerkleTreeBuilder<AuthIndex, ExodusError> for NetworkCurve {
|
|
||||||
type Hash = <SubstrateBlake2Hasher as GhostHasher>::Hash;
|
|
||||||
|
|
||||||
fn build_merkle_tree<C, V, F>(
|
|
||||||
values: &BTreeMap<Identifier<C>, V>,
|
|
||||||
serialize_fn: F,
|
|
||||||
) -> Result<Vec<Self::Hash>, ExodusError>
|
|
||||||
where
|
|
||||||
C: frost_core::Ciphersuite,
|
|
||||||
<<C as Ciphersuite>::Group as Group>::Field: frost_core::Field,
|
|
||||||
F: Fn(&V) -> Result<Vec<u8>, ExodusError>
|
|
||||||
{
|
|
||||||
use ghost_helpers::merkle_tree::generate_tree;
|
|
||||||
|
|
||||||
type CField<C> = <<C as Ciphersuite>::Group as Group>::Field;
|
|
||||||
|
|
||||||
let max_index = values.keys()
|
|
||||||
.next_back()
|
|
||||||
.and_then(|id| {
|
|
||||||
let id_scalar = id.to_scalar();
|
|
||||||
let id_bytes = <CField<C> as frost_core::Field>::serialize(&id_scalar);
|
|
||||||
Self::convert_identifier_to_index(id_bytes.as_ref()).ok()
|
|
||||||
})
|
|
||||||
.ok_or(ExodusError::IncorrectNumberOfIdentifiers)?;
|
|
||||||
|
|
||||||
generate_tree::<SubstrateBlake2Hasher, _, _, _, _>(
|
|
||||||
max_index,
|
|
||||||
values.iter(),
|
|
||||||
|(identifier, value)| {
|
|
||||||
let id_scalar = identifier.to_scalar();
|
|
||||||
let id_bytes = <CField<C> as frost_core::Field>::serialize(&id_scalar);
|
|
||||||
|
|
||||||
let index = Self::convert_identifier_to_index(id_bytes.as_ref())?;
|
|
||||||
let value_bytes = serialize_fn(value).map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
// NOTE: revisit
|
|
||||||
// Is it possible to use [u8; CONSTANT] where constant is
|
|
||||||
// purely dependant on provided generic.
|
|
||||||
let mut preimage = Vec::<u8>::with_capacity(id_bytes.as_ref().len() + value_bytes.len());
|
|
||||||
preimage.extend_from_slice(id_bytes.as_ref());
|
|
||||||
preimage.extend_from_slice(value_bytes.as_ref());
|
|
||||||
|
|
||||||
Ok((index as usize, preimage))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_merkle_proof_from_tree<C, V, F>(
|
|
||||||
values: &BTreeMap<Identifier<C>, V>,
|
|
||||||
merkle_tree: &[Self::Hash],
|
|
||||||
authority_index: AuthIndex,
|
|
||||||
serialize_fn: F,
|
|
||||||
) -> Result<(Vec<u8>, Vec<Self::Hash>), ExodusError>
|
|
||||||
where
|
|
||||||
C: frost_core::Ciphersuite,
|
|
||||||
<<C as Ciphersuite>::Group as Group>::Field: frost_core::Field,
|
|
||||||
F: Fn(&V) -> Result<Vec<u8>, ExodusError>
|
|
||||||
{
|
|
||||||
use ghost_helpers::merkle_tree::generate_proof;
|
|
||||||
|
|
||||||
type CField<C> = <<C as Ciphersuite>::Group as Group>::Field;
|
|
||||||
|
|
||||||
let non_zero_index = Self::non_zero_index(authority_index)?;
|
|
||||||
let identifier = Identifier::try_from(non_zero_index)
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let value = values.get(&identifier).ok_or(ExodusError::InvalidParticipantId)?;
|
|
||||||
let value_bytes = serialize_fn(&value).map_err(|_| ExodusError::SerializationError)?;
|
|
||||||
|
|
||||||
let max_index = values.keys()
|
|
||||||
.next_back()
|
|
||||||
.and_then(|id| {
|
|
||||||
let id_scalar = id.to_scalar();
|
|
||||||
let id_bytes = <CField<C> as frost_core::Field>::serialize(&id_scalar);
|
|
||||||
Self::convert_identifier_to_index(id_bytes.as_ref()).ok()
|
|
||||||
})
|
|
||||||
.ok_or(ExodusError::IncorrectNumberOfIdentifiers)?;
|
|
||||||
|
|
||||||
let proof = generate_proof::<SubstrateBlake2Hasher, _>(
|
|
||||||
merkle_tree,
|
|
||||||
max_index,
|
|
||||||
authority_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((value_bytes, proof))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_merkle_proof(
|
|
||||||
&self,
|
|
||||||
values: &[u8],
|
|
||||||
merkle_proof: &[Self::Hash],
|
|
||||||
merkle_root: Self::Hash,
|
|
||||||
authority_index: AuthIndex,
|
|
||||||
) -> Result<(), ExodusError> {
|
|
||||||
use ghost_helpers::merkle_tree::verify_tree_proof;
|
|
||||||
|
|
||||||
let preimage = with_ciphersuite!(self, |f| {
|
|
||||||
let non_zero_index = Self::non_zero_index(authority_index)?;
|
|
||||||
let identifier = f::Identifier::try_from(non_zero_index)
|
|
||||||
.map_err(|_| ExodusError::InvalidParticipantId)?;
|
|
||||||
|
|
||||||
let mut preimage = identifier.serialize();
|
|
||||||
preimage.extend_from_slice(values);
|
|
||||||
|
|
||||||
Ok(preimage)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let is_verified = verify_tree_proof::<SubstrateBlake2Hasher, _>(
|
|
||||||
&preimage,
|
|
||||||
merkle_proof,
|
|
||||||
merkle_root,
|
|
||||||
authority_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
is_verified.then(|| ()).ok_or(ExodusError::InvalidMerkleProof)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
#[macro_export]
|
|
||||||
macro_rules! with_ciphersuite {
|
|
||||||
($curve:expr, |$alias:ident| $body:block) => {
|
|
||||||
match $curve {
|
|
||||||
NetworkCurve::Secp256k1 => {
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use frost_secp256k1_tr as $alias;
|
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use frost_secp256k1_tr::Secp256K1Sha256TR as Ciphersuite;
|
|
||||||
|
|
||||||
use $alias::keys::EvenY;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
trait UniversalNormalizer {
|
|
||||||
fn normalize_package(self) -> Self;
|
|
||||||
fn is_normalized(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: EvenY> UniversalNormalizer for T {
|
|
||||||
#[inline]
|
|
||||||
fn normalize_package(self) -> Self {
|
|
||||||
self.into_even_y(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_normalized(&self) -> bool {
|
|
||||||
self.has_even_y()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$body
|
|
||||||
},
|
|
||||||
NetworkCurve::Ed25519 => {
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use frost_ed25519 as $alias;
|
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use frost_ed25519::Ed25519Sha512 as Ciphersuite;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
trait UniversalNormalizer {
|
|
||||||
fn normalize_package(self) -> Self;
|
|
||||||
fn is_normalized(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> UniversalNormalizer for T {
|
|
||||||
#[inline]
|
|
||||||
fn normalize_package(self) -> Self { self }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_normalized(&self) -> bool { true }
|
|
||||||
}
|
|
||||||
|
|
||||||
$body
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod converter;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod merkle;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod dkg;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod ecdh;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod frost;
|
|
||||||
@ -1,216 +0,0 @@
|
|||||||
#![cfg(any(test, feature = "runtime-benchmarks"))]
|
|
||||||
|
|
||||||
use frame_system::EnsureRoot;
|
|
||||||
use frame_support::{
|
|
||||||
derive_impl, parameter_types,
|
|
||||||
traits::{ConstU32, ConstU64},
|
|
||||||
};
|
|
||||||
use crate as ghost_exodus;
|
|
||||||
|
|
||||||
use pallet_session::historical as pallet_session_historical;
|
|
||||||
|
|
||||||
use sp_runtime::{
|
|
||||||
BuildStorage, curve::PiecewiseLinear, traits::ConvertInto,
|
|
||||||
testing::{TestXt, UintAuthorityId},
|
|
||||||
};
|
|
||||||
|
|
||||||
use sp_staking::SessionIndex;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use ghost_helpers::networks::{NetworkInitiationBuilder, NetworkDataBuilder};
|
|
||||||
|
|
||||||
pub type MockedBalance = u64;
|
|
||||||
pub type MockedNetworkId = u64;
|
|
||||||
type Block = frame_system::mocking::MockBlock<Runtime>;
|
|
||||||
|
|
||||||
frame_support::construct_runtime!(
|
|
||||||
pub enum Runtime {
|
|
||||||
System: frame_system,
|
|
||||||
Session: pallet_session,
|
|
||||||
Historical: pallet_session_historical,
|
|
||||||
Balances: pallet_balances,
|
|
||||||
Networks: ghost_networks,
|
|
||||||
Exodus: ghost_exodus,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
|
||||||
impl frame_system::Config for Runtime {
|
|
||||||
type Block = Block;
|
|
||||||
type AccountData = pallet_balances::AccountData<MockedBalance>;
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub static FixedAuthorities: Vec<u64> = vec![0, 1, 2, 3];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestSessionManager;
|
|
||||||
impl pallet_session::SessionManager<u64> for TestSessionManager {
|
|
||||||
fn new_session(_new_index: SessionIndex) -> Option<Vec<u64>> {
|
|
||||||
Some(FixedAuthorities::get())
|
|
||||||
}
|
|
||||||
fn end_session(_: SessionIndex) {}
|
|
||||||
fn start_session(_: SessionIndex) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_session::historical::SessionManager<u64, u64> for TestSessionManager {
|
|
||||||
fn new_session(_new_index: SessionIndex) -> Option<Vec<(u64, u64)>> {
|
|
||||||
Some(FixedAuthorities::get().iter().map(|l| (*l, *l)).collect())
|
|
||||||
}
|
|
||||||
fn end_session(_: SessionIndex) {}
|
|
||||||
fn start_session(_: SessionIndex) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub const Period: u64 = 1;
|
|
||||||
pub const Offset: u64 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_session::Config for Runtime {
|
|
||||||
type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
|
|
||||||
type SessionManager =
|
|
||||||
pallet_session::historical::NoteHistoricalRoot<Runtime, TestSessionManager>;
|
|
||||||
type SessionHandler = (Exodus,);
|
|
||||||
type ValidatorId = u64;
|
|
||||||
type ValidatorIdOf = ConvertInto;
|
|
||||||
type Keys = UintAuthorityId;
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
|
|
||||||
type WeightInfo = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pallet_session::historical::Config for Runtime {
|
|
||||||
type FullIdentification = u64;
|
|
||||||
type FullIdentificationOf = ConvertInto;
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub const MaxNetworks: u32 = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ghost_networks::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
type Currency = Balances;
|
|
||||||
type NetworkId = MockedNetworkId;
|
|
||||||
type RegisterOrigin = EnsureRoot<Self::AccountId>;
|
|
||||||
type UpdateOrigin = EnsureRoot<Self::AccountId>;
|
|
||||||
type RemoveOrigin = EnsureRoot<Self::AccountId>;
|
|
||||||
type MaxNetworks = MaxNetworks;
|
|
||||||
type WeightInfo = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
pallet_staking_reward_curve::build! {
|
|
||||||
const REWARD_CURVE: PiecewiseLinear<'static> = curve!(
|
|
||||||
min_inflation: 0_006_000,
|
|
||||||
max_inflation: 1_000_000,
|
|
||||||
ideal_stake: 0_690_000,
|
|
||||||
falloff: 0_050_000,
|
|
||||||
max_piece_count: 100,
|
|
||||||
test_precision: 0_005_000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_types! {
|
|
||||||
pub static ExistentialDeposit: u64 = 2;
|
|
||||||
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
|
|
||||||
pub const HistoryDepth: u32 = 10;
|
|
||||||
pub const EpochDuration: u64 = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
|
||||||
impl pallet_balances::Config for Runtime {
|
|
||||||
type ExistentialDeposit = ExistentialDeposit;
|
|
||||||
type MaxReserves = ConstU32<2>;
|
|
||||||
type AccountStore = System;
|
|
||||||
type WeightInfo = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ghost_exodus::Config for Runtime {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
type AuthorityId = UintAuthorityId;
|
|
||||||
|
|
||||||
type ValidatorSet = Historical;
|
|
||||||
type Currency = Balances;
|
|
||||||
type NetworkDataHandler = Networks;
|
|
||||||
type BlockNumberProvider = System;
|
|
||||||
type DisabledValidators = Session;
|
|
||||||
|
|
||||||
type UnsignedPriority = ConstU64<{ 1 << 20 }>;
|
|
||||||
type UnsignedLongevity = ConstU64<1>;
|
|
||||||
type MaxAuthorities = ConstU32<10>;
|
|
||||||
type MaxAuthoritiesChunks = ConstU32<1>;
|
|
||||||
|
|
||||||
type DkgRoundPeriod = ConstU64<100>;
|
|
||||||
type RemovalLimit = ConstU32<20>;
|
|
||||||
type MaxCursorLen = ConstU32<20>;
|
|
||||||
|
|
||||||
type WeightInfo = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Extrinsic<Call> = TestXt<Call, ()>;
|
|
||||||
|
|
||||||
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
|
|
||||||
where
|
|
||||||
RuntimeCall: From<LocalCall>,
|
|
||||||
{
|
|
||||||
type OverarchingCall = RuntimeCall;
|
|
||||||
type Extrinsic = Extrinsic<RuntimeCall>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn new_test_ext_benchmarks() -> sp_io::TestExternalities {
|
|
||||||
let t = frame_system::GenesisConfig::<Runtime>::default()
|
|
||||||
.build_storage()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
sp_io::TestExternalities::new(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub fn new_test_ext() -> (
|
|
||||||
sp_io::TestExternalities,
|
|
||||||
std::sync::Arc<parking_lot::RwLock<sp_core::offchain::testing::PoolState>>,
|
|
||||||
) {
|
|
||||||
let mut t = frame_system::GenesisConfig::<Runtime>::default()
|
|
||||||
.build_storage()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let network_data = NetworkDataBuilder::default()
|
|
||||||
.with_block_deviation(69)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let networks = vec![
|
|
||||||
NetworkInitiationBuilder::<MockedNetworkId, u64>::default()
|
|
||||||
.with_network_id(420)
|
|
||||||
.with_gatekeeper_amount(0)
|
|
||||||
.with_network_data(network_data)
|
|
||||||
.build(),
|
|
||||||
];
|
|
||||||
|
|
||||||
ghost_networks::GenesisConfig::<Runtime> { networks }
|
|
||||||
.assimilate_storage(&mut t)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut ext = sp_io::TestExternalities::new(t);
|
|
||||||
|
|
||||||
ext.execute_with(|| {
|
|
||||||
for &id in FixedAuthorities::get().iter() {
|
|
||||||
System::inc_providers(&id);
|
|
||||||
assert_eq!(
|
|
||||||
Session::set_keys(RuntimeOrigin::signed(id), id.into(), vec![],),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let (offchain, _) = sp_core::offchain::testing::TestOffchainExt::with_offchain_db(ext.offchain_db());
|
|
||||||
let (tx_pool, tx_pool_state) = sp_core::offchain::testing::TestTransactionPoolExt::new();
|
|
||||||
|
|
||||||
ext.register_extension(sp_core::offchain::OffchainDbExt::new(offchain.clone()));
|
|
||||||
ext.register_extension(sp_core::offchain::OffchainWorkerExt::new(offchain));
|
|
||||||
ext.register_extension(sp_core::offchain::TransactionPoolExt::new(tx_pool));
|
|
||||||
|
|
||||||
ext.execute_with(|| System::set_block_number(1));
|
|
||||||
|
|
||||||
(ext, tx_pool_state)
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
use codec::{Encode, Decode};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_runtime::RuntimeDebug;
|
|
||||||
|
|
||||||
use frame_support::{BoundedVec, traits::ConstU32};
|
|
||||||
|
|
||||||
use crate::{ExodusHash, ELEMENT_MAX_BYTES};
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
|
||||||
pub struct ConsensusState<Bitmap> {
|
|
||||||
pub element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
|
||||||
pub participants: Bitmap,
|
|
||||||
pub merkle_root: ExodusHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Bitmap> ConsensusState<Bitmap> {
|
|
||||||
pub fn new(
|
|
||||||
element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
|
||||||
participants: Bitmap,
|
|
||||||
merkle_root: ExodusHash,
|
|
||||||
) -> Self {
|
|
||||||
Self { element_bytes, participants, merkle_root }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,295 +0,0 @@
|
|||||||
use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto};
|
|
||||||
use codec::{Encode, Decode};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use strum::EnumCount;
|
|
||||||
|
|
||||||
use frame_support::{BoundedVec, traits::ConstU32};
|
|
||||||
|
|
||||||
use ghost_helpers::networks::NetworkType;
|
|
||||||
use ghost_traits::bounded_bitmap::{
|
|
||||||
BoundedBitmapGenerator, BoundedBitmapReader, BoundedBitmapWriter,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{ELEMENT_MAX_BYTES, ExodusSession, DkgIndex};
|
|
||||||
|
|
||||||
type RotationSessions = BoundedVec<RotationSession, ConstU32<{ NetworkType::COUNT as u32 }>>;
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
|
||||||
pub struct ReadyDkgAuthorities<AuthoritiesBitmap> {
|
|
||||||
authorities_indexes: AuthoritiesBitmap,
|
|
||||||
dkg_index: DkgIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AuthoritiesBitmap> ReadyDkgAuthorities<AuthoritiesBitmap>
|
|
||||||
where
|
|
||||||
AuthoritiesBitmap: BoundedBitmapReader,
|
|
||||||
{
|
|
||||||
pub fn contains_index<N>(&self, index: N) -> bool
|
|
||||||
where N: UniqueSaturatedInto<usize>,
|
|
||||||
{
|
|
||||||
self.authorities_indexes.contains(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_indexes(&self) -> &AuthoritiesBitmap {
|
|
||||||
&self.authorities_indexes
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_dkg_index(&self) -> DkgIndex {
|
|
||||||
self.dkg_index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AuthoritiesBitmap, Block>
|
|
||||||
From<&PendingDkgAuthorities<AuthoritiesBitmap, Block>>
|
|
||||||
for ReadyDkgAuthorities<AuthoritiesBitmap>
|
|
||||||
where
|
|
||||||
Block: Copy,
|
|
||||||
AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
|
|
||||||
for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>,
|
|
||||||
{
|
|
||||||
fn from(pending: &PendingDkgAuthorities<AuthoritiesBitmap, Block>) -> Self {
|
|
||||||
Self {
|
|
||||||
authorities_indexes: pending.get_indexes().clone(),
|
|
||||||
dkg_index: pending.get_dkg_index(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
|
||||||
pub struct RotationSession {
|
|
||||||
pub exodus_session: ExodusSession,
|
|
||||||
pub network_type: NetworkType
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
|
||||||
pub struct PendingDkgAuthorities<AuthoritiesBitmap, Block> {
|
|
||||||
verifying_key: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
|
||||||
authorities_indexes: AuthoritiesBitmap,
|
|
||||||
rotation_sessions: RotationSessions,
|
|
||||||
dkg_state: DkgState<Block>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AuthoritiesBitmap, Block> PendingDkgAuthorities<AuthoritiesBitmap, Block>
|
|
||||||
where
|
|
||||||
Block: Copy,
|
|
||||||
AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
|
|
||||||
for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>,
|
|
||||||
{
|
|
||||||
pub fn new_dkg(&mut self, authorities_len: usize) {
|
|
||||||
self.authorities_indexes = AuthoritiesBitmap::empty(authorities_len);
|
|
||||||
self.dkg_state.restart();
|
|
||||||
self.rotation_sessions = Default::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_block(&mut self, block: Block) {
|
|
||||||
self.dkg_state.set_block(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_verifying_key(
|
|
||||||
&mut self,
|
|
||||||
verifying_key: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
|
||||||
) {
|
|
||||||
self.verifying_key = verifying_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nullify_indexes(&mut self) {
|
|
||||||
self.verifying_key = Default::default();
|
|
||||||
self.authorities_indexes.nullify();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn restart_dkg(&mut self) {
|
|
||||||
self.verifying_key = Default::default();
|
|
||||||
self.authorities_indexes.nullify();
|
|
||||||
self.dkg_state.restart();
|
|
||||||
self.rotation_sessions = Default::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_phase(&mut self) {
|
|
||||||
self.dkg_state.next_phase()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn intersect_indexes(&mut self, mask: &AuthoritiesBitmap) {
|
|
||||||
self.authorities_indexes &= mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dkg_pending(&self) -> bool {
|
|
||||||
self.dkg_state.is_dkg_pending()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dkg_vacant(&self) -> bool {
|
|
||||||
self.dkg_state.is_dkg_vacant()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_dkg_index(&self) -> DkgIndex {
|
|
||||||
self.dkg_state.get_dkg_index()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_block(&self) -> Block {
|
|
||||||
self.dkg_state.get_block()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_phase(&self) -> DkgPhase {
|
|
||||||
self.dkg_state.get_phase()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_verifying_key(
|
|
||||||
&self
|
|
||||||
) -> BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>> {
|
|
||||||
self.verifying_key.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_zero_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_first_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_second_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round2
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_third_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round3
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_fourth_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round4
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_fifth_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round5
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_sixth_phase(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Round6
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dkg_finalized(&self) -> bool {
|
|
||||||
self.dkg_state.get_phase() == DkgPhase::Finalized
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_index<N>(&self, index: N) -> bool
|
|
||||||
where N: UniqueSaturatedInto<usize>,
|
|
||||||
{
|
|
||||||
self.authorities_indexes.contains(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_index<N>(&mut self, i: N) -> Option<N>
|
|
||||||
where N: Copy + TryInto<usize>
|
|
||||||
{
|
|
||||||
self.authorities_indexes.insert(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_push_rotation_session(
|
|
||||||
&mut self,
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
network_type: NetworkType,
|
|
||||||
) -> Option<()> {
|
|
||||||
let rotation_session = RotationSession {
|
|
||||||
exodus_session,
|
|
||||||
network_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.rotation_sessions.try_push(rotation_session).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count_ones<N>(&self) -> N
|
|
||||||
where u32: UniqueSaturatedInto<N>,
|
|
||||||
{
|
|
||||||
self.authorities_indexes.count_ones()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn highest_bit<N>(&self) -> Option<N>
|
|
||||||
where usize: UniqueSaturatedInto<N>,
|
|
||||||
{
|
|
||||||
self.authorities_indexes.highest_bit()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_indexes(&self) -> &AuthoritiesBitmap {
|
|
||||||
&self.authorities_indexes
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_rotation_sessions(&self) -> impl Iterator<Item = &RotationSession> {
|
|
||||||
self.rotation_sessions.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
|
||||||
pub struct DkgState<Block> {
|
|
||||||
block: Block,
|
|
||||||
index: DkgIndex,
|
|
||||||
phase: DkgPhase,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: Copy> DkgState<Block> {
|
|
||||||
fn is_dkg_pending(&self) -> bool { self.phase.is_dkg_pending() }
|
|
||||||
fn is_dkg_vacant(&self) -> bool { self.phase.is_dkg_vacant() }
|
|
||||||
|
|
||||||
fn get_dkg_index(&self) -> DkgIndex { self.index }
|
|
||||||
fn get_phase(&self) -> DkgPhase { self.phase }
|
|
||||||
fn get_block(&self) -> Block { self.block }
|
|
||||||
|
|
||||||
fn restart(&mut self) {
|
|
||||||
self.phase = Default::default();
|
|
||||||
self.index = self.index.checked_add(1)
|
|
||||||
.unwrap_or_default();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_phase(&mut self) {
|
|
||||||
self.phase.next_phase()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_block(&mut self, block: Block) {
|
|
||||||
self.block = block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
|
||||||
pub enum DkgPhase {
|
|
||||||
#[default]
|
|
||||||
Vacant,
|
|
||||||
Round0,
|
|
||||||
Round1,
|
|
||||||
Round2,
|
|
||||||
Round3,
|
|
||||||
Round4,
|
|
||||||
Round5,
|
|
||||||
Round6,
|
|
||||||
Round7,
|
|
||||||
Finalized,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DkgPhase {
|
|
||||||
fn is_dkg_pending(&self) -> bool {
|
|
||||||
match &self {
|
|
||||||
Self::Finalized | Self::Round7 => false,
|
|
||||||
_ => true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_dkg_vacant(&self) -> bool {
|
|
||||||
match &self {
|
|
||||||
Self::Vacant => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_phase(&mut self) {
|
|
||||||
*self = match *self {
|
|
||||||
Self::Vacant => Self::Round0,
|
|
||||||
Self::Round0 => Self::Round1,
|
|
||||||
Self::Round1 => Self::Round2,
|
|
||||||
Self::Round2 => Self::Round3,
|
|
||||||
Self::Round3 => Self::Round4,
|
|
||||||
Self::Round4 => Self::Round5,
|
|
||||||
Self::Round5 => Self::Round6,
|
|
||||||
Self::Round6 => Self::Round7,
|
|
||||||
Self::Round7 => Self::Finalized,
|
|
||||||
Self::Finalized => Self::Finalized,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
use sp_std::vec::Vec;
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_runtime::RuntimeDebug;
|
|
||||||
use codec::{Encode, Decode};
|
|
||||||
|
|
||||||
use frame_support::BoundedVec;
|
|
||||||
use crate::ExodusError;
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, Ord, PartialOrd, RuntimeDebug, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct EncryptionData<T: crate::pallet::Config> {
|
|
||||||
pub ciphertext: BoundedVec<u8, crate::CiphertextMaxBytes<T>>,
|
|
||||||
pub nonce: [u8; crate::ENCRYPTION_NONCE_MAX_BYTES as usize],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: crate::pallet::Config> EncryptionData<T> {
|
|
||||||
pub fn try_new(
|
|
||||||
ciphertext_vec: Vec<u8>,
|
|
||||||
nonce_bytes: &[u8],
|
|
||||||
) -> Result<Self, ExodusError> {
|
|
||||||
if nonce_bytes.len() != crate::ENCRYPTION_NONCE_MAX_BYTES as usize {
|
|
||||||
return Err(ExodusError::EncryptionFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ciphertext =
|
|
||||||
BoundedVec::<u8, crate::CiphertextMaxBytes<T>>::try_from(ciphertext_vec)
|
|
||||||
.map_err(|_| ExodusError::EncryptionFailed)?;
|
|
||||||
|
|
||||||
let mut nonce = [0u8; crate::ENCRYPTION_NONCE_MAX_BYTES as usize];
|
|
||||||
nonce.copy_from_slice(nonce_bytes);
|
|
||||||
|
|
||||||
Ok(Self { ciphertext, nonce })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,732 +0,0 @@
|
|||||||
use scale_info::TypeInfo;
|
|
||||||
use codec::{Encode, Decode};
|
|
||||||
use sp_runtime::{
|
|
||||||
Perbill, RuntimeDebug, traits::UniqueSaturatedInto,
|
|
||||||
helpers_128bit::multiply_by_rational_with_rounding,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
EvmAddress, EvmBytes32, ExodusSession, RoastSession,
|
|
||||||
MAX_MESSAGE_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait EvmAbiEncode {
|
|
||||||
fn size(&self) -> usize;
|
|
||||||
fn abi_encode(&self, buffer: &mut [u8], cursor: &mut usize) -> Option<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_evm_encodable {
|
|
||||||
(numeric: $( $ty:ty ),* ) => {
|
|
||||||
$(
|
|
||||||
impl EvmAbiEncode for $ty {
|
|
||||||
#[inline(always)]
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
core::mem::size_of::<Self>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn abi_encode(&self, buffer: &mut [u8], cursor: &mut usize) -> Option<()> {
|
|
||||||
let val_len = self.size();
|
|
||||||
let padding = 32 - val_len;
|
|
||||||
|
|
||||||
let padding_slice = buffer.get_mut(*cursor..*cursor + padding)?;
|
|
||||||
padding_slice.fill(0);
|
|
||||||
*cursor += padding;
|
|
||||||
|
|
||||||
let value_slice = buffer.get_mut(*cursor..*cursor + val_len)?;
|
|
||||||
value_slice.copy_from_slice(&self.to_be_bytes());
|
|
||||||
*cursor += val_len;
|
|
||||||
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
|
|
||||||
(bytes_fixed: $( $ty:ty ),* ) => {
|
|
||||||
$(
|
|
||||||
impl EvmAbiEncode for $ty {
|
|
||||||
#[inline(always)]
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
<Self as AsRef<[u8]>>::as_ref(self).len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn abi_encode(&self, buffer: &mut [u8], cursor: &mut usize) -> Option<()> {
|
|
||||||
if self.size() > 32 { return None; }
|
|
||||||
|
|
||||||
let val_len = self.size();
|
|
||||||
let padding = 32 - val_len;
|
|
||||||
|
|
||||||
let padding_slice = buffer.get_mut(*cursor..*cursor + padding)?;
|
|
||||||
padding_slice.fill(0);
|
|
||||||
*cursor += padding;
|
|
||||||
|
|
||||||
let value_slice = buffer.get_mut(*cursor..*cursor + val_len)?;
|
|
||||||
value_slice.copy_from_slice(self.as_ref());
|
|
||||||
*cursor += val_len;
|
|
||||||
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
|
|
||||||
(const_array: $( [ $inner_ty:ty ; $len:expr ] ),* $(,)? ) => {
|
|
||||||
$(
|
|
||||||
impl EvmAbiEncode for [ $inner_ty ; $len ] {
|
|
||||||
#[inline(always)]
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
$len * 32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn abi_encode(&self, buffer: &mut [u8], cursor: &mut usize) -> Option<()> {
|
|
||||||
for item in self.iter() {
|
|
||||||
EvmAbiEncode::abi_encode(item, buffer, cursor)?;
|
|
||||||
}
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
|
|
||||||
(bytes_slice: ) => {
|
|
||||||
impl EvmAbiEncode for [u8] {
|
|
||||||
#[inline(always)]
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
let content_len = self.len();
|
|
||||||
let remainder = content_len % 32;
|
|
||||||
let alignment = if remainder == 0 { 0 } else { 32 - remainder };
|
|
||||||
32 + content_len + alignment
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn abi_encode(&self, buffer: &mut [u8], cursor: &mut usize) -> Option<()> {
|
|
||||||
if self.size() >= MAX_MESSAGE_SIZE as usize { return None; }
|
|
||||||
|
|
||||||
let pointer = cursor.checked_sub(4).map(|x| x.saturating_add(32))?;
|
|
||||||
let head_pointer = pointer as u32;
|
|
||||||
EvmAbiEncode::abi_encode(&head_pointer, buffer, cursor);
|
|
||||||
|
|
||||||
let content_len = self.len() as u32;
|
|
||||||
|
|
||||||
EvmAbiEncode::abi_encode(&content_len, buffer, cursor)?;
|
|
||||||
|
|
||||||
let value_slice = buffer.get_mut(*cursor..*cursor + self.len())?;
|
|
||||||
value_slice.copy_from_slice(self);
|
|
||||||
*cursor += self.len();
|
|
||||||
|
|
||||||
let remainder = self.len() % 32;
|
|
||||||
if remainder != 0 {
|
|
||||||
let alignment = 32 - remainder;
|
|
||||||
let alignment_slice = buffer.get_mut(*cursor..*cursor + alignment)?;
|
|
||||||
alignment_slice.fill(0);
|
|
||||||
*cursor += alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_evm_encodable!(numeric: u8, u16, u32, u64, u128);
|
|
||||||
impl_evm_encodable!(bytes_fixed: EvmAddress, EvmBytes32);
|
|
||||||
impl_evm_encodable!(const_array: [u128; 3], [u128; 2], [u32; 2], [u8; 2]);
|
|
||||||
impl_evm_encodable!(bytes_slice:);
|
|
||||||
|
|
||||||
macro_rules! abi_encode_evm_message {
|
|
||||||
($buffer:expr, $sig:expr, [ $( $val:expr ),* $(,)? ]) => {{
|
|
||||||
let mut cursor = 0;
|
|
||||||
let mut no_overflow = true;
|
|
||||||
|
|
||||||
let hash = sp_io::hashing::keccak_256($sig.as_bytes());
|
|
||||||
|
|
||||||
if let Some(selector_slice) = $buffer.get_mut(0..4) {
|
|
||||||
selector_slice.copy_from_slice(&hash[0..4]);
|
|
||||||
cursor += 4;
|
|
||||||
|
|
||||||
$(
|
|
||||||
if no_overflow {
|
|
||||||
if EvmAbiEncode::abi_encode($val, $buffer, &mut cursor).is_none() {
|
|
||||||
no_overflow = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
} else {
|
|
||||||
no_overflow = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
no_overflow.then(|| &$buffer[0..cursor])
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum GovernanceStatus {
|
|
||||||
ReserveDepositor = 0,
|
|
||||||
ReserveToken = 1,
|
|
||||||
LiquidityDepositor = 2,
|
|
||||||
LiquidityToken = 3,
|
|
||||||
RewardManager = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub enum GovernanceAction {
|
|
||||||
SetDistributor { distributor: EvmAddress },
|
|
||||||
SetWarmupPeriod { warmup_period: u128 },
|
|
||||||
UpdateGatekeeperAddress { new_gatekeeper: EvmAddress },
|
|
||||||
|
|
||||||
SetBounty { bounty: u128 },
|
|
||||||
AddPool { address: EvmAddress },
|
|
||||||
RemovePool { index: u128 },
|
|
||||||
SetAdjustment { rate: u128, target: u128, add: bool },
|
|
||||||
|
|
||||||
Enable { status: GovernanceStatus, address: EvmAddress, calculator: EvmAddress },
|
|
||||||
Disable { status: GovernanceStatus, address: EvmAddress },
|
|
||||||
ForfeitReserves { router: EvmAddress, liquidity: u128, destroyer_mode: bool },
|
|
||||||
RedeemReserves { router: EvmAddress, amount: u128 },
|
|
||||||
Withdraw { token: EvmAddress, amount: u128 },
|
|
||||||
AuditReserves,
|
|
||||||
|
|
||||||
Close { id: u128 },
|
|
||||||
Create {
|
|
||||||
market: [u128; 3],
|
|
||||||
terms: [u128; 2],
|
|
||||||
token: EvmAddress,
|
|
||||||
intervals: [u32; 2],
|
|
||||||
booleans: [bool; 2],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GovernanceAction {
|
|
||||||
pub fn get_message<'a>(
|
|
||||||
&self,
|
|
||||||
message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize],
|
|
||||||
) -> Option<&'a [u8]> {
|
|
||||||
match &self {
|
|
||||||
Self::SetDistributor { distributor } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"setDistributor(address)",
|
|
||||||
[distributor]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::SetWarmupPeriod { warmup_period } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"setWarmupPeriod(uint256)",
|
|
||||||
[warmup_period]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::UpdateGatekeeperAddress { new_gatekeeper } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"updateGatekeeperAddress(address)",
|
|
||||||
[new_gatekeeper]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::SetBounty { bounty } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"setBounty(uint256)",
|
|
||||||
[bounty]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::AddPool { address } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"addPool(address)",
|
|
||||||
[address]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::RemovePool { index } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"removePool(uint256)",
|
|
||||||
[index]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::SetAdjustment { rate, target, add } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"setAdjustment(uint256,uint256,bool)",
|
|
||||||
[rate, target, &(*add as u8)]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::Enable { status, address, calculator } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"enable(uint8,address,address)",
|
|
||||||
[&(*status as u8), address, calculator]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::Disable { status, address } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"disable(uint8,address)",
|
|
||||||
[&(*status as u8), address]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::ForfeitReserves { router, liquidity, destroyer_mode } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"forfeitReserves(address,uint256,bool)",
|
|
||||||
[router, liquidity, &(*destroyer_mode as u8)]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::RedeemReserves { router, amount } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"redeemReserve(address,uint256)",
|
|
||||||
[router, amount]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::Withdraw { token, amount } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"withdraw(address,uint256)",
|
|
||||||
[token, amount]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Self::AuditReserves => {
|
|
||||||
abi_encode_evm_message!(message_buffer, "auditReserves()", [])
|
|
||||||
},
|
|
||||||
Self::Close { id } => {
|
|
||||||
abi_encode_evm_message!(message_buffer, "close(uint256)", [id])
|
|
||||||
},
|
|
||||||
Self::Create { market, terms, token, intervals, booleans } => {
|
|
||||||
let booleans_u8 = [booleans[0] as u8, booleans[1] as u8];
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"create(uint256[3],uint256[2],address,uint32[2],bool[2])",
|
|
||||||
[market, terms, token, intervals, &booleans_u8]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub struct ExodusRequest<NetworkId, Balance> {
|
|
||||||
pub r#type: ExodusRequestType<NetworkId, Balance>,
|
|
||||||
pub next_roast_session: RoastSession,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub enum ExodusRequestType<NetworkId, Balance> {
|
|
||||||
EvmRotation {
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
public_key: EvmBytes32,
|
|
||||||
parity: u8,
|
|
||||||
},
|
|
||||||
UtxoRotation,
|
|
||||||
EvmBridgeOut {
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
network_id: NetworkId,
|
|
||||||
amount: Balance,
|
|
||||||
bounty: Perbill,
|
|
||||||
receiver: EvmAddress,
|
|
||||||
},
|
|
||||||
EvmGovernance {
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
network_id: NetworkId,
|
|
||||||
target: EvmAddress,
|
|
||||||
action: GovernanceAction,
|
|
||||||
},
|
|
||||||
UtxoBridgeOut { network_id: NetworkId },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkId, Balance> ExodusRequestType<NetworkId, Balance>
|
|
||||||
where
|
|
||||||
NetworkId: UniqueSaturatedInto<u64> + Copy,
|
|
||||||
Balance: UniqueSaturatedInto<u128> + Copy,
|
|
||||||
{
|
|
||||||
pub fn get_message<'a>(
|
|
||||||
&self,
|
|
||||||
message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize],
|
|
||||||
) -> Option<&'a [u8]> {
|
|
||||||
match &self {
|
|
||||||
ExodusRequestType::EvmRotation { exodus_session, public_key, parity } => {
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"rotate(uint256,bytes32,uint8)",
|
|
||||||
[exodus_session, public_key, parity]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ExodusRequestType::EvmGovernance { exodus_session, network_id, target, action } => {
|
|
||||||
let mut action_buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
|
||||||
let action_payload = action.get_message(&mut action_buffer)?;
|
|
||||||
|
|
||||||
let mut metadata_buffer = [0u8; 32];
|
|
||||||
|
|
||||||
let chain_id_u64: u64 = (*network_id).unique_saturated_into();
|
|
||||||
metadata_buffer[0..8].copy_from_slice(&chain_id_u64.to_be_bytes());
|
|
||||||
metadata_buffer[8..28].copy_from_slice(target.as_ref());
|
|
||||||
|
|
||||||
let packed_metadata = EvmBytes32::from(&metadata_buffer);
|
|
||||||
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"govern(uint256,uint256,bytes)",
|
|
||||||
[exodus_session, &packed_metadata, action_payload]
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ExodusRequestType::EvmBridgeOut { exodus_session, network_id, amount, bounty, receiver } => {
|
|
||||||
let mut buffer = [0u8; 32];
|
|
||||||
buffer[0..20].copy_from_slice(receiver.as_ref());
|
|
||||||
|
|
||||||
let chain_id_u64: u64 = (*network_id).unique_saturated_into();
|
|
||||||
buffer[20..28].copy_from_slice(&chain_id_u64.to_be_bytes());
|
|
||||||
|
|
||||||
let scaled_bounty = multiply_by_rational_with_rounding(
|
|
||||||
bounty.deconstruct() as u128,
|
|
||||||
1u128 << 32,
|
|
||||||
1_000_000_000,
|
|
||||||
sp_runtime::Rounding::Down
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let bounty_uint32 = scaled_bounty as u32;
|
|
||||||
buffer[28..32].copy_from_slice(&bounty_uint32.to_be_bytes());
|
|
||||||
|
|
||||||
let mut amount_buffer = [0u8; 32];
|
|
||||||
let amount_u128: u128 = (*amount).unique_saturated_into();
|
|
||||||
amount_buffer[16..32].copy_from_slice(&amount_u128.to_be_bytes());
|
|
||||||
|
|
||||||
let packed_metadata = EvmBytes32::from(buffer);
|
|
||||||
let amount_u256 = EvmBytes32::from(amount_buffer);
|
|
||||||
|
|
||||||
abi_encode_evm_message!(
|
|
||||||
message_buffer,
|
|
||||||
"recall(uint256,uint256,uint256)",
|
|
||||||
[exodus_session, &amount_u256, &packed_metadata]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkId, Balance> ExodusRequest<NetworkId, Balance>
|
|
||||||
where
|
|
||||||
NetworkId: UniqueSaturatedInto<u64> + Copy,
|
|
||||||
Balance: UniqueSaturatedInto<u128> + Copy,
|
|
||||||
{
|
|
||||||
pub fn get_message<'a>(
|
|
||||||
&self,
|
|
||||||
message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize],
|
|
||||||
) -> Option<&'a [u8]> {
|
|
||||||
self.r#type.get_message(message_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evm_rotation(
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
public_key: EvmBytes32,
|
|
||||||
parity: u8,
|
|
||||||
) -> Self {
|
|
||||||
let rotation = ExodusRequestType::EvmRotation {
|
|
||||||
exodus_session,
|
|
||||||
public_key,
|
|
||||||
parity,
|
|
||||||
};
|
|
||||||
Self::default_request_for(rotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evm_bridge_out(
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
network_id: NetworkId,
|
|
||||||
amount: Balance,
|
|
||||||
bounty: Perbill,
|
|
||||||
receiver: EvmAddress,
|
|
||||||
) -> Self {
|
|
||||||
let evm_bridge_out = ExodusRequestType::EvmBridgeOut {
|
|
||||||
exodus_session,
|
|
||||||
network_id,
|
|
||||||
amount,
|
|
||||||
bounty,
|
|
||||||
receiver,
|
|
||||||
};
|
|
||||||
Self::default_request_for(evm_bridge_out)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evm_governance(
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
network_id: NetworkId,
|
|
||||||
target: EvmAddress,
|
|
||||||
action: GovernanceAction,
|
|
||||||
) -> Self {
|
|
||||||
let evm_governance = ExodusRequestType::EvmGovernance {
|
|
||||||
exodus_session,
|
|
||||||
network_id,
|
|
||||||
target,
|
|
||||||
action,
|
|
||||||
};
|
|
||||||
Self::default_request_for(evm_governance)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_rotation(&self) -> bool {
|
|
||||||
match &self.r#type {
|
|
||||||
ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_request_for(
|
|
||||||
request_type: ExodusRequestType<NetworkId, Balance>,
|
|
||||||
) -> Self {
|
|
||||||
Self { r#type: request_type, next_roast_session: Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use sp_io::hashing::keccak_256;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rotation_message_valid() {
|
|
||||||
let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
|
||||||
let public_key = EvmBytes32::repeat_byte(0x11);
|
|
||||||
let session = 69u64;
|
|
||||||
|
|
||||||
let rotation_request = ExodusRequest::<u64, u128>::evm_rotation(
|
|
||||||
session,
|
|
||||||
public_key.clone(),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let calldata = rotation_request.get_message(&mut buffer).unwrap();
|
|
||||||
assert_eq!(calldata.len(), 100);
|
|
||||||
|
|
||||||
let expected_hash = keccak_256("rotate(uint256,bytes32,uint8)".as_bytes());
|
|
||||||
|
|
||||||
assert_eq!(calldata[0..4], expected_hash[0..4]);
|
|
||||||
assert_eq!(calldata[35], 0x45);
|
|
||||||
assert_eq!(&calldata[4..35], &[0u8; 31]);
|
|
||||||
assert_eq!(&calldata[36..68], public_key.as_ref());
|
|
||||||
assert_eq!(calldata[99], 1);
|
|
||||||
assert_eq!(&calldata[68..99], &[0u8; 31]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bridge_out_message_valid() {
|
|
||||||
let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
|
||||||
let receiver = EvmAddress::repeat_byte(0xAA);
|
|
||||||
let network_id = 420u64;
|
|
||||||
let session = 69u64;
|
|
||||||
|
|
||||||
let bridge_request = ExodusRequest::<u64, u128>::evm_bridge_out(
|
|
||||||
session,
|
|
||||||
network_id,
|
|
||||||
1_000_000u128,
|
|
||||||
sp_runtime::Perbill::from_percent(1),
|
|
||||||
receiver.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
let calldata = bridge_request.get_message(&mut buffer).unwrap();
|
|
||||||
assert_eq!(calldata.len(), 100);
|
|
||||||
|
|
||||||
let expected_hash = keccak_256("recall(uint256,uint256,uint256)".as_bytes());
|
|
||||||
assert_eq!(calldata[0..4], expected_hash[0..4]);
|
|
||||||
assert_eq!(calldata[35], 69);
|
|
||||||
assert_eq!(calldata[67], 0x40);
|
|
||||||
assert_eq!(calldata[66], 0x42);
|
|
||||||
assert_eq!(calldata[65], 0x0F);
|
|
||||||
|
|
||||||
let metadata_slot = &calldata[68..100];
|
|
||||||
assert_eq!(&metadata_slot[0..20], receiver.as_ref());
|
|
||||||
assert_eq!(metadata_slot[27], 0xA4);
|
|
||||||
assert_eq!(metadata_slot[26], 0x01);
|
|
||||||
|
|
||||||
assert_eq!(metadata_slot[31], 0x28);
|
|
||||||
assert_eq!(metadata_slot[30], 0x5C);
|
|
||||||
assert_eq!(metadata_slot[29], 0x8F);
|
|
||||||
assert_eq!(metadata_slot[28], 0x02);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_governance_message_valid() {
|
|
||||||
let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize];
|
|
||||||
let target = EvmAddress::repeat_byte(0xAA);
|
|
||||||
let network_id = 420u64;
|
|
||||||
let session = 69u64;
|
|
||||||
|
|
||||||
let actions = vec![
|
|
||||||
GovernanceAction::SetDistributor { distributor: EvmAddress::repeat_byte(0x11) },
|
|
||||||
GovernanceAction::SetWarmupPeriod { warmup_period: 3600 },
|
|
||||||
GovernanceAction::UpdateGatekeeperAddress { new_gatekeeper: EvmAddress::repeat_byte(0x22) },
|
|
||||||
|
|
||||||
GovernanceAction::SetBounty { bounty: 500 },
|
|
||||||
GovernanceAction::AddPool { address: EvmAddress::repeat_byte(0x22) },
|
|
||||||
GovernanceAction::RemovePool { index: 2 },
|
|
||||||
GovernanceAction::SetAdjustment { rate: 10, target: 100, add: true },
|
|
||||||
|
|
||||||
GovernanceAction::Enable {
|
|
||||||
status: GovernanceStatus::ReserveDepositor,
|
|
||||||
address: EvmAddress::repeat_byte(0x33),
|
|
||||||
calculator: EvmAddress::repeat_byte(0x44)
|
|
||||||
},
|
|
||||||
GovernanceAction::Disable {
|
|
||||||
status: GovernanceStatus::ReserveToken,
|
|
||||||
address: EvmAddress::repeat_byte(0x55)
|
|
||||||
},
|
|
||||||
GovernanceAction::ForfeitReserves {
|
|
||||||
router: EvmAddress::repeat_byte(0x66),
|
|
||||||
liquidity: 5000,
|
|
||||||
destroyer_mode: false
|
|
||||||
},
|
|
||||||
GovernanceAction::RedeemReserves { router: EvmAddress::repeat_byte(0x77), amount: 10000 },
|
|
||||||
GovernanceAction::Withdraw { token: EvmAddress::repeat_byte(0x88), amount: 2500 },
|
|
||||||
GovernanceAction::AuditReserves,
|
|
||||||
|
|
||||||
GovernanceAction::Close { id: 99 },
|
|
||||||
GovernanceAction::Create {
|
|
||||||
market: [69, 420, 1337],
|
|
||||||
terms: [69, 420],
|
|
||||||
token: EvmAddress::repeat_byte(0x99),
|
|
||||||
intervals: [69, 1337],
|
|
||||||
booleans: [true, false],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (index, action) in actions.into_iter().enumerate() {
|
|
||||||
let action_clone = action.clone();
|
|
||||||
|
|
||||||
let governance_request = ExodusRequest::<u64, u128>::evm_governance(
|
|
||||||
session,
|
|
||||||
network_id,
|
|
||||||
target,
|
|
||||||
action,
|
|
||||||
);
|
|
||||||
|
|
||||||
let message = governance_request
|
|
||||||
.get_message(&mut buffer)
|
|
||||||
.get_or_insert(&[])
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
assert!(!message.is_empty(), "Failed to encode action at index {}", index);
|
|
||||||
|
|
||||||
let expected_master_hash = keccak_256("govern(uint256,uint256,bytes)".as_bytes());
|
|
||||||
assert_eq!(message[0..4], expected_master_hash[0..4]);
|
|
||||||
|
|
||||||
assert_eq!(message[35], 69);
|
|
||||||
assert_eq!(&message[4..35], &[0u8; 31]);
|
|
||||||
|
|
||||||
assert_eq!(message[43], 0xA4);
|
|
||||||
assert_eq!(message[42], 0x01);
|
|
||||||
assert_eq!(&message[44..64], target.as_ref());
|
|
||||||
assert_eq!(&message[64..68], &[0u8; 4]);
|
|
||||||
|
|
||||||
assert_eq!(message[99], 96);
|
|
||||||
|
|
||||||
let inner_payload_len = u32::from_be_bytes(message[128..132].try_into().unwrap()) as usize;
|
|
||||||
let inner_payload = &message[132..132 + inner_payload_len];
|
|
||||||
|
|
||||||
assert_eq!((message.len() - 4) % 32, 0);
|
|
||||||
|
|
||||||
match action_clone {
|
|
||||||
GovernanceAction::SetDistributor { distributor } => {
|
|
||||||
let hash = keccak_256("setDistributor(address)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(&inner_payload[16..36], distributor.as_ref());
|
|
||||||
},
|
|
||||||
GovernanceAction::SetWarmupPeriod { warmup_period } => {
|
|
||||||
let hash = keccak_256("setWarmupPeriod(uint256)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[inner_payload.len() - 1], warmup_period as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::UpdateGatekeeperAddress { new_gatekeeper } => {
|
|
||||||
let hash = keccak_256("updateGatekeeperAddress(address)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(&inner_payload[16..36], new_gatekeeper.as_ref());
|
|
||||||
},
|
|
||||||
GovernanceAction::SetBounty { bounty } => {
|
|
||||||
let hash = keccak_256("setBounty(uint256)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[inner_payload.len() - 1], bounty as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::AddPool { address } => {
|
|
||||||
let hash = keccak_256("addPool(address)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(&inner_payload[16..36], address.as_ref());
|
|
||||||
},
|
|
||||||
GovernanceAction::RemovePool { index } => {
|
|
||||||
let hash = keccak_256("removePool(uint256)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[inner_payload.len() - 1], index as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::SetAdjustment { rate, target, add } => {
|
|
||||||
let hash = keccak_256("setAdjustment(uint256,uint256,bool)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[35], rate as u8);
|
|
||||||
assert_eq!(inner_payload[67], target as u8);
|
|
||||||
assert_eq!(inner_payload[99], add as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::Enable { status, address, calculator } => {
|
|
||||||
let hash = keccak_256("enable(uint8,address,address)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[35], status as u8);
|
|
||||||
assert_eq!(&inner_payload[48..68], address.as_ref());
|
|
||||||
assert_eq!(&inner_payload[80..100], calculator.as_ref());
|
|
||||||
},
|
|
||||||
GovernanceAction::Disable { status, address } => {
|
|
||||||
let hash = keccak_256("disable(uint8,address)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[35], status as u8);
|
|
||||||
assert_eq!(&inner_payload[48..68], address.as_ref());
|
|
||||||
},
|
|
||||||
GovernanceAction::ForfeitReserves { router, liquidity, destroyer_mode } => {
|
|
||||||
let hash = keccak_256("forfeitReserves(address,uint256,bool)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(&inner_payload[16..36], router.as_ref());
|
|
||||||
assert_eq!(inner_payload[67], liquidity as u8);
|
|
||||||
assert_eq!(inner_payload[99], destroyer_mode as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::RedeemReserves { router, amount } => {
|
|
||||||
let hash = keccak_256("redeemReserve(address,uint256)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(&inner_payload[16..36], router.as_ref());
|
|
||||||
assert_eq!(inner_payload[67], amount as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::Withdraw { token, amount } => {
|
|
||||||
let hash = keccak_256("withdraw(address,uint256)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(&inner_payload[16..36], token.as_ref());
|
|
||||||
assert_eq!(inner_payload[67], amount as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::AuditReserves => {
|
|
||||||
let hash = keccak_256("auditReserves()".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload.len(), 4);
|
|
||||||
},
|
|
||||||
GovernanceAction::Close { id } => {
|
|
||||||
let hash = keccak_256("close(uint256)".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload[inner_payload.len() - 1], id as u8);
|
|
||||||
},
|
|
||||||
GovernanceAction::Create { market, terms, token, intervals, booleans } => {
|
|
||||||
let hash = keccak_256("create(uint256[3],uint256[2],address,uint32[2],bool[2])".as_bytes());
|
|
||||||
assert_eq!(inner_payload[0..4], hash[0..4]);
|
|
||||||
assert_eq!(inner_payload.len(), 324);
|
|
||||||
assert_eq!(inner_payload[35], 69);
|
|
||||||
|
|
||||||
assert_eq!(u128::from_be_bytes(inner_payload[20..36].try_into().unwrap()), market[0]);
|
|
||||||
assert_eq!(u128::from_be_bytes(inner_payload[52..68].try_into().unwrap()), market[1]);
|
|
||||||
assert_eq!(u128::from_be_bytes(inner_payload[84..100].try_into().unwrap()), market[2]);
|
|
||||||
|
|
||||||
assert_eq!(u128::from_be_bytes(inner_payload[116..132].try_into().unwrap()), terms[0]);
|
|
||||||
assert_eq!(u128::from_be_bytes(inner_payload[148..164].try_into().unwrap()), terms[1]);
|
|
||||||
|
|
||||||
assert_eq!(&inner_payload[176..196], token.as_ref());
|
|
||||||
|
|
||||||
assert_eq!(u32::from_be_bytes(inner_payload[224..228].try_into().unwrap()), intervals[0]);
|
|
||||||
assert_eq!(u32::from_be_bytes(inner_payload[256..260].try_into().unwrap()), intervals[1]);
|
|
||||||
|
|
||||||
assert_eq!(inner_payload[291], booleans[0] as u8);
|
|
||||||
assert_eq!(inner_payload[323], booleans[1] as u8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,611 +0,0 @@
|
|||||||
use codec::{Encode, Decode};
|
|
||||||
|
|
||||||
use sp_std::{vec::Vec, collections::btree_map::BTreeMap};
|
|
||||||
use sp_runtime::{
|
|
||||||
RuntimeDebug, RuntimeAppPublic,
|
|
||||||
traits::{AtLeast32BitUnsigned, Saturating},
|
|
||||||
offchain::storage::StorageValueRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{DkgPackage, ExodusPackage, ExodusOk, PackageContext};
|
|
||||||
use crate::{AuthIndex, ExodusSession, ExodusHash, ExodusError};
|
|
||||||
|
|
||||||
fn create_storage_key(prefix: &[u8], index: &[u8], network: &[u8]) -> Vec<u8> {
|
|
||||||
let mut key = crate::DB_STORAGE_PREFIX.to_vec();
|
|
||||||
key.extend(prefix);
|
|
||||||
key.extend(index);
|
|
||||||
key.extend(network);
|
|
||||||
key
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct PersistentData<BlockNumber, DataType> {
|
|
||||||
pub release_block: BlockNumber,
|
|
||||||
pub data: DataType,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub enum DkgStorageEnum {
|
|
||||||
Round0 { package_hash: ExodusHash },
|
|
||||||
Round1 {
|
|
||||||
secret_package: Vec<u8>,
|
|
||||||
public_package: Vec<u8>,
|
|
||||||
},
|
|
||||||
Round2 {
|
|
||||||
secret_package: Vec<u8>,
|
|
||||||
public_packages: BTreeMap<AuthIndex, Vec<u8>>,
|
|
||||||
},
|
|
||||||
Round3 { decrypted: BTreeMap<AuthIndex, Vec<u8>> },
|
|
||||||
Round4,
|
|
||||||
Round5 {
|
|
||||||
secret_share: Vec<u8>,
|
|
||||||
merkle_proof: Vec<ExodusHash>,
|
|
||||||
},
|
|
||||||
Round6,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct ExodusNonce(Vec<u8>);
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct ExodusGroup {
|
|
||||||
pub self_binding_factor: Vec<u8>,
|
|
||||||
pub binding_factors_proof: Vec<ExodusHash>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct ExodusShare;
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct ExodusExile;
|
|
||||||
|
|
||||||
type ExodusNonces<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusNonce>>;
|
|
||||||
type ExodusGroups<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusGroup>>;
|
|
||||||
type ExodusShares<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusShare>>;
|
|
||||||
type ExodusExiles<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusExile>>;
|
|
||||||
|
|
||||||
pub struct ExodusStorageWrapper<ExodusInnerType> {
|
|
||||||
pub inner: ExodusInnerType,
|
|
||||||
pub data_changed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ExodusInnerType> ExodusStorageWrapper<ExodusInnerType> {
|
|
||||||
pub fn new(inner: ExodusInnerType) -> Self {
|
|
||||||
Self { inner, data_changed: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ExodusStorage<BlockNumber, NetworkCurve>
|
|
||||||
where
|
|
||||||
BlockNumber: Default,
|
|
||||||
NetworkCurve: Default,
|
|
||||||
{
|
|
||||||
current_block: BlockNumber,
|
|
||||||
block_longevity: BlockNumber,
|
|
||||||
pub network_curve: NetworkCurve,
|
|
||||||
pub authority_index: AuthIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, NC> ExodusStorage<B, NC>
|
|
||||||
where
|
|
||||||
B: AtLeast32BitUnsigned + Copy,
|
|
||||||
B: Encode + Decode + Default,
|
|
||||||
NC: Encode + Decode + Default + Copy,
|
|
||||||
{
|
|
||||||
pub fn with_network_curve(mut self, network_curve: NC) -> Self {
|
|
||||||
self.network_curve = network_curve;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current_block(mut self, current_block: B) -> Self {
|
|
||||||
self.current_block = current_block;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_block_longevity(mut self, block_longevity: B) -> Self {
|
|
||||||
self.block_longevity = block_longevity;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_authority_index(mut self, authority_index: AuthIndex) -> Self {
|
|
||||||
self.authority_index = authority_index;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_local_storage(&self) -> ExodusRecords<B> {
|
|
||||||
let encoded_index = self.authority_index.encode();
|
|
||||||
let encoded_network = self.network_curve.encode();
|
|
||||||
|
|
||||||
let nonces_key = create_storage_key(crate::EXODUS_PREFIX_NONCES, &encoded_index, &encoded_network);
|
|
||||||
let exodus_nonces = StorageValueRef::persistent(&nonces_key)
|
|
||||||
.get::<ExodusNonces<B>>()
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let shares_key = create_storage_key(crate::EXODUS_PREFIX_SHARES, &encoded_index, &encoded_network);
|
|
||||||
let exodus_shares = StorageValueRef::persistent(&shares_key)
|
|
||||||
.get::<ExodusShares<B>>()
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let groups_key = create_storage_key(crate::EXODUS_PREFIX_BLENDS, &encoded_index, &encoded_network);
|
|
||||||
let exodus_groups = StorageValueRef::persistent(&groups_key)
|
|
||||||
.get::<ExodusGroups<B>>()
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let exiles_key = create_storage_key(crate::EXODUS_PREFIX_EXILES, &encoded_index, &encoded_network);
|
|
||||||
let exodus_exiles = StorageValueRef::persistent(&exiles_key)
|
|
||||||
.get::<ExodusExiles<B>>()
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
ExodusRecords {
|
|
||||||
exodus_nonces: ExodusStorageWrapper::new(exodus_nonces),
|
|
||||||
exodus_shares: ExodusStorageWrapper::new(exodus_shares),
|
|
||||||
exodus_groups: ExodusStorageWrapper::new(exodus_groups),
|
|
||||||
exodus_exiles: ExodusStorageWrapper::new(exodus_exiles),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_signed_call_with_context<T, A, F1, F2, F3>(
|
|
||||||
&self,
|
|
||||||
authority_key: &A,
|
|
||||||
exodus_records: &mut ExodusRecords<B>,
|
|
||||||
make_data: F1,
|
|
||||||
submit_call: F2,
|
|
||||||
insert_data: F3,
|
|
||||||
) -> Result<(), ExodusError>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
A: RuntimeAppPublic,
|
|
||||||
F1: FnOnce(PackageContext<NC>) -> Result<ExodusPackage<NC, T>, ExodusError>,
|
|
||||||
F2: FnOnce(ExodusPackage<NC, T>, A::Signature) -> Result<(), ExodusError>,
|
|
||||||
F3: FnOnce(&mut ExodusRecords<B>, B),
|
|
||||||
{
|
|
||||||
let context = PackageContext::default()
|
|
||||||
.with_authority_index(self.authority_index)
|
|
||||||
.with_network_curve(self.network_curve);
|
|
||||||
|
|
||||||
let package = make_data(context)?;
|
|
||||||
|
|
||||||
let signature = authority_key
|
|
||||||
.sign(&package.encode())
|
|
||||||
.ok_or(ExodusError::BadSignature(self.authority_index))?;
|
|
||||||
|
|
||||||
submit_call(package, signature)?;
|
|
||||||
|
|
||||||
let next_release_block = self.current_block.saturating_add(self.block_longevity);
|
|
||||||
insert_data(exodus_records, next_release_block);
|
|
||||||
|
|
||||||
self.try_write_local_storage(exodus_records);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn force_clear_local_storage(&self) {
|
|
||||||
let encoded_index = self.authority_index.encode();
|
|
||||||
let encoded_network = self.network_curve.encode();
|
|
||||||
|
|
||||||
let nonces_key = create_storage_key(crate::EXODUS_PREFIX_NONCES, &encoded_index, &encoded_network);
|
|
||||||
let shares_key = create_storage_key(crate::EXODUS_PREFIX_SHARES, &encoded_index, &encoded_network);
|
|
||||||
let groups_key = create_storage_key(crate::EXODUS_PREFIX_BLENDS, &encoded_index, &encoded_network);
|
|
||||||
let exiles_key = create_storage_key(crate::EXODUS_PREFIX_EXILES, &encoded_index, &encoded_network);
|
|
||||||
|
|
||||||
StorageValueRef::persistent(&nonces_key).clear();
|
|
||||||
StorageValueRef::persistent(&shares_key).clear();
|
|
||||||
StorageValueRef::persistent(&groups_key).clear();
|
|
||||||
StorageValueRef::persistent(&exiles_key).clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_write_local_storage(&self, records: &ExodusRecords<B>) {
|
|
||||||
let encoded_index = self.authority_index.encode();
|
|
||||||
let encoded_network = self.network_curve.encode();
|
|
||||||
|
|
||||||
if records.exodus_nonces.data_changed {
|
|
||||||
let nonces_key = create_storage_key(crate::EXODUS_PREFIX_NONCES, &encoded_index, &encoded_network);
|
|
||||||
StorageValueRef::persistent(&nonces_key).set(&records.exodus_nonces.inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if records.exodus_shares.data_changed {
|
|
||||||
let shares_key = create_storage_key(crate::EXODUS_PREFIX_SHARES, &encoded_index, &encoded_network);
|
|
||||||
StorageValueRef::persistent(&shares_key).set(&records.exodus_shares.inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if records.exodus_groups.data_changed {
|
|
||||||
let groups_key = create_storage_key(crate::EXODUS_PREFIX_BLENDS, &encoded_index, &encoded_network);
|
|
||||||
StorageValueRef::persistent(&groups_key).set(&records.exodus_groups.inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if records.exodus_exiles.data_changed {
|
|
||||||
let exiles_key = create_storage_key(crate::EXODUS_PREFIX_EXILES, &encoded_index, &encoded_network);
|
|
||||||
StorageValueRef::persistent(&exiles_key).set(&records.exodus_exiles.inner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ExodusRecords<BlockNumber>
|
|
||||||
where
|
|
||||||
BlockNumber: AtLeast32BitUnsigned,
|
|
||||||
{
|
|
||||||
exodus_nonces: ExodusStorageWrapper<ExodusNonces<BlockNumber>>,
|
|
||||||
exodus_shares: ExodusStorageWrapper<ExodusShares<BlockNumber>>,
|
|
||||||
exodus_groups: ExodusStorageWrapper<ExodusGroups<BlockNumber>>,
|
|
||||||
exodus_exiles: ExodusStorageWrapper<ExodusExiles<BlockNumber>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<BlockNumber> ExodusRecords<BlockNumber>
|
|
||||||
where
|
|
||||||
BlockNumber: AtLeast32BitUnsigned + Default,
|
|
||||||
{
|
|
||||||
pub fn get_secret_nonce(&self, exodus_session: &ExodusSession) -> Option<&[u8]> {
|
|
||||||
self.exodus_nonces.inner
|
|
||||||
.get(exodus_session)
|
|
||||||
.map(|v| v.data.0.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_group_record(
|
|
||||||
&self,
|
|
||||||
exodus_session: &ExodusSession,
|
|
||||||
) -> Option<ExodusGroup> {
|
|
||||||
self.exodus_groups.inner
|
|
||||||
.get(exodus_session)
|
|
||||||
.cloned()
|
|
||||||
.map(|storage| storage.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_exodus_session_banned(
|
|
||||||
&self,
|
|
||||||
exodus_session: &ExodusSession,
|
|
||||||
) -> bool {
|
|
||||||
self.exodus_exiles.inner.contains_key(exodus_session)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_group_registered(
|
|
||||||
&self,
|
|
||||||
exodus_session: &ExodusSession,
|
|
||||||
) -> bool {
|
|
||||||
self.exodus_groups.inner.contains_key(exodus_session)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_waiting_period_for_nonce(
|
|
||||||
&self,
|
|
||||||
exodus_session: &ExodusSession,
|
|
||||||
current_block: &BlockNumber
|
|
||||||
) -> bool {
|
|
||||||
self.exodus_nonces.inner
|
|
||||||
.get(exodus_session)
|
|
||||||
.map(|data| data.release_block.gt(current_block))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_waiting_period_for_share(
|
|
||||||
&self,
|
|
||||||
exodus_session: &ExodusSession,
|
|
||||||
current_block: &BlockNumber,
|
|
||||||
) -> bool {
|
|
||||||
self.exodus_shares.inner
|
|
||||||
.get(exodus_session)
|
|
||||||
.map(|data| data.release_block.gt(¤t_block))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_waiting_period_for_group(
|
|
||||||
&self,
|
|
||||||
exodus_session: &ExodusSession,
|
|
||||||
current_block: &BlockNumber,
|
|
||||||
) -> bool {
|
|
||||||
self.exodus_groups.inner
|
|
||||||
.get(exodus_session)
|
|
||||||
.map(|data| data.release_block.gt(¤t_block))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exclude_exodus_session(
|
|
||||||
&mut self,
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
) {
|
|
||||||
let exodus_exile = PersistentData {
|
|
||||||
data: ExodusExile,
|
|
||||||
release_block: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.exodus_exiles.inner.insert(exodus_session, exodus_exile);
|
|
||||||
self.exodus_exiles.data_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_exodus_nonce(
|
|
||||||
&mut self,
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
next_release_block: BlockNumber,
|
|
||||||
nonce_commitment: Vec<u8>,
|
|
||||||
) {
|
|
||||||
let exodus_nonce = PersistentData {
|
|
||||||
data: ExodusNonce(nonce_commitment),
|
|
||||||
release_block: next_release_block,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.exodus_nonces.inner.insert(exodus_session, exodus_nonce);
|
|
||||||
self.exodus_nonces.data_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_exodus_share(
|
|
||||||
&mut self,
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
next_release_block: BlockNumber,
|
|
||||||
) {
|
|
||||||
let exodus_share = PersistentData {
|
|
||||||
data: ExodusShare,
|
|
||||||
release_block: next_release_block,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.exodus_shares.inner.insert(exodus_session, exodus_share);
|
|
||||||
self.exodus_shares.data_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_exodus_group(
|
|
||||||
&mut self,
|
|
||||||
exodus_session: ExodusSession,
|
|
||||||
next_release_block: BlockNumber,
|
|
||||||
self_binding_factor: Vec<u8>,
|
|
||||||
binding_factors_proof: Vec<ExodusHash>,
|
|
||||||
) {
|
|
||||||
let exodus_group = PersistentData {
|
|
||||||
data: ExodusGroup { self_binding_factor, binding_factors_proof },
|
|
||||||
release_block: next_release_block,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.exodus_groups.inner.insert(exodus_session, exodus_group);
|
|
||||||
self.exodus_groups.data_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
|
||||||
pub struct DkgStorage<BlockNumber, NetworkCurve>
|
|
||||||
where
|
|
||||||
BlockNumber: Copy + Clone,
|
|
||||||
NetworkCurve: Copy + Clone,
|
|
||||||
{
|
|
||||||
block_longevity: BlockNumber,
|
|
||||||
current_block: BlockNumber,
|
|
||||||
pub network_curve: NetworkCurve,
|
|
||||||
pub authority_index: AuthIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, NC> DkgStorage<B, NC>
|
|
||||||
where
|
|
||||||
B: Saturating + Encode + Decode + Copy + Default + PartialOrd,
|
|
||||||
NC: Encode + Copy + Default,
|
|
||||||
{
|
|
||||||
pub fn with_network_curve(mut self, network_curve: NC) -> Self {
|
|
||||||
self.network_curve = network_curve;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current_block(mut self, current_block: B) -> Self {
|
|
||||||
self.current_block = current_block;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_block_longevity(mut self, block_longevity: B) -> Self {
|
|
||||||
self.block_longevity = block_longevity;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_authority_index(mut self, authority_index: AuthIndex) -> Self {
|
|
||||||
self.authority_index = authority_index;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_release_block(&self) -> B {
|
|
||||||
self.current_block.saturating_add(self.block_longevity)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_dkg_result(
|
|
||||||
&self,
|
|
||||||
dkg_record: DkgRecord<B>,
|
|
||||||
) -> Result<ExodusOk<NC>, ExodusError> {
|
|
||||||
match dkg_record.prefix_slice {
|
|
||||||
crate::DKG_ROUND0_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(0u8, self.authority_index, self.network_curve)),
|
|
||||||
crate::DKG_ROUND1_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(1u8, self.authority_index, self.network_curve)),
|
|
||||||
crate::DKG_ROUND2_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(2u8, self.authority_index, self.network_curve)),
|
|
||||||
crate::DKG_ROUND3_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(3u8, self.authority_index, self.network_curve)),
|
|
||||||
crate::DKG_ROUND4_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(4u8, self.authority_index, self.network_curve)),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !dkg_record.prefix_slice.starts_with(crate::DKG_SECRET_PREFIX) {
|
|
||||||
return Err(ExodusError::UnknownPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
match &dkg_record.maybe_stored_data {
|
|
||||||
Some(PersistentData { data: DkgStorageEnum::Round5 { .. }, .. }) => {
|
|
||||||
Ok(ExodusOk::DkgRoundRegistered(5u8, self.authority_index, self.network_curve))
|
|
||||||
},
|
|
||||||
Some(PersistentData { data: DkgStorageEnum::Round6 { .. }, .. }) => {
|
|
||||||
Ok(ExodusOk::DkgRoundRegistered(6u8, self.authority_index, self.network_curve))
|
|
||||||
},
|
|
||||||
_ => Err(ExodusError::UnknownPrefix),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_local_storage<'a>(
|
|
||||||
&self,
|
|
||||||
prefix_slice: &'a [u8],
|
|
||||||
authority_index: AuthIndex,
|
|
||||||
) -> DkgRecord<'a, B> {
|
|
||||||
let encoded_index = authority_index.encode();
|
|
||||||
let encoded_network = self.network_curve.encode();
|
|
||||||
let key = create_storage_key(prefix_slice, &encoded_index, &encoded_network);
|
|
||||||
|
|
||||||
let maybe_stored_data = StorageValueRef::persistent(&key)
|
|
||||||
.get::<PersistentData<B, DkgStorageEnum>>()
|
|
||||||
.ok()
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
DkgRecord { prefix_slice, maybe_stored_data, current_block: self.current_block }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_signed_call_with_context<T, A, F1, F2>(
|
|
||||||
&self,
|
|
||||||
authority_key: &A,
|
|
||||||
dkg_record: &mut DkgRecord<B>,
|
|
||||||
make_data: F1,
|
|
||||||
submit_call: F2,
|
|
||||||
) -> Result<(), ExodusError>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
A: RuntimeAppPublic,
|
|
||||||
F1: FnOnce(PackageContext<NC>) -> Result<DkgPackage<NC, T>, ExodusError>,
|
|
||||||
F2: FnOnce(DkgPackage<NC, T>, A::Signature) -> Result<(), ExodusError>,
|
|
||||||
{
|
|
||||||
let context = PackageContext::default()
|
|
||||||
.with_authority_index(self.authority_index)
|
|
||||||
.with_network_curve(self.network_curve);
|
|
||||||
|
|
||||||
let package = make_data(context)?;
|
|
||||||
|
|
||||||
let signature = authority_key
|
|
||||||
.sign(&package.encode())
|
|
||||||
.ok_or(ExodusError::BadSignature(self.authority_index))?;
|
|
||||||
|
|
||||||
submit_call(package, signature)?;
|
|
||||||
|
|
||||||
self.try_write_local_storage(dkg_record);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_write_local_storage(
|
|
||||||
&self,
|
|
||||||
dkg_record: &DkgRecord<B>,
|
|
||||||
) {
|
|
||||||
let encoded_index = self.authority_index.encode();
|
|
||||||
let encoded_network = self.network_curve.encode();
|
|
||||||
let key = create_storage_key(
|
|
||||||
dkg_record.prefix_slice,
|
|
||||||
&encoded_index,
|
|
||||||
&encoded_network,
|
|
||||||
);
|
|
||||||
|
|
||||||
match &dkg_record.maybe_stored_data {
|
|
||||||
Some(to_store) => StorageValueRef::persistent(&key).set(&to_store),
|
|
||||||
None => StorageValueRef::persistent(&key).clear(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_local_storage<'a>(
|
|
||||||
&self,
|
|
||||||
prefix: &'a [u8],
|
|
||||||
) {
|
|
||||||
let encoded_index = self.authority_index.encode();
|
|
||||||
let encoded_network = self.network_curve.encode();
|
|
||||||
let key = create_storage_key(prefix, &encoded_index, &encoded_network);
|
|
||||||
|
|
||||||
StorageValueRef::persistent(&key).clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DkgRecord<'a, BlockNumber> {
|
|
||||||
maybe_stored_data: Option<PersistentData<BlockNumber, DkgStorageEnum>>,
|
|
||||||
current_block: BlockNumber,
|
|
||||||
prefix_slice: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, BlockNumber> DkgRecord<'a, BlockNumber>
|
|
||||||
where
|
|
||||||
BlockNumber: Default + Copy + PartialOrd,
|
|
||||||
{
|
|
||||||
pub fn is_waiting_period(&self) -> bool {
|
|
||||||
self.maybe_stored_data
|
|
||||||
.as_ref()
|
|
||||||
.map(|data| data.release_block.gt(&self.current_block))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn storage_exists(&self) -> bool {
|
|
||||||
self.maybe_stored_data.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_inner(&self) -> Option<&DkgStorageEnum> {
|
|
||||||
self.maybe_stored_data.as_ref().map(|stored| &stored.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_inner_mut(&mut self) -> Option<&mut DkgStorageEnum> {
|
|
||||||
self.maybe_stored_data.as_mut().map(|stored| &mut stored.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_update_max_signers<F>(
|
|
||||||
&mut self,
|
|
||||||
get_new_secret: F
|
|
||||||
) -> Result<&Vec<u8>, ExodusError>
|
|
||||||
where
|
|
||||||
F: FnOnce(&[u8]) -> Result<Vec<u8>, ExodusError>,
|
|
||||||
{
|
|
||||||
enum RoundType<'a> {
|
|
||||||
R1 { public_package: &'a Vec<u8> },
|
|
||||||
R2 { public_packages: &'a BTreeMap<AuthIndex, Vec<u8>> },
|
|
||||||
}
|
|
||||||
|
|
||||||
let (old_secret, round_type) = self.maybe_stored_data.as_ref()
|
|
||||||
.map(|persistent| match &persistent.data {
|
|
||||||
DkgStorageEnum::Round1 { secret_package, public_package } => {
|
|
||||||
Ok((secret_package, RoundType::R1 { public_package }))
|
|
||||||
},
|
|
||||||
DkgStorageEnum::Round2 { secret_package, public_packages } => {
|
|
||||||
Ok((secret_package, RoundType::R2 { public_packages }))
|
|
||||||
},
|
|
||||||
_ => Err(ExodusError::NothingToWrite),
|
|
||||||
})
|
|
||||||
.ok_or(ExodusError::NothingToWrite)??;
|
|
||||||
|
|
||||||
let new_secret = get_new_secret(old_secret)?;
|
|
||||||
let new_data = match round_type {
|
|
||||||
RoundType::R1 { public_package } => DkgStorageEnum::Round1 {
|
|
||||||
secret_package: new_secret,
|
|
||||||
public_package: public_package.clone(),
|
|
||||||
},
|
|
||||||
RoundType::R2 { public_packages } => DkgStorageEnum::Round2 {
|
|
||||||
secret_package: new_secret,
|
|
||||||
public_packages: public_packages.clone(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
self.update_with_block(None, new_data)?;
|
|
||||||
|
|
||||||
self.maybe_stored_data.as_ref()
|
|
||||||
.map(|persistent| match &persistent.data {
|
|
||||||
DkgStorageEnum::Round1 { secret_package, .. } |
|
|
||||||
DkgStorageEnum::Round2 { secret_package, .. } => Ok(secret_package),
|
|
||||||
_ => Err(ExodusError::NothingToWrite),
|
|
||||||
})
|
|
||||||
.ok_or(ExodusError::NothingToWrite)?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_with_block(
|
|
||||||
&mut self,
|
|
||||||
maybe_release_block: Option<BlockNumber>,
|
|
||||||
new_data: DkgStorageEnum,
|
|
||||||
) -> Result<(), ExodusError> {
|
|
||||||
use core::mem::discriminant as d;
|
|
||||||
|
|
||||||
let release_block = match &self.maybe_stored_data {
|
|
||||||
Some(existing) => {
|
|
||||||
if d(&existing.data) != d(&new_data) {
|
|
||||||
return Err(ExodusError::NothingToWrite);
|
|
||||||
}
|
|
||||||
maybe_release_block.unwrap_or(existing.release_block)
|
|
||||||
}
|
|
||||||
None => maybe_release_block.unwrap_or_default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.maybe_stored_data = Some(PersistentData { release_block, data: new_data });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
mod exodus_request;
|
|
||||||
mod local_storage;
|
|
||||||
mod encryption;
|
|
||||||
mod consensus;
|
|
||||||
mod signature;
|
|
||||||
mod packages;
|
|
||||||
mod success;
|
|
||||||
mod removal;
|
|
||||||
mod nonce;
|
|
||||||
mod roast;
|
|
||||||
mod dkg;
|
|
||||||
|
|
||||||
pub use exodus_request::*;
|
|
||||||
pub use local_storage::*;
|
|
||||||
pub use encryption::*;
|
|
||||||
pub use consensus::*;
|
|
||||||
pub use signature::*;
|
|
||||||
pub use packages::*;
|
|
||||||
pub use success::*;
|
|
||||||
pub use removal::*;
|
|
||||||
pub use nonce::*;
|
|
||||||
pub use roast::*;
|
|
||||||
pub use dkg::*;
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
use frame_support::{BoundedVec, traits::ConstU32};
|
|
||||||
use codec::{Encode, Decode};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_runtime::RuntimeDebug;
|
|
||||||
|
|
||||||
use crate::ELEMENT_MAX_BYTES;
|
|
||||||
|
|
||||||
type NonceType = BoundedVec<u8, ConstU32<{ ELEMENT_MAX_BYTES }>>;
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub struct ExodusSeparatedNonce {
|
|
||||||
pub hiding: NonceType,
|
|
||||||
pub binding: NonceType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExodusSeparatedNonce {
|
|
||||||
pub fn new(hiding: NonceType, binding: NonceType) -> Self {
|
|
||||||
Self { hiding, binding }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,466 +0,0 @@
|
|||||||
use sp_std::vec::Vec;
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_runtime::RuntimeDebug;
|
|
||||||
use codec::{Encode, Decode};
|
|
||||||
|
|
||||||
use frame_support::{BoundedVec, traits::ConstU32};
|
|
||||||
use crate::{AuthIndex, ExodusSession, ExodusHash, ExodusError};
|
|
||||||
|
|
||||||
pub trait PackageMetadata<NetworkCurve>
|
|
||||||
where
|
|
||||||
NetworkCurve: Copy,
|
|
||||||
{
|
|
||||||
fn context(&self) -> &PackageContext<NetworkCurve>;
|
|
||||||
|
|
||||||
fn get_authority_index(&self) -> AuthIndex {
|
|
||||||
self.context().get_authority_index()
|
|
||||||
}
|
|
||||||
fn get_network_curve(&self) -> NetworkCurve {
|
|
||||||
self.context().get_network_curve()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub enum ExodusPackage<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
NonceCommitment(ExodusNonceCommitment<NetworkCurve>),
|
|
||||||
GroupCommitment(ExodusGroupCommitment<NetworkCurve>),
|
|
||||||
SignatureShare(ExodusSignatureShare<NetworkCurve, T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkCurve, T> ExodusPackage<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
pub fn bytes_len(&self) -> u32 {
|
|
||||||
let length = match self {
|
|
||||||
Self::GroupCommitment(package) => package.group_commitment.len(),
|
|
||||||
Self::SignatureShare(package) => {
|
|
||||||
(package.binding_factors_proof.len() * ExodusHash::len_bytes())
|
|
||||||
+ package.self_binding_factor.len()
|
|
||||||
+ package.signature_share.len()
|
|
||||||
},
|
|
||||||
Self::NonceCommitment(package) => {
|
|
||||||
package.hiding_commitment.len()
|
|
||||||
+ package.binding_commitment.len()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
length as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_exodus_session(&self) -> ExodusSession {
|
|
||||||
match &self {
|
|
||||||
Self::SignatureShare(package) => package.session,
|
|
||||||
Self::NonceCommitment(package) => package.session,
|
|
||||||
Self::GroupCommitment(package) => package.session,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub enum DkgPackage<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
Round0(Round0Package<NetworkCurve>),
|
|
||||||
Round1(Round1Package<NetworkCurve, T>),
|
|
||||||
Round2(Round2Package<NetworkCurve, T>),
|
|
||||||
Round3(Round3Package<NetworkCurve, T>),
|
|
||||||
Round4(Round4Package<NetworkCurve, T>),
|
|
||||||
Round5(Round5Package<NetworkCurve>),
|
|
||||||
Round6(Round6Package<NetworkCurve, T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkCurve, T> DkgPackage<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
pub fn bytes_len(&self) -> u32 {
|
|
||||||
let length = match self {
|
|
||||||
Self::Round0(_) => ExodusHash::len_bytes(),
|
|
||||||
Self::Round1(round1) => round1.package.len(),
|
|
||||||
Self::Round2(round2) => round2.bundle.bitmask.len(),
|
|
||||||
Self::Round3(round3) => round3.indices.len(),
|
|
||||||
Self::Round4(round4) => round4.bundle.bitmask.len(),
|
|
||||||
Self::Round5(round5) => {
|
|
||||||
round5.verifying_key.len()
|
|
||||||
.saturating_add(ExodusHash::len_bytes())
|
|
||||||
},
|
|
||||||
Self::Round6(round6) => round6.merkle_proof.len(),
|
|
||||||
};
|
|
||||||
|
|
||||||
length as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
pub struct PackageContext<NetworkCurve> {
|
|
||||||
authority_index: AuthIndex,
|
|
||||||
network_curve: NetworkCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkCurve> PackageContext<NetworkCurve>
|
|
||||||
where
|
|
||||||
NetworkCurve: Copy,
|
|
||||||
{
|
|
||||||
pub fn get_authority_index(&self) -> AuthIndex {
|
|
||||||
self.authority_index
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_network_curve(&self) -> NetworkCurve {
|
|
||||||
self.network_curve
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_network_curve(mut self, network_curve: NetworkCurve) -> Self {
|
|
||||||
self.network_curve = network_curve;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_authority_index(mut self, authority_index: AuthIndex) -> Self {
|
|
||||||
self.authority_index = authority_index;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
pub struct BundledPackages {
|
|
||||||
pub blob: Vec<u8>,
|
|
||||||
pub bitmask: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct EncryptionBundledPackages<T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
pub blob: BoundedVec<u8, crate::EncryptedRound2BlobMaxBytes<T>>,
|
|
||||||
pub bitmask: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: crate::pallet::Config> EncryptionBundledPackages<T> {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
let blob_len = self.blob.len();
|
|
||||||
let bitmask_len = self.bitmask.len();
|
|
||||||
|
|
||||||
bitmask_len.saturating_add(blob_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: crate::pallet::Config> TryFrom<BundledPackages> for EncryptionBundledPackages<T> {
|
|
||||||
type Error = ExodusError;
|
|
||||||
|
|
||||||
fn try_from(source: BundledPackages) -> Result<Self, Self::Error> {
|
|
||||||
let blob =
|
|
||||||
BoundedVec::<u8, crate::EncryptedRound2BlobMaxBytes<T>>::try_from(source.blob)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let bitmask = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(source.bitmask)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(Self { blob, bitmask })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct JustificationsBundledPackages<T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
pub blob: BoundedVec<u8, crate::Round2BlobMaxBytes<T>>,
|
|
||||||
pub bitmask: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: crate::pallet::Config> JustificationsBundledPackages<T> {
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
let blob_len = self.blob.len();
|
|
||||||
let bitmask_len = self.bitmask.len();
|
|
||||||
|
|
||||||
bitmask_len.saturating_add(blob_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: crate::pallet::Config> TryFrom<BundledPackages> for JustificationsBundledPackages<T> {
|
|
||||||
type Error = ExodusError;
|
|
||||||
|
|
||||||
fn try_from(source: BundledPackages) -> Result<Self, Self::Error> {
|
|
||||||
let blob =
|
|
||||||
BoundedVec::<u8, crate::Round2BlobMaxBytes<T>>::try_from(source.blob)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let bitmask = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(source.bitmask)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(Self { blob, bitmask })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct ExodusNonceCommitment<NetworkCurve> {
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub hiding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
|
||||||
pub binding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
|
||||||
pub session: ExodusSession,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
pub struct ExodusGroupCommitment<NetworkCurve> {
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub group_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
|
||||||
pub binding_factors_root: ExodusHash,
|
|
||||||
pub session: ExodusSession,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct ExodusSignatureShare<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub binding_factors_proof: BoundedVec<ExodusHash, crate::BindingFactorsProof<T>>,
|
|
||||||
pub signature_share: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>,
|
|
||||||
pub self_binding_factor: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>,
|
|
||||||
pub session: ExodusSession,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
pub struct Round0Package<NetworkCurve> {
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub package_hash: ExodusHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct Round1Package<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub package: BoundedVec<u8, crate::Round1MaxBytes<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct Round2Package<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub bundle: EncryptionBundledPackages<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct Round3Package<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub indices: BoundedVec<u8, crate::AccusedIndicesMaxBytes<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct Round4Package<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub bundle: JustificationsBundledPackages<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
pub struct Round5Package<NetworkCurve> {
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
|
||||||
pub merkle_root: ExodusHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct Round6Package<NetworkCurve, T>
|
|
||||||
where
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
context: PackageContext<NetworkCurve>,
|
|
||||||
pub verifying_share: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
|
||||||
pub merkle_proof: BoundedVec<ExodusHash, crate::MerkleProofMaxSize<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkCurve> PackageContext<NetworkCurve> {
|
|
||||||
pub fn with_exodus_nonce_commitment(
|
|
||||||
self,
|
|
||||||
session: ExodusSession,
|
|
||||||
hiding_commitment_vec: Vec<u8>,
|
|
||||||
binding_commitment_vec: Vec<u8>,
|
|
||||||
) -> Result<ExodusNonceCommitment<NetworkCurve>, ExodusError> {
|
|
||||||
let hiding_commitment =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(hiding_commitment_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_commitment =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(binding_commitment_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(ExodusNonceCommitment { context: self, hiding_commitment, binding_commitment, session })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_exodus_group_commitment(
|
|
||||||
self,
|
|
||||||
session: ExodusSession,
|
|
||||||
commitment_vec: Vec<u8>,
|
|
||||||
binding_factors_root: ExodusHash,
|
|
||||||
) -> Result<ExodusGroupCommitment<NetworkCurve>, ExodusError> {
|
|
||||||
let group_commitment =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(commitment_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(ExodusGroupCommitment {
|
|
||||||
context: self,
|
|
||||||
group_commitment,
|
|
||||||
binding_factors_root,
|
|
||||||
session,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_exodus_signature_share<T: crate::pallet::Config>(
|
|
||||||
self,
|
|
||||||
session: ExodusSession,
|
|
||||||
signature_share_vec: Vec<u8>,
|
|
||||||
binding_factors_proof_vec: Vec<ExodusHash>,
|
|
||||||
self_binding_factor_vec: Vec<u8>,
|
|
||||||
) -> Result<ExodusSignatureShare<NetworkCurve, T>, ExodusError> {
|
|
||||||
let signature_share =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>::try_from(signature_share_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let binding_factors_proof =
|
|
||||||
BoundedVec::<ExodusHash, crate::BindingFactorsProof<T>>::try_from(binding_factors_proof_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let self_binding_factor =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>::try_from(self_binding_factor_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(ExodusSignatureShare {
|
|
||||||
context: self,
|
|
||||||
binding_factors_proof,
|
|
||||||
self_binding_factor,
|
|
||||||
signature_share,
|
|
||||||
session,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round0(
|
|
||||||
self,
|
|
||||||
package_hash: ExodusHash,
|
|
||||||
) -> Round0Package<NetworkCurve> {
|
|
||||||
Round0Package { context: self, package_hash }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round1<T: crate::pallet::Config>(
|
|
||||||
self,
|
|
||||||
package_vec: Vec<u8>,
|
|
||||||
) -> Result<Round1Package<NetworkCurve, T>, ExodusError> {
|
|
||||||
let package =
|
|
||||||
BoundedVec::<u8, crate::Round1MaxBytes<T>>::try_from(package_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(Round1Package { context: self, package })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round2<T: crate::pallet::Config>(
|
|
||||||
self,
|
|
||||||
bundle_raw: BundledPackages,
|
|
||||||
) -> Result<Round2Package<NetworkCurve, T>, ExodusError> {
|
|
||||||
let bundle: EncryptionBundledPackages<T> = bundle_raw.try_into()?;
|
|
||||||
Ok(Round2Package { context: self, bundle })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round3<T: crate::pallet::Config>(
|
|
||||||
self,
|
|
||||||
indices_vec: Vec<u8>
|
|
||||||
) -> Result<Round3Package<NetworkCurve, T>, ExodusError> {
|
|
||||||
let indices =
|
|
||||||
BoundedVec::<u8, crate::AccusedIndicesMaxBytes<T>>::try_from(indices_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(Round3Package { context: self, indices })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round4<T: crate::pallet::Config>(
|
|
||||||
self,
|
|
||||||
bundle_raw: BundledPackages,
|
|
||||||
) -> Result<Round4Package<NetworkCurve, T>, ExodusError> {
|
|
||||||
let bundle: JustificationsBundledPackages<T> = bundle_raw.try_into()?;
|
|
||||||
Ok(Round4Package { context: self, bundle })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round5(
|
|
||||||
self,
|
|
||||||
verifying_key_vec: Vec<u8>,
|
|
||||||
merkle_root: ExodusHash,
|
|
||||||
) -> Result<Round5Package<NetworkCurve>, ExodusError> {
|
|
||||||
let verifying_key =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_key_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(Round5Package { context: self, verifying_key, merkle_root })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_dkg_round6<T: crate::pallet::Config>(
|
|
||||||
self,
|
|
||||||
verifying_share_vec: Vec<u8>,
|
|
||||||
merkle_proof_vec: Vec<ExodusHash>,
|
|
||||||
) -> Result<Round6Package<NetworkCurve, T>, ExodusError> {
|
|
||||||
let verifying_share =
|
|
||||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_share_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
let merkle_proof =
|
|
||||||
BoundedVec::<ExodusHash, crate::MerkleProofMaxSize<T>>::try_from(merkle_proof_vec)
|
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
|
||||||
|
|
||||||
Ok(Round6Package { context: self, verifying_share, merkle_proof })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NC, T> PackageMetadata<NC> for ExodusPackage<NC, T>
|
|
||||||
where
|
|
||||||
NC: Copy,
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
fn context(&self) -> &PackageContext<NC> {
|
|
||||||
match self {
|
|
||||||
Self::NonceCommitment(package) => &package.context,
|
|
||||||
Self::GroupCommitment(package) => &package.context,
|
|
||||||
Self::SignatureShare(package) => &package.context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NC, T> PackageMetadata<NC> for DkgPackage<NC, T>
|
|
||||||
where
|
|
||||||
NC: Copy,
|
|
||||||
T: crate::pallet::Config,
|
|
||||||
{
|
|
||||||
fn context(&self) -> &PackageContext<NC> {
|
|
||||||
match self {
|
|
||||||
Self::Round0(package) => &package.context,
|
|
||||||
Self::Round1(package) => &package.context,
|
|
||||||
Self::Round2(package) => &package.context,
|
|
||||||
Self::Round3(package) => &package.context,
|
|
||||||
Self::Round4(package) => &package.context,
|
|
||||||
Self::Round5(package) => &package.context,
|
|
||||||
Self::Round6(package) => &package.context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
use frame_support::weights::Weight;
|
|
||||||
|
|
||||||
pub struct DkgRemovalResult {
|
|
||||||
weight: Weight,
|
|
||||||
cleared: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DkgRemovalResult {
|
|
||||||
pub fn new<F>(try_clean_storage: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnOnce() -> (Weight, bool)
|
|
||||||
{
|
|
||||||
let (weight, cleared) = try_clean_storage();
|
|
||||||
Self { weight, cleared }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fully_cleared(&self) -> bool {
|
|
||||||
self.cleared
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn used_weight(&self) -> Weight {
|
|
||||||
self.weight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
use codec::{Encode, Decode};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
use sp_runtime::RuntimeDebug;
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub enum RoastStatus {
|
|
||||||
#[default]
|
|
||||||
NonceCommitments,
|
|
||||||
GroupCommitments,
|
|
||||||
PartialSignatures,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub struct RoastSessionState<Bitmap>
|
|
||||||
where
|
|
||||||
Bitmap: Default,
|
|
||||||
{
|
|
||||||
pub status: RoastStatus,
|
|
||||||
pub nonce_committers: Bitmap,
|
|
||||||
pub group_committers: Bitmap,
|
|
||||||
pub partial_signers: Bitmap,
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
use scale_info::TypeInfo;
|
|
||||||
use codec::{Encode, Decode};
|
|
||||||
use frame_support::{BoundedVec, traits::ConstU32};
|
|
||||||
use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto};
|
|
||||||
|
|
||||||
type ExodusNonceT = BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>;
|
|
||||||
type ExodusSignatureT = BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>;
|
|
||||||
type Type<NetworkId, Balance> = super::ExodusRequestType<NetworkId, Balance>;
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
|
|
||||||
pub struct ExodusSignedMessage<NetworkId: Default, Balance: Default> {
|
|
||||||
pub nonce: ExodusNonceT,
|
|
||||||
pub signature: ExodusSignatureT,
|
|
||||||
pub r#type: Type<NetworkId, Balance>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkId, Balance> ExodusSignedMessage<NetworkId, Balance>
|
|
||||||
where
|
|
||||||
NetworkId: UniqueSaturatedInto<u64> + Copy + Default,
|
|
||||||
Balance: UniqueSaturatedInto<u128> + Copy + Default,
|
|
||||||
{
|
|
||||||
pub fn new<E, S>(
|
|
||||||
nonce_iter: E,
|
|
||||||
signature_iter: S,
|
|
||||||
r#type: Type<NetworkId, Balance>
|
|
||||||
) -> Option<Self>
|
|
||||||
where
|
|
||||||
E: Iterator<Item = u8> + ExactSizeIterator,
|
|
||||||
S: Iterator<Item = u8> + ExactSizeIterator,
|
|
||||||
{
|
|
||||||
let mut nonce = ExodusNonceT::new();
|
|
||||||
nonce.try_extend(nonce_iter).ok()?;
|
|
||||||
|
|
||||||
let mut signature = ExodusSignatureT::new();
|
|
||||||
signature.try_extend(signature_iter).ok()?;
|
|
||||||
|
|
||||||
Some(Self { nonce, signature, r#type })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_message<'a>(
|
|
||||||
&self,
|
|
||||||
message_buffer: &'a mut [u8; crate::MAX_MESSAGE_SIZE as usize],
|
|
||||||
) -> Option<&'a [u8]> {
|
|
||||||
self.r#type.get_message(message_buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
use crate::{AuthIndex, ExodusSession, DkgIndex, RoastSession};
|
|
||||||
|
|
||||||
pub enum ExodusOk<NetworkCurve> {
|
|
||||||
ExodusEmptyRequests(AuthIndex, NetworkCurve),
|
|
||||||
ExodusWaitingState(AuthIndex, NetworkCurve),
|
|
||||||
ExodusNonceCommitted(ExodusSession, RoastSession, AuthIndex, NetworkCurve),
|
|
||||||
ExodusGroupCommitted(ExodusSession, RoastSession, AuthIndex, NetworkCurve),
|
|
||||||
ExodusSignedPartially(ExodusSession, RoastSession, AuthIndex, NetworkCurve),
|
|
||||||
ExodusVerifyingShareMissed(AuthIndex, DkgIndex, NetworkCurve),
|
|
||||||
|
|
||||||
DkgNotPartOfRound(u8, AuthIndex, NetworkCurve),
|
|
||||||
DkgRoundRegistered(u8, AuthIndex, NetworkCurve),
|
|
||||||
DkgPreviousPurged(AuthIndex, NetworkCurve),
|
|
||||||
DkgRoundsCleared(AuthIndex, NetworkCurve),
|
|
||||||
DkgRoundAlreadyPassed(u8, AuthIndex, NetworkCurve),
|
|
||||||
DkgRoundWaitingPeriod(u8, AuthIndex, NetworkCurve),
|
|
||||||
DkgRoundNothingStored(u8, AuthIndex, NetworkCurve),
|
|
||||||
DkgRoundMaxSignersNarrowed(u8, AuthIndex, AuthIndex, AuthIndex, NetworkCurve),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<NetworkCurve> core::fmt::Display for ExodusOk<NetworkCurve>
|
|
||||||
where
|
|
||||||
NetworkCurve: core::fmt::Display
|
|
||||||
{
|
|
||||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ExodusOk::ExodusEmptyRequests(auth_index, curve) => write!(fmt, "No EXODUS requests found by authority #{auth_index} for curve {curve}."),
|
|
||||||
ExodusOk::ExodusWaitingState(auth_index, curve) => write!(fmt, "Waiting for EXODUS requests to be processed by authority #{auth_index} for curve {curve}."),
|
|
||||||
ExodusOk::ExodusNonceCommitted(exodus, roast, auth_index, curve) => write!(fmt, "Nonce committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."),
|
|
||||||
ExodusOk::ExodusGroupCommitted(exodus, roast, auth_index, curve) => write!(fmt, "Aggregated nonce and binding factor proof committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."),
|
|
||||||
ExodusOk::ExodusSignedPartially(exodus, roast, auth_index, curve) => write!(fmt, "Partial signature created for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."),
|
|
||||||
ExodusOk::ExodusVerifyingShareMissed(auth_index, dkg_index, curve) => write!(fmt, "EXODUS Round no verifying share for authority #{auth_index} based on DKG session #{dkg_index} for curve {curve}."),
|
|
||||||
|
|
||||||
ExodusOk::DkgNotPartOfRound(round, auth_index, curve) => write!(fmt, "DKG authority #{auth_index} for network curve {curve} is not part of Round{round}."),
|
|
||||||
ExodusOk::DkgRoundRegistered(round, auth_index, curve) => write!(fmt, "DKG Round{round} registered by authority #{auth_index} for network curve {curve}."),
|
|
||||||
ExodusOk::DkgPreviousPurged(auth_index, curve) => write!(fmt, "Storage prepared for new DKG session by authority #{auth_index} for network curve {curve}."),
|
|
||||||
ExodusOk::DkgRoundsCleared(auth_index, curve) => write!(fmt, "DKG Rounds cleared from storage by authority #{auth_index} for network curve {curve}."),
|
|
||||||
ExodusOk::DkgRoundAlreadyPassed(round, auth_index, curve) => write!(fmt, "DKG Round{round} already done by authority #{auth_index} for network curve {curve}."),
|
|
||||||
ExodusOk::DkgRoundWaitingPeriod(round, auth_index, curve) => write!(fmt, "DKG Round{round} waiting to be applied by authority #{auth_index} for network curve {curve}."),
|
|
||||||
ExodusOk::DkgRoundNothingStored(round, auth_index, curve) => write!(fmt, "DKG nothing stored from Round{round} by authority #{auth_index} for network curve {curve}."),
|
|
||||||
ExodusOk::DkgRoundMaxSignersNarrowed(round, auth_index, prev_max, curr_max, curve) => write!(fmt, "DKG Round{round} narrowed max_signers from {prev_max} to {curr_max} by {auth_index} for network curve {curve}."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,354 +0,0 @@
|
|||||||
// This file is part of Ghost Network.
|
|
||||||
|
|
||||||
// Ghost Network is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Ghost Network is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Ghost Network. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Autogenerated weights for `ghost_exodus`
|
|
||||||
//!
|
|
||||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
|
|
||||||
//! DATE: 2026-09-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
|
||||||
//! WORST CASE MAP SIZE: `1000000`
|
|
||||||
//! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz`
|
|
||||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024
|
|
||||||
|
|
||||||
// Executed Command:
|
|
||||||
// ./target/release/ghost
|
|
||||||
// benchmark
|
|
||||||
// pallet
|
|
||||||
// --chain=casper-dev
|
|
||||||
// --steps=50
|
|
||||||
// --repeat=20
|
|
||||||
// --pallet=ghost_exodus
|
|
||||||
// --extrinsic=*
|
|
||||||
// --wasm-execution=compiled
|
|
||||||
// --heap-pages=4096
|
|
||||||
// --header=./file_header.txt
|
|
||||||
// --output=./runtime/casper/src/weights/ghost_exodus.rs
|
|
||||||
|
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
|
||||||
#![allow(unused_parens)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use frame_support::{
|
|
||||||
traits::Get,
|
|
||||||
weights::{Weight, constants::RocksDbWeight},
|
|
||||||
};
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
pub trait WeightInfo {
|
|
||||||
fn register_round0_package() -> Weight;
|
|
||||||
fn register_round1_package(package_len: u32) -> Weight;
|
|
||||||
fn register_encrypted_round2_packages(packages: u32,) -> Weight;
|
|
||||||
fn register_round3_complaints(indices: u32) -> Weight;
|
|
||||||
fn register_round4_justifications(packages: u32) -> Weight;
|
|
||||||
fn register_round5_verifying_package() -> Weight;
|
|
||||||
fn register_round6_public_share_package(packages: u32) -> Weight;
|
|
||||||
fn register_nonce_commitment() -> Weight;
|
|
||||||
fn register_group_commitment() -> Weight;
|
|
||||||
fn register_signature_share() -> Weight;
|
|
||||||
|
|
||||||
fn register_evm_bridge_out_exodus() -> Weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Weight functions for `ghost_exodus`.
|
|
||||||
impl WeightInfo for () {
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Round0Packages` (r:0 w:1)
|
|
||||||
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
fn register_round0_package() -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `351`
|
|
||||||
// Estimated: `3816`
|
|
||||||
// Minimum execution time: 239_012_000 picoseconds.
|
|
||||||
Weight::from_parts(241_388_000, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3816))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(4))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(3))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Round0Packages` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::Round0Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Round1Packages` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::Round1Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// The range of component `c` is `[4, 300]`.
|
|
||||||
fn register_round1_package(c: u32, ) -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `391 + c * (32 ±0)`
|
|
||||||
// Estimated: `3856 + c * (32 ±0)`
|
|
||||||
// Minimum execution time: 1_339_828_000 picoseconds.
|
|
||||||
Weight::from_parts(1_212_625_790, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3856))
|
|
||||||
// Standard Error: 11_831
|
|
||||||
.saturating_add(Weight::from_parts(33_120_868, 0).saturating_mul(c.into()))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(6))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(2))
|
|
||||||
.saturating_add(Weight::from_parts(0, 32).saturating_mul(c.into()))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Round2Packages` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::Round2Packages` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// The range of component `c` is `[1, 37]`.
|
|
||||||
fn register_encrypted_round2_packages(c: u32, ) -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `317 + c * (257 ±0)`
|
|
||||||
// Estimated: `3783 + c * (257 ±0)`
|
|
||||||
// Minimum execution time: 251_478_000 picoseconds.
|
|
||||||
Weight::from_parts(254_144_621, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3783))
|
|
||||||
// Standard Error: 24_613
|
|
||||||
.saturating_add(Weight::from_parts(6_595_819, 0).saturating_mul(c.into()))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(5))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(2))
|
|
||||||
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Complaints` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::Complaints` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// The range of component `c` is `[1, 37]`.
|
|
||||||
fn register_round3_complaints(c: u32, ) -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `317 + c * (257 ±0)`
|
|
||||||
// Estimated: `3783 + c * (257 ±0)`
|
|
||||||
// Minimum execution time: 236_155_000 picoseconds.
|
|
||||||
Weight::from_parts(242_055_948, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3783))
|
|
||||||
// Standard Error: 15_623
|
|
||||||
.saturating_add(Weight::from_parts(360_411, 0).saturating_mul(c.into()))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(5))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(2))
|
|
||||||
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Justifications` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::Justifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// The range of component `c` is `[1, 37]`.
|
|
||||||
fn register_round4_justifications(c: u32, ) -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `317 + c * (257 ±0)`
|
|
||||||
// Estimated: `3783 + c * (257 ±0)`
|
|
||||||
// Minimum execution time: 249_507_000 picoseconds.
|
|
||||||
Weight::from_parts(248_505_595, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3783))
|
|
||||||
// Standard Error: 20_302
|
|
||||||
.saturating_add(Weight::from_parts(4_191_526, 0).saturating_mul(c.into()))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(5))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(2))
|
|
||||||
.saturating_add(Weight::from_parts(0, 257).saturating_mul(c.into()))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::Verifications` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::Verifications` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
fn register_round5_verifying_package() -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `448`
|
|
||||||
// Estimated: `3913`
|
|
||||||
// Minimum execution time: 264_214_000 picoseconds.
|
|
||||||
Weight::from_parts(266_905_000, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3913))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(6))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(3))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::VerifyingKeyConsensus` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::VerifyingKeyConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::VerifyingShares` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::VerifyingShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::VerifyingSharesParticipants` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::VerifyingSharesParticipants` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// The range of component `c` is `[1, 9]`.
|
|
||||||
fn register_round6_public_share_package(c: u32, ) -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `0 + c * (770 ±0)`
|
|
||||||
// Estimated: `1551 + c * (1019 ±27)`
|
|
||||||
// Minimum execution time: 281_073_000 picoseconds.
|
|
||||||
Weight::from_parts(280_778_987, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 1551))
|
|
||||||
// Standard Error: 92_951
|
|
||||||
.saturating_add(Weight::from_parts(3_489_552, 0).saturating_mul(c.into()))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(7))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(3))
|
|
||||||
.saturating_add(Weight::from_parts(0, 1019).saturating_mul(c.into()))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusRequests` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::RoastSessions` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::NonceCommitments` (r:0 w:1)
|
|
||||||
/// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
fn register_nonce_commitment() -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `588`
|
|
||||||
// Estimated: `4053`
|
|
||||||
// Minimum execution time: 291_972_000 picoseconds.
|
|
||||||
Weight::from_parts(295_122_000, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 4053))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(8))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(5))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::RoastSessions` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusRequests` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::GroupCommitters` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::GroupCommitters` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
fn register_group_commitment() -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `968`
|
|
||||||
// Estimated: `4433`
|
|
||||||
// Minimum execution time: 331_528_000 picoseconds.
|
|
||||||
Weight::from_parts(333_974_000, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 4433))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(10))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(4))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::NetworkCurves` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::NetworkCurves` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveDkgAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveDkgAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveAuthorities` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveAuthorities` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::RoastSessions` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::RoastSessions` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusActivity` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusActivity` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::GroupCommitmentsConsensus` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::GroupCommitmentsConsensus` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusRequests` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusSignedGovernance` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusSignedGovernance` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::RoastSessionStates` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::RoastSessionStates` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::NonceCommitments` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::NonceCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::VerifyingShares` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::VerifyingShares` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ActiveVerifyingKey` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::ActiveVerifyingKey` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::SignatureScalars` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::SignatureScalars` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::GroupCommitters` (r:0 w:1)
|
|
||||||
/// Proof: `GhostExodus::GroupCommitters` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
fn register_signature_share() -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `11259`
|
|
||||||
// Estimated: `14724`
|
|
||||||
// Minimum execution time: 3_842_259_000 picoseconds.
|
|
||||||
Weight::from_parts(3_985_167_000, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 14724))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(14))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(8))
|
|
||||||
}
|
|
||||||
/// Storage: `GhostNetworks::Networks` (r:1 w:0)
|
|
||||||
/// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostNetworks::GatekeeperAmounts` (r:1 w:1)
|
|
||||||
/// Proof: `GhostNetworks::GatekeeperAmounts` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostNetworks::NetworkImbalance` (r:1 w:1)
|
|
||||||
/// Proof: `GhostNetworks::NetworkImbalance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::QualificationDkgState` (r:1 w:0)
|
|
||||||
/// Proof: `GhostExodus::QualificationDkgState` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::CurrentExodus` (r:1 w:1)
|
|
||||||
/// Proof: `GhostExodus::CurrentExodus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
|
||||||
/// Storage: `GhostExodus::ExodusRequests` (r:0 w:1)
|
|
||||||
/// Proof: `GhostExodus::ExodusRequests` (`max_values`: None, `max_size`: None, mode: `Measured`)
|
|
||||||
fn register_evm_bridge_out_exodus() -> Weight {
|
|
||||||
// Proof Size summary in bytes:
|
|
||||||
// Measured: `448`
|
|
||||||
// Estimated: `3913`
|
|
||||||
// Minimum execution time: 137_053_000 picoseconds.
|
|
||||||
Weight::from_parts(138_693_000, 0)
|
|
||||||
.saturating_add(Weight::from_parts(0, 3913))
|
|
||||||
.saturating_add(RocksDbWeight::get().reads(5))
|
|
||||||
.saturating_add(RocksDbWeight::get().writes(4))
|
|
||||||
}
|
|
||||||
}
|
|
||||||