mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 21:49:26 +00:00
Compare commits
1 Commits
90804c4c30
...
ff-0.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7e8fd6388 |
2
.github/LICENSE
vendored
2
.github/LICENSE
vendored
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022-2025 Luke Parker
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
2
.github/actions/bitcoin/action.yml
vendored
2
.github/actions/bitcoin/action.yml
vendored
@@ -12,7 +12,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Bitcoin Daemon Cache
|
- name: Bitcoin Daemon Cache
|
||||||
id: cache-bitcoind
|
id: cache-bitcoind
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2
|
||||||
with:
|
with:
|
||||||
path: bitcoin.tar.gz
|
path: bitcoin.tar.gz
|
||||||
key: bitcoind-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}
|
key: bitcoind-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}
|
||||||
|
|||||||
37
.github/actions/build-dependencies/action.yml
vendored
37
.github/actions/build-dependencies/action.yml
vendored
@@ -7,15 +7,13 @@ runs:
|
|||||||
- name: Remove unused packages
|
- name: Remove unused packages
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt remove -y "*powershell*" "*nuget*" "*bazel*" "*ansible*" "*terraform*" "*heroku*" "*aws*" azure-cli
|
sudo apt remove -y "*msbuild*" "*powershell*" "*nuget*" "*bazel*" "*ansible*" "*terraform*" "*heroku*" "*aws*" azure-cli
|
||||||
sudo apt remove -y "*nodejs*" "*npm*" "*yarn*" "*java*" "*kotlin*" "*golang*" "*swift*" "*julia*" "*fortran*" "*android*"
|
sudo apt remove -y "*nodejs*" "*npm*" "*yarn*" "*java*" "*kotlin*" "*golang*" "*swift*" "*julia*" "*fortran*" "*android*"
|
||||||
sudo apt remove -y "*apache2*" "*nginx*" "*firefox*" "*chromium*" "*chrome*" "*edge*"
|
sudo apt remove -y "*apache2*" "*nginx*" "*firefox*" "*chromium*" "*chrome*" "*edge*"
|
||||||
|
|
||||||
sudo apt remove -y --allow-remove-essential -f shim-signed *python3*
|
|
||||||
# This removal command requires the prior removals due to unmet dependencies otherwise
|
|
||||||
sudo apt remove -y "*qemu*" "*sql*" "*texinfo*" "*imagemagick*"
|
sudo apt remove -y "*qemu*" "*sql*" "*texinfo*" "*imagemagick*"
|
||||||
# Reinstall python3 as a general dependency of a functional operating system
|
sudo apt autoremove -y
|
||||||
sudo apt install python3
|
sudo apt clean
|
||||||
|
docker system prune -a --volumes
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
- name: Remove unused packages
|
- name: Remove unused packages
|
||||||
@@ -43,34 +41,9 @@ runs:
|
|||||||
- name: Install solc
|
- name: Install solc
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cargo +1.89 install svm-rs --version =0.5.18
|
cargo install svm-rs
|
||||||
svm install 0.8.26
|
svm install 0.8.26
|
||||||
svm use 0.8.26
|
svm use 0.8.26
|
||||||
|
|
||||||
- name: Remove preinstalled Docker
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
docker system prune -a --volumes
|
|
||||||
sudo apt remove -y *docker*
|
|
||||||
# Install uidmap which will be required for the explicitly installed Docker
|
|
||||||
sudo apt install uidmap
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
|
|
||||||
- name: Update system dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt update -y
|
|
||||||
sudo apt upgrade -y
|
|
||||||
sudo apt autoremove -y
|
|
||||||
sudo apt clean
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
|
|
||||||
- name: Install rootless Docker
|
|
||||||
uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19
|
|
||||||
with:
|
|
||||||
rootless: true
|
|
||||||
set-host: true
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
|
|
||||||
# - name: Cache Rust
|
# - name: Cache Rust
|
||||||
# uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43
|
# uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43
|
||||||
|
|||||||
2
.github/actions/monero-wallet-rpc/action.yml
vendored
2
.github/actions/monero-wallet-rpc/action.yml
vendored
@@ -12,7 +12,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Monero Wallet RPC Cache
|
- name: Monero Wallet RPC Cache
|
||||||
id: cache-monero-wallet-rpc
|
id: cache-monero-wallet-rpc
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2
|
||||||
with:
|
with:
|
||||||
path: monero-wallet-rpc
|
path: monero-wallet-rpc
|
||||||
key: monero-wallet-rpc-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}
|
key: monero-wallet-rpc-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}
|
||||||
|
|||||||
2
.github/actions/monero/action.yml
vendored
2
.github/actions/monero/action.yml
vendored
@@ -12,7 +12,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Monero Daemon Cache
|
- name: Monero Daemon Cache
|
||||||
id: cache-monerod
|
id: cache-monerod
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2
|
||||||
with:
|
with:
|
||||||
path: /usr/bin/monerod
|
path: /usr/bin/monerod
|
||||||
key: monerod-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}
|
key: monerod-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }}
|
||||||
|
|||||||
2
.github/nightly-version
vendored
2
.github/nightly-version
vendored
@@ -1 +1 @@
|
|||||||
nightly-2025-09-01
|
nightly-2025-02-01
|
||||||
|
|||||||
14
.github/workflows/crypto-tests.yml
vendored
14
.github/workflows/crypto-tests.yml
vendored
@@ -32,17 +32,13 @@ jobs:
|
|||||||
-p dalek-ff-group \
|
-p dalek-ff-group \
|
||||||
-p minimal-ed448 \
|
-p minimal-ed448 \
|
||||||
-p ciphersuite \
|
-p ciphersuite \
|
||||||
-p ciphersuite-kp256 \
|
|
||||||
-p multiexp \
|
-p multiexp \
|
||||||
-p schnorr-signatures \
|
-p schnorr-signatures \
|
||||||
-p prime-field \
|
-p dleq \
|
||||||
-p short-weierstrass \
|
-p generalized-bulletproofs \
|
||||||
-p secq256k1 \
|
-p generalized-bulletproofs-circuit-abstraction \
|
||||||
-p embedwards25519 \
|
-p ec-divisors \
|
||||||
|
-p generalized-bulletproofs-ec-gadgets \
|
||||||
-p dkg \
|
-p dkg \
|
||||||
-p dkg-recovery \
|
|
||||||
-p dkg-dealer \
|
|
||||||
-p dkg-musig \
|
|
||||||
-p dkg-evrf \
|
|
||||||
-p modular-frost \
|
-p modular-frost \
|
||||||
-p frost-schnorrkel
|
-p frost-schnorrkel
|
||||||
|
|||||||
6
.github/workflows/daily-deny.yml
vendored
6
.github/workflows/daily-deny.yml
vendored
@@ -12,13 +12,13 @@ jobs:
|
|||||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
- name: Advisory Cache
|
- name: Advisory Cache
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/advisory-db
|
path: ~/.cargo/advisory-db
|
||||||
key: rust-advisory-db
|
key: rust-advisory-db
|
||||||
|
|
||||||
- name: Install cargo deny
|
- name: Install cargo deny
|
||||||
run: cargo +1.89 install cargo-deny --version =0.18.3
|
run: cargo install --locked cargo-deny
|
||||||
|
|
||||||
- name: Run cargo deny
|
- name: Run cargo deny
|
||||||
run: cargo deny -L error --all-features check --hide-inclusion-graph
|
run: cargo deny -L error --all-features check
|
||||||
|
|||||||
105
.github/workflows/lint.yml
vendored
105
.github/workflows/lint.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: ./.github/actions/build-dependencies
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
- name: Install nightly rust
|
- name: Install nightly rust
|
||||||
run: rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32v1-none -c rust-src -c clippy
|
run: rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32-unknown-unknown -c clippy
|
||||||
|
|
||||||
- name: Run Clippy
|
- name: Run Clippy
|
||||||
run: cargo +${{ steps.nightly.outputs.version }} clippy --all-features --all-targets -- -D warnings -A clippy::items_after_test_module
|
run: cargo +${{ steps.nightly.outputs.version }} clippy --all-features --all-targets -- -D warnings -A clippy::items_after_test_module
|
||||||
@@ -46,16 +46,16 @@ jobs:
|
|||||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
- name: Advisory Cache
|
- name: Advisory Cache
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/advisory-db
|
path: ~/.cargo/advisory-db
|
||||||
key: rust-advisory-db
|
key: rust-advisory-db
|
||||||
|
|
||||||
- name: Install cargo deny
|
- name: Install cargo deny
|
||||||
run: cargo +1.89 install cargo-deny --version =0.18.3
|
run: cargo install --locked cargo-deny
|
||||||
|
|
||||||
- name: Run cargo deny
|
- name: Run cargo deny
|
||||||
run: cargo deny -L error --all-features check --hide-inclusion-graph
|
run: cargo deny -L error --all-features check
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -88,101 +88,8 @@ jobs:
|
|||||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
- name: Verify all dependencies are in use
|
- name: Verify all dependencies are in use
|
||||||
run: |
|
run: |
|
||||||
cargo +1.89 install cargo-machete --version =0.8.0
|
cargo install cargo-machete
|
||||||
cargo +1.89 machete
|
cargo machete
|
||||||
|
|
||||||
msrv:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
|
||||||
- name: Verify claimed `rust-version`
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cargo +1.89 install cargo-msrv --version =0.18.4
|
|
||||||
|
|
||||||
function check_msrv {
|
|
||||||
# We `cd` into the directory passed as the first argument, but will return to the
|
|
||||||
# directory called from.
|
|
||||||
return_to=$(pwd)
|
|
||||||
echo "Checking $1"
|
|
||||||
cd $1
|
|
||||||
|
|
||||||
# We then find the existing `rust-version` using `grep` (for the right line) and then a
|
|
||||||
# regex (to strip to just the major and minor version).
|
|
||||||
existing=$(cat ./Cargo.toml | grep "rust-version" | grep -Eo "[0-9]+\.[0-9]+")
|
|
||||||
|
|
||||||
# We then backup the `Cargo.toml`, allowing us to restore it after, saving time on future
|
|
||||||
# MSRV checks (as they'll benefit from immediately exiting if the queried version is less
|
|
||||||
# than the declared MSRV).
|
|
||||||
mv ./Cargo.toml ./Cargo.toml.bak
|
|
||||||
|
|
||||||
# We then use an inverted (`-v`) grep to remove the existing `rust-version` from the
|
|
||||||
# `Cargo.toml`, as required because else earlier versions of Rust won't even attempt to
|
|
||||||
# compile this crate.
|
|
||||||
cat ./Cargo.toml.bak | grep -v "rust-version" > Cargo.toml
|
|
||||||
|
|
||||||
# We then find the actual `rust-version` using `cargo-msrv` (again stripping to just the
|
|
||||||
# major and minor version).
|
|
||||||
actual=$(cargo msrv find --output-format minimal | grep -Eo "^[0-9]+\.[0-9]+")
|
|
||||||
|
|
||||||
# Finally, we compare the two.
|
|
||||||
echo "Declared rust-version: $existing"
|
|
||||||
echo "Actual rust-version: $actual"
|
|
||||||
[ $existing == $actual ]
|
|
||||||
result=$?
|
|
||||||
|
|
||||||
# Restore the original `Cargo.toml`.
|
|
||||||
rm Cargo.toml
|
|
||||||
mv ./Cargo.toml.bak ./Cargo.toml
|
|
||||||
|
|
||||||
# Return to the directory called from and return the result.
|
|
||||||
cd $return_to
|
|
||||||
return $result
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check each member of the workspace
|
|
||||||
function check_workspace {
|
|
||||||
# Get the members array from the workspace's `Cargo.toml`
|
|
||||||
cargo_toml_lines=$(cat ./Cargo.toml | wc -l)
|
|
||||||
members=$(cat Cargo.toml | grep "members\ \=\ \[" -m1 -A$cargo_toml_lines | grep "]" -m1 -B$cargo_toml_lines)
|
|
||||||
# Parse out any comments, including comments post-fixed on the same line as an entry
|
|
||||||
members=$(echo "$members" | grep -Ev "^[[:space:]]+#" | grep -Ev "^[[:space:]]?$" | awk -F',' '{print $1","}')
|
|
||||||
# Prune `members = [` to `[` by replacing the first line with just `[`
|
|
||||||
members=$(echo "$members" | sed "1s/.*/\[/")
|
|
||||||
# Remove the trailing comma by replacing the last line's "," with ""
|
|
||||||
members=$(echo "$members" | sed "$(($(echo "$members" | wc -l) - 1))s/\,//")
|
|
||||||
# Correct the last line, which was malleated to "]," when pruning comments
|
|
||||||
members=$(echo "$members" | sed "$(echo "$members" | wc -l)s/\]\,/\]/")
|
|
||||||
|
|
||||||
# Don't check the patches
|
|
||||||
members=$(echo "$members" | grep -v "patches")
|
|
||||||
# Don't check the following
|
|
||||||
# Most of these are binaries, with the exception of the Substrate runtime which has a
|
|
||||||
# bespoke build pipeline
|
|
||||||
members=$(echo "$members" | grep -v "networks/ethereum/relayer\"")
|
|
||||||
members=$(echo "$members" | grep -v "message-queue\"")
|
|
||||||
members=$(echo "$members" | grep -v "processor/bin\"")
|
|
||||||
members=$(echo "$members" | grep -v "processor/bitcoin\"")
|
|
||||||
members=$(echo "$members" | grep -v "processor/ethereum\"")
|
|
||||||
members=$(echo "$members" | grep -v "processor/monero\"")
|
|
||||||
members=$(echo "$members" | grep -v "coordinator\"")
|
|
||||||
members=$(echo "$members" | grep -v "substrate/runtime\"")
|
|
||||||
members=$(echo "$members" | grep -v "substrate/node\"")
|
|
||||||
members=$(echo "$members" | grep -v "orchestration\"")
|
|
||||||
|
|
||||||
# Don't check the tests
|
|
||||||
members=$(echo "$members" | grep -v "mini\"")
|
|
||||||
members=$(echo "$members" | grep -v "tests/")
|
|
||||||
|
|
||||||
echo $members | jq -r ".[]" | while read -r member; do
|
|
||||||
check_msrv $member
|
|
||||||
correct=$?
|
|
||||||
if [ $correct -ne 0 ]; then
|
|
||||||
return $correct
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
check_workspace
|
|
||||||
|
|
||||||
slither:
|
slither:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
72
.github/workflows/monero-tests.yaml
vendored
Normal file
72
.github/workflows/monero-tests.yaml
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
name: Monero Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
paths:
|
||||||
|
- "networks/monero/**"
|
||||||
|
- "processor/**"
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "networks/monero/**"
|
||||||
|
- "processor/**"
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Only run these once since they will be consistent regardless of any node
|
||||||
|
unit-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Test Dependencies
|
||||||
|
uses: ./.github/actions/test-dependencies
|
||||||
|
|
||||||
|
- name: Run Unit Tests Without Features
|
||||||
|
run: |
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-io --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-generators --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-primitives --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-mlsag --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-clsag --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-borromean --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-bulletproofs --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-serai --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-rpc --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-simple-request-rpc --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-address --lib
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-wallet --lib
|
||||||
|
|
||||||
|
# Doesn't run unit tests with features as the tests workflow will
|
||||||
|
|
||||||
|
integration-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Test against all supported protocol versions
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
version: [v0.17.3.2, v0.18.3.4]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Test Dependencies
|
||||||
|
uses: ./.github/actions/test-dependencies
|
||||||
|
with:
|
||||||
|
monero-version: ${{ matrix.version }}
|
||||||
|
|
||||||
|
- name: Run Integration Tests Without Features
|
||||||
|
run: |
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-serai --test '*'
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-simple-request-rpc --test '*'
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-wallet --test '*'
|
||||||
|
|
||||||
|
- name: Run Integration Tests
|
||||||
|
# Don't run if the the tests workflow also will
|
||||||
|
if: ${{ matrix.version != 'v0.18.3.4' }}
|
||||||
|
run: |
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-serai --all-features --test '*'
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-simple-request-rpc --test '*'
|
||||||
|
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --package monero-wallet --all-features --test '*'
|
||||||
259
.github/workflows/msrv.yml
vendored
Normal file
259
.github/workflows/msrv.yml
vendored
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
name: Weekly MSRV Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * 0"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
msrv-common:
|
||||||
|
name: Run cargo msrv on common
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on common
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path common/zalloc/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path common/std-shims/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path common/env/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path common/db/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path common/task/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path common/request/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path common/patchable-async-sleep/Cargo.toml
|
||||||
|
|
||||||
|
msrv-crypto:
|
||||||
|
name: Run cargo msrv on crypto
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on crypto
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path crypto/transcript/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path crypto/ff-group-tests/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/dalek-ff-group/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/ed448/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path crypto/multiexp/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path crypto/dleq/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/ciphersuite/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/schnorr/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path crypto/evrf/generalized-bulletproofs/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/evrf/circuit-abstraction/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/evrf/divisors/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/evrf/ec-gadgets/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/evrf/embedwards25519/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/evrf/secq256k1/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path crypto/dkg/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/frost/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path crypto/schnorrkel/Cargo.toml
|
||||||
|
|
||||||
|
msrv-networks:
|
||||||
|
name: Run cargo msrv on networks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on networks
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path networks/bitcoin/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path networks/ethereum/build-contracts/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/ethereum/schnorr/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/ethereum/alloy-simple-request-transport/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/ethereum/relayer/Cargo.toml --features parity-db
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path networks/monero/io/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/generators/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/ringct/mlsag/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/ringct/clsag/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/ringct/borromean/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/ringct/bulletproofs/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/rpc/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/rpc/simple-request/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/wallet/address/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/wallet/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path networks/monero/verify-chain/Cargo.toml
|
||||||
|
|
||||||
|
msrv-message-queue:
|
||||||
|
name: Run cargo msrv on message-queue
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on message-queue
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path message-queue/Cargo.toml --features parity-db
|
||||||
|
|
||||||
|
msrv-processor:
|
||||||
|
name: Run cargo msrv on processor
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on processor
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path processor/view-keys/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/messages/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/scanner/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/scheduler/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/scheduler/smart-contract/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/scheduler/utxo/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/scheduler/utxo/standard/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/scheduler/utxo/transaction-chaining/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/key-gen/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/frost-attempt-manager/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/signers/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/bin/Cargo.toml --features parity-db
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/bitcoin/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/ethereum/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/ethereum/test-primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/ethereum/erc20/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/ethereum/deployer/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/ethereum/router/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path processor/ethereum/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path processor/monero/Cargo.toml
|
||||||
|
|
||||||
|
msrv-coordinator:
|
||||||
|
name: Run cargo msrv on coordinator
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on coordinator
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path coordinator/tributary-sdk/tendermint/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/tributary-sdk/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/cosign/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/substrate/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/tributary/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/p2p/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/p2p/libp2p/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path coordinator/Cargo.toml
|
||||||
|
|
||||||
|
msrv-substrate:
|
||||||
|
name: Run cargo msrv on substrate
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on substrate
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path substrate/primitives/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/coins/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/coins/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/dex/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/economic-security/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/genesis-liquidity/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/genesis-liquidity/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/in-instructions/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/in-instructions/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/validator-sets/pallet/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/validator-sets/primitives/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/emissions/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/emissions/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/signals/primitives/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/signals/pallet/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/abi/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/client/Cargo.toml
|
||||||
|
|
||||||
|
cargo msrv verify --manifest-path substrate/runtime/Cargo.toml
|
||||||
|
cargo msrv verify --manifest-path substrate/node/Cargo.toml
|
||||||
|
|
||||||
|
msrv-orchestration:
|
||||||
|
name: Run cargo msrv on orchestration
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on message-queue
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path orchestration/Cargo.toml
|
||||||
|
|
||||||
|
msrv-mini:
|
||||||
|
name: Run cargo msrv on mini
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
|
- name: Install cargo msrv
|
||||||
|
run: cargo install --locked cargo-msrv
|
||||||
|
|
||||||
|
- name: Run cargo msrv on mini
|
||||||
|
run: |
|
||||||
|
cargo msrv verify --manifest-path mini/Cargo.toml
|
||||||
13
.github/workflows/networks-tests.yml
vendored
13
.github/workflows/networks-tests.yml
vendored
@@ -34,3 +34,16 @@ jobs:
|
|||||||
-p ethereum-schnorr-contract \
|
-p ethereum-schnorr-contract \
|
||||||
-p alloy-simple-request-transport \
|
-p alloy-simple-request-transport \
|
||||||
-p serai-ethereum-relayer \
|
-p serai-ethereum-relayer \
|
||||||
|
-p monero-io \
|
||||||
|
-p monero-generators \
|
||||||
|
-p monero-primitives \
|
||||||
|
-p monero-mlsag \
|
||||||
|
-p monero-clsag \
|
||||||
|
-p monero-borromean \
|
||||||
|
-p monero-bulletproofs \
|
||||||
|
-p monero-serai \
|
||||||
|
-p monero-rpc \
|
||||||
|
-p monero-simple-request-rpc \
|
||||||
|
-p monero-address \
|
||||||
|
-p monero-wallet \
|
||||||
|
-p monero-serai-verify-chain
|
||||||
|
|||||||
14
.github/workflows/no-std.yml
vendored
14
.github/workflows/no-std.yml
vendored
@@ -28,18 +28,8 @@ jobs:
|
|||||||
- name: Install Build Dependencies
|
- name: Install Build Dependencies
|
||||||
uses: ./.github/actions/build-dependencies
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
- name: Get nightly version to use
|
|
||||||
id: nightly
|
|
||||||
shell: bash
|
|
||||||
run: echo "version=$(cat .github/nightly-version)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Install RISC-V Toolchain
|
- name: Install RISC-V Toolchain
|
||||||
run: |
|
run: sudo apt update && sudo apt install -y gcc-riscv64-unknown-elf gcc-multilib && rustup target add riscv32imac-unknown-none-elf
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y gcc-riscv64-unknown-elf gcc-multilib
|
|
||||||
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal --component rust-src --target riscv32imac-unknown-none-elf
|
|
||||||
|
|
||||||
- name: Verify no-std builds
|
- name: Verify no-std builds
|
||||||
run: |
|
run: CFLAGS=-I/usr/include cargo build --target riscv32imac-unknown-none-elf -p serai-no-std-tests
|
||||||
CFLAGS=-I/usr/include cargo +${{ steps.nightly.outputs.version }} build --target riscv32imac-unknown-none-elf -Z build-std=core -p serai-no-std-tests
|
|
||||||
CFLAGS=-I/usr/include cargo +${{ steps.nightly.outputs.version }} build --target riscv32imac-unknown-none-elf -Z build-std=core,alloc -p serai-no-std-tests --features "alloc"
|
|
||||||
|
|||||||
12
.github/workflows/pages.yml
vendored
12
.github/workflows/pages.yml
vendored
@@ -46,16 +46,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
uses: actions/checkout@v3
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
cache-version: 0
|
cache-version: 0
|
||||||
working-directory: "${{ github.workspace }}/docs"
|
working-directory: "${{ github.workspace }}/docs"
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
id: pages
|
id: pages
|
||||||
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b
|
uses: actions/configure-pages@v3
|
||||||
- name: Build with Jekyll
|
- name: Build with Jekyll
|
||||||
run: cd ${{ github.workspace }}/docs && bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
|
run: cd ${{ github.workspace }}/docs && bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
|
||||||
env:
|
env:
|
||||||
@@ -69,12 +69,12 @@ jobs:
|
|||||||
uses: ./.github/actions/build-dependencies
|
uses: ./.github/actions/build-dependencies
|
||||||
- name: Buld Rust docs
|
- name: Buld Rust docs
|
||||||
run: |
|
run: |
|
||||||
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32v1-none -c rust-docs -c rust-src
|
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32-unknown-unknown -c rust-docs
|
||||||
RUSTDOCFLAGS="--cfg docsrs" cargo +${{ steps.nightly.outputs.version }} doc --workspace --all-features
|
RUSTDOCFLAGS="--cfg docsrs" cargo +${{ steps.nightly.outputs.version }} doc --workspace --all-features
|
||||||
mv target/doc docs/_site/rust
|
mv target/doc docs/_site/rust
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: "docs/_site/"
|
path: "docs/_site/"
|
||||||
|
|
||||||
@@ -88,4 +88,4 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e
|
uses: actions/deploy-pages@v4
|
||||||
|
|||||||
1
.github/workflows/tests.yml
vendored
1
.github/workflows/tests.yml
vendored
@@ -61,7 +61,6 @@ jobs:
|
|||||||
-p serai-monero-processor \
|
-p serai-monero-processor \
|
||||||
-p tendermint-machine \
|
-p tendermint-machine \
|
||||||
-p tributary-sdk \
|
-p tributary-sdk \
|
||||||
-p serai-cosign-types \
|
|
||||||
-p serai-cosign \
|
-p serai-cosign \
|
||||||
-p serai-coordinator-substrate \
|
-p serai-coordinator-substrate \
|
||||||
-p serai-coordinator-tributary \
|
-p serai-coordinator-tributary \
|
||||||
|
|||||||
6534
Cargo.lock
generated
6534
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
148
Cargo.toml
148
Cargo.toml
@@ -1,17 +1,20 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
|
# Version patches
|
||||||
|
"patches/parking_lot_core",
|
||||||
|
"patches/parking_lot",
|
||||||
|
"patches/zstd",
|
||||||
|
"patches/rocksdb",
|
||||||
|
|
||||||
|
# std patches
|
||||||
|
"patches/matches",
|
||||||
|
"patches/is-terminal",
|
||||||
|
|
||||||
# Rewrites/redirects
|
# Rewrites/redirects
|
||||||
"patches/option-ext",
|
"patches/option-ext",
|
||||||
"patches/directories-next",
|
"patches/directories-next",
|
||||||
|
|
||||||
# monero-oxide expects `ciphersuite`, yet the `ciphersuite` in-tree here has breaking changes
|
|
||||||
# This re-exports the in-tree `ciphersuite` _without_ changes breaking to monero-oxide
|
|
||||||
# Not included in workspace to prevent having two crates with the same name (an error)
|
|
||||||
# "patches/ciphersuite",
|
|
||||||
# Same for `dalek-ff-group`
|
|
||||||
# "patches/dalek-ff-group",
|
|
||||||
|
|
||||||
"common/std-shims",
|
"common/std-shims",
|
||||||
"common/zalloc",
|
"common/zalloc",
|
||||||
"common/patchable-async-sleep",
|
"common/patchable-async-sleep",
|
||||||
@@ -26,21 +29,19 @@ members = [
|
|||||||
"crypto/dalek-ff-group",
|
"crypto/dalek-ff-group",
|
||||||
"crypto/ed448",
|
"crypto/ed448",
|
||||||
"crypto/ciphersuite",
|
"crypto/ciphersuite",
|
||||||
"crypto/ciphersuite/kp256",
|
|
||||||
|
|
||||||
"crypto/multiexp",
|
"crypto/multiexp",
|
||||||
"crypto/schnorr",
|
"crypto/schnorr",
|
||||||
|
"crypto/dleq",
|
||||||
|
|
||||||
"crypto/prime-field",
|
"crypto/evrf/secq256k1",
|
||||||
"crypto/short-weierstrass",
|
"crypto/evrf/embedwards25519",
|
||||||
"crypto/secq256k1",
|
"crypto/evrf/generalized-bulletproofs",
|
||||||
"crypto/embedwards25519",
|
"crypto/evrf/circuit-abstraction",
|
||||||
|
"crypto/evrf/divisors",
|
||||||
|
"crypto/evrf/ec-gadgets",
|
||||||
|
|
||||||
"crypto/dkg",
|
"crypto/dkg",
|
||||||
"crypto/dkg/recovery",
|
|
||||||
"crypto/dkg/dealer",
|
|
||||||
"crypto/dkg/musig",
|
|
||||||
"crypto/dkg/evrf",
|
|
||||||
"crypto/frost",
|
"crypto/frost",
|
||||||
"crypto/schnorrkel",
|
"crypto/schnorrkel",
|
||||||
|
|
||||||
@@ -51,6 +52,20 @@ members = [
|
|||||||
"networks/ethereum/alloy-simple-request-transport",
|
"networks/ethereum/alloy-simple-request-transport",
|
||||||
"networks/ethereum/relayer",
|
"networks/ethereum/relayer",
|
||||||
|
|
||||||
|
"networks/monero/io",
|
||||||
|
"networks/monero/generators",
|
||||||
|
"networks/monero/primitives",
|
||||||
|
"networks/monero/ringct/mlsag",
|
||||||
|
"networks/monero/ringct/clsag",
|
||||||
|
"networks/monero/ringct/borromean",
|
||||||
|
"networks/monero/ringct/bulletproofs",
|
||||||
|
"networks/monero",
|
||||||
|
"networks/monero/rpc",
|
||||||
|
"networks/monero/rpc/simple-request",
|
||||||
|
"networks/monero/wallet/address",
|
||||||
|
"networks/monero/wallet",
|
||||||
|
"networks/monero/verify-chain",
|
||||||
|
|
||||||
"message-queue",
|
"message-queue",
|
||||||
|
|
||||||
"processor/messages",
|
"processor/messages",
|
||||||
@@ -80,7 +95,6 @@ members = [
|
|||||||
|
|
||||||
"coordinator/tributary-sdk/tendermint",
|
"coordinator/tributary-sdk/tendermint",
|
||||||
"coordinator/tributary-sdk",
|
"coordinator/tributary-sdk",
|
||||||
"coordinator/cosign/types",
|
|
||||||
"coordinator/cosign",
|
"coordinator/cosign",
|
||||||
"coordinator/substrate",
|
"coordinator/substrate",
|
||||||
"coordinator/tributary",
|
"coordinator/tributary",
|
||||||
@@ -89,16 +103,30 @@ members = [
|
|||||||
"coordinator",
|
"coordinator",
|
||||||
|
|
||||||
"substrate/primitives",
|
"substrate/primitives",
|
||||||
"substrate/abi",
|
|
||||||
|
|
||||||
"substrate/coins",
|
"substrate/coins/primitives",
|
||||||
"substrate/validator-sets",
|
"substrate/coins/pallet",
|
||||||
"substrate/signals",
|
|
||||||
"substrate/dex",
|
"substrate/dex/pallet",
|
||||||
"substrate/genesis-liquidity",
|
|
||||||
"substrate/economic-security",
|
"substrate/validator-sets/primitives",
|
||||||
"substrate/emissions",
|
"substrate/validator-sets/pallet",
|
||||||
"substrate/in-instructions",
|
|
||||||
|
"substrate/genesis-liquidity/primitives",
|
||||||
|
"substrate/genesis-liquidity/pallet",
|
||||||
|
|
||||||
|
"substrate/emissions/primitives",
|
||||||
|
"substrate/emissions/pallet",
|
||||||
|
|
||||||
|
"substrate/economic-security/pallet",
|
||||||
|
|
||||||
|
"substrate/in-instructions/primitives",
|
||||||
|
"substrate/in-instructions/pallet",
|
||||||
|
|
||||||
|
"substrate/signals/primitives",
|
||||||
|
"substrate/signals/pallet",
|
||||||
|
|
||||||
|
"substrate/abi",
|
||||||
|
|
||||||
"substrate/runtime",
|
"substrate/runtime",
|
||||||
"substrate/node",
|
"substrate/node",
|
||||||
@@ -119,70 +147,56 @@ members = [
|
|||||||
"tests/reproducible-runtime",
|
"tests/reproducible-runtime",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev.package]
|
|
||||||
# Always compile Monero (and a variety of dependencies) with optimizations due
|
# Always compile Monero (and a variety of dependencies) with optimizations due
|
||||||
# to the extensive operations required for Bulletproofs
|
# to the extensive operations required for Bulletproofs
|
||||||
|
[profile.dev.package]
|
||||||
subtle = { opt-level = 3 }
|
subtle = { opt-level = 3 }
|
||||||
|
|
||||||
sha3 = { opt-level = 3 }
|
|
||||||
blake2 = { opt-level = 3 }
|
|
||||||
|
|
||||||
ff = { opt-level = 3 }
|
ff = { opt-level = 3 }
|
||||||
group = { opt-level = 3 }
|
group = { opt-level = 3 }
|
||||||
|
|
||||||
crypto-bigint = { opt-level = 3 }
|
crypto-bigint = { opt-level = 3 }
|
||||||
|
secp256k1 = { opt-level = 3 }
|
||||||
curve25519-dalek = { opt-level = 3 }
|
curve25519-dalek = { opt-level = 3 }
|
||||||
dalek-ff-group = { opt-level = 3 }
|
dalek-ff-group = { opt-level = 3 }
|
||||||
|
minimal-ed448 = { opt-level = 3 }
|
||||||
|
|
||||||
multiexp = { opt-level = 3 }
|
multiexp = { opt-level = 3 }
|
||||||
|
|
||||||
|
secq256k1 = { opt-level = 3 }
|
||||||
|
embedwards25519 = { opt-level = 3 }
|
||||||
|
generalized-bulletproofs = { opt-level = 3 }
|
||||||
|
generalized-bulletproofs-circuit-abstraction = { opt-level = 3 }
|
||||||
|
ec-divisors = { opt-level = 3 }
|
||||||
|
generalized-bulletproofs-ec-gadgets = { opt-level = 3 }
|
||||||
|
|
||||||
|
dkg = { opt-level = 3 }
|
||||||
|
|
||||||
monero-generators = { opt-level = 3 }
|
monero-generators = { opt-level = 3 }
|
||||||
monero-borromean = { opt-level = 3 }
|
monero-borromean = { opt-level = 3 }
|
||||||
monero-bulletproofs = { opt-level = 3 }
|
monero-bulletproofs = { opt-level = 3 }
|
||||||
monero-mlsag = { opt-level = 3 }
|
monero-mlsag = { opt-level = 3 }
|
||||||
monero-clsag = { opt-level = 3 }
|
monero-clsag = { opt-level = 3 }
|
||||||
monero-oxide = { opt-level = 3 }
|
|
||||||
|
|
||||||
# Always compile the eVRF DKG tree with optimizations as well
|
|
||||||
secp256k1 = { opt-level = 3 }
|
|
||||||
secq256k1 = { opt-level = 3 }
|
|
||||||
embedwards25519 = { opt-level = 3 }
|
|
||||||
generalized-bulletproofs = { opt-level = 3 }
|
|
||||||
generalized-bulletproofs-circuit-abstraction = { opt-level = 3 }
|
|
||||||
generalized-bulletproofs-ec-gadgets = { opt-level = 3 }
|
|
||||||
|
|
||||||
# revm also effectively requires being built with optimizations
|
|
||||||
revm = { opt-level = 3 }
|
|
||||||
revm-bytecode = { opt-level = 3 }
|
|
||||||
revm-context = { opt-level = 3 }
|
|
||||||
revm-context-interface = { opt-level = 3 }
|
|
||||||
revm-database = { opt-level = 3 }
|
|
||||||
revm-database-interface = { opt-level = 3 }
|
|
||||||
revm-handler = { opt-level = 3 }
|
|
||||||
revm-inspector = { opt-level = 3 }
|
|
||||||
revm-interpreter = { opt-level = 3 }
|
|
||||||
revm-precompile = { opt-level = 3 }
|
|
||||||
revm-primitives = { opt-level = 3 }
|
|
||||||
revm-state = { opt-level = 3 }
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "unwind"
|
panic = "unwind"
|
||||||
overflow-checks = true
|
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Dependencies from monero-oxide which originate from within our own tree
|
|
||||||
std-shims = { path = "common/std-shims" }
|
|
||||||
simple-request = { path = "common/request" }
|
|
||||||
multiexp = { path = "crypto/multiexp" }
|
|
||||||
flexible-transcript = { path = "crypto/transcript" }
|
|
||||||
ciphersuite = { path = "patches/ciphersuite" }
|
|
||||||
dalek-ff-group = { path = "patches/dalek-ff-group" }
|
|
||||||
minimal-ed448 = { path = "crypto/ed448" }
|
|
||||||
modular-frost = { path = "crypto/frost" }
|
|
||||||
|
|
||||||
# https://github.com/rust-lang-nursery/lazy-static.rs/issues/201
|
# https://github.com/rust-lang-nursery/lazy-static.rs/issues/201
|
||||||
lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev = "5735630d46572f1e5377c8f2ba0f79d18f53b10c" }
|
lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev = "5735630d46572f1e5377c8f2ba0f79d18f53b10c" }
|
||||||
|
|
||||||
|
parking_lot_core = { path = "patches/parking_lot_core" }
|
||||||
|
parking_lot = { path = "patches/parking_lot" }
|
||||||
|
# wasmtime pulls in an old version for this
|
||||||
|
zstd = { path = "patches/zstd" }
|
||||||
|
# Needed for WAL compression
|
||||||
|
rocksdb = { path = "patches/rocksdb" }
|
||||||
|
|
||||||
|
# is-terminal now has an std-based solution with an equivalent API
|
||||||
|
is-terminal = { path = "patches/is-terminal" }
|
||||||
|
# So does matches
|
||||||
|
matches = { path = "patches/matches" }
|
||||||
|
|
||||||
# directories-next was created because directories was unmaintained
|
# directories-next was created because directories was unmaintained
|
||||||
# directories-next is now unmaintained while directories is maintained
|
# directories-next is now unmaintained while directories is maintained
|
||||||
# The directories author pulls in ridiculously pointless crates and prefers
|
# The directories author pulls in ridiculously pointless crates and prefers
|
||||||
@@ -191,13 +205,7 @@ lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev
|
|||||||
option-ext = { path = "patches/option-ext" }
|
option-ext = { path = "patches/option-ext" }
|
||||||
directories-next = { path = "patches/directories-next" }
|
directories-next = { path = "patches/directories-next" }
|
||||||
|
|
||||||
# Patch to include `FromUniformBytes<64>` over Scalar
|
|
||||||
k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
|
|
||||||
p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
incompatible_msrv = "allow" # Manually verified with a GitHub workflow
|
|
||||||
manual_is_multiple_of = "allow"
|
|
||||||
unwrap_or_default = "allow"
|
unwrap_or_default = "allow"
|
||||||
map_unwrap_or = "allow"
|
map_unwrap_or = "allow"
|
||||||
needless_continue = "allow"
|
needless_continue = "allow"
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ issued at the discretion of the Immunefi program managers.
|
|||||||
- [Website](https://serai.exchange/): https://serai.exchange/
|
- [Website](https://serai.exchange/): https://serai.exchange/
|
||||||
- [Immunefi](https://immunefi.com/bounty/serai/): https://immunefi.com/bounty/serai/
|
- [Immunefi](https://immunefi.com/bounty/serai/): https://immunefi.com/bounty/serai/
|
||||||
- [Twitter](https://twitter.com/SeraiDEX): https://twitter.com/SeraiDEX
|
- [Twitter](https://twitter.com/SeraiDEX): https://twitter.com/SeraiDEX
|
||||||
|
- [Mastodon](https://cryptodon.lol/@serai): https://cryptodon.lol/@serai
|
||||||
- [Discord](https://discord.gg/mpEUtJR3vz): https://discord.gg/mpEUtJR3vz
|
- [Discord](https://discord.gg/mpEUtJR3vz): https://discord.gg/mpEUtJR3vz
|
||||||
- [Matrix](https://matrix.to/#/#serai:matrix.org): https://matrix.to/#/#serai:matrix.org
|
- [Matrix](https://matrix.to/#/#serai:matrix.org): https://matrix.to/#/#serai:matrix.org
|
||||||
- [Reddit](https://www.reddit.com/r/SeraiDEX/): https://www.reddit.com/r/SeraiDEX/
|
- [Reddit](https://www.reddit.com/r/SeraiDEX/): https://www.reddit.com/r/SeraiDEX/
|
||||||
|
|||||||
Binary file not shown.
427
audits/Trail of Bits ethereum contracts April 2025/LICENSE
Normal file
427
audits/Trail of Bits ethereum contracts April 2025/LICENSE
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
Attribution-ShareAlike 4.0 International
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||||
|
does not provide legal services or legal advice. Distribution of
|
||||||
|
Creative Commons public licenses does not create a lawyer-client or
|
||||||
|
other relationship. Creative Commons makes its licenses and related
|
||||||
|
information available on an "as-is" basis. Creative Commons gives no
|
||||||
|
warranties regarding its licenses, any material licensed under their
|
||||||
|
terms and conditions, or any related information. Creative Commons
|
||||||
|
disclaims all liability for damages resulting from their use to the
|
||||||
|
fullest extent possible.
|
||||||
|
|
||||||
|
Using Creative Commons Public Licenses
|
||||||
|
|
||||||
|
Creative Commons public licenses provide a standard set of terms and
|
||||||
|
conditions that creators and other rights holders may use to share
|
||||||
|
original works of authorship and other material subject to copyright
|
||||||
|
and certain other rights specified in the public license below. The
|
||||||
|
following considerations are for informational purposes only, are not
|
||||||
|
exhaustive, and do not form part of our licenses.
|
||||||
|
|
||||||
|
Considerations for licensors: Our public licenses are
|
||||||
|
intended for use by those authorized to give the public
|
||||||
|
permission to use material in ways otherwise restricted by
|
||||||
|
copyright and certain other rights. Our licenses are
|
||||||
|
irrevocable. Licensors should read and understand the terms
|
||||||
|
and conditions of the license they choose before applying it.
|
||||||
|
Licensors should also secure all rights necessary before
|
||||||
|
applying our licenses so that the public can reuse the
|
||||||
|
material as expected. Licensors should clearly mark any
|
||||||
|
material not subject to the license. This includes other CC-
|
||||||
|
licensed material, or material used under an exception or
|
||||||
|
limitation to copyright. More considerations for licensors:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensors
|
||||||
|
|
||||||
|
Considerations for the public: By using one of our public
|
||||||
|
licenses, a licensor grants the public permission to use the
|
||||||
|
licensed material under specified terms and conditions. If
|
||||||
|
the licensor's permission is not necessary for any reason--for
|
||||||
|
example, because of any applicable exception or limitation to
|
||||||
|
copyright--then that use is not regulated by the license. Our
|
||||||
|
licenses grant only permissions under copyright and certain
|
||||||
|
other rights that a licensor has authority to grant. Use of
|
||||||
|
the licensed material may still be restricted for other
|
||||||
|
reasons, including because others have copyright or other
|
||||||
|
rights in the material. A licensor may make special requests,
|
||||||
|
such as asking that all changes be marked or described.
|
||||||
|
Although not required by our licenses, you are encouraged to
|
||||||
|
respect those requests where reasonable. More considerations
|
||||||
|
for the public:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensees
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||||
|
License
|
||||||
|
|
||||||
|
By exercising the Licensed Rights (defined below), You accept and agree
|
||||||
|
to be bound by the terms and conditions of this Creative Commons
|
||||||
|
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||||
|
License"). To the extent this Public License may be interpreted as a
|
||||||
|
contract, You are granted the Licensed Rights in consideration of Your
|
||||||
|
acceptance of these terms and conditions, and the Licensor grants You
|
||||||
|
such rights in consideration of benefits the Licensor receives from
|
||||||
|
making the Licensed Material available under these terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
|
||||||
|
Section 1 -- Definitions.
|
||||||
|
|
||||||
|
a. Adapted Material means material subject to Copyright and Similar
|
||||||
|
Rights that is derived from or based upon the Licensed Material
|
||||||
|
and in which the Licensed Material is translated, altered,
|
||||||
|
arranged, transformed, or otherwise modified in a manner requiring
|
||||||
|
permission under the Copyright and Similar Rights held by the
|
||||||
|
Licensor. For purposes of this Public License, where the Licensed
|
||||||
|
Material is a musical work, performance, or sound recording,
|
||||||
|
Adapted Material is always produced where the Licensed Material is
|
||||||
|
synched in timed relation with a moving image.
|
||||||
|
|
||||||
|
b. Adapter's License means the license You apply to Your Copyright
|
||||||
|
and Similar Rights in Your contributions to Adapted Material in
|
||||||
|
accordance with the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
c. BY-SA Compatible License means a license listed at
|
||||||
|
creativecommons.org/compatiblelicenses, approved by Creative
|
||||||
|
Commons as essentially the equivalent of this Public License.
|
||||||
|
|
||||||
|
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||||
|
closely related to copyright including, without limitation,
|
||||||
|
performance, broadcast, sound recording, and Sui Generis Database
|
||||||
|
Rights, without regard to how the rights are labeled or
|
||||||
|
categorized. For purposes of this Public License, the rights
|
||||||
|
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||||
|
Rights.
|
||||||
|
|
||||||
|
e. Effective Technological Measures means those measures that, in the
|
||||||
|
absence of proper authority, may not be circumvented under laws
|
||||||
|
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||||
|
Treaty adopted on December 20, 1996, and/or similar international
|
||||||
|
agreements.
|
||||||
|
|
||||||
|
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||||
|
any other exception or limitation to Copyright and Similar Rights
|
||||||
|
that applies to Your use of the Licensed Material.
|
||||||
|
|
||||||
|
g. License Elements means the license attributes listed in the name
|
||||||
|
of a Creative Commons Public License. The License Elements of this
|
||||||
|
Public License are Attribution and ShareAlike.
|
||||||
|
|
||||||
|
h. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
i. Licensed Rights means the rights granted to You subject to the
|
||||||
|
terms and conditions of this Public License, which are limited to
|
||||||
|
all Copyright and Similar Rights that apply to Your use of the
|
||||||
|
Licensed Material and that the Licensor has authority to license.
|
||||||
|
|
||||||
|
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
k. Share means to provide material to the public by any means or
|
||||||
|
process that requires permission under the Licensed Rights, such
|
||||||
|
as reproduction, public display, public performance, distribution,
|
||||||
|
dissemination, communication, or importation, and to make material
|
||||||
|
available to the public including in ways that members of the
|
||||||
|
public may access the material from a place and at a time
|
||||||
|
individually chosen by them.
|
||||||
|
|
||||||
|
l. Sui Generis Database Rights means rights other than copyright
|
||||||
|
resulting from Directive 96/9/EC of the European Parliament and of
|
||||||
|
the Council of 11 March 1996 on the legal protection of databases,
|
||||||
|
as amended and/or succeeded, as well as other essentially
|
||||||
|
equivalent rights anywhere in the world.
|
||||||
|
|
||||||
|
m. You means the individual or entity exercising the Licensed Rights
|
||||||
|
under this Public License. Your has a corresponding meaning.
|
||||||
|
|
||||||
|
|
||||||
|
Section 2 -- Scope.
|
||||||
|
|
||||||
|
a. License grant.
|
||||||
|
|
||||||
|
1. Subject to the terms and conditions of this Public License,
|
||||||
|
the Licensor hereby grants You a worldwide, royalty-free,
|
||||||
|
non-sublicensable, non-exclusive, irrevocable license to
|
||||||
|
exercise the Licensed Rights in the Licensed Material to:
|
||||||
|
|
||||||
|
a. reproduce and Share the Licensed Material, in whole or
|
||||||
|
in part; and
|
||||||
|
|
||||||
|
b. produce, reproduce, and Share Adapted Material.
|
||||||
|
|
||||||
|
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||||
|
Exceptions and Limitations apply to Your use, this Public
|
||||||
|
License does not apply, and You do not need to comply with
|
||||||
|
its terms and conditions.
|
||||||
|
|
||||||
|
3. Term. The term of this Public License is specified in Section
|
||||||
|
6(a).
|
||||||
|
|
||||||
|
4. Media and formats; technical modifications allowed. The
|
||||||
|
Licensor authorizes You to exercise the Licensed Rights in
|
||||||
|
all media and formats whether now known or hereafter created,
|
||||||
|
and to make technical modifications necessary to do so. The
|
||||||
|
Licensor waives and/or agrees not to assert any right or
|
||||||
|
authority to forbid You from making technical modifications
|
||||||
|
necessary to exercise the Licensed Rights, including
|
||||||
|
technical modifications necessary to circumvent Effective
|
||||||
|
Technological Measures. For purposes of this Public License,
|
||||||
|
simply making modifications authorized by this Section 2(a)
|
||||||
|
(4) never produces Adapted Material.
|
||||||
|
|
||||||
|
5. Downstream recipients.
|
||||||
|
|
||||||
|
a. Offer from the Licensor -- Licensed Material. Every
|
||||||
|
recipient of the Licensed Material automatically
|
||||||
|
receives an offer from the Licensor to exercise the
|
||||||
|
Licensed Rights under the terms and conditions of this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
b. Additional offer from the Licensor -- Adapted Material.
|
||||||
|
Every recipient of Adapted Material from You
|
||||||
|
automatically receives an offer from the Licensor to
|
||||||
|
exercise the Licensed Rights in the Adapted Material
|
||||||
|
under the conditions of the Adapter's License You apply.
|
||||||
|
|
||||||
|
c. No downstream restrictions. You may not offer or impose
|
||||||
|
any additional or different terms or conditions on, or
|
||||||
|
apply any Effective Technological Measures to, the
|
||||||
|
Licensed Material if doing so restricts exercise of the
|
||||||
|
Licensed Rights by any recipient of the Licensed
|
||||||
|
Material.
|
||||||
|
|
||||||
|
6. No endorsement. Nothing in this Public License constitutes or
|
||||||
|
may be construed as permission to assert or imply that You
|
||||||
|
are, or that Your use of the Licensed Material is, connected
|
||||||
|
with, or sponsored, endorsed, or granted official status by,
|
||||||
|
the Licensor or others designated to receive attribution as
|
||||||
|
provided in Section 3(a)(1)(A)(i).
|
||||||
|
|
||||||
|
b. Other rights.
|
||||||
|
|
||||||
|
1. Moral rights, such as the right of integrity, are not
|
||||||
|
licensed under this Public License, nor are publicity,
|
||||||
|
privacy, and/or other similar personality rights; however, to
|
||||||
|
the extent possible, the Licensor waives and/or agrees not to
|
||||||
|
assert any such rights held by the Licensor to the limited
|
||||||
|
extent necessary to allow You to exercise the Licensed
|
||||||
|
Rights, but not otherwise.
|
||||||
|
|
||||||
|
2. Patent and trademark rights are not licensed under this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
3. To the extent possible, the Licensor waives any right to
|
||||||
|
collect royalties from You for the exercise of the Licensed
|
||||||
|
Rights, whether directly or through a collecting society
|
||||||
|
under any voluntary or waivable statutory or compulsory
|
||||||
|
licensing scheme. In all other cases the Licensor expressly
|
||||||
|
reserves any right to collect such royalties.
|
||||||
|
|
||||||
|
|
||||||
|
Section 3 -- License Conditions.
|
||||||
|
|
||||||
|
Your exercise of the Licensed Rights is expressly made subject to the
|
||||||
|
following conditions.
|
||||||
|
|
||||||
|
a. Attribution.
|
||||||
|
|
||||||
|
1. If You Share the Licensed Material (including in modified
|
||||||
|
form), You must:
|
||||||
|
|
||||||
|
a. retain the following if it is supplied by the Licensor
|
||||||
|
with the Licensed Material:
|
||||||
|
|
||||||
|
i. identification of the creator(s) of the Licensed
|
||||||
|
Material and any others designated to receive
|
||||||
|
attribution, in any reasonable manner requested by
|
||||||
|
the Licensor (including by pseudonym if
|
||||||
|
designated);
|
||||||
|
|
||||||
|
ii. a copyright notice;
|
||||||
|
|
||||||
|
iii. a notice that refers to this Public License;
|
||||||
|
|
||||||
|
iv. a notice that refers to the disclaimer of
|
||||||
|
warranties;
|
||||||
|
|
||||||
|
v. a URI or hyperlink to the Licensed Material to the
|
||||||
|
extent reasonably practicable;
|
||||||
|
|
||||||
|
b. indicate if You modified the Licensed Material and
|
||||||
|
retain an indication of any previous modifications; and
|
||||||
|
|
||||||
|
c. indicate the Licensed Material is licensed under this
|
||||||
|
Public License, and include the text of, or the URI or
|
||||||
|
hyperlink to, this Public License.
|
||||||
|
|
||||||
|
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||||
|
reasonable manner based on the medium, means, and context in
|
||||||
|
which You Share the Licensed Material. For example, it may be
|
||||||
|
reasonable to satisfy the conditions by providing a URI or
|
||||||
|
hyperlink to a resource that includes the required
|
||||||
|
information.
|
||||||
|
|
||||||
|
3. If requested by the Licensor, You must remove any of the
|
||||||
|
information required by Section 3(a)(1)(A) to the extent
|
||||||
|
reasonably practicable.
|
||||||
|
|
||||||
|
b. ShareAlike.
|
||||||
|
|
||||||
|
In addition to the conditions in Section 3(a), if You Share
|
||||||
|
Adapted Material You produce, the following conditions also apply.
|
||||||
|
|
||||||
|
1. The Adapter's License You apply must be a Creative Commons
|
||||||
|
license with the same License Elements, this version or
|
||||||
|
later, or a BY-SA Compatible License.
|
||||||
|
|
||||||
|
2. You must include the text of, or the URI or hyperlink to, the
|
||||||
|
Adapter's License You apply. You may satisfy this condition
|
||||||
|
in any reasonable manner based on the medium, means, and
|
||||||
|
context in which You Share Adapted Material.
|
||||||
|
|
||||||
|
3. You may not offer or impose any additional or different terms
|
||||||
|
or conditions on, or apply any Effective Technological
|
||||||
|
Measures to, Adapted Material that restrict exercise of the
|
||||||
|
rights granted under the Adapter's License You apply.
|
||||||
|
|
||||||
|
|
||||||
|
Section 4 -- Sui Generis Database Rights.
|
||||||
|
|
||||||
|
Where the Licensed Rights include Sui Generis Database Rights that
|
||||||
|
apply to Your use of the Licensed Material:
|
||||||
|
|
||||||
|
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||||
|
to extract, reuse, reproduce, and Share all or a substantial
|
||||||
|
portion of the contents of the database;
|
||||||
|
|
||||||
|
b. if You include all or a substantial portion of the database
|
||||||
|
contents in a database in which You have Sui Generis Database
|
||||||
|
Rights, then the database in which You have Sui Generis Database
|
||||||
|
Rights (but not its individual contents) is Adapted Material,
|
||||||
|
|
||||||
|
including for purposes of Section 3(b); and
|
||||||
|
c. You must comply with the conditions in Section 3(a) if You Share
|
||||||
|
all or a substantial portion of the contents of the database.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 4 supplements and does not
|
||||||
|
replace Your obligations under this Public License where the Licensed
|
||||||
|
Rights include other Copyright and Similar Rights.
|
||||||
|
|
||||||
|
|
||||||
|
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||||
|
|
||||||
|
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||||
|
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||||
|
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||||
|
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||||
|
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||||
|
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||||
|
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||||
|
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||||
|
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||||
|
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||||
|
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||||
|
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||||
|
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||||
|
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||||
|
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||||
|
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
c. The disclaimer of warranties and limitation of liability provided
|
||||||
|
above shall be interpreted in a manner that, to the extent
|
||||||
|
possible, most closely approximates an absolute disclaimer and
|
||||||
|
waiver of all liability.
|
||||||
|
|
||||||
|
|
||||||
|
Section 6 -- Term and Termination.
|
||||||
|
|
||||||
|
a. This Public License applies for the term of the Copyright and
|
||||||
|
Similar Rights licensed here. However, if You fail to comply with
|
||||||
|
this Public License, then Your rights under this Public License
|
||||||
|
terminate automatically.
|
||||||
|
|
||||||
|
b. Where Your right to use the Licensed Material has terminated under
|
||||||
|
Section 6(a), it reinstates:
|
||||||
|
|
||||||
|
1. automatically as of the date the violation is cured, provided
|
||||||
|
it is cured within 30 days of Your discovery of the
|
||||||
|
violation; or
|
||||||
|
|
||||||
|
2. upon express reinstatement by the Licensor.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||||
|
right the Licensor may have to seek remedies for Your violations
|
||||||
|
of this Public License.
|
||||||
|
|
||||||
|
c. For the avoidance of doubt, the Licensor may also offer the
|
||||||
|
Licensed Material under separate terms or conditions or stop
|
||||||
|
distributing the Licensed Material at any time; however, doing so
|
||||||
|
will not terminate this Public License.
|
||||||
|
|
||||||
|
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 7 -- Other Terms and Conditions.
|
||||||
|
|
||||||
|
a. The Licensor shall not be bound by any additional or different
|
||||||
|
terms or conditions communicated by You unless expressly agreed.
|
||||||
|
|
||||||
|
b. Any arrangements, understandings, or agreements regarding the
|
||||||
|
Licensed Material not stated herein are separate from and
|
||||||
|
independent of the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 8 -- Interpretation.
|
||||||
|
|
||||||
|
a. For the avoidance of doubt, this Public License does not, and
|
||||||
|
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||||
|
conditions on any use of the Licensed Material that could lawfully
|
||||||
|
be made without permission under this Public License.
|
||||||
|
|
||||||
|
b. To the extent possible, if any provision of this Public License is
|
||||||
|
deemed unenforceable, it shall be automatically reformed to the
|
||||||
|
minimum extent necessary to make it enforceable. If the provision
|
||||||
|
cannot be reformed, it shall be severed from this Public License
|
||||||
|
without affecting the enforceability of the remaining terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
c. No term or condition of this Public License will be waived and no
|
||||||
|
failure to comply consented to unless expressly agreed to by the
|
||||||
|
Licensor.
|
||||||
|
|
||||||
|
d. Nothing in this Public License constitutes or may be interpreted
|
||||||
|
as a limitation upon, or waiver of, any privileges and immunities
|
||||||
|
that apply to the Licensor or You, including from the legal
|
||||||
|
processes of any jurisdiction or authority.
|
||||||
|
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons is not a party to its public
|
||||||
|
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||||
|
its public licenses to material it publishes and in those instances
|
||||||
|
will be considered the “Licensor.” The text of the Creative Commons
|
||||||
|
public licenses is dedicated to the public domain under the CC0 Public
|
||||||
|
Domain Dedication. Except for the limited purpose of indicating that
|
||||||
|
material is shared under a Creative Commons public license or as
|
||||||
|
otherwise permitted by the Creative Commons policies published at
|
||||||
|
creativecommons.org/policies, Creative Commons does not authorize the
|
||||||
|
use of the trademark "Creative Commons" or any other trademark or logo
|
||||||
|
of Creative Commons without its prior written consent including,
|
||||||
|
without limitation, in connection with any unauthorized modifications
|
||||||
|
to any of its public licenses or any other arrangements,
|
||||||
|
understandings, or agreements concerning use of licensed material. For
|
||||||
|
the avoidance of doubt, this paragraph does not form part of the
|
||||||
|
public licenses.
|
||||||
|
|
||||||
|
Creative Commons may be contacted at creativecommons.org.
|
||||||
@@ -11,4 +11,4 @@ It is encompassing up to commit 4e0c58464fc4673623938335f06e2e9ea96ca8dd.
|
|||||||
|
|
||||||
Please see
|
Please see
|
||||||
https://github.com/trailofbits/publications/blob/30c4fa3ebf39ff8e4d23ba9567344ec9691697b5/reviews/2025-04-serai-dex-security-review.pdf
|
https://github.com/trailofbits/publications/blob/30c4fa3ebf39ff8e4d23ba9567344ec9691697b5/reviews/2025-04-serai-dex-security-review.pdf
|
||||||
for the actual report.
|
for provenance.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/common/db"
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.71"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -18,7 +18,7 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-db = { version = "0.4", default-features = false, optional = true }
|
parity-db = { version = "0.4", default-features = false, optional = true }
|
||||||
rocksdb = { version = "0.24", default-features = false, features = ["zstd"], optional = true }
|
rocksdb = { version = "0.23", default-features = false, features = ["zstd"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
parity-db = ["dep:parity-db"]
|
parity-db = ["dep:parity-db"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022-2025 Luke Parker
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub fn serai_db_key(
|
|||||||
///
|
///
|
||||||
/// Creates a unit struct and a default implementation for the `key`, `get`, and `set`. The macro
|
/// Creates a unit struct and a default implementation for the `key`, `get`, and `set`. The macro
|
||||||
/// uses a syntax similar to defining a function. Parameters are concatenated to produce a key,
|
/// uses a syntax similar to defining a function. Parameters are concatenated to produce a key,
|
||||||
/// they must be `borsh` serializable. The return type is used to auto (de)serialize the database
|
/// they must be `scale` encodable. The return type is used to auto encode and decode the database
|
||||||
/// value bytes using `borsh`.
|
/// value bytes using `borsh`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -54,10 +54,11 @@ macro_rules! create_db {
|
|||||||
)?;
|
)?;
|
||||||
impl$(<$($generic_name: $generic_type),+>)? $field_name$(<$($generic_name),+>)? {
|
impl$(<$($generic_name: $generic_type),+>)? $field_name$(<$($generic_name),+>)? {
|
||||||
pub(crate) fn key($($arg: $arg_type),*) -> Vec<u8> {
|
pub(crate) fn key($($arg: $arg_type),*) -> Vec<u8> {
|
||||||
|
use scale::Encode;
|
||||||
$crate::serai_db_key(
|
$crate::serai_db_key(
|
||||||
stringify!($db_name).as_bytes(),
|
stringify!($db_name).as_bytes(),
|
||||||
stringify!($field_name).as_bytes(),
|
stringify!($field_name).as_bytes(),
|
||||||
&borsh::to_vec(&($($arg),*)).unwrap(),
|
($($arg),*).encode()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn set(
|
pub(crate) fn set(
|
||||||
|
|||||||
2
common/env/Cargo.toml
vendored
2
common/env/Cargo.toml
vendored
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/common/env"
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.64"
|
rust-version = "1.71"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|||||||
2
common/env/LICENSE
vendored
2
common/env/LICENSE
vendored
@@ -1,6 +1,6 @@
|
|||||||
AGPL-3.0-only license
|
AGPL-3.0-only license
|
||||||
|
|
||||||
Copyright (c) 2023-2025 Luke Parker
|
Copyright (c) 2023 Luke Parker
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License Version 3 as
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/common/patchable-a
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["async", "sleep", "tokio", "smol", "async-std"]
|
keywords = ["async", "sleep", "tokio", "smol", "async-std"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.71"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024-2025 Luke Parker
|
Copyright (c) 2024 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023-2025 Luke Parker
|
Copyright (c) 2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "std-shims"
|
name = "std-shims"
|
||||||
version = "0.1.4"
|
version = "0.1.1"
|
||||||
description = "A series of std shims to make alloc more feasible"
|
description = "A series of std shims to make alloc more feasible"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/common/std-shims"
|
repository = "https://github.com/serai-dex/serai/tree/develop/common/std-shims"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["nostd", "no_std", "alloc", "io"]
|
keywords = ["nostd", "no_std", "alloc", "io"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.80"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -17,9 +17,8 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustversion = { version = "1", default-features = false }
|
spin = { version = "0.9", default-features = false, features = ["use_ticket_mutex", "lazy"] }
|
||||||
spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "once", "lazy"] }
|
hashbrown = { version = "0.15", default-features = false, features = ["default-hasher", "inline-more"] }
|
||||||
hashbrown = { version = "0.16", default-features = false, features = ["default-hasher", "inline-more"] }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = []
|
std = []
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023-2025 Luke Parker
|
Copyright (c) 2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -3,9 +3,4 @@
|
|||||||
A crate which passes through to std when the default `std` feature is enabled,
|
A crate which passes through to std when the default `std` feature is enabled,
|
||||||
yet provides a series of shims when it isn't.
|
yet provides a series of shims when it isn't.
|
||||||
|
|
||||||
No guarantee of one-to-one parity is provided. The shims provided aim to be sufficient for the
|
`HashSet` and `HashMap` are provided via `hashbrown`.
|
||||||
average case.
|
|
||||||
|
|
||||||
`HashSet` and `HashMap` are provided via `hashbrown`. Synchronization primitives are provided via
|
|
||||||
`spin` (avoiding a requirement on `critical-section`).
|
|
||||||
types are not guaranteed to be
|
|
||||||
|
|||||||
@@ -11,64 +11,3 @@ pub mod io;
|
|||||||
pub use alloc::vec;
|
pub use alloc::vec;
|
||||||
pub use alloc::str;
|
pub use alloc::str;
|
||||||
pub use alloc::string;
|
pub use alloc::string;
|
||||||
|
|
||||||
pub mod prelude {
|
|
||||||
#[rustversion::before(1.73)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub trait StdShimsDivCeil {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self;
|
|
||||||
}
|
|
||||||
#[rustversion::before(1.73)]
|
|
||||||
mod impl_divceil {
|
|
||||||
use super::StdShimsDivCeil;
|
|
||||||
impl StdShimsDivCeil for u8 {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self {
|
|
||||||
(self + (rhs - 1)) / rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl StdShimsDivCeil for u16 {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self {
|
|
||||||
(self + (rhs - 1)) / rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl StdShimsDivCeil for u32 {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self {
|
|
||||||
(self + (rhs - 1)) / rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl StdShimsDivCeil for u64 {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self {
|
|
||||||
(self + (rhs - 1)) / rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl StdShimsDivCeil for u128 {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self {
|
|
||||||
(self + (rhs - 1)) / rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl StdShimsDivCeil for usize {
|
|
||||||
fn div_ceil(self, rhs: Self) -> Self {
|
|
||||||
(self + (rhs - 1)) / rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[rustversion::before(1.74)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub trait StdShimsIoErrorOther {
|
|
||||||
fn other<E>(error: E) -> Self
|
|
||||||
where
|
|
||||||
E: Into<Box<dyn std::error::Error + Send + Sync>>;
|
|
||||||
}
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[rustversion::before(1.74)]
|
|
||||||
impl StdShimsIoErrorOther for std::io::Error {
|
|
||||||
fn other<E>(error: E) -> Self
|
|
||||||
where
|
|
||||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
|
||||||
{
|
|
||||||
std::io::Error::new(std::io::ErrorKind::Other, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,11 +25,7 @@ mod mutex_shim {
|
|||||||
}
|
}
|
||||||
pub use mutex_shim::{ShimMutex as Mutex, MutexGuard};
|
pub use mutex_shim::{ShimMutex as Mutex, MutexGuard};
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use spin::Lazy as LazyLock;
|
|
||||||
#[rustversion::before(1.80)]
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use spin::Lazy as LazyLock;
|
|
||||||
#[rustversion::since(1.80)]
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::sync::LazyLock;
|
pub use std::sync::LazyLock;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub use spin::Lazy as LazyLock;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
AGPL-3.0-only license
|
AGPL-3.0-only license
|
||||||
|
|
||||||
Copyright (c) 2022-2025 Luke Parker
|
Copyright (c) 2022-2024 Luke Parker
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License Version 3 as
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/common/zalloc"
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
# This must be specified with the patch version, else Rust believes `1.77` < `1.77.0` and will
|
rust-version = "1.77"
|
||||||
# refuse to compile due to relying on versions introduced with `1.77.0`
|
|
||||||
rust-version = "1.77.0"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022-2025 Luke Parker
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -21,16 +22,15 @@ zeroize = { version = "^1.5", default-features = false, features = ["std"] }
|
|||||||
bitvec = { version = "1", default-features = false, features = ["std"] }
|
bitvec = { version = "1", default-features = false, features = ["std"] }
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
||||||
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"] }
|
ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std", "ristretto"] }
|
||||||
ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std"] }
|
dkg = { path = "../crypto/dkg", default-features = false, features = ["std"] }
|
||||||
dkg = { package = "dkg-musig", path = "../crypto/dkg/musig", default-features = false, features = ["std"] }
|
|
||||||
frost = { package = "modular-frost", path = "../crypto/frost" }
|
|
||||||
frost-schnorrkel = { path = "../crypto/schnorrkel" }
|
frost-schnorrkel = { path = "../crypto/schnorrkel" }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive", "bit-vec"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
zalloc = { path = "../common/zalloc" }
|
zalloc = { path = "../common/zalloc" }
|
||||||
@@ -42,7 +42,7 @@ messages = { package = "serai-processor-messages", path = "../processor/messages
|
|||||||
message-queue = { package = "serai-message-queue", path = "../message-queue" }
|
message-queue = { package = "serai-message-queue", path = "../message-queue" }
|
||||||
tributary-sdk = { path = "./tributary-sdk" }
|
tributary-sdk = { path = "./tributary-sdk" }
|
||||||
|
|
||||||
serai-client = { path = "../substrate/client", default-features = false, features = ["serai"] }
|
serai-client = { path = "../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
rust-version = "1.85"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -18,11 +18,12 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
||||||
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
@@ -30,5 +31,3 @@ tokio = { version = "1", default-features = false }
|
|||||||
|
|
||||||
serai-db = { path = "../../common/db", version = "0.1.1" }
|
serai-db = { path = "../../common/db", version = "0.1.1" }
|
||||||
serai-task = { path = "../../common/task", version = "0.1" }
|
serai-task = { path = "../../common/task", version = "0.1" }
|
||||||
|
|
||||||
serai-cosign-types = { path = "./types" }
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
AGPL-3.0-only license
|
AGPL-3.0-only license
|
||||||
|
|
||||||
Copyright (c) 2023-2025 Luke Parker
|
Copyright (c) 2023-2024 Luke Parker
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License Version 3 as
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ impl<D: Db> ContinuallyRan for CosignIntendTask<D> {
|
|||||||
|
|
||||||
// Tell each set of their expectation to cosign this block
|
// Tell each set of their expectation to cosign this block
|
||||||
for set in global_session_info.sets {
|
for set in global_session_info.sets {
|
||||||
log::debug!("{set:?} will be cosigning block #{block_number}");
|
log::debug!("{:?} will be cosigning block #{block_number}", set);
|
||||||
IntendedCosigns::send(
|
IntendedCosigns::send(
|
||||||
&mut txn,
|
&mut txn,
|
||||||
set,
|
set,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::{sync::Arc, collections::HashMap, time::Instant};
|
|||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
|
||||||
|
use scale::{Encode, Decode};
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_client::{
|
use serai_client::{
|
||||||
@@ -18,8 +19,6 @@ use serai_client::{
|
|||||||
use serai_db::*;
|
use serai_db::*;
|
||||||
use serai_task::*;
|
use serai_task::*;
|
||||||
|
|
||||||
use serai_cosign_types::*;
|
|
||||||
|
|
||||||
/// The cosigns which are intended to be performed.
|
/// The cosigns which are intended to be performed.
|
||||||
mod intend;
|
mod intend;
|
||||||
/// The evaluator of the cosigns.
|
/// The evaluator of the cosigns.
|
||||||
@@ -79,6 +78,68 @@ enum HasEvents {
|
|||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An intended cosign.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct CosignIntent {
|
||||||
|
/// The global session this cosign is being performed under.
|
||||||
|
pub global_session: [u8; 32],
|
||||||
|
/// The number of the block to cosign.
|
||||||
|
pub block_number: u64,
|
||||||
|
/// The hash of the block to cosign.
|
||||||
|
pub block_hash: [u8; 32],
|
||||||
|
/// If this cosign must be handled before further cosigns are.
|
||||||
|
pub notable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A cosign.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct Cosign {
|
||||||
|
/// The global session this cosign is being performed under.
|
||||||
|
pub global_session: [u8; 32],
|
||||||
|
/// The number of the block to cosign.
|
||||||
|
pub block_number: u64,
|
||||||
|
/// The hash of the block to cosign.
|
||||||
|
pub block_hash: [u8; 32],
|
||||||
|
/// The actual cosigner.
|
||||||
|
pub cosigner: ExternalNetworkId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CosignIntent {
|
||||||
|
/// Convert this into a `Cosign`.
|
||||||
|
pub fn into_cosign(self, cosigner: ExternalNetworkId) -> Cosign {
|
||||||
|
let CosignIntent { global_session, block_number, block_hash, notable: _ } = self;
|
||||||
|
Cosign { global_session, block_number, block_hash, cosigner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cosign {
|
||||||
|
/// The message to sign to sign this cosign.
|
||||||
|
///
|
||||||
|
/// This must be signed with schnorrkel, the context set to `COSIGN_CONTEXT`.
|
||||||
|
pub fn signature_message(&self) -> Vec<u8> {
|
||||||
|
// We use a schnorrkel context to domain-separate this
|
||||||
|
self.encode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A signed cosign.
|
||||||
|
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct SignedCosign {
|
||||||
|
/// The cosign.
|
||||||
|
pub cosign: Cosign,
|
||||||
|
/// The signature for the cosign.
|
||||||
|
pub signature: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignedCosign {
|
||||||
|
fn verify_signature(&self, signer: serai_client::Public) -> bool {
|
||||||
|
let Ok(signer) = schnorrkel::PublicKey::from_bytes(&signer.0) else { return false };
|
||||||
|
let Ok(signature) = schnorrkel::Signature::from_bytes(&self.signature) else { return false };
|
||||||
|
|
||||||
|
signer.verify_simple(COSIGN_CONTEXT, &self.cosign.signature_message(), &signature).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
create_db! {
|
create_db! {
|
||||||
Cosign {
|
Cosign {
|
||||||
// The following are populated by the intend task and used throughout the library
|
// The following are populated by the intend task and used throughout the library
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-cosign-types"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Evaluator of cosigns for the Serai network"
|
|
||||||
license = "AGPL-3.0-only"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/cosign"
|
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|
||||||
keywords = []
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
rust-version = "1.85"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../../substrate/primitives", default-features = false, features = ["std"] }
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
#![deny(missing_docs)]
|
|
||||||
//! Types used when cosigning Serai. For more info, please see `serai-cosign`.
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
|
||||||
|
|
||||||
use serai_primitives::{crypto::Public, network_id::ExternalNetworkId};
|
|
||||||
|
|
||||||
/// The schnorrkel context to used when signing a cosign.
|
|
||||||
pub const COSIGN_CONTEXT: &[u8] = b"/serai/coordinator/cosign";
|
|
||||||
|
|
||||||
/// An intended cosign.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
|
||||||
pub struct CosignIntent {
|
|
||||||
/// The global session this cosign is being performed under.
|
|
||||||
pub global_session: [u8; 32],
|
|
||||||
/// The number of the block to cosign.
|
|
||||||
pub block_number: u64,
|
|
||||||
/// The hash of the block to cosign.
|
|
||||||
pub block_hash: [u8; 32],
|
|
||||||
/// If this cosign must be handled before further cosigns are.
|
|
||||||
pub notable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A cosign.
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
|
||||||
pub struct Cosign {
|
|
||||||
/// The global session this cosign is being performed under.
|
|
||||||
pub global_session: [u8; 32],
|
|
||||||
/// The number of the block to cosign.
|
|
||||||
pub block_number: u64,
|
|
||||||
/// The hash of the block to cosign.
|
|
||||||
pub block_hash: [u8; 32],
|
|
||||||
/// The actual cosigner.
|
|
||||||
pub cosigner: ExternalNetworkId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CosignIntent {
|
|
||||||
/// Convert this into a `Cosign`.
|
|
||||||
pub fn into_cosign(self, cosigner: ExternalNetworkId) -> Cosign {
|
|
||||||
let CosignIntent { global_session, block_number, block_hash, notable: _ } = self;
|
|
||||||
Cosign { global_session, block_number, block_hash, cosigner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cosign {
|
|
||||||
/// The message to sign to sign this cosign.
|
|
||||||
///
|
|
||||||
/// This must be signed with schnorrkel, the context set to `COSIGN_CONTEXT`.
|
|
||||||
pub fn signature_message(&self) -> Vec<u8> {
|
|
||||||
// We use a schnorrkel context to domain-separate this
|
|
||||||
borsh::to_vec(self).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A signed cosign.
|
|
||||||
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
|
|
||||||
pub struct SignedCosign {
|
|
||||||
/// The cosign.
|
|
||||||
pub cosign: Cosign,
|
|
||||||
/// The signature for the cosign.
|
|
||||||
pub signature: [u8; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SignedCosign {
|
|
||||||
/// Verify a cosign's signature.
|
|
||||||
pub fn verify_signature(&self, signer: Public) -> bool {
|
|
||||||
let Ok(signer) = schnorrkel::PublicKey::from_bytes(&signer.0) else { return false };
|
|
||||||
let Ok(signature) = schnorrkel::Signature::from_bytes(&self.signature) else { return false };
|
|
||||||
|
|
||||||
signer.verify_simple(COSIGN_CONTEXT, &self.cosign.signature_message(), &signature).is_ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
rust-version = "1.85"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -22,7 +22,7 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
|||||||
|
|
||||||
serai-db = { path = "../../common/db", version = "0.1" }
|
serai-db = { path = "../../common/db", version = "0.1" }
|
||||||
|
|
||||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||||
serai-cosign = { path = "../cosign" }
|
serai-cosign = { path = "../cosign" }
|
||||||
tributary-sdk = { path = "../tributary-sdk" }
|
tributary-sdk = { path = "../tributary-sdk" }
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
rust-version = "1.87"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -23,13 +23,13 @@ async-trait = { version = "0.1", default-features = false }
|
|||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["std"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["std"] }
|
||||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
||||||
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai"] }
|
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||||
serai-cosign = { path = "../../cosign" }
|
serai-cosign = { path = "../../cosign" }
|
||||||
tributary-sdk = { path = "../../tributary-sdk" }
|
tributary-sdk = { path = "../../tributary-sdk" }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use serai_primitives::{MAX_KEY_SHARES_PER_SET, ExternalValidatorSet};
|
use serai_client::validator_sets::primitives::{MAX_KEY_SHARES_PER_SET, ExternalValidatorSet};
|
||||||
|
|
||||||
use futures_lite::FutureExt;
|
use futures_lite::FutureExt;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_primitives::{network_id::ExternalNetworkId, validator_sets::ExternalValidatorSet};
|
use serai_client::{primitives::ExternalNetworkId, validator_sets::primitives::ExternalValidatorSet};
|
||||||
|
|
||||||
use serai_db::Db;
|
use serai_db::Db;
|
||||||
use tributary_sdk::{ReadWrite, TransactionTrait, Tributary, TributaryReader};
|
use tributary_sdk::{ReadWrite, TransactionTrait, Tributary, TributaryReader};
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ use std::{boxed::Box, collections::HashMap};
|
|||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use ciphersuite::{group::GroupEncoding, *};
|
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||||
use dkg::{Participant, musig};
|
|
||||||
use frost_schnorrkel::{
|
use frost_schnorrkel::{
|
||||||
frost::{curve::Ristretto, FrostError, sign::*},
|
frost::{
|
||||||
|
dkg::{Participant, musig::musig},
|
||||||
|
FrostError,
|
||||||
|
sign::*,
|
||||||
|
},
|
||||||
Schnorrkel,
|
Schnorrkel,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,7 +33,7 @@ fn schnorrkel() -> Schnorrkel {
|
|||||||
|
|
||||||
fn our_i(
|
fn our_i(
|
||||||
set: &NewSetInformation,
|
set: &NewSetInformation,
|
||||||
key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
data: &HashMap<Participant, Vec<u8>>,
|
data: &HashMap<Participant, Vec<u8>>,
|
||||||
) -> Participant {
|
) -> Participant {
|
||||||
let public = SeraiAddress((Ristretto::generator() * key.deref()).to_bytes());
|
let public = SeraiAddress((Ristretto::generator() * key.deref()).to_bytes());
|
||||||
@@ -124,7 +127,7 @@ pub(crate) struct ConfirmDkgTask<CD: DbTrait, TD: DbTrait> {
|
|||||||
set: NewSetInformation,
|
set: NewSetInformation,
|
||||||
tributary_db: TD,
|
tributary_db: TD,
|
||||||
|
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
signer: Option<Signer>,
|
signer: Option<Signer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +136,7 @@ impl<CD: DbTrait, TD: DbTrait> ConfirmDkgTask<CD, TD> {
|
|||||||
db: CD,
|
db: CD,
|
||||||
set: NewSetInformation,
|
set: NewSetInformation,
|
||||||
tributary_db: TD,
|
tributary_db: TD,
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { db, set, tributary_db, key, signer: None }
|
Self { db, set, tributary_db, key, signer: None }
|
||||||
}
|
}
|
||||||
@@ -152,15 +155,16 @@ impl<CD: DbTrait, TD: DbTrait> ConfirmDkgTask<CD, TD> {
|
|||||||
db: &mut CD,
|
db: &mut CD,
|
||||||
set: ExternalValidatorSet,
|
set: ExternalValidatorSet,
|
||||||
attempt: u32,
|
attempt: u32,
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
signer: &mut Option<Signer>,
|
signer: &mut Option<Signer>,
|
||||||
) {
|
) {
|
||||||
// Perform the preprocess
|
// Perform the preprocess
|
||||||
let public_key = Ristretto::generator() * key.deref();
|
|
||||||
let (machine, preprocess) = AlgorithmMachine::new(
|
let (machine, preprocess) = AlgorithmMachine::new(
|
||||||
schnorrkel(),
|
schnorrkel(),
|
||||||
// We use a 1-of-1 Musig here as we don't know who will actually be in this Musig yet
|
// We use a 1-of-1 Musig here as we don't know who will actually be in this Musig yet
|
||||||
musig(musig_context(set.into()), key, &[public_key]).unwrap(),
|
musig(&musig_context(set.into()), key, &[Ristretto::generator() * key.deref()])
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
)
|
)
|
||||||
.preprocess(&mut OsRng);
|
.preprocess(&mut OsRng);
|
||||||
// We take the preprocess so we can use it in a distinct machine with the actual Musig
|
// We take the preprocess so we can use it in a distinct machine with the actual Musig
|
||||||
@@ -195,7 +199,7 @@ impl<CD: DbTrait, TD: DbTrait> ContinuallyRan for ConfirmDkgTask<CD, TD> {
|
|||||||
// If we were sent a key to set, create the signer for it
|
// If we were sent a key to set, create the signer for it
|
||||||
if self.signer.is_none() && KeysToConfirm::get(&self.db, self.set.set).is_some() {
|
if self.signer.is_none() && KeysToConfirm::get(&self.db, self.set.set).is_some() {
|
||||||
// Create and publish the initial preprocess
|
// Create and publish the initial preprocess
|
||||||
Self::preprocess(&mut self.db, self.set.set, 0, self.key.clone(), &mut self.signer);
|
Self::preprocess(&mut self.db, self.set.set, 0, &self.key, &mut self.signer);
|
||||||
|
|
||||||
made_progress = true;
|
made_progress = true;
|
||||||
}
|
}
|
||||||
@@ -215,13 +219,7 @@ impl<CD: DbTrait, TD: DbTrait> ContinuallyRan for ConfirmDkgTask<CD, TD> {
|
|||||||
id: messages::sign::SignId { attempt, .. },
|
id: messages::sign::SignId { attempt, .. },
|
||||||
} => {
|
} => {
|
||||||
// Create and publish the preprocess for the specified attempt
|
// Create and publish the preprocess for the specified attempt
|
||||||
Self::preprocess(
|
Self::preprocess(&mut self.db, self.set.set, attempt, &self.key, &mut self.signer);
|
||||||
&mut self.db,
|
|
||||||
self.set.set,
|
|
||||||
attempt,
|
|
||||||
self.key.clone(),
|
|
||||||
&mut self.signer,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
messages::sign::CoordinatorMessage::Preprocesses {
|
messages::sign::CoordinatorMessage::Preprocesses {
|
||||||
id: messages::sign::SignId { attempt, .. },
|
id: messages::sign::SignId { attempt, .. },
|
||||||
@@ -260,9 +258,9 @@ impl<CD: DbTrait, TD: DbTrait> ContinuallyRan for ConfirmDkgTask<CD, TD> {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let keys =
|
let keys = musig(&musig_context(self.set.set.into()), &self.key, &musig_public_keys)
|
||||||
musig(musig_context(self.set.set.into()), self.key.clone(), &musig_public_keys)
|
.unwrap()
|
||||||
.unwrap();
|
.into();
|
||||||
|
|
||||||
// Rebuild the machine
|
// Rebuild the machine
|
||||||
let (machine, preprocess_from_cache) =
|
let (machine, preprocess_from_cache) =
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ use std::{sync::Arc, collections::HashMap, time::Instant};
|
|||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{ff::PrimeField, GroupEncoding},
|
group::{ff::PrimeField, GroupEncoding},
|
||||||
*,
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
|
|
||||||
use borsh::BorshDeserialize;
|
use borsh::BorshDeserialize;
|
||||||
@@ -352,7 +351,7 @@ async fn main() {
|
|||||||
let mut key_bytes = [0; 32];
|
let mut key_bytes = [0; 32];
|
||||||
key_bytes.copy_from_slice(&key_vec);
|
key_bytes.copy_from_slice(&key_vec);
|
||||||
key_vec.zeroize();
|
key_vec.zeroize();
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::from_repr(key_bytes).unwrap());
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::from_repr(key_bytes).unwrap());
|
||||||
key_bytes.zeroize();
|
key_bytes.zeroize();
|
||||||
key
|
key
|
||||||
};
|
};
|
||||||
@@ -439,7 +438,7 @@ async fn main() {
|
|||||||
EphemeralEventStream::new(
|
EphemeralEventStream::new(
|
||||||
db.clone(),
|
db.clone(),
|
||||||
serai.clone(),
|
serai.clone(),
|
||||||
SeraiAddress((<Ristretto as WrappedGroup>::generator() * serai_key.deref()).to_bytes()),
|
SeraiAddress((<Ristretto as Ciphersuite>::generator() * serai_key.deref()).to_bytes()),
|
||||||
)
|
)
|
||||||
.continually_run(substrate_ephemeral_task_def, vec![substrate_task]),
|
.continually_run(substrate_ephemeral_task_def, vec![substrate_task]),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use ciphersuite::*;
|
use ciphersuite::{Ciphersuite, Ristretto};
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ use serai_coordinator_p2p::P2p;
|
|||||||
use crate::{Db, KeySet};
|
use crate::{Db, KeySet};
|
||||||
|
|
||||||
pub(crate) struct SubstrateTask<P: P2p> {
|
pub(crate) struct SubstrateTask<P: P2p> {
|
||||||
pub(crate) serai_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
pub(crate) serai_key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
pub(crate) db: Db,
|
pub(crate) db: Db,
|
||||||
pub(crate) message_queue: Arc<MessageQueue>,
|
pub(crate) message_queue: Arc<MessageQueue>,
|
||||||
pub(crate) p2p: P,
|
pub(crate) p2p: P,
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ use std::sync::Arc;
|
|||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use blake2::{digest::typenum::U32, Digest, Blake2s};
|
use blake2::{digest::typenum::U32, Digest, Blake2s};
|
||||||
use ciphersuite::*;
|
use ciphersuite::{Ciphersuite, Ristretto};
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};
|
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};
|
||||||
|
|
||||||
|
use scale::Encode;
|
||||||
use serai_client::validator_sets::primitives::ExternalValidatorSet;
|
use serai_client::validator_sets::primitives::ExternalValidatorSet;
|
||||||
|
|
||||||
use tributary_sdk::{TransactionKind, TransactionError, ProvidedError, TransactionTrait, Tributary};
|
use tributary_sdk::{TransactionKind, TransactionError, ProvidedError, TransactionTrait, Tributary};
|
||||||
@@ -67,7 +67,9 @@ async fn provide_transaction<TD: DbTrait, P: P2p>(
|
|||||||
// advancing
|
// advancing
|
||||||
Err(ProvidedError::LocalMismatchesOnChain) => loop {
|
Err(ProvidedError::LocalMismatchesOnChain) => loop {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Tributary {set:?} was supposed to provide {tx:?} but peers disagree, halting Tributary",
|
"Tributary {:?} was supposed to provide {:?} but peers disagree, halting Tributary",
|
||||||
|
set,
|
||||||
|
tx,
|
||||||
);
|
);
|
||||||
// Print this every five minutes as this does need to be handled
|
// Print this every five minutes as this does need to be handled
|
||||||
tokio::time::sleep(Duration::from_secs(5 * 60)).await;
|
tokio::time::sleep(Duration::from_secs(5 * 60)).await;
|
||||||
@@ -159,7 +161,7 @@ impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
async fn add_signed_unsigned_transaction<TD: DbTrait, P: P2p>(
|
async fn add_signed_unsigned_transaction<TD: DbTrait, P: P2p>(
|
||||||
tributary: &Tributary<TD, Transaction, P>,
|
tributary: &Tributary<TD, Transaction, P>,
|
||||||
key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
mut tx: Transaction,
|
mut tx: Transaction,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// If this is a signed transaction, sign it
|
// If this is a signed transaction, sign it
|
||||||
@@ -212,7 +214,7 @@ async fn add_with_recognition_check<TD: DbTrait, P: P2p>(
|
|||||||
set: ExternalValidatorSet,
|
set: ExternalValidatorSet,
|
||||||
tributary_db: &mut TD,
|
tributary_db: &mut TD,
|
||||||
tributary: &Tributary<TD, Transaction, P>,
|
tributary: &Tributary<TD, Transaction, P>,
|
||||||
key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let kind = tx.kind();
|
let kind = tx.kind();
|
||||||
@@ -251,7 +253,7 @@ pub(crate) struct AddTributaryTransactionsTask<CD: DbTrait, TD: DbTrait, P: P2p>
|
|||||||
tributary_db: TD,
|
tributary_db: TD,
|
||||||
tributary: Tributary<TD, Transaction, P>,
|
tributary: Tributary<TD, Transaction, P>,
|
||||||
set: NewSetInformation,
|
set: NewSetInformation,
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
}
|
}
|
||||||
impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for AddTributaryTransactionsTask<CD, TD, P> {
|
impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for AddTributaryTransactionsTask<CD, TD, P> {
|
||||||
type Error = DoesNotError;
|
type Error = DoesNotError;
|
||||||
@@ -381,7 +383,7 @@ pub(crate) struct SignSlashReportTask<CD: DbTrait, TD: DbTrait, P: P2p> {
|
|||||||
tributary_db: TD,
|
tributary_db: TD,
|
||||||
tributary: Tributary<TD, Transaction, P>,
|
tributary: Tributary<TD, Transaction, P>,
|
||||||
set: NewSetInformation,
|
set: NewSetInformation,
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
}
|
}
|
||||||
impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for SignSlashReportTask<CD, TD, P> {
|
impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for SignSlashReportTask<CD, TD, P> {
|
||||||
type Error = DoesNotError;
|
type Error = DoesNotError;
|
||||||
@@ -469,7 +471,7 @@ pub(crate) async fn spawn_tributary<P: P2p>(
|
|||||||
p2p: P,
|
p2p: P,
|
||||||
p2p_add_tributary: &mpsc::UnboundedSender<(ExternalValidatorSet, Tributary<Db, Transaction, P>)>,
|
p2p_add_tributary: &mpsc::UnboundedSender<(ExternalValidatorSet, Tributary<Db, Transaction, P>)>,
|
||||||
set: NewSetInformation,
|
set: NewSetInformation,
|
||||||
serai_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
serai_key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
) {
|
) {
|
||||||
// Don't spawn retired Tributaries
|
// Don't spawn retired Tributaries
|
||||||
if crate::db::RetiredTributary::get(&db, set.set.network).map(|session| session.0) >=
|
if crate::db::RetiredTributary::get(&db, set.set.network).map(|session| session.0) >=
|
||||||
@@ -478,8 +480,7 @@ pub(crate) async fn spawn_tributary<P: P2p>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let genesis =
|
let genesis = <[u8; 32]>::from(Blake2s::<U32>::digest((set.serai_block, set.set).encode()));
|
||||||
<[u8; 32]>::from(Blake2s::<U32>::digest(borsh::to_vec(&(set.serai_block, set.set)).unwrap()));
|
|
||||||
|
|
||||||
// Since the Serai block will be finalized, then cosigned, before we handle this, this time will
|
// Since the Serai block will be finalized, then cosigned, before we handle this, this time will
|
||||||
// be a couple of minutes stale. While the Tributary will still function with a start time in the
|
// be a couple of minutes stale. While the Tributary will still function with a start time in the
|
||||||
@@ -490,7 +491,7 @@ pub(crate) async fn spawn_tributary<P: P2p>(
|
|||||||
|
|
||||||
let mut tributary_validators = Vec::with_capacity(set.validators.len());
|
let mut tributary_validators = Vec::with_capacity(set.validators.len());
|
||||||
for (validator, weight) in set.validators.iter().copied() {
|
for (validator, weight) in set.validators.iter().copied() {
|
||||||
let validator_key = <Ristretto as GroupIo>::read_G(&mut validator.0.as_slice())
|
let validator_key = <Ristretto as Ciphersuite>::read_G(&mut validator.0.as_slice())
|
||||||
.expect("Serai validator had an invalid public key");
|
.expect("Serai validator had an invalid public key");
|
||||||
let weight = u64::from(weight);
|
let weight = u64::from(weight);
|
||||||
tributary_validators.push((validator_key, weight));
|
tributary_validators.push((validator_key, weight));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
rust-version = "1.85"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -20,11 +20,12 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bitvec = { version = "1", default-features = false, features = ["std"] }
|
bitvec = { version = "1", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive", "bit-vec"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai"] }
|
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai", "borsh"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
AGPL-3.0-only license
|
AGPL-3.0-only license
|
||||||
|
|
||||||
Copyright (c) 2023-2025 Luke Parker
|
Copyright (c) 2023-2024 Luke Parker
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License Version 3 as
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use scale::{Encode, Decode};
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use dkg::Participant;
|
use dkg::Participant;
|
||||||
@@ -177,13 +178,14 @@ impl Keys {
|
|||||||
signature_participants,
|
signature_participants,
|
||||||
signature,
|
signature,
|
||||||
);
|
);
|
||||||
_public_db::Keys::set(txn, set.network, &(set.session, tx));
|
_public_db::Keys::set(txn, set.network, &(set.session, tx.encode()));
|
||||||
}
|
}
|
||||||
pub(crate) fn take(
|
pub(crate) fn take(
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
network: ExternalNetworkId,
|
network: ExternalNetworkId,
|
||||||
) -> Option<(Session, Transaction)> {
|
) -> Option<(Session, Transaction)> {
|
||||||
_public_db::Keys::take(txn, network)
|
let (session, tx) = _public_db::Keys::take(txn, network)?;
|
||||||
|
Some((session, <_>::decode(&mut tx.as_slice()).unwrap()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,12 +226,13 @@ impl SlashReports {
|
|||||||
slash_report,
|
slash_report,
|
||||||
signature,
|
signature,
|
||||||
);
|
);
|
||||||
_public_db::SlashReports::set(txn, set.network, &(set.session, tx));
|
_public_db::SlashReports::set(txn, set.network, &(set.session, tx.encode()));
|
||||||
}
|
}
|
||||||
pub(crate) fn take(
|
pub(crate) fn take(
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
network: ExternalNetworkId,
|
network: ExternalNetworkId,
|
||||||
) -> Option<(Session, Transaction)> {
|
) -> Option<(Session, Transaction)> {
|
||||||
_public_db::SlashReports::take(txn, network)
|
let (session, tx) = _public_db::SlashReports::take(txn, network)?;
|
||||||
|
Some((session, <_>::decode(&mut tx.as_slice()).unwrap()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ license = "AGPL-3.0-only"
|
|||||||
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/tributary-sdk"
|
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/tributary-sdk"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -24,19 +24,18 @@ zeroize = { version = "^1.5", default-features = false, features = ["std"] }
|
|||||||
rand = { version = "0.8", default-features = false, features = ["std"] }
|
rand = { version = "0.8", default-features = false, features = ["std"] }
|
||||||
rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
||||||
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["std", "recommended"] }
|
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["std", "recommended"] }
|
||||||
|
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", default-features = false, features = ["std"] }
|
ciphersuite = { package = "ciphersuite", path = "../../crypto/ciphersuite", version = "0.4", default-features = false, features = ["std", "ristretto"] }
|
||||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, features = ["std"] }
|
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", version = "0.5", default-features = false, features = ["std"] }
|
||||||
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", version = "0.5", default-features = false, features = ["std", "aggregate"] }
|
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
serai-db = { path = "../../common/db", version = "0.1" }
|
serai-db = { path = "../../common/db", version = "0.1" }
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std", "sink", "channel"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std", "sink", "channel"] }
|
||||||
futures-channel = { version = "0.3", default-features = false, features = ["std", "sink"] }
|
futures-channel = { version = "0.3", default-features = false, features = ["std", "sink"] }
|
||||||
tendermint = { package = "tendermint-machine", path = "./tendermint", version = "0.2" }
|
tendermint = { package = "tendermint-machine", path = "./tendermint", version = "0.2" }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
AGPL-3.0-only license
|
AGPL-3.0-only license
|
||||||
|
|
||||||
Copyright (c) 2023-2025 Luke Parker
|
Copyright (c) 2023 Luke Parker
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License Version 3 as
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use std::collections::{VecDeque, HashSet};
|
use std::collections::{VecDeque, HashSet};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||||
use ciphersuite::{group::GroupEncoding, *};
|
|
||||||
|
|
||||||
use serai_db::{Get, DbTxn, Db};
|
use serai_db::{Get, DbTxn, Db};
|
||||||
|
|
||||||
use borsh::BorshDeserialize;
|
use scale::Decode;
|
||||||
|
|
||||||
use tendermint::ext::{Network, Commit};
|
use tendermint::ext::{Network, Commit};
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ pub(crate) struct Blockchain<D: Db, T: TransactionTrait> {
|
|||||||
|
|
||||||
block_number: u64,
|
block_number: u64,
|
||||||
tip: [u8; 32],
|
tip: [u8; 32],
|
||||||
participants: HashSet<<Ristretto as WrappedGroup>::G>,
|
participants: HashSet<<Ristretto as Ciphersuite>::G>,
|
||||||
|
|
||||||
provided: ProvidedTransactions<D, T>,
|
provided: ProvidedTransactions<D, T>,
|
||||||
mempool: Mempool<D, T>,
|
mempool: Mempool<D, T>,
|
||||||
@@ -56,20 +55,20 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
|
|||||||
}
|
}
|
||||||
fn next_nonce_key(
|
fn next_nonce_key(
|
||||||
genesis: &[u8; 32],
|
genesis: &[u8; 32],
|
||||||
signer: &<Ristretto as WrappedGroup>::G,
|
signer: &<Ristretto as Ciphersuite>::G,
|
||||||
order: &[u8],
|
order: &[u8],
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
D::key(
|
D::key(
|
||||||
b"tributary_blockchain",
|
b"tributary_blockchain",
|
||||||
b"next_nonce",
|
b"next_nonce",
|
||||||
[genesis.as_slice(), signer.to_bytes().as_slice(), order].concat(),
|
[genesis.as_ref(), signer.to_bytes().as_ref(), order].concat(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
db: D,
|
db: D,
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
participants: &[<Ristretto as WrappedGroup>::G],
|
participants: &[<Ristretto as Ciphersuite>::G],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut res = Self {
|
let mut res = Self {
|
||||||
db: Some(db.clone()),
|
db: Some(db.clone()),
|
||||||
@@ -106,7 +105,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
|
|||||||
|
|
||||||
pub(crate) fn block_from_db(db: &D, genesis: [u8; 32], block: &[u8; 32]) -> Option<Block<T>> {
|
pub(crate) fn block_from_db(db: &D, genesis: [u8; 32], block: &[u8; 32]) -> Option<Block<T>> {
|
||||||
db.get(Self::block_key(&genesis, block))
|
db.get(Self::block_key(&genesis, block))
|
||||||
.map(|bytes| Block::<T>::read::<&[u8]>(&mut bytes.as_slice()).unwrap())
|
.map(|bytes| Block::<T>::read::<&[u8]>(&mut bytes.as_ref()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn commit_from_db(db: &D, genesis: [u8; 32], block: &[u8; 32]) -> Option<Vec<u8>> {
|
pub(crate) fn commit_from_db(db: &D, genesis: [u8; 32], block: &[u8; 32]) -> Option<Vec<u8>> {
|
||||||
@@ -166,7 +165,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
|
|||||||
// we must have a commit per valid hash
|
// we must have a commit per valid hash
|
||||||
let commit = Self::commit_from_db(db, genesis, &hash).unwrap();
|
let commit = Self::commit_from_db(db, genesis, &hash).unwrap();
|
||||||
// commit has to be valid if it is coming from our db
|
// commit has to be valid if it is coming from our db
|
||||||
Some(Commit::<N::SignatureScheme>::deserialize_reader(&mut commit.as_slice()).unwrap())
|
Some(Commit::<N::SignatureScheme>::decode(&mut commit.as_ref()).unwrap())
|
||||||
};
|
};
|
||||||
let unsigned_in_chain =
|
let unsigned_in_chain =
|
||||||
|hash: [u8; 32]| db.get(Self::unsigned_included_key(&self.genesis, &hash)).is_some();
|
|hash: [u8; 32]| db.get(Self::unsigned_included_key(&self.genesis, &hash)).is_some();
|
||||||
@@ -196,7 +195,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
|
|||||||
|
|
||||||
pub(crate) fn next_nonce(
|
pub(crate) fn next_nonce(
|
||||||
&self,
|
&self,
|
||||||
signer: &<Ristretto as WrappedGroup>::G,
|
signer: &<Ristretto as Ciphersuite>::G,
|
||||||
order: &[u8],
|
order: &[u8],
|
||||||
) -> Option<u32> {
|
) -> Option<u32> {
|
||||||
if let Some(next_nonce) = self.mempool.next_nonce_in_mempool(signer, order.to_vec()) {
|
if let Some(next_nonce) = self.mempool.next_nonce_in_mempool(signer, order.to_vec()) {
|
||||||
@@ -241,7 +240,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
|
|||||||
let commit = |block: u64| -> Option<Commit<N::SignatureScheme>> {
|
let commit = |block: u64| -> Option<Commit<N::SignatureScheme>> {
|
||||||
let commit = self.commit_by_block_number(block)?;
|
let commit = self.commit_by_block_number(block)?;
|
||||||
// commit has to be valid if it is coming from our db
|
// commit has to be valid if it is coming from our db
|
||||||
Some(Commit::<N::SignatureScheme>::deserialize_reader(&mut commit.as_slice()).unwrap())
|
Some(Commit::<N::SignatureScheme>::decode(&mut commit.as_ref()).unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut txn_db = db.clone();
|
let mut txn_db = db.clone();
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ use std::{sync::Arc, io};
|
|||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use borsh::BorshDeserialize;
|
use ciphersuite::{Ciphersuite, Ristretto};
|
||||||
|
|
||||||
use ciphersuite::*;
|
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
|
|
||||||
|
use scale::Decode;
|
||||||
use futures_channel::mpsc::UnboundedReceiver;
|
use futures_channel::mpsc::UnboundedReceiver;
|
||||||
use futures_util::{StreamExt, SinkExt};
|
use futures_util::{StreamExt, SinkExt};
|
||||||
use ::tendermint::{
|
use ::tendermint::{
|
||||||
@@ -163,8 +161,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
|||||||
db: D,
|
db: D,
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
start_time: u64,
|
start_time: u64,
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
validators: Vec<(<Ristretto as WrappedGroup>::G, u64)>,
|
validators: Vec<(<Ristretto as Ciphersuite>::G, u64)>,
|
||||||
p2p: P,
|
p2p: P,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
log::info!("new Tributary with genesis {}", hex::encode(genesis));
|
log::info!("new Tributary with genesis {}", hex::encode(genesis));
|
||||||
@@ -178,7 +176,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
|||||||
let block_number = BlockNumber(blockchain.block_number());
|
let block_number = BlockNumber(blockchain.block_number());
|
||||||
|
|
||||||
let start_time = if let Some(commit) = blockchain.commit(&blockchain.tip()) {
|
let start_time = if let Some(commit) = blockchain.commit(&blockchain.tip()) {
|
||||||
Commit::<Validators>::deserialize_reader(&mut commit.as_slice()).unwrap().end_time
|
Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time
|
||||||
} else {
|
} else {
|
||||||
start_time
|
start_time
|
||||||
};
|
};
|
||||||
@@ -236,7 +234,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
|||||||
|
|
||||||
pub async fn next_nonce(
|
pub async fn next_nonce(
|
||||||
&self,
|
&self,
|
||||||
signer: &<Ristretto as WrappedGroup>::G,
|
signer: &<Ristretto as Ciphersuite>::G,
|
||||||
order: &[u8],
|
order: &[u8],
|
||||||
) -> Option<u32> {
|
) -> Option<u32> {
|
||||||
self.network.blockchain.read().await.next_nonce(signer, order)
|
self.network.blockchain.read().await.next_nonce(signer, order)
|
||||||
@@ -277,8 +275,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let block = TendermintBlock(block.serialize());
|
let block = TendermintBlock(block.serialize());
|
||||||
let mut commit_ref = commit.as_slice();
|
let mut commit_ref = commit.as_ref();
|
||||||
let Ok(commit) = Commit::<Arc<Validators>>::deserialize_reader(&mut commit_ref) else {
|
let Ok(commit) = Commit::<Arc<Validators>>::decode(&mut commit_ref) else {
|
||||||
log::error!("sent an invalidly serialized commit");
|
log::error!("sent an invalidly serialized commit");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -328,7 +326,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
|||||||
|
|
||||||
Some(&TENDERMINT_MESSAGE) => {
|
Some(&TENDERMINT_MESSAGE) => {
|
||||||
let Ok(msg) =
|
let Ok(msg) =
|
||||||
SignedMessageFor::<TendermintNetwork<D, T, P>>::deserialize_reader(&mut &msg[1 ..])
|
SignedMessageFor::<TendermintNetwork<D, T, P>>::decode::<&[u8]>(&mut &msg[1 ..])
|
||||||
else {
|
else {
|
||||||
log::error!("received invalid tendermint message");
|
log::error!("received invalid tendermint message");
|
||||||
return false;
|
return false;
|
||||||
@@ -368,17 +366,15 @@ impl<D: Db, T: TransactionTrait> TributaryReader<D, T> {
|
|||||||
Blockchain::<D, T>::commit_from_db(&self.0, self.1, hash)
|
Blockchain::<D, T>::commit_from_db(&self.0, self.1, hash)
|
||||||
}
|
}
|
||||||
pub fn parsed_commit(&self, hash: &[u8; 32]) -> Option<Commit<Validators>> {
|
pub fn parsed_commit(&self, hash: &[u8; 32]) -> Option<Commit<Validators>> {
|
||||||
self
|
self.commit(hash).map(|commit| Commit::<Validators>::decode(&mut commit.as_ref()).unwrap())
|
||||||
.commit(hash)
|
|
||||||
.map(|commit| Commit::<Validators>::deserialize_reader(&mut commit.as_slice()).unwrap())
|
|
||||||
}
|
}
|
||||||
pub fn block_after(&self, hash: &[u8; 32]) -> Option<[u8; 32]> {
|
pub fn block_after(&self, hash: &[u8; 32]) -> Option<[u8; 32]> {
|
||||||
Blockchain::<D, T>::block_after(&self.0, self.1, hash)
|
Blockchain::<D, T>::block_after(&self.0, self.1, hash)
|
||||||
}
|
}
|
||||||
pub fn time_of_block(&self, hash: &[u8; 32]) -> Option<u64> {
|
pub fn time_of_block(&self, hash: &[u8; 32]) -> Option<u64> {
|
||||||
self.commit(hash).map(|commit| {
|
self
|
||||||
Commit::<Validators>::deserialize_reader(&mut commit.as_slice()).unwrap().end_time
|
.commit(hash)
|
||||||
})
|
.map(|commit| Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locally_provided_txs_in_block(&self, hash: &[u8; 32], order: &str) -> bool {
|
pub fn locally_provided_txs_in_block(&self, hash: &[u8; 32], order: &str) -> bool {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{Ciphersuite, Ristretto};
|
||||||
use ciphersuite::*;
|
|
||||||
|
|
||||||
use serai_db::{DbTxn, Db};
|
use serai_db::{DbTxn, Db};
|
||||||
|
|
||||||
@@ -21,9 +20,9 @@ pub(crate) struct Mempool<D: Db, T: TransactionTrait> {
|
|||||||
db: D,
|
db: D,
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
|
|
||||||
last_nonce_in_mempool: HashMap<(<Ristretto as WrappedGroup>::G, Vec<u8>), u32>,
|
last_nonce_in_mempool: HashMap<(<Ristretto as Ciphersuite>::G, Vec<u8>), u32>,
|
||||||
txs: HashMap<[u8; 32], Transaction<T>>,
|
txs: HashMap<[u8; 32], Transaction<T>>,
|
||||||
txs_per_signer: HashMap<<Ristretto as WrappedGroup>::G, u32>,
|
txs_per_signer: HashMap<<Ristretto as Ciphersuite>::G, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Db, T: TransactionTrait> Mempool<D, T> {
|
impl<D: Db, T: TransactionTrait> Mempool<D, T> {
|
||||||
@@ -107,7 +106,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
|
|||||||
// Returns Ok(true) if new, Ok(false) if an already present unsigned, or the error.
|
// Returns Ok(true) if new, Ok(false) if an already present unsigned, or the error.
|
||||||
pub(crate) fn add<
|
pub(crate) fn add<
|
||||||
N: Network,
|
N: Network,
|
||||||
F: FnOnce(<Ristretto as WrappedGroup>::G, Vec<u8>) -> Option<u32>,
|
F: FnOnce(<Ristretto as Ciphersuite>::G, Vec<u8>) -> Option<u32>,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
blockchain_next_nonce: F,
|
blockchain_next_nonce: F,
|
||||||
@@ -179,7 +178,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
|
|||||||
// Returns None if the mempool doesn't have a nonce tracked.
|
// Returns None if the mempool doesn't have a nonce tracked.
|
||||||
pub(crate) fn next_nonce_in_mempool(
|
pub(crate) fn next_nonce_in_mempool(
|
||||||
&self,
|
&self,
|
||||||
signer: &<Ristretto as WrappedGroup>::G,
|
signer: &<Ristretto as Ciphersuite>::G,
|
||||||
order: Vec<u8>,
|
order: Vec<u8>,
|
||||||
) -> Option<u32> {
|
) -> Option<u32> {
|
||||||
self.last_nonce_in_mempool.get(&(*signer, order)).copied().map(|nonce| nonce + 1)
|
self.last_nonce_in_mempool.get(&(*signer, order)).copied().map(|nonce| nonce + 1)
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ use rand_chacha::ChaCha12Rng;
|
|||||||
use transcript::{Transcript, RecommendedTranscript};
|
use transcript::{Transcript, RecommendedTranscript};
|
||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{ff::PrimeField, GroupEncoding},
|
group::{
|
||||||
*,
|
GroupEncoding,
|
||||||
|
ff::{Field, PrimeField},
|
||||||
|
},
|
||||||
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
use schnorr::{
|
use schnorr::{
|
||||||
SchnorrSignature,
|
SchnorrSignature,
|
||||||
aggregate::{SchnorrAggregator, SchnorrAggregate},
|
aggregate::{SchnorrAggregator, SchnorrAggregate},
|
||||||
@@ -21,7 +23,7 @@ use schnorr::{
|
|||||||
|
|
||||||
use serai_db::Db;
|
use serai_db::Db;
|
||||||
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use scale::{Encode, Decode};
|
||||||
use tendermint::{
|
use tendermint::{
|
||||||
SignedMessageFor,
|
SignedMessageFor,
|
||||||
ext::{
|
ext::{
|
||||||
@@ -48,26 +50,24 @@ fn challenge(
|
|||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
nonce: &[u8],
|
nonce: &[u8],
|
||||||
msg: &[u8],
|
msg: &[u8],
|
||||||
) -> <Ristretto as WrappedGroup>::F {
|
) -> <Ristretto as Ciphersuite>::F {
|
||||||
let mut transcript = RecommendedTranscript::new(b"Tributary Chain Tendermint Message");
|
let mut transcript = RecommendedTranscript::new(b"Tributary Chain Tendermint Message");
|
||||||
transcript.append_message(b"genesis", genesis);
|
transcript.append_message(b"genesis", genesis);
|
||||||
transcript.append_message(b"key", key);
|
transcript.append_message(b"key", key);
|
||||||
transcript.append_message(b"nonce", nonce);
|
transcript.append_message(b"nonce", nonce);
|
||||||
transcript.append_message(b"message", msg);
|
transcript.append_message(b"message", msg);
|
||||||
|
|
||||||
<Ristretto as WrappedGroup>::F::from_bytes_mod_order_wide(
|
<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(&transcript.challenge(b"schnorr").into())
|
||||||
&transcript.challenge(b"schnorr").into(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Signer {
|
pub struct Signer {
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
key: Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signer {
|
impl Signer {
|
||||||
pub(crate) fn new(genesis: [u8; 32], key: Zeroizing<<Ristretto as WrappedGroup>::F>) -> Signer {
|
pub(crate) fn new(genesis: [u8; 32], key: Zeroizing<<Ristretto as Ciphersuite>::F>) -> Signer {
|
||||||
Signer { genesis, key }
|
Signer { genesis, key }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,10 +100,10 @@ impl SignerTrait for Signer {
|
|||||||
assert_eq!(nonce_ref, [0; 64].as_ref());
|
assert_eq!(nonce_ref, [0; 64].as_ref());
|
||||||
|
|
||||||
let nonce =
|
let nonce =
|
||||||
Zeroizing::new(<Ristretto as WrappedGroup>::F::from_bytes_mod_order_wide(&nonce_arr));
|
Zeroizing::new(<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(&nonce_arr));
|
||||||
nonce_arr.zeroize();
|
nonce_arr.zeroize();
|
||||||
|
|
||||||
assert!(!bool::from(nonce.ct_eq(&<Ristretto as WrappedGroup>::F::ZERO)));
|
assert!(!bool::from(nonce.ct_eq(&<Ristretto as Ciphersuite>::F::ZERO)));
|
||||||
|
|
||||||
let challenge = challenge(
|
let challenge = challenge(
|
||||||
self.genesis,
|
self.genesis,
|
||||||
@@ -132,7 +132,7 @@ pub struct Validators {
|
|||||||
impl Validators {
|
impl Validators {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
validators: Vec<(<Ristretto as WrappedGroup>::G, u64)>,
|
validators: Vec<(<Ristretto as Ciphersuite>::G, u64)>,
|
||||||
) -> Option<Validators> {
|
) -> Option<Validators> {
|
||||||
let mut total_weight = 0;
|
let mut total_weight = 0;
|
||||||
let mut weights = HashMap::new();
|
let mut weights = HashMap::new();
|
||||||
@@ -163,6 +163,7 @@ impl SignatureScheme for Validators {
|
|||||||
type AggregateSignature = Vec<u8>;
|
type AggregateSignature = Vec<u8>;
|
||||||
type Signer = Arc<Signer>;
|
type Signer = Arc<Signer>;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: &Self::Signature) -> bool {
|
fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: &Self::Signature) -> bool {
|
||||||
if !self.weights.contains_key(&validator) {
|
if !self.weights.contains_key(&validator) {
|
||||||
return false;
|
return false;
|
||||||
@@ -195,6 +196,7 @@ impl SignatureScheme for Validators {
|
|||||||
aggregate.serialize()
|
aggregate.serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn verify_aggregate(
|
fn verify_aggregate(
|
||||||
&self,
|
&self,
|
||||||
signers: &[Self::ValidatorId],
|
signers: &[Self::ValidatorId],
|
||||||
@@ -219,7 +221,7 @@ impl SignatureScheme for Validators {
|
|||||||
signers
|
signers
|
||||||
.iter()
|
.iter()
|
||||||
.zip(challenges)
|
.zip(challenges)
|
||||||
.map(|(s, c)| (<Ristretto as GroupIo>::read_G(&mut s.as_slice()).unwrap(), c))
|
.map(|(s, c)| (<Ristretto as Ciphersuite>::read_G(&mut s.as_slice()).unwrap(), c))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.as_slice(),
|
.as_slice(),
|
||||||
)
|
)
|
||||||
@@ -248,7 +250,7 @@ impl Weights for Validators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub struct TendermintBlock(pub Vec<u8>);
|
pub struct TendermintBlock(pub Vec<u8>);
|
||||||
impl BlockTrait for TendermintBlock {
|
impl BlockTrait for TendermintBlock {
|
||||||
type Id = [u8; 32];
|
type Id = [u8; 32];
|
||||||
@@ -300,7 +302,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Network for TendermintNetwork<D, T, P>
|
|||||||
fn broadcast(&mut self, msg: SignedMessageFor<Self>) -> impl Send + Future<Output = ()> {
|
fn broadcast(&mut self, msg: SignedMessageFor<Self>) -> impl Send + Future<Output = ()> {
|
||||||
async move {
|
async move {
|
||||||
let mut to_broadcast = vec![TENDERMINT_MESSAGE];
|
let mut to_broadcast = vec![TENDERMINT_MESSAGE];
|
||||||
msg.serialize(&mut to_broadcast).unwrap();
|
to_broadcast.extend(msg.encode());
|
||||||
self.p2p.broadcast(self.genesis, to_broadcast).await
|
self.p2p.broadcast(self.genesis, to_broadcast).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,7 +392,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Network for TendermintNetwork<D, T, P>
|
|||||||
return invalid_block();
|
return invalid_block();
|
||||||
};
|
};
|
||||||
|
|
||||||
let encoded_commit = borsh::to_vec(&commit).unwrap();
|
let encoded_commit = commit.encode();
|
||||||
loop {
|
loop {
|
||||||
let block_res = self.blockchain.write().await.add_block::<Self>(
|
let block_res = self.blockchain.write().await.add_block::<Self>(
|
||||||
&block,
|
&block,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use borsh::BorshDeserialize;
|
use scale::{Encode, Decode, IoReader};
|
||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{Ciphersuite, Ristretto};
|
||||||
use ciphersuite::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
transaction::{Transaction, TransactionKind, TransactionError},
|
transaction::{Transaction, TransactionKind, TransactionError},
|
||||||
@@ -27,14 +26,14 @@ pub enum TendermintTx {
|
|||||||
|
|
||||||
impl ReadWrite for TendermintTx {
|
impl ReadWrite for TendermintTx {
|
||||||
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
Evidence::deserialize_reader(reader)
|
Evidence::decode(&mut IoReader(reader))
|
||||||
.map(TendermintTx::SlashEvidence)
|
.map(TendermintTx::SlashEvidence)
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid evidence format"))
|
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid evidence format"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
TendermintTx::SlashEvidence(ev) => writer.write_all(&borsh::to_vec(&ev).unwrap()),
|
TendermintTx::SlashEvidence(ev) => writer.write_all(&ev.encode()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +49,7 @@ impl Transaction for TendermintTx {
|
|||||||
Blake2s256::digest(self.serialize()).into()
|
Blake2s256::digest(self.serialize()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sig_hash(&self, _genesis: [u8; 32]) -> <Ristretto as WrappedGroup>::F {
|
fn sig_hash(&self, _genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F {
|
||||||
match self {
|
match self {
|
||||||
TendermintTx::SlashEvidence(_) => panic!("sig_hash called on slash evidence transaction"),
|
TendermintTx::SlashEvidence(_) => panic!("sig_hash called on slash evidence transaction"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use std::{sync::Arc, io, collections::HashMap, fmt::Debug};
|
use std::{sync::Arc, io, collections::HashMap, fmt::Debug};
|
||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
use ciphersuite::{
|
||||||
use dalek_ff_group::Ristretto;
|
group::{ff::Field, Group},
|
||||||
use ciphersuite::{group::Group, *};
|
Ciphersuite, Ristretto,
|
||||||
|
};
|
||||||
use schnorr::SchnorrSignature;
|
use schnorr::SchnorrSignature;
|
||||||
|
|
||||||
use serai_db::MemDb;
|
use serai_db::MemDb;
|
||||||
@@ -29,11 +30,11 @@ impl NonceTransaction {
|
|||||||
nonce,
|
nonce,
|
||||||
distinguisher,
|
distinguisher,
|
||||||
Signed {
|
Signed {
|
||||||
signer: <Ristretto as WrappedGroup>::G::identity(),
|
signer: <Ristretto as Ciphersuite>::G::identity(),
|
||||||
nonce,
|
nonce,
|
||||||
signature: SchnorrSignature::<Ristretto> {
|
signature: SchnorrSignature::<Ristretto> {
|
||||||
R: <Ristretto as WrappedGroup>::G::identity(),
|
R: <Ristretto as Ciphersuite>::G::identity(),
|
||||||
s: <Ristretto as WrappedGroup>::F::ZERO,
|
s: <Ristretto as Ciphersuite>::F::ZERO,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ use rand::rngs::OsRng;
|
|||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
|
||||||
use ciphersuite::*;
|
|
||||||
|
|
||||||
use serai_db::{DbTxn, Db, MemDb};
|
use serai_db::{DbTxn, Db, MemDb};
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ type N = TendermintNetwork<MemDb, SignedTransaction, DummyP2p>;
|
|||||||
|
|
||||||
fn new_blockchain<T: TransactionTrait>(
|
fn new_blockchain<T: TransactionTrait>(
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
participants: &[<Ristretto as WrappedGroup>::G],
|
participants: &[<Ristretto as Ciphersuite>::G],
|
||||||
) -> (MemDb, Blockchain<MemDb, T>) {
|
) -> (MemDb, Blockchain<MemDb, T>) {
|
||||||
let db = MemDb::new();
|
let db = MemDb::new();
|
||||||
let blockchain = Blockchain::new(db.clone(), genesis, participants);
|
let blockchain = Blockchain::new(db.clone(), genesis, participants);
|
||||||
@@ -82,7 +81,7 @@ fn invalid_block() {
|
|||||||
assert!(blockchain.verify_block::<N>(&block, &validators, false).is_err());
|
assert!(blockchain.verify_block::<N>(&block, &validators, false).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
|
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
|
||||||
|
|
||||||
// Not a participant
|
// Not a participant
|
||||||
@@ -134,7 +133,7 @@ fn invalid_block() {
|
|||||||
blockchain.verify_block::<N>(&block, &validators, false).unwrap();
|
blockchain.verify_block::<N>(&block, &validators, false).unwrap();
|
||||||
match &mut block.transactions[0] {
|
match &mut block.transactions[0] {
|
||||||
Transaction::Application(tx) => {
|
Transaction::Application(tx) => {
|
||||||
tx.1.signature.s += <Ristretto as WrappedGroup>::F::ONE;
|
tx.1.signature.s += <Ristretto as Ciphersuite>::F::ONE;
|
||||||
}
|
}
|
||||||
_ => panic!("non-signed tx found"),
|
_ => panic!("non-signed tx found"),
|
||||||
}
|
}
|
||||||
@@ -150,7 +149,7 @@ fn invalid_block() {
|
|||||||
fn signed_transaction() {
|
fn signed_transaction() {
|
||||||
let genesis = new_genesis();
|
let genesis = new_genesis();
|
||||||
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
|
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
|
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
|
||||||
let signer = tx.1.signer;
|
let signer = tx.1.signer;
|
||||||
|
|
||||||
@@ -339,7 +338,7 @@ fn provided_transaction() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn tendermint_evidence_tx() {
|
async fn tendermint_evidence_tx() {
|
||||||
let genesis = new_genesis();
|
let genesis = new_genesis();
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
let signer = Signer::new(genesis, key.clone());
|
let signer = Signer::new(genesis, key.clone());
|
||||||
let signer_id = Ristretto::generator() * key.deref();
|
let signer_id = Ristretto::generator() * key.deref();
|
||||||
let validators = Arc::new(Validators::new(genesis, vec![(signer_id, 1)]).unwrap());
|
let validators = Arc::new(Validators::new(genesis, vec![(signer_id, 1)]).unwrap());
|
||||||
@@ -379,7 +378,7 @@ async fn tendermint_evidence_tx() {
|
|||||||
let mut mempool: Vec<Transaction<SignedTransaction>> = vec![];
|
let mut mempool: Vec<Transaction<SignedTransaction>> = vec![];
|
||||||
let mut signers = vec![];
|
let mut signers = vec![];
|
||||||
for _ in 0 .. 5 {
|
for _ in 0 .. 5 {
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
let signer = Signer::new(genesis, key.clone());
|
let signer = Signer::new(genesis, key.clone());
|
||||||
let signer_id = Ristretto::generator() * key.deref();
|
let signer_id = Ristretto::generator() * key.deref();
|
||||||
signers.push((signer_id, 1));
|
signers.push((signer_id, 1));
|
||||||
@@ -446,7 +445,7 @@ async fn block_tx_ordering() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let genesis = new_genesis();
|
let genesis = new_genesis();
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
|
|
||||||
// signer
|
// signer
|
||||||
let signer = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0).1.signer;
|
let signer = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0).1.signer;
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use std::{sync::Arc, collections::HashMap};
|
|||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand::{RngCore, rngs::OsRng};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
|
||||||
use ciphersuite::*;
|
|
||||||
|
|
||||||
use tendermint::ext::Commit;
|
use tendermint::ext::Commit;
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ async fn mempool_addition() {
|
|||||||
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
||||||
};
|
};
|
||||||
let unsigned_in_chain = |_: [u8; 32]| false;
|
let unsigned_in_chain = |_: [u8; 32]| false;
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
|
|
||||||
let first_tx = signed_transaction(&mut OsRng, genesis, &key, 0);
|
let first_tx = signed_transaction(&mut OsRng, genesis, &key, 0);
|
||||||
let signer = first_tx.1.signer;
|
let signer = first_tx.1.signer;
|
||||||
@@ -125,7 +124,7 @@ async fn mempool_addition() {
|
|||||||
|
|
||||||
// If the mempool doesn't have a nonce for an account, it should successfully use the
|
// If the mempool doesn't have a nonce for an account, it should successfully use the
|
||||||
// blockchain's
|
// blockchain's
|
||||||
let second_key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let second_key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
let tx = signed_transaction(&mut OsRng, genesis, &second_key, 2);
|
let tx = signed_transaction(&mut OsRng, genesis, &second_key, 2);
|
||||||
let second_signer = tx.1.signer;
|
let second_signer = tx.1.signer;
|
||||||
assert_eq!(mempool.next_nonce_in_mempool(&second_signer, vec![]), None);
|
assert_eq!(mempool.next_nonce_in_mempool(&second_signer, vec![]), None);
|
||||||
@@ -165,7 +164,7 @@ fn too_many_mempool() {
|
|||||||
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
||||||
};
|
};
|
||||||
let unsigned_in_chain = |_: [u8; 32]| false;
|
let unsigned_in_chain = |_: [u8; 32]| false;
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
|
|
||||||
// We should be able to add transactions up to the limit
|
// We should be able to add transactions up to the limit
|
||||||
for i in 0 .. ACCOUNT_MEMPOOL_LIMIT {
|
for i in 0 .. ACCOUNT_MEMPOOL_LIMIT {
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ use rand::{RngCore, CryptoRng, rngs::OsRng};
|
|||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{
|
||||||
use ciphersuite::{group::Group, *};
|
group::{ff::Field, Group},
|
||||||
|
Ciphersuite, Ristretto,
|
||||||
|
};
|
||||||
use schnorr::SchnorrSignature;
|
use schnorr::SchnorrSignature;
|
||||||
|
|
||||||
|
use scale::Encode;
|
||||||
|
|
||||||
use ::tendermint::{
|
use ::tendermint::{
|
||||||
ext::{Network, Signer as SignerTrait, SignatureScheme, BlockNumber, RoundNumber},
|
ext::{Network, Signer as SignerTrait, SignatureScheme, BlockNumber, RoundNumber},
|
||||||
SignedMessageFor, DataFor, Message, SignedMessage, Data, Evidence,
|
SignedMessageFor, DataFor, Message, SignedMessage, Data, Evidence,
|
||||||
@@ -29,11 +33,11 @@ mod tendermint;
|
|||||||
|
|
||||||
pub fn random_signed<R: RngCore + CryptoRng>(rng: &mut R) -> Signed {
|
pub fn random_signed<R: RngCore + CryptoRng>(rng: &mut R) -> Signed {
|
||||||
Signed {
|
Signed {
|
||||||
signer: <Ristretto as WrappedGroup>::G::random(&mut *rng),
|
signer: <Ristretto as Ciphersuite>::G::random(&mut *rng),
|
||||||
nonce: u32::try_from(rng.next_u64() >> 32 >> 1).unwrap(),
|
nonce: u32::try_from(rng.next_u64() >> 32 >> 1).unwrap(),
|
||||||
signature: SchnorrSignature::<Ristretto> {
|
signature: SchnorrSignature::<Ristretto> {
|
||||||
R: <Ristretto as WrappedGroup>::G::random(&mut *rng),
|
R: <Ristretto as Ciphersuite>::G::random(&mut *rng),
|
||||||
s: <Ristretto as WrappedGroup>::F::random(rng),
|
s: <Ristretto as Ciphersuite>::F::random(rng),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,18 +136,18 @@ impl Transaction for SignedTransaction {
|
|||||||
pub fn signed_transaction<R: RngCore + CryptoRng>(
|
pub fn signed_transaction<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
nonce: u32,
|
nonce: u32,
|
||||||
) -> SignedTransaction {
|
) -> SignedTransaction {
|
||||||
let mut data = vec![0; 512];
|
let mut data = vec![0; 512];
|
||||||
rng.fill_bytes(&mut data);
|
rng.fill_bytes(&mut data);
|
||||||
|
|
||||||
let signer = <Ristretto as WrappedGroup>::generator() * **key;
|
let signer = <Ristretto as Ciphersuite>::generator() * **key;
|
||||||
|
|
||||||
let mut tx =
|
let mut tx =
|
||||||
SignedTransaction(data, Signed { signer, nonce, signature: random_signed(rng).signature });
|
SignedTransaction(data, Signed { signer, nonce, signature: random_signed(rng).signature });
|
||||||
|
|
||||||
let sig_nonce = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(rng));
|
let sig_nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(rng));
|
||||||
tx.1.signature.R = Ristretto::generator() * sig_nonce.deref();
|
tx.1.signature.R = Ristretto::generator() * sig_nonce.deref();
|
||||||
tx.1.signature = SchnorrSignature::sign(key, sig_nonce, tx.sig_hash(genesis));
|
tx.1.signature = SchnorrSignature::sign(key, sig_nonce, tx.sig_hash(genesis));
|
||||||
|
|
||||||
@@ -158,7 +162,7 @@ pub fn random_signed_transaction<R: RngCore + CryptoRng>(
|
|||||||
let mut genesis = [0; 32];
|
let mut genesis = [0; 32];
|
||||||
rng.fill_bytes(&mut genesis);
|
rng.fill_bytes(&mut genesis);
|
||||||
|
|
||||||
let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut *rng));
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut *rng));
|
||||||
// Shift over an additional bit to ensure it won't overflow when incremented
|
// Shift over an additional bit to ensure it won't overflow when incremented
|
||||||
let nonce = u32::try_from(rng.next_u64() >> 32 >> 1).unwrap();
|
let nonce = u32::try_from(rng.next_u64() >> 32 >> 1).unwrap();
|
||||||
|
|
||||||
@@ -175,11 +179,12 @@ pub async fn tendermint_meta() -> ([u8; 32], Signer, [u8; 32], Arc<Validators>)
|
|||||||
// signer
|
// signer
|
||||||
let genesis = new_genesis();
|
let genesis = new_genesis();
|
||||||
let signer =
|
let signer =
|
||||||
Signer::new(genesis, Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng)));
|
Signer::new(genesis, Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)));
|
||||||
let validator_id = signer.validator_id().await.unwrap();
|
let validator_id = signer.validator_id().await.unwrap();
|
||||||
|
|
||||||
// schema
|
// schema
|
||||||
let signer_pub = <Ristretto as GroupIo>::read_G::<&[u8]>(&mut validator_id.as_slice()).unwrap();
|
let signer_pub =
|
||||||
|
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut validator_id.as_slice()).unwrap();
|
||||||
let validators = Arc::new(Validators::new(genesis, vec![(signer_pub, 1)]).unwrap());
|
let validators = Arc::new(Validators::new(genesis, vec![(signer_pub, 1)]).unwrap());
|
||||||
|
|
||||||
(genesis, signer, validator_id, validators)
|
(genesis, signer, validator_id, validators)
|
||||||
@@ -198,7 +203,7 @@ pub async fn signed_from_data<N: Network>(
|
|||||||
round: RoundNumber(round_number),
|
round: RoundNumber(round_number),
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
let sig = signer.sign(&borsh::to_vec(&msg).unwrap()).await;
|
let sig = signer.sign(&msg.encode()).await;
|
||||||
SignedMessage { msg, sig }
|
SignedMessage { msg, sig }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,5 +216,5 @@ pub async fn random_evidence_tx<N: Network>(
|
|||||||
let data = Data::Proposal(Some(RoundNumber(0)), b);
|
let data = Data::Proposal(Some(RoundNumber(0)), b);
|
||||||
let signer_id = signer.validator_id().await.unwrap();
|
let signer_id = signer.validator_id().await.unwrap();
|
||||||
let signed = signed_from_data::<N>(signer, signer_id, 0, 0, data).await;
|
let signed = signed_from_data::<N>(signer, signer_id, 0, 0, data).await;
|
||||||
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(borsh::to_vec(&signed).unwrap()))
|
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ use rand::rngs::OsRng;
|
|||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
|
||||||
use ciphersuite::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
@@ -69,7 +68,7 @@ fn signed_transaction() {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut tx = tx.clone();
|
let mut tx = tx.clone();
|
||||||
tx.1.signature.s += <Ristretto as WrappedGroup>::F::ONE;
|
tx.1.signature.s += <Ristretto as Ciphersuite>::F::ONE;
|
||||||
assert!(verify_transaction(&tx, genesis, &mut |_, _| Some(tx.1.nonce)).is_err());
|
assert!(verify_transaction(&tx, genesis, &mut |_, _| Some(tx.1.nonce)).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ use std::sync::Arc;
|
|||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand::{RngCore, rngs::OsRng};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use ciphersuite::{Ristretto, Ciphersuite, group::ff::Field};
|
||||||
use ciphersuite::*;
|
|
||||||
|
use scale::Encode;
|
||||||
|
|
||||||
use tendermint::{
|
use tendermint::{
|
||||||
time::CanonicalInstant,
|
time::CanonicalInstant,
|
||||||
@@ -50,10 +51,7 @@ async fn invalid_valid_round() {
|
|||||||
async move {
|
async move {
|
||||||
let data = Data::Proposal(valid_round, TendermintBlock(vec![]));
|
let data = Data::Proposal(valid_round, TendermintBlock(vec![]));
|
||||||
let signed = signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, data).await;
|
let signed = signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, data).await;
|
||||||
(
|
(signed.clone(), TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode())))
|
||||||
signed.clone(),
|
|
||||||
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(borsh::to_vec(&signed).unwrap())),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,8 +69,7 @@ async fn invalid_valid_round() {
|
|||||||
let mut random_sig = [0u8; 64];
|
let mut random_sig = [0u8; 64];
|
||||||
OsRng.fill_bytes(&mut random_sig);
|
OsRng.fill_bytes(&mut random_sig);
|
||||||
signed.sig = random_sig;
|
signed.sig = random_sig;
|
||||||
let tx =
|
let tx = TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode()));
|
||||||
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(borsh::to_vec(&signed).unwrap()));
|
|
||||||
|
|
||||||
// should fail
|
// should fail
|
||||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||||
@@ -92,10 +89,7 @@ async fn invalid_precommit_signature() {
|
|||||||
let signed =
|
let signed =
|
||||||
signed_from_data::<N>(signer.clone().into(), signer_id, 1, 0, Data::Precommit(precommit))
|
signed_from_data::<N>(signer.clone().into(), signer_id, 1, 0, Data::Precommit(precommit))
|
||||||
.await;
|
.await;
|
||||||
(
|
(signed.clone(), TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(signed.encode())))
|
||||||
signed.clone(),
|
|
||||||
TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap())),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,8 +119,7 @@ async fn invalid_precommit_signature() {
|
|||||||
let mut random_sig = [0u8; 64];
|
let mut random_sig = [0u8; 64];
|
||||||
OsRng.fill_bytes(&mut random_sig);
|
OsRng.fill_bytes(&mut random_sig);
|
||||||
signed.sig = random_sig;
|
signed.sig = random_sig;
|
||||||
let tx =
|
let tx = TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(signed.encode()));
|
||||||
TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap()));
|
|
||||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,32 +137,24 @@ async fn evidence_with_prevote() {
|
|||||||
// it should fail for all reasons.
|
// it should fail for all reasons.
|
||||||
let mut txs = vec![];
|
let mut txs = vec![];
|
||||||
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(
|
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(
|
||||||
borsh::to_vec(
|
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||||
&&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
.await
|
||||||
.await,
|
.encode(),
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)));
|
)));
|
||||||
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidValidRound(
|
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidValidRound(
|
||||||
borsh::to_vec(
|
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||||
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
.await
|
||||||
.await,
|
.encode(),
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)));
|
)));
|
||||||
// Since these require a second message, provide this one again
|
// Since these require a second message, provide this one again
|
||||||
// ConflictingMessages can be fired for actually conflicting Prevotes however
|
// ConflictingMessages can be fired for actually conflicting Prevotes however
|
||||||
txs.push(TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
txs.push(TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(
|
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||||
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
.await
|
||||||
.await,
|
.encode(),
|
||||||
)
|
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||||
.unwrap(),
|
.await
|
||||||
borsh::to_vec(
|
.encode(),
|
||||||
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
|
||||||
.await,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)));
|
)));
|
||||||
txs
|
txs
|
||||||
}
|
}
|
||||||
@@ -203,16 +188,16 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
// non-conflicting data should fail
|
// non-conflicting data should fail
|
||||||
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
|
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
));
|
));
|
||||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||||
|
|
||||||
// conflicting data should pass
|
// conflicting data should pass
|
||||||
let signed_2 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
|
let signed_2 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
|
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
|
||||||
|
|
||||||
@@ -220,16 +205,16 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
// (except for Precommit)
|
// (except for Precommit)
|
||||||
let signed_2 = signed_for_b_r(0, 1, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
|
let signed_2 = signed_for_b_r(0, 1, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
||||||
|
|
||||||
// Proposals for different block numbers should also fail as evidence
|
// Proposals for different block numbers should also fail as evidence
|
||||||
let signed_2 = signed_for_b_r(1, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
|
let signed_2 = signed_for_b_r(1, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
||||||
}
|
}
|
||||||
@@ -239,16 +224,16 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
// non-conflicting data should fail
|
// non-conflicting data should fail
|
||||||
let signed_1 = signed_for_b_r(0, 0, Data::Prevote(Some([0x11; 32]))).await;
|
let signed_1 = signed_for_b_r(0, 0, Data::Prevote(Some([0x11; 32]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
));
|
));
|
||||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||||
|
|
||||||
// conflicting data should pass
|
// conflicting data should pass
|
||||||
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(Some([0x22; 32]))).await;
|
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(Some([0x22; 32]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
|
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
|
||||||
|
|
||||||
@@ -256,16 +241,16 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
// (except for Precommit)
|
// (except for Precommit)
|
||||||
let signed_2 = signed_for_b_r(0, 1, Data::Prevote(Some([0x22; 32]))).await;
|
let signed_2 = signed_for_b_r(0, 1, Data::Prevote(Some([0x22; 32]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
||||||
|
|
||||||
// Proposals for different block numbers should also fail as evidence
|
// Proposals for different block numbers should also fail as evidence
|
||||||
let signed_2 = signed_for_b_r(1, 0, Data::Prevote(Some([0x22; 32]))).await;
|
let signed_2 = signed_for_b_r(1, 0, Data::Prevote(Some([0x22; 32]))).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
||||||
}
|
}
|
||||||
@@ -275,7 +260,7 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
|
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
|
||||||
|
|
||||||
let signer_2 =
|
let signer_2 =
|
||||||
Signer::new(genesis, Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng)));
|
Signer::new(genesis, Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)));
|
||||||
let signed_id_2 = signer_2.validator_id().await.unwrap();
|
let signed_id_2 = signer_2.validator_id().await.unwrap();
|
||||||
let signed_2 = signed_from_data::<N>(
|
let signed_2 = signed_from_data::<N>(
|
||||||
signer_2.into(),
|
signer_2.into(),
|
||||||
@@ -287,14 +272,15 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// update schema so that we don't fail due to invalid signature
|
// update schema so that we don't fail due to invalid signature
|
||||||
let signer_pub = <Ristretto as GroupIo>::read_G::<&[u8]>(&mut signer_id.as_slice()).unwrap();
|
let signer_pub =
|
||||||
|
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut signer_id.as_slice()).unwrap();
|
||||||
let signer_pub_2 =
|
let signer_pub_2 =
|
||||||
<Ristretto as GroupIo>::read_G::<&[u8]>(&mut signed_id_2.as_slice()).unwrap();
|
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut signed_id_2.as_slice()).unwrap();
|
||||||
let validators =
|
let validators =
|
||||||
Arc::new(Validators::new(genesis, vec![(signer_pub, 1), (signer_pub_2, 1)]).unwrap());
|
Arc::new(Validators::new(genesis, vec![(signer_pub, 1), (signer_pub_2, 1)]).unwrap());
|
||||||
|
|
||||||
@@ -306,8 +292,8 @@ async fn conflicting_msgs_evidence_tx() {
|
|||||||
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![]))).await;
|
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![]))).await;
|
||||||
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(None)).await;
|
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(None)).await;
|
||||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||||
borsh::to_vec(&signed_1).unwrap(),
|
signed_1.encode(),
|
||||||
borsh::to_vec(&signed_2).unwrap(),
|
signed_2.encode(),
|
||||||
));
|
));
|
||||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ use blake2::{Digest, Blake2b512};
|
|||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{Group, GroupEncoding},
|
group::{Group, GroupEncoding},
|
||||||
*,
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
use schnorr::SchnorrSignature;
|
use schnorr::SchnorrSignature;
|
||||||
|
|
||||||
use crate::{TRANSACTION_SIZE_LIMIT, ReadWrite};
|
use crate::{TRANSACTION_SIZE_LIMIT, ReadWrite};
|
||||||
@@ -43,7 +42,7 @@ pub enum TransactionError {
|
|||||||
/// Data for a signed transaction.
|
/// Data for a signed transaction.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Signed {
|
pub struct Signed {
|
||||||
pub signer: <Ristretto as WrappedGroup>::G,
|
pub signer: <Ristretto as Ciphersuite>::G,
|
||||||
pub nonce: u32,
|
pub nonce: u32,
|
||||||
pub signature: SchnorrSignature<Ristretto>,
|
pub signature: SchnorrSignature<Ristretto>,
|
||||||
}
|
}
|
||||||
@@ -160,10 +159,10 @@ pub trait Transaction: 'static + Send + Sync + Clone + Eq + Debug + ReadWrite {
|
|||||||
/// Do not override this unless you know what you're doing.
|
/// Do not override this unless you know what you're doing.
|
||||||
///
|
///
|
||||||
/// Panics if called on non-signed transactions.
|
/// Panics if called on non-signed transactions.
|
||||||
fn sig_hash(&self, genesis: [u8; 32]) -> <Ristretto as WrappedGroup>::F {
|
fn sig_hash(&self, genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
TransactionKind::Signed(order, Signed { signature, .. }) => {
|
TransactionKind::Signed(order, Signed { signature, .. }) => {
|
||||||
<Ristretto as WrappedGroup>::F::from_bytes_mod_order_wide(
|
<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(
|
||||||
&Blake2b512::digest(
|
&Blake2b512::digest(
|
||||||
[
|
[
|
||||||
b"Tributary Signed Transaction",
|
b"Tributary Signed Transaction",
|
||||||
@@ -182,8 +181,8 @@ pub trait Transaction: 'static + Send + Sync + Clone + Eq + Debug + ReadWrite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GAIN: FnMut(&<Ristretto as WrappedGroup>::G, &[u8]) -> Option<u32> {}
|
pub trait GAIN: FnMut(&<Ristretto as Ciphersuite>::G, &[u8]) -> Option<u32> {}
|
||||||
impl<F: FnMut(&<Ristretto as WrappedGroup>::G, &[u8]) -> Option<u32>> GAIN for F {}
|
impl<F: FnMut(&<Ristretto as Ciphersuite>::G, &[u8]) -> Option<u32>> GAIN for F {}
|
||||||
|
|
||||||
pub(crate) fn verify_transaction<F: GAIN, T: Transaction>(
|
pub(crate) fn verify_transaction<F: GAIN, T: Transaction>(
|
||||||
tx: &T,
|
tx: &T,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ license = "MIT"
|
|||||||
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/tendermint"
|
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/tendermint"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.75"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -21,7 +21,7 @@ thiserror = { version = "2", default-features = false, features = ["std"] }
|
|||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
parity-scale-codec = { version = "3", default-features = false, features = ["std", "derive"] }
|
||||||
|
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std", "async-await-macro", "sink", "channel"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std", "async-await-macro", "sink", "channel"] }
|
||||||
futures-channel = { version = "0.3", default-features = false, features = ["std", "sink"] }
|
futures-channel = { version = "0.3", default-features = false, features = ["std", "sink"] }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022-2025 Luke Parker
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -3,41 +3,33 @@ use std::{sync::Arc, collections::HashSet};
|
|||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use parity_scale_codec::{Encode, Decode};
|
||||||
|
|
||||||
use crate::{SignedMessageFor, SlashEvent, commit_msg};
|
use crate::{SignedMessageFor, SlashEvent, commit_msg};
|
||||||
|
|
||||||
/// An alias for a series of traits required for a type to be usable as a validator ID,
|
/// An alias for a series of traits required for a type to be usable as a validator ID,
|
||||||
/// automatically implemented for all types satisfying those traits.
|
/// automatically implemented for all types satisfying those traits.
|
||||||
pub trait ValidatorId:
|
pub trait ValidatorId:
|
||||||
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + BorshSerialize + BorshDeserialize
|
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#[rustfmt::skip]
|
impl<V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode> ValidatorId
|
||||||
impl<
|
for V
|
||||||
V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + BorshSerialize + BorshDeserialize,
|
|
||||||
> ValidatorId for V
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An alias for a series of traits required for a type to be usable as a signature,
|
/// An alias for a series of traits required for a type to be usable as a signature,
|
||||||
/// automatically implemented for all types satisfying those traits.
|
/// automatically implemented for all types satisfying those traits.
|
||||||
pub trait Signature:
|
pub trait Signature: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode {}
|
||||||
Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize
|
impl<S: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode> Signature for S {}
|
||||||
{
|
|
||||||
}
|
|
||||||
impl<S: Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize> Signature
|
|
||||||
for S
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type aliases which are distinct according to the type system
|
// Type aliases which are distinct according to the type system
|
||||||
|
|
||||||
/// A struct containing a Block Number, wrapped to have a distinct type.
|
/// A struct containing a Block Number, wrapped to have a distinct type.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||||
pub struct BlockNumber(pub u64);
|
pub struct BlockNumber(pub u64);
|
||||||
/// A struct containing a round number, wrapped to have a distinct type.
|
/// A struct containing a round number, wrapped to have a distinct type.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||||
pub struct RoundNumber(pub u32);
|
pub struct RoundNumber(pub u32);
|
||||||
|
|
||||||
/// A signer for a validator.
|
/// A signer for a validator.
|
||||||
@@ -122,6 +114,7 @@ impl<S: SignatureScheme> SignatureScheme for Arc<S> {
|
|||||||
self.as_ref().aggregate(validators, msg, sigs)
|
self.as_ref().aggregate(validators, msg, sigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn verify_aggregate(
|
fn verify_aggregate(
|
||||||
&self,
|
&self,
|
||||||
signers: &[Self::ValidatorId],
|
signers: &[Self::ValidatorId],
|
||||||
@@ -135,7 +128,7 @@ impl<S: SignatureScheme> SignatureScheme for Arc<S> {
|
|||||||
/// A commit for a specific block.
|
/// A commit for a specific block.
|
||||||
///
|
///
|
||||||
/// The list of validators have weight exceeding the threshold for a valid commit.
|
/// The list of validators have weight exceeding the threshold for a valid commit.
|
||||||
#[derive(PartialEq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||||
pub struct Commit<S: SignatureScheme> {
|
pub struct Commit<S: SignatureScheme> {
|
||||||
/// End time of the round which created this commit, used as the start time of the next block.
|
/// End time of the round which created this commit, used as the start time of the next block.
|
||||||
pub end_time: u64,
|
pub end_time: u64,
|
||||||
@@ -193,7 +186,7 @@ impl<W: Weights> Weights for Arc<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Simplified error enum representing a block's validity.
|
/// Simplified error enum representing a block's validity.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, Encode, Decode)]
|
||||||
pub enum BlockError {
|
pub enum BlockError {
|
||||||
/// Malformed block which is wholly invalid.
|
/// Malformed block which is wholly invalid.
|
||||||
#[error("invalid block")]
|
#[error("invalid block")]
|
||||||
@@ -205,20 +198,9 @@ pub enum BlockError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trait representing a Block.
|
/// Trait representing a Block.
|
||||||
pub trait Block:
|
pub trait Block: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode {
|
||||||
Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize
|
|
||||||
{
|
|
||||||
// Type used to identify blocks. Presumably a cryptographic hash of the block.
|
// Type used to identify blocks. Presumably a cryptographic hash of the block.
|
||||||
type Id: Send
|
type Id: Send + Sync + Copy + Clone + PartialEq + Eq + AsRef<[u8]> + Debug + Encode + Decode;
|
||||||
+ Sync
|
|
||||||
+ Copy
|
|
||||||
+ Clone
|
|
||||||
+ PartialEq
|
|
||||||
+ Eq
|
|
||||||
+ AsRef<[u8]>
|
|
||||||
+ Debug
|
|
||||||
+ BorshSerialize
|
|
||||||
+ BorshDeserialize;
|
|
||||||
|
|
||||||
/// Return the deterministic, unique ID for this block.
|
/// Return the deterministic, unique ID for this block.
|
||||||
fn id(&self) -> Self::Id;
|
fn id(&self) -> Self::Id;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::{
|
|||||||
collections::{VecDeque, HashMap},
|
collections::{VecDeque, HashMap},
|
||||||
};
|
};
|
||||||
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use parity_scale_codec::{Encode, Decode, IoReader};
|
||||||
|
|
||||||
use futures_channel::mpsc;
|
use futures_channel::mpsc;
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
@@ -41,14 +41,14 @@ pub fn commit_msg(end_time: u64, id: &[u8]) -> Vec<u8> {
|
|||||||
[&end_time.to_le_bytes(), id].concat()
|
[&end_time.to_le_bytes(), id].concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||||
pub enum Step {
|
pub enum Step {
|
||||||
Propose,
|
Propose,
|
||||||
Prevote,
|
Prevote,
|
||||||
Precommit,
|
Precommit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Eq, Debug, Encode, Decode)]
|
||||||
pub enum Data<B: Block, S: Signature> {
|
pub enum Data<B: Block, S: Signature> {
|
||||||
Proposal(Option<RoundNumber>, B),
|
Proposal(Option<RoundNumber>, B),
|
||||||
Prevote(Option<B::Id>),
|
Prevote(Option<B::Id>),
|
||||||
@@ -90,7 +90,7 @@ impl<B: Block, S: Signature> Data<B, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub struct Message<V: ValidatorId, B: Block, S: Signature> {
|
pub struct Message<V: ValidatorId, B: Block, S: Signature> {
|
||||||
pub sender: V,
|
pub sender: V,
|
||||||
pub block: BlockNumber,
|
pub block: BlockNumber,
|
||||||
@@ -100,7 +100,7 @@ pub struct Message<V: ValidatorId, B: Block, S: Signature> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A signed Tendermint consensus message to be broadcast to the other validators.
|
/// A signed Tendermint consensus message to be broadcast to the other validators.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub struct SignedMessage<V: ValidatorId, B: Block, S: Signature> {
|
pub struct SignedMessage<V: ValidatorId, B: Block, S: Signature> {
|
||||||
pub msg: Message<V, B, S>,
|
pub msg: Message<V, B, S>,
|
||||||
pub sig: S,
|
pub sig: S,
|
||||||
@@ -117,18 +117,18 @@ impl<V: ValidatorId, B: Block, S: Signature> SignedMessage<V, B, S> {
|
|||||||
&self,
|
&self,
|
||||||
signer: &Scheme,
|
signer: &Scheme,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
signer.verify(self.msg.sender, &borsh::to_vec(&self.msg).unwrap(), &self.sig)
|
signer.verify(self.msg.sender, &self.msg.encode(), &self.sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub enum SlashReason {
|
pub enum SlashReason {
|
||||||
FailToPropose,
|
FailToPropose,
|
||||||
InvalidBlock,
|
InvalidBlock,
|
||||||
InvalidProposer,
|
InvalidProposer,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub enum Evidence {
|
pub enum Evidence {
|
||||||
ConflictingMessages(Vec<u8>, Vec<u8>),
|
ConflictingMessages(Vec<u8>, Vec<u8>),
|
||||||
InvalidPrecommit(Vec<u8>),
|
InvalidPrecommit(Vec<u8>),
|
||||||
@@ -159,7 +159,7 @@ pub type SignedMessageFor<N> = SignedMessage<
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
pub fn decode_signed_message<N: Network>(mut data: &[u8]) -> Option<SignedMessageFor<N>> {
|
pub fn decode_signed_message<N: Network>(mut data: &[u8]) -> Option<SignedMessageFor<N>> {
|
||||||
SignedMessageFor::<N>::deserialize_reader(&mut data).ok()
|
SignedMessageFor::<N>::decode(&mut data).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_and_verify_signed_message<N: Network>(
|
fn decode_and_verify_signed_message<N: Network>(
|
||||||
@@ -339,7 +339,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
target: "tendermint",
|
target: "tendermint",
|
||||||
"proposer for block {}, round {round:?} was {} (me: {res})",
|
"proposer for block {}, round {round:?} was {} (me: {res})",
|
||||||
self.block.number.0,
|
self.block.number.0,
|
||||||
hex::encode(borsh::to_vec(&proposer).unwrap()),
|
hex::encode(proposer.encode()),
|
||||||
);
|
);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -420,11 +420,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
// TODO: If the new slash event has evidence, emit to prevent a low-importance slash from
|
// TODO: If the new slash event has evidence, emit to prevent a low-importance slash from
|
||||||
// cancelling emission of high-importance slashes
|
// cancelling emission of high-importance slashes
|
||||||
if !self.block.slashes.contains(&validator) {
|
if !self.block.slashes.contains(&validator) {
|
||||||
log::info!(
|
log::info!(target: "tendermint", "Slashing validator {}", hex::encode(validator.encode()));
|
||||||
target: "tendermint",
|
|
||||||
"Slashing validator {}",
|
|
||||||
hex::encode(borsh::to_vec(&validator).unwrap()),
|
|
||||||
);
|
|
||||||
self.block.slashes.insert(validator);
|
self.block.slashes.insert(validator);
|
||||||
self.network.slash(validator, slash_event).await;
|
self.network.slash(validator, slash_event).await;
|
||||||
}
|
}
|
||||||
@@ -674,7 +670,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
self
|
self
|
||||||
.slash(
|
.slash(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
SlashEvent::WithEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap())),
|
SlashEvent::WithEvidence(Evidence::InvalidPrecommit(signed.encode())),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
Err(TendermintError::Malicious)?;
|
Err(TendermintError::Malicious)?;
|
||||||
@@ -745,10 +741,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
self.broadcast(Data::Prevote(None));
|
self.broadcast(Data::Prevote(None));
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
.slash(
|
.slash(msg.sender, SlashEvent::WithEvidence(Evidence::InvalidValidRound(msg.encode())))
|
||||||
msg.sender,
|
|
||||||
SlashEvent::WithEvidence(Evidence::InvalidValidRound(borsh::to_vec(&msg).unwrap())),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
Err(TendermintError::Malicious)?;
|
Err(TendermintError::Malicious)?;
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1032,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
|
|
||||||
while !messages.is_empty() {
|
while !messages.is_empty() {
|
||||||
self.network.broadcast(
|
self.network.broadcast(
|
||||||
SignedMessageFor::<N>::deserialize_reader(&mut messages)
|
SignedMessageFor::<N>::decode(&mut IoReader(&mut messages))
|
||||||
.expect("saved invalid message to DB")
|
.expect("saved invalid message to DB")
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
@@ -1064,7 +1057,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
} {
|
} {
|
||||||
if our_message {
|
if our_message {
|
||||||
assert!(sig.is_none());
|
assert!(sig.is_none());
|
||||||
sig = Some(self.signer.sign(&borsh::to_vec(&msg).unwrap()).await);
|
sig = Some(self.signer.sign(&msg.encode()).await);
|
||||||
}
|
}
|
||||||
let sig = sig.unwrap();
|
let sig = sig.unwrap();
|
||||||
|
|
||||||
@@ -1084,7 +1077,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
|||||||
let message_tape_key = message_tape_key(self.genesis);
|
let message_tape_key = message_tape_key(self.genesis);
|
||||||
let mut txn = self.db.txn();
|
let mut txn = self.db.txn();
|
||||||
let mut message_tape = txn.get(&message_tape_key).unwrap_or(vec![]);
|
let mut message_tape = txn.get(&message_tape_key).unwrap_or(vec![]);
|
||||||
signed_msg.serialize(&mut message_tape).unwrap();
|
message_tape.extend(signed_msg.encode());
|
||||||
txn.put(&message_tape_key, message_tape);
|
txn.put(&message_tape_key, message_tape);
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use std::{sync::Arc, collections::HashMap};
|
use std::{sync::Arc, collections::HashMap};
|
||||||
|
|
||||||
|
use parity_scale_codec::Encode;
|
||||||
|
|
||||||
use crate::{ext::*, RoundNumber, Step, DataFor, SignedMessageFor, Evidence};
|
use crate::{ext::*, RoundNumber, Step, DataFor, SignedMessageFor, Evidence};
|
||||||
|
|
||||||
type RoundLog<N> = HashMap<<N as Network>::ValidatorId, HashMap<Step, SignedMessageFor<N>>>;
|
type RoundLog<N> = HashMap<<N as Network>::ValidatorId, HashMap<Step, SignedMessageFor<N>>>;
|
||||||
@@ -37,10 +39,7 @@ impl<N: Network> MessageLog<N> {
|
|||||||
target: "tendermint",
|
target: "tendermint",
|
||||||
"Validator sent multiple messages for the same block + round + step"
|
"Validator sent multiple messages for the same block + round + step"
|
||||||
);
|
);
|
||||||
Err(Evidence::ConflictingMessages(
|
Err(Evidence::ConflictingMessages(existing.encode(), signed.encode()))?;
|
||||||
borsh::to_vec(&existing).unwrap(),
|
|
||||||
borsh::to_vec(&signed).unwrap(),
|
|
||||||
))?;
|
|
||||||
}
|
}
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
time::{UNIX_EPOCH, SystemTime, Duration},
|
time::{UNIX_EPOCH, SystemTime, Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use parity_scale_codec::{Encode, Decode};
|
||||||
|
|
||||||
use futures_util::sink::SinkExt;
|
use futures_util::sink::SinkExt;
|
||||||
use tokio::{sync::RwLock, time::sleep};
|
use tokio::{sync::RwLock, time::sleep};
|
||||||
@@ -46,6 +46,7 @@ impl SignatureScheme for TestSignatureScheme {
|
|||||||
type AggregateSignature = Vec<[u8; 32]>;
|
type AggregateSignature = Vec<[u8; 32]>;
|
||||||
type Signer = TestSigner;
|
type Signer = TestSigner;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn verify(&self, validator: u16, msg: &[u8], sig: &[u8; 32]) -> bool {
|
fn verify(&self, validator: u16, msg: &[u8], sig: &[u8; 32]) -> bool {
|
||||||
(sig[.. 2] == validator.to_le_bytes()) && (sig[2 ..] == [msg, &[0; 30]].concat()[.. 30])
|
(sig[.. 2] == validator.to_le_bytes()) && (sig[2 ..] == [msg, &[0; 30]].concat()[.. 30])
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ impl SignatureScheme for TestSignatureScheme {
|
|||||||
sigs.to_vec()
|
sigs.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn verify_aggregate(
|
fn verify_aggregate(
|
||||||
&self,
|
&self,
|
||||||
signers: &[TestValidatorId],
|
signers: &[TestValidatorId],
|
||||||
@@ -89,7 +91,7 @@ impl Weights for TestWeights {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
struct TestBlock {
|
struct TestBlock {
|
||||||
id: TestBlockId,
|
id: TestBlockId,
|
||||||
valid: Result<(), BlockError>,
|
valid: Result<(), BlockError>,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
keywords = []
|
keywords = []
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
rust-version = "1.85"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -21,15 +21,15 @@ workspace = true
|
|||||||
zeroize = { version = "^1.5", default-features = false, features = ["std"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["std"] }
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
|
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
|
||||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, features = ["std"] }
|
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
||||||
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", default-features = false, features = ["std"] }
|
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||||
|
|
||||||
serai-db = { path = "../../common/db" }
|
serai-db = { path = "../../common/db" }
|
||||||
serai-task = { path = "../../common/task", version = "0.1" }
|
serai-task = { path = "../../common/task", version = "0.1" }
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use scale::Encode;
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_primitives::{address::SeraiAddress, validator_sets::primitives::ExternalValidatorSet};
|
use serai_client::{primitives::SeraiAddress, validator_sets::primitives::ExternalValidatorSet};
|
||||||
|
|
||||||
use messages::sign::{VariantSignId, SignId};
|
use messages::sign::{VariantSignId, SignId};
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ use serai_cosign::CosignIntent;
|
|||||||
use crate::transaction::SigningProtocolRound;
|
use crate::transaction::SigningProtocolRound;
|
||||||
|
|
||||||
/// A topic within the database which the group participates in
|
/// A topic within the database which the group participates in
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Topic {
|
pub enum Topic {
|
||||||
/// Vote to remove a participant
|
/// Vote to remove a participant
|
||||||
RemoveParticipant {
|
RemoveParticipant {
|
||||||
@@ -122,7 +123,7 @@ impl Topic {
|
|||||||
Topic::DkgConfirmation { attempt, round: _ } => Some({
|
Topic::DkgConfirmation { attempt, round: _ } => Some({
|
||||||
let id = {
|
let id = {
|
||||||
let mut id = [0; 32];
|
let mut id = [0; 32];
|
||||||
let encoded_set = borsh::to_vec(set).unwrap();
|
let encoded_set = set.encode();
|
||||||
id[.. encoded_set.len()].copy_from_slice(&encoded_set);
|
id[.. encoded_set.len()].copy_from_slice(&encoded_set);
|
||||||
VariantSignId::Batch(id)
|
VariantSignId::Batch(id)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ use std::collections::HashMap;
|
|||||||
use ciphersuite::group::GroupEncoding;
|
use ciphersuite::group::GroupEncoding;
|
||||||
use dkg::Participant;
|
use dkg::Participant;
|
||||||
|
|
||||||
use serai_primitives::{
|
use serai_client::{
|
||||||
address::SeraiAddress,
|
primitives::SeraiAddress,
|
||||||
validator_sets::{ExternalValidatorSet, Slash},
|
validator_sets::primitives::{ExternalValidatorSet, Slash},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serai_db::*;
|
use serai_db::*;
|
||||||
@@ -253,7 +253,7 @@ impl<TD: Db, TDT: DbTxn, P: P2p> ScanBlock<'_, TD, TDT, P> {
|
|||||||
let signer = signer(signed);
|
let signer = signer(signed);
|
||||||
|
|
||||||
// Check the participant voted to be removed actually exists
|
// Check the participant voted to be removed actually exists
|
||||||
if !self.validators.contains(&participant) {
|
if !self.validators.iter().any(|validator| *validator == participant) {
|
||||||
TributaryDb::fatal_slash(
|
TributaryDb::fatal_slash(
|
||||||
self.tributary_txn,
|
self.tributary_txn,
|
||||||
self.set.set,
|
self.set.set,
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ use rand_core::{RngCore, CryptoRng};
|
|||||||
|
|
||||||
use blake2::{digest::typenum::U32, Digest, Blake2b};
|
use blake2::{digest::typenum::U32, Digest, Blake2b};
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{Group, GroupEncoding},
|
group::{ff::Field, Group, GroupEncoding},
|
||||||
*,
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
use dalek_ff_group::Ristretto;
|
|
||||||
use schnorr::SchnorrSignature;
|
use schnorr::SchnorrSignature;
|
||||||
|
|
||||||
|
use scale::Encode;
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_primitives::{addess::SeraiAddress, validator_sets::MAX_KEY_SHARES_PER_SET};
|
use serai_client::{primitives::SeraiAddress, validator_sets::primitives::MAX_KEY_SHARES_PER_SET};
|
||||||
|
|
||||||
use messages::sign::VariantSignId;
|
use messages::sign::VariantSignId;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ use tributary_sdk::{
|
|||||||
use crate::db::Topic;
|
use crate::db::Topic;
|
||||||
|
|
||||||
/// The round this data is for, within a signing protocol.
|
/// The round this data is for, within a signing protocol.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum SigningProtocolRound {
|
pub enum SigningProtocolRound {
|
||||||
/// A preprocess.
|
/// A preprocess.
|
||||||
Preprocess,
|
Preprocess,
|
||||||
@@ -51,7 +51,7 @@ impl SigningProtocolRound {
|
|||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct Signed {
|
pub struct Signed {
|
||||||
/// The signer.
|
/// The signer.
|
||||||
signer: <Ristretto as WrappedGroup>::G,
|
signer: <Ristretto as Ciphersuite>::G,
|
||||||
/// The signature.
|
/// The signature.
|
||||||
signature: SchnorrSignature<Ristretto>,
|
signature: SchnorrSignature<Ristretto>,
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ impl BorshDeserialize for Signed {
|
|||||||
|
|
||||||
impl Signed {
|
impl Signed {
|
||||||
/// Fetch the signer.
|
/// Fetch the signer.
|
||||||
pub(crate) fn signer(&self) -> <Ristretto as WrappedGroup>::G {
|
pub(crate) fn signer(&self) -> <Ristretto as Ciphersuite>::G {
|
||||||
self.signer
|
self.signer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,10 +85,10 @@ impl Signed {
|
|||||||
impl Default for Signed {
|
impl Default for Signed {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
signer: <Ristretto as WrappedGroup>::G::identity(),
|
signer: <Ristretto as Ciphersuite>::G::identity(),
|
||||||
signature: SchnorrSignature {
|
signature: SchnorrSignature {
|
||||||
R: <Ristretto as WrappedGroup>::G::identity(),
|
R: <Ristretto as Ciphersuite>::G::identity(),
|
||||||
s: <Ristretto as WrappedGroup>::F::ZERO,
|
s: <Ristretto as Ciphersuite>::F::ZERO,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,20 +241,19 @@ impl TransactionTrait for Transaction {
|
|||||||
fn kind(&self) -> TransactionKind {
|
fn kind(&self) -> TransactionKind {
|
||||||
match self {
|
match self {
|
||||||
Transaction::RemoveParticipant { participant, signed } => TransactionKind::Signed(
|
Transaction::RemoveParticipant { participant, signed } => TransactionKind::Signed(
|
||||||
borsh::to_vec(&(b"RemoveParticipant".as_slice(), participant)).unwrap(),
|
(b"RemoveParticipant", participant).encode(),
|
||||||
signed.to_tributary_signed(0),
|
signed.to_tributary_signed(0),
|
||||||
),
|
),
|
||||||
|
|
||||||
Transaction::DkgParticipation { signed, .. } => TransactionKind::Signed(
|
Transaction::DkgParticipation { signed, .. } => {
|
||||||
borsh::to_vec(b"DkgParticipation".as_slice()).unwrap(),
|
TransactionKind::Signed(b"DkgParticipation".encode(), signed.to_tributary_signed(0))
|
||||||
signed.to_tributary_signed(0),
|
}
|
||||||
),
|
|
||||||
Transaction::DkgConfirmationPreprocess { attempt, signed, .. } => TransactionKind::Signed(
|
Transaction::DkgConfirmationPreprocess { attempt, signed, .. } => TransactionKind::Signed(
|
||||||
borsh::to_vec(b"DkgConfirmation".as_slice(), attempt).unwrap(),
|
(b"DkgConfirmation", attempt).encode(),
|
||||||
signed.to_tributary_signed(0),
|
signed.to_tributary_signed(0),
|
||||||
),
|
),
|
||||||
Transaction::DkgConfirmationShare { attempt, signed, .. } => TransactionKind::Signed(
|
Transaction::DkgConfirmationShare { attempt, signed, .. } => TransactionKind::Signed(
|
||||||
borsh::to_vec(b"DkgConfirmation".as_slice(), attempt).unwrap(),
|
(b"DkgConfirmation", attempt).encode(),
|
||||||
signed.to_tributary_signed(1),
|
signed.to_tributary_signed(1),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -264,14 +263,13 @@ impl TransactionTrait for Transaction {
|
|||||||
Transaction::Batch { .. } => TransactionKind::Provided("Batch"),
|
Transaction::Batch { .. } => TransactionKind::Provided("Batch"),
|
||||||
|
|
||||||
Transaction::Sign { id, attempt, round, signed, .. } => TransactionKind::Signed(
|
Transaction::Sign { id, attempt, round, signed, .. } => TransactionKind::Signed(
|
||||||
borsh::to_vec(b"Sign".as_slice(), id, attempt).unwrap(),
|
(b"Sign", id, attempt).encode(),
|
||||||
signed.to_tributary_signed(round.nonce()),
|
signed.to_tributary_signed(round.nonce()),
|
||||||
),
|
),
|
||||||
|
|
||||||
Transaction::SlashReport { signed, .. } => TransactionKind::Signed(
|
Transaction::SlashReport { signed, .. } => {
|
||||||
borsh::to_vec(b"SlashReport".as_slice()).unwrap(),
|
TransactionKind::Signed(b"SlashReport".encode(), signed.to_tributary_signed(0))
|
||||||
signed.to_tributary_signed(0),
|
}
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,7 +355,7 @@ impl Transaction {
|
|||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
|
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
) {
|
) {
|
||||||
fn signed(tx: &mut Transaction) -> &mut Signed {
|
fn signed(tx: &mut Transaction) -> &mut Signed {
|
||||||
#[allow(clippy::match_same_arms)] // This doesn't make semantic sense here
|
#[allow(clippy::match_same_arms)] // This doesn't make semantic sense here
|
||||||
@@ -381,13 +379,13 @@ impl Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decide the nonce to sign with
|
// Decide the nonce to sign with
|
||||||
let sig_nonce = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(rng));
|
let sig_nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(rng));
|
||||||
|
|
||||||
{
|
{
|
||||||
// Set the signer and the nonce
|
// Set the signer and the nonce
|
||||||
let signed = signed(self);
|
let signed = signed(self);
|
||||||
signed.signer = Ristretto::generator() * key.deref();
|
signed.signer = Ristretto::generator() * key.deref();
|
||||||
signed.signature.R = <Ristretto as WrappedGroup>::generator() * sig_nonce.deref();
|
signed.signature.R = <Ristretto as Ciphersuite>::generator() * sig_nonce.deref();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the signature hash (which now includes `R || A` making it valid as the challenge)
|
// Get the signature hash (which now includes `R || A` making it valid as the challenge)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ciphersuite"
|
name = "ciphersuite"
|
||||||
version = "0.4.2"
|
version = "0.4.1"
|
||||||
description = "Ciphersuites built around ff/group"
|
description = "Ciphersuites built around ff/group"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ciphersuite"
|
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ciphersuite"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["ciphersuite", "ff", "group"]
|
keywords = ["ciphersuite", "ff", "group"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85"
|
rust-version = "1.80"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -17,32 +17,69 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
std-shims = { path = "../../common/std-shims", version = "0.1.4", default-features = false, optional = true }
|
std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-features = false, optional = true }
|
||||||
|
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["derive"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["derive"] }
|
||||||
subtle = { version = "^2.4", default-features = false }
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
|
||||||
digest = { version = "0.11.0-rc.1", default-features = false }
|
digest = { version = "0.10", default-features = false }
|
||||||
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false }
|
||||||
|
sha2 = { version = "0.10", default-features = false, optional = true }
|
||||||
|
sha3 = { version = "0.10", default-features = false, optional = true }
|
||||||
|
|
||||||
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||||
group = { version = "0.13", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
|
|
||||||
|
dalek-ff-group = { path = "../dalek-ff-group", version = "0.4", default-features = false, optional = true }
|
||||||
|
|
||||||
|
elliptic-curve = { version = "0.13", default-features = false, features = ["hash2curve"], optional = true }
|
||||||
|
p256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"], optional = true }
|
||||||
|
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"], optional = true }
|
||||||
|
|
||||||
|
minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
ff-group-tests = { version = "0.13", path = "../ff-group-tests" }
|
ff-group-tests = { version = "0.13", path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["std-shims", "zeroize/alloc", "digest/alloc", "ff/alloc"]
|
alloc = ["std-shims"]
|
||||||
std = [
|
std = [
|
||||||
"alloc",
|
|
||||||
|
|
||||||
"std-shims/std",
|
"std-shims/std",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
|
||||||
"zeroize/std",
|
"zeroize/std",
|
||||||
"subtle/std",
|
"subtle/std",
|
||||||
|
|
||||||
|
"digest/std",
|
||||||
|
"transcript/std",
|
||||||
|
"sha2?/std",
|
||||||
|
"sha3?/std",
|
||||||
|
|
||||||
"ff/std",
|
"ff/std",
|
||||||
|
|
||||||
|
"dalek-ff-group?/std",
|
||||||
|
|
||||||
|
"elliptic-curve?/std",
|
||||||
|
"p256?/std",
|
||||||
|
"k256?/std",
|
||||||
|
"minimal-ed448?/std",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
dalek = ["sha2", "dalek-ff-group"]
|
||||||
|
ed25519 = ["dalek"]
|
||||||
|
ristretto = ["dalek"]
|
||||||
|
|
||||||
|
kp256 = ["sha2", "elliptic-curve"]
|
||||||
|
p256 = ["kp256", "dep:p256"]
|
||||||
|
secp256k1 = ["kp256", "k256"]
|
||||||
|
|
||||||
|
ed448 = ["sha3", "minimal-ed448"]
|
||||||
|
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021-2025 Luke Parker
|
Copyright (c) 2021-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ Secp256k1 and P-256 are offered via [k256](https://crates.io/crates/k256) and
|
|||||||
[p256](https://crates.io/crates/p256), two libraries maintained by
|
[p256](https://crates.io/crates/p256), two libraries maintained by
|
||||||
[RustCrypto](https://github.com/RustCrypto).
|
[RustCrypto](https://github.com/RustCrypto).
|
||||||
|
|
||||||
Please see the [`ciphersuite-kp256`](https://docs.rs/ciphersuite-kp256) crate for more info.
|
Their `hash_to_F` is the
|
||||||
|
[IETF's hash to curve](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html),
|
||||||
|
yet applied to their scalar field.
|
||||||
|
|
||||||
### Ed25519/Ristretto
|
### Ed25519/Ristretto
|
||||||
|
|
||||||
@@ -25,7 +27,11 @@ Ed25519/Ristretto are offered via
|
|||||||
[dalek-ff-group](https://crates.io/crates/dalek-ff-group), an ff/group wrapper
|
[dalek-ff-group](https://crates.io/crates/dalek-ff-group), an ff/group wrapper
|
||||||
around [curve25519-dalek](https://crates.io/crates/curve25519-dalek).
|
around [curve25519-dalek](https://crates.io/crates/curve25519-dalek).
|
||||||
|
|
||||||
Please see the [`dalek-ff-group`](https://docs.rs/dalek-ff-group) crate for more info.
|
Their `hash_to_F` is the wide reduction of SHA2-512, as used in
|
||||||
|
[RFC-8032](https://www.rfc-editor.org/rfc/rfc8032). This is also compliant with
|
||||||
|
the draft
|
||||||
|
[RFC-RISTRETTO](https://www.ietf.org/archive/id/draft-irtf-cfrg-ristretto255-decaf448-05.html).
|
||||||
|
The domain-separation tag is naively prefixed to the message.
|
||||||
|
|
||||||
### Ed448
|
### Ed448
|
||||||
|
|
||||||
@@ -33,4 +39,6 @@ Ed448 is offered via [minimal-ed448](https://crates.io/crates/minimal-ed448), an
|
|||||||
explicitly not recommended, unaudited, incomplete Ed448 implementation, limited
|
explicitly not recommended, unaudited, incomplete Ed448 implementation, limited
|
||||||
to its prime-order subgroup.
|
to its prime-order subgroup.
|
||||||
|
|
||||||
Please see the [`minimal-ed448`](https://docs.rs/minimal-ed448) crate for more info.
|
Its `hash_to_F` is the wide reduction of SHAKE256, with a 114-byte output, as
|
||||||
|
used in [RFC-8032](https://www.rfc-editor.org/rfc/rfc8032). The
|
||||||
|
domain-separation tag is naively prefixed to the message.
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ciphersuite-kp256"
|
|
||||||
version = "0.4.0"
|
|
||||||
description = "Ciphersuites built around ff/group"
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ciphersuite/kp256"
|
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|
||||||
keywords = ["ciphersuite", "ff", "group"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.85"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rand_core = { version = "0.6", default-features = false }
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
sha2 = { version = "0.11.0-rc.2", default-features = false }
|
|
||||||
|
|
||||||
p256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"] }
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"] }
|
|
||||||
|
|
||||||
ciphersuite = { path = "../", version = "0.4", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
|
||||||
|
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
|
||||||
|
|
||||||
ff-group-tests = { version = "0.13", path = "../../ff-group-tests" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
alloc = ["ciphersuite/alloc"]
|
|
||||||
std = [
|
|
||||||
"rand_core/std",
|
|
||||||
|
|
||||||
"zeroize/std",
|
|
||||||
|
|
||||||
"p256/std",
|
|
||||||
"k256/std",
|
|
||||||
|
|
||||||
"ciphersuite/std",
|
|
||||||
]
|
|
||||||
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Ciphersuite {k, p}256
|
|
||||||
|
|
||||||
SECP256k1 and P-256 Ciphersuites around k256 and p256.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
use sha2::Sha512;
|
|
||||||
|
|
||||||
use ciphersuite::{WrappedGroup, Id, WithPreferredHash, GroupCanonicalEncoding};
|
|
||||||
|
|
||||||
pub use k256;
|
|
||||||
pub use p256;
|
|
||||||
|
|
||||||
macro_rules! kp_curve {
|
|
||||||
(
|
|
||||||
$feature: literal,
|
|
||||||
$lib: ident,
|
|
||||||
|
|
||||||
$Ciphersuite: ident,
|
|
||||||
$ID: literal
|
|
||||||
) => {
|
|
||||||
impl WrappedGroup for $Ciphersuite {
|
|
||||||
type F = $lib::Scalar;
|
|
||||||
type G = $lib::ProjectivePoint;
|
|
||||||
fn generator() -> Self::G {
|
|
||||||
$lib::ProjectivePoint::GENERATOR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Id for $Ciphersuite {
|
|
||||||
const ID: &'static [u8] = $ID;
|
|
||||||
}
|
|
||||||
impl WithPreferredHash for $Ciphersuite {
|
|
||||||
type H = Sha512;
|
|
||||||
}
|
|
||||||
impl GroupCanonicalEncoding for $Ciphersuite {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ciphersuite for Secp256k1.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
|
||||||
pub struct Secp256k1;
|
|
||||||
kp_curve!("secp256k1", k256, Secp256k1, b"secp256k1");
|
|
||||||
#[test]
|
|
||||||
fn test_secp256k1() {
|
|
||||||
ff_group_tests::group::test_prime_group_bits::<_, k256::ProjectivePoint>(&mut rand_core::OsRng);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ciphersuite for P-256.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
|
||||||
pub struct P256;
|
|
||||||
kp_curve!("p256", p256, P256, b"P-256");
|
|
||||||
#[test]
|
|
||||||
fn test_p256() {
|
|
||||||
ff_group_tests::group::test_prime_group_bits::<_, p256::ProjectivePoint>(&mut rand_core::OsRng);
|
|
||||||
}
|
|
||||||
106
crypto/ciphersuite/src/dalek.rs
Normal file
106
crypto/ciphersuite/src/dalek.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use sha2::{Digest, Sha512};
|
||||||
|
|
||||||
|
use group::Group;
|
||||||
|
use dalek_ff_group::Scalar;
|
||||||
|
|
||||||
|
use crate::Ciphersuite;
|
||||||
|
|
||||||
|
macro_rules! dalek_curve {
|
||||||
|
(
|
||||||
|
$feature: literal,
|
||||||
|
|
||||||
|
$Ciphersuite: ident,
|
||||||
|
$Point: ident,
|
||||||
|
$ID: literal
|
||||||
|
) => {
|
||||||
|
use dalek_ff_group::$Point;
|
||||||
|
|
||||||
|
impl Ciphersuite for $Ciphersuite {
|
||||||
|
type F = Scalar;
|
||||||
|
type G = $Point;
|
||||||
|
type H = Sha512;
|
||||||
|
|
||||||
|
const ID: &'static [u8] = $ID;
|
||||||
|
|
||||||
|
fn generator() -> Self::G {
|
||||||
|
$Point::generator()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
|
||||||
|
let res = Scalar::from_bytes_mod_order_wide(&scalar);
|
||||||
|
scalar.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
||||||
|
Scalar::from_hash(Sha512::new_with_prefix(&[dst, data].concat()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ciphersuite for Ristretto.
|
||||||
|
///
|
||||||
|
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
||||||
|
/// between the two. This means `dst: b"abc", data: b"def"`, will produce the same scalar as
|
||||||
|
/// `dst: "abcdef", data: b""`. Please use carefully, not letting dsts be substrings of each other.
|
||||||
|
#[cfg(any(test, feature = "ristretto"))]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
|
pub struct Ristretto;
|
||||||
|
#[cfg(any(test, feature = "ristretto"))]
|
||||||
|
dalek_curve!("ristretto", Ristretto, RistrettoPoint, b"ristretto");
|
||||||
|
#[cfg(any(test, feature = "ristretto"))]
|
||||||
|
#[test]
|
||||||
|
fn test_ristretto() {
|
||||||
|
ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Ristretto::hash_to_F(
|
||||||
|
b"FROST-RISTRETTO255-SHA512-v11nonce",
|
||||||
|
&hex::decode(
|
||||||
|
"\
|
||||||
|
81800157bb554f299fe0b6bd658e4c4591d74168b5177bf55e8dceed59dc80c7\
|
||||||
|
5c3430d391552f6e60ecdc093ff9f6f4488756aa6cebdbad75a768010b8f830e"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
.to_bytes()
|
||||||
|
.as_ref(),
|
||||||
|
&hex::decode("40f58e8df202b21c94f826e76e4647efdb0ea3ca7ae7e3689bc0cbe2e2f6660c").unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ciphersuite for Ed25519, inspired by RFC-8032.
|
||||||
|
///
|
||||||
|
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
||||||
|
/// between the two. This means `dst: b"abc", data: b"def"`, will produce the same scalar as
|
||||||
|
/// `dst: "abcdef", data: b""`. Please use carefully, not letting dsts be substrings of each other.
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
|
pub struct Ed25519;
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
dalek_curve!("ed25519", Ed25519, EdwardsPoint, b"edwards25519");
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
#[test]
|
||||||
|
fn test_ed25519() {
|
||||||
|
ff_group_tests::group::test_prime_group_bits::<_, EdwardsPoint>(&mut rand_core::OsRng);
|
||||||
|
|
||||||
|
// Ideally, a test vector from RFC-8032 (not FROST) would be here
|
||||||
|
// Unfortunately, the IETF draft doesn't provide any vectors for the derived challenges
|
||||||
|
assert_eq!(
|
||||||
|
Ed25519::hash_to_F(
|
||||||
|
b"FROST-ED25519-SHA512-v11nonce",
|
||||||
|
&hex::decode(
|
||||||
|
"\
|
||||||
|
9d06a6381c7a4493929761a73692776772b274236fb5cfcc7d1b48ac3a9c249f\
|
||||||
|
929dcc590407aae7d388761cddb0c0db6f5627aea8e217f4a033f2ec83d93509"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
.to_bytes()
|
||||||
|
.as_ref(),
|
||||||
|
&hex::decode("70652da3e8d7533a0e4b9e9104f01b48c396b5b553717784ed8d05c6a36b9609").unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
110
crypto/ciphersuite/src/ed448.rs
Normal file
110
crypto/ciphersuite/src/ed448.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use digest::{
|
||||||
|
typenum::U114, core_api::BlockSizeUser, Update, Output, OutputSizeUser, FixedOutput,
|
||||||
|
ExtendableOutput, XofReader, HashMarker, Digest,
|
||||||
|
};
|
||||||
|
use sha3::Shake256;
|
||||||
|
|
||||||
|
use group::Group;
|
||||||
|
use minimal_ed448::{Scalar, Point};
|
||||||
|
|
||||||
|
use crate::Ciphersuite;
|
||||||
|
|
||||||
|
/// Shake256, fixed to a 114-byte output, as used by Ed448.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Shake256_114(Shake256);
|
||||||
|
impl BlockSizeUser for Shake256_114 {
|
||||||
|
type BlockSize = <Shake256 as BlockSizeUser>::BlockSize;
|
||||||
|
fn block_size() -> usize {
|
||||||
|
Shake256::block_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl OutputSizeUser for Shake256_114 {
|
||||||
|
type OutputSize = U114;
|
||||||
|
fn output_size() -> usize {
|
||||||
|
114
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Update for Shake256_114 {
|
||||||
|
fn update(&mut self, data: &[u8]) {
|
||||||
|
self.0.update(data);
|
||||||
|
}
|
||||||
|
fn chain(mut self, data: impl AsRef<[u8]>) -> Self {
|
||||||
|
Update::update(&mut self, data.as_ref());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FixedOutput for Shake256_114 {
|
||||||
|
fn finalize_fixed(self) -> Output<Self> {
|
||||||
|
let mut res = Default::default();
|
||||||
|
FixedOutput::finalize_into(self, &mut res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
fn finalize_into(self, out: &mut Output<Self>) {
|
||||||
|
let mut reader = self.0.finalize_xof();
|
||||||
|
reader.read(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl HashMarker for Shake256_114 {}
|
||||||
|
|
||||||
|
/// Ciphersuite for Ed448, inspired by RFC-8032. This is not recommended for usage.
|
||||||
|
///
|
||||||
|
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
||||||
|
/// between the two. This means `dst: b"abc", data: b"def"`, will produce the same scalar as
|
||||||
|
/// `dst: "abcdef", data: b""`. Please use carefully, not letting dsts be substrings of each other.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
|
pub struct Ed448;
|
||||||
|
impl Ciphersuite for Ed448 {
|
||||||
|
type F = Scalar;
|
||||||
|
type G = Point;
|
||||||
|
type H = Shake256_114;
|
||||||
|
|
||||||
|
const ID: &'static [u8] = b"ed448";
|
||||||
|
|
||||||
|
fn generator() -> Self::G {
|
||||||
|
Point::generator()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
|
||||||
|
let res = Self::hash_to_F(b"Ciphersuite-reduce_512", &scalar);
|
||||||
|
scalar.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
||||||
|
Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_ref().try_into().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ed448() {
|
||||||
|
use ff::PrimeField;
|
||||||
|
|
||||||
|
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
|
||||||
|
|
||||||
|
// Ideally, a test vector from RFC-8032 (not FROST) would be here
|
||||||
|
// Unfortunately, the IETF draft doesn't provide any vectors for the derived challenges
|
||||||
|
assert_eq!(
|
||||||
|
Ed448::hash_to_F(
|
||||||
|
b"FROST-ED448-SHAKE256-v11nonce",
|
||||||
|
&hex::decode(
|
||||||
|
"\
|
||||||
|
89bf16040081ff2990336b200613787937ebe1f024b8cdff90eb6f1c741d91c1\
|
||||||
|
4a2b2f5858a932ad3d3b18bd16e76ced3070d72fd79ae4402df201f5\
|
||||||
|
25e754716a1bc1b87a502297f2a99d89ea054e0018eb55d39562fd01\
|
||||||
|
00"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
.to_repr()
|
||||||
|
.to_vec(),
|
||||||
|
hex::decode(
|
||||||
|
"\
|
||||||
|
67a6f023e77361707c6e894c625e809e80f33fdb310810053ae29e28\
|
||||||
|
e7011f3193b9020e73c183a98cc3a519160ed759376dd92c94831622\
|
||||||
|
00"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
192
crypto/ciphersuite/src/kp256.rs
Normal file
192
crypto/ciphersuite/src/kp256.rs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use group::ff::PrimeField;
|
||||||
|
|
||||||
|
use elliptic_curve::{
|
||||||
|
generic_array::GenericArray,
|
||||||
|
bigint::{NonZero, CheckedAdd, Encoding, U384, U512},
|
||||||
|
hash2curve::{Expander, ExpandMsg, ExpandMsgXmd},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ciphersuite;
|
||||||
|
|
||||||
|
macro_rules! kp_curve {
|
||||||
|
(
|
||||||
|
$feature: literal,
|
||||||
|
$lib: ident,
|
||||||
|
|
||||||
|
$Ciphersuite: ident,
|
||||||
|
$ID: literal
|
||||||
|
) => {
|
||||||
|
impl Ciphersuite for $Ciphersuite {
|
||||||
|
type F = $lib::Scalar;
|
||||||
|
type G = $lib::ProjectivePoint;
|
||||||
|
type H = Sha256;
|
||||||
|
|
||||||
|
const ID: &'static [u8] = $ID;
|
||||||
|
|
||||||
|
fn generator() -> Self::G {
|
||||||
|
$lib::ProjectivePoint::GENERATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_512(scalar: [u8; 64]) -> Self::F {
|
||||||
|
let mut modulus = [0; 64];
|
||||||
|
modulus[32 ..].copy_from_slice(&(Self::F::ZERO - Self::F::ONE).to_bytes());
|
||||||
|
let modulus = U512::from_be_slice(&modulus).checked_add(&U512::ONE).unwrap();
|
||||||
|
|
||||||
|
let mut wide =
|
||||||
|
U512::from_be_bytes(scalar).rem(&NonZero::new(modulus).unwrap()).to_be_bytes();
|
||||||
|
|
||||||
|
let mut array = *GenericArray::from_slice(&wide[32 ..]);
|
||||||
|
let res = $lib::Scalar::from_repr(array).unwrap();
|
||||||
|
|
||||||
|
wide.zeroize();
|
||||||
|
array.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
||||||
|
// While one of these two libraries does support directly hashing to the Scalar field, the
|
||||||
|
// other doesn't. While that's probably an oversight, this is a universally working method
|
||||||
|
|
||||||
|
// This method is from
|
||||||
|
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html
|
||||||
|
// Specifically, Section 5
|
||||||
|
|
||||||
|
// While that draft, overall, is intended for hashing to curves, that necessitates
|
||||||
|
// detailing how to hash to a finite field. The draft comments that its mechanism for
|
||||||
|
// doing so, which it uses to derive field elements, is also applicable to the scalar field
|
||||||
|
|
||||||
|
// The hash_to_field function is intended to provide unbiased values
|
||||||
|
// In order to do so, a wide reduction from an extra k bits is applied, minimizing bias to
|
||||||
|
// 2^-k
|
||||||
|
// k is intended to be the bits of security of the suite, which is 128 for secp256k1 and
|
||||||
|
// P-256
|
||||||
|
const K: usize = 128;
|
||||||
|
|
||||||
|
// L is the amount of bytes of material which should be used in the wide reduction
|
||||||
|
// The 256 is for the bit-length of the primes, rounded up to the nearest byte threshold
|
||||||
|
// This is a simplification of the formula from the end of section 5
|
||||||
|
const L: usize = (256 + K) / 8; // 48
|
||||||
|
|
||||||
|
// In order to perform this reduction, we need to use 48-byte numbers
|
||||||
|
// First, convert the modulus to a 48-byte number
|
||||||
|
// This is done by getting -1 as bytes, parsing it into a U384, and then adding back one
|
||||||
|
let mut modulus = [0; L];
|
||||||
|
// The byte repr of scalars will be 32 big-endian bytes
|
||||||
|
// Set the lower 32 bytes of our 48-byte array accordingly
|
||||||
|
modulus[16 ..].copy_from_slice(&(Self::F::ZERO - Self::F::ONE).to_bytes());
|
||||||
|
// Use a checked_add + unwrap since this addition cannot fail (being a 32-byte value with
|
||||||
|
// 48-bytes of space)
|
||||||
|
// While a non-panicking saturating_add/wrapping_add could be used, they'd likely be less
|
||||||
|
// performant
|
||||||
|
let modulus = U384::from_be_slice(&modulus).checked_add(&U384::ONE).unwrap();
|
||||||
|
|
||||||
|
// The defined P-256 and secp256k1 ciphersuites both use expand_message_xmd
|
||||||
|
let mut wide = U384::from_be_bytes({
|
||||||
|
let mut bytes = [0; 48];
|
||||||
|
ExpandMsgXmd::<Sha256>::expand_message(&[msg], &[dst], 48)
|
||||||
|
.unwrap()
|
||||||
|
.fill_bytes(&mut bytes);
|
||||||
|
bytes
|
||||||
|
})
|
||||||
|
.rem(&NonZero::new(modulus).unwrap())
|
||||||
|
.to_be_bytes();
|
||||||
|
|
||||||
|
// Now that this has been reduced back to a 32-byte value, grab the lower 32-bytes
|
||||||
|
let mut array = *GenericArray::from_slice(&wide[16 ..]);
|
||||||
|
let res = $lib::Scalar::from_repr(array).unwrap();
|
||||||
|
|
||||||
|
// Zeroize the temp values we can due to the possibility hash_to_F is being used for nonces
|
||||||
|
wide.zeroize();
|
||||||
|
array.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn test_oversize_dst<C: Ciphersuite>() {
|
||||||
|
use sha2::Digest;
|
||||||
|
|
||||||
|
// The draft specifies DSTs >255 bytes should be hashed into a 32-byte DST
|
||||||
|
let oversize_dst = [0x00; 256];
|
||||||
|
let actual_dst = Sha256::digest([b"H2C-OVERSIZE-DST-".as_ref(), &oversize_dst].concat());
|
||||||
|
// Test the hash_to_F function handles this
|
||||||
|
// If it didn't, these would return different values
|
||||||
|
assert_eq!(C::hash_to_F(&oversize_dst, &[]), C::hash_to_F(&actual_dst, &[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ciphersuite for Secp256k1.
|
||||||
|
///
|
||||||
|
/// hash_to_F is implemented via the IETF draft for hash to curve's hash_to_field (v16).
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
|
pub struct Secp256k1;
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
|
kp_curve!("secp256k1", k256, Secp256k1, b"secp256k1");
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
|
#[test]
|
||||||
|
fn test_secp256k1() {
|
||||||
|
ff_group_tests::group::test_prime_group_bits::<_, k256::ProjectivePoint>(&mut rand_core::OsRng);
|
||||||
|
|
||||||
|
// Ideally, a test vector from hash_to_field (not FROST) would be here
|
||||||
|
// Unfortunately, the IETF draft only provides vectors for field elements, not scalars
|
||||||
|
// Vectors have been requested in
|
||||||
|
// https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/issues/343
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Secp256k1::hash_to_F(
|
||||||
|
b"FROST-secp256k1-SHA256-v11nonce",
|
||||||
|
&hex::decode(
|
||||||
|
"\
|
||||||
|
80cbea5e405d169999d8c4b30b755fedb26ab07ec8198cda4873ed8ce5e16773\
|
||||||
|
08f89ffe80ac94dcb920c26f3f46140bfc7f95b493f8310f5fc1ea2b01f4254c"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
.to_repr()
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
hex::decode("acc83278035223c1ba464e2d11bfacfc872b2b23e1041cf5f6130da21e4d8068").unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
test_oversize_dst::<Secp256k1>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ciphersuite for P-256.
|
||||||
|
///
|
||||||
|
/// hash_to_F is implemented via the IETF draft for hash to curve's hash_to_field (v16).
|
||||||
|
#[cfg(feature = "p256")]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
|
pub struct P256;
|
||||||
|
#[cfg(feature = "p256")]
|
||||||
|
kp_curve!("p256", p256, P256, b"P-256");
|
||||||
|
#[cfg(feature = "p256")]
|
||||||
|
#[test]
|
||||||
|
fn test_p256() {
|
||||||
|
ff_group_tests::group::test_prime_group_bits::<_, p256::ProjectivePoint>(&mut rand_core::OsRng);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
P256::hash_to_F(
|
||||||
|
b"FROST-P256-SHA256-v11nonce",
|
||||||
|
&hex::decode(
|
||||||
|
"\
|
||||||
|
f4e8cf80aec3f888d997900ac7e3e349944b5a6b47649fc32186d2f1238103c6\
|
||||||
|
0c9c1a0fe806c184add50bbdcac913dda73e482daf95dcb9f35dbb0d8a9f7731"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
.to_repr()
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
hex::decode("f871dfcf6bcd199342651adc361b92c941cb6a0d8c8c1a3b91d79e2c1bf3722d").unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
test_oversize_dst::<P256>();
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Ciphersuites for elliptic curves premised on ff/group.
|
Ciphersuites for elliptic curves premised on ff/group.
|
||||||
|
|
||||||
This library was
|
This library, except for the not recommended Ed448 ciphersuite, was
|
||||||
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf),
|
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf),
|
||||||
culminating in commit
|
culminating in commit
|
||||||
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06).
|
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06).
|
||||||
|
|||||||
@@ -3,147 +3,122 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
#[allow(unused_imports)]
|
|
||||||
use std_shims::prelude::*;
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use std_shims::io::{self, Read};
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use subtle::{CtOption, ConstantTimeEq, ConditionallySelectable};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
pub use digest;
|
use zeroize::Zeroize;
|
||||||
use digest::{array::ArraySize, OutputSizeUser, Digest, HashMarker};
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
|
use digest::{core_api::BlockSizeUser, Digest, HashMarker};
|
||||||
|
use transcript::SecureDigest;
|
||||||
|
|
||||||
pub use group;
|
pub use group;
|
||||||
use group::{
|
use group::{
|
||||||
ff::{PrimeField, PrimeFieldBits},
|
ff::{Field, PrimeField, PrimeFieldBits},
|
||||||
Group, GroupOps,
|
Group, GroupOps,
|
||||||
prime::PrimeGroup,
|
prime::PrimeGroup,
|
||||||
};
|
};
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use group::GroupEncoding;
|
use group::GroupEncoding;
|
||||||
|
|
||||||
pub trait FromUniformBytes<T> {
|
#[cfg(feature = "dalek")]
|
||||||
fn from_uniform_bytes(bytes: &T) -> Self;
|
mod dalek;
|
||||||
}
|
#[cfg(feature = "ristretto")]
|
||||||
impl<const N: usize, F: group::ff::FromUniformBytes<N>> FromUniformBytes<[u8; N]> for F {
|
pub use dalek::Ristretto;
|
||||||
fn from_uniform_bytes(bytes: &[u8; N]) -> Self {
|
#[cfg(feature = "ed25519")]
|
||||||
F::from_uniform_bytes(bytes)
|
pub use dalek::Ed25519;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A marker trait for fields which fleshes them out a bit more.
|
#[cfg(feature = "kp256")]
|
||||||
pub trait F: PrimeField + PrimeFieldBits + Zeroize {}
|
mod kp256;
|
||||||
impl<Fi: PrimeField + PrimeFieldBits + Zeroize> F for Fi {}
|
#[cfg(feature = "secp256k1")]
|
||||||
/// A marker trait for groups which fleshes them out a bit more.
|
pub use kp256::Secp256k1;
|
||||||
pub trait G:
|
#[cfg(feature = "p256")]
|
||||||
Group + GroupOps + GroupEncoding + PrimeGroup + ConstantTimeEq + ConditionallySelectable + Zeroize
|
pub use kp256::P256;
|
||||||
{
|
|
||||||
}
|
|
||||||
impl<
|
|
||||||
Gr: Group
|
|
||||||
+ GroupOps
|
|
||||||
+ GroupEncoding
|
|
||||||
+ PrimeGroup
|
|
||||||
+ ConstantTimeEq
|
|
||||||
+ ConditionallySelectable
|
|
||||||
+ Zeroize,
|
|
||||||
> G for Gr
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `Group` type which has been wrapped into the current type.
|
#[cfg(feature = "ed448")]
|
||||||
///
|
mod ed448;
|
||||||
/// This avoids having to re-implement all of the `Group` traits on the wrapper.
|
#[cfg(feature = "ed448")]
|
||||||
// TODO: Remove these bounds
|
pub use ed448::*;
|
||||||
pub trait WrappedGroup:
|
|
||||||
|
/// Unified trait defining a ciphersuite around an elliptic curve.
|
||||||
|
pub trait Ciphersuite:
|
||||||
'static + Send + Sync + Clone + Copy + PartialEq + Eq + Debug + Zeroize
|
'static + Send + Sync + Clone + Copy + PartialEq + Eq + Debug + Zeroize
|
||||||
{
|
{
|
||||||
/// Scalar field element type.
|
/// Scalar field element type.
|
||||||
// This is available via `G::Scalar` yet `WG::G::Scalar` is ambiguous, forcing horrific accesses
|
// This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses
|
||||||
type F: F;
|
type F: PrimeField + PrimeFieldBits + Zeroize;
|
||||||
/// Group element type.
|
/// Group element type.
|
||||||
type G: Group<Scalar = Self::F> + G;
|
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq;
|
||||||
/// Generator for the group.
|
/// Hash algorithm used with this curve.
|
||||||
fn generator() -> Self::G;
|
// Requires BlockSizeUser so it can be used within Hkdf which requires that.
|
||||||
}
|
type H: Send + Clone + BlockSizeUser + Digest + HashMarker + SecureDigest;
|
||||||
impl<Gr: G<Scalar: F>> WrappedGroup for Gr {
|
|
||||||
type F = <Gr as Group>::Scalar;
|
|
||||||
type G = Gr;
|
|
||||||
fn generator() -> Self::G {
|
|
||||||
<Self::G as Group>::generator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An ID for an object.
|
/// ID for this curve.
|
||||||
pub trait Id {
|
|
||||||
// The ID.
|
|
||||||
const ID: &'static [u8];
|
const ID: &'static [u8];
|
||||||
}
|
|
||||||
|
|
||||||
/// A group with a preferred hash function.
|
/// Generator for the group.
|
||||||
pub trait WithPreferredHash:
|
// While group does provide this in its API, privacy coins may want to use a custom basepoint
|
||||||
WrappedGroup<
|
fn generator() -> Self::G;
|
||||||
F: FromUniformBytes<<<Self::H as OutputSizeUser>::OutputSize as ArraySize>::ArrayType<u8>>,
|
|
||||||
>
|
/// Reduce 512 bits into a uniform scalar.
|
||||||
{
|
///
|
||||||
type H: Send + Clone + Digest + HashMarker;
|
/// If 512 bits is insufficient to perform a reduction into a uniform scalar, the ciphersuite
|
||||||
|
/// will perform a hash to sample the necessary bits.
|
||||||
|
fn reduce_512(scalar: [u8; 64]) -> Self::F;
|
||||||
|
|
||||||
|
/// Hash the provided domain-separation tag and message to a scalar. Ciphersuites MAY naively
|
||||||
|
/// prefix the tag to the message, enabling transpotion between the two. Accordingly, this
|
||||||
|
/// function should NOT be used in any scheme where one tag is a valid substring of another
|
||||||
|
/// UNLESS the specific Ciphersuite is verified to handle the DST securely.
|
||||||
|
///
|
||||||
|
/// Verifying specific ciphersuites have secure tag handling is not recommended, due to it
|
||||||
|
/// breaking the intended modularity of ciphersuites. Instead, component-specific tags with
|
||||||
|
/// further purpose tags are recommended ("Schnorr-nonce", "Schnorr-chal").
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn hash_to_F(data: &[u8]) -> Self::F {
|
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F;
|
||||||
Self::F::from_uniform_bytes(&Self::H::digest(data).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A group which always encodes points canonically and supports decoding points while checking
|
|
||||||
/// they have a canonical encoding.
|
|
||||||
pub trait GroupCanonicalEncoding: WrappedGroup {
|
|
||||||
/// Decode a point from its canonical encoding.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the point was invalid or not the encoding wasn't canonical.
|
|
||||||
///
|
|
||||||
/// If `<Self::G as GroupEncoding>::from_bytes` already only accepts canonical encodings, this
|
|
||||||
/// SHOULD be overriden with `<Self::G as GroupEncoding>::from_bytes(bytes)`.
|
|
||||||
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
|
|
||||||
let res = Self::G::from_bytes(bytes).unwrap_or(Self::generator());
|
|
||||||
// Safe due to the bound points are always encoded canonically
|
|
||||||
let canonical = res.to_bytes().as_ref().ct_eq(bytes.as_ref());
|
|
||||||
CtOption::new(res, canonical)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `std::io` extensions for `GroupCanonicalEncoding.`
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub trait GroupIo: GroupCanonicalEncoding {
|
|
||||||
/// Read a canonical field element from something implementing `std::io::Read`.
|
|
||||||
fn read_F<R: Read>(reader: &mut R) -> io::Result<Self::F> {
|
|
||||||
let mut bytes = <Self::F as PrimeField>::Repr::default();
|
|
||||||
reader.read_exact(bytes.as_mut())?;
|
|
||||||
|
|
||||||
// `ff` mandates this is canonical
|
|
||||||
let res = Option::<Self::F>::from(Self::F::from_repr(bytes))
|
|
||||||
.ok_or_else(|| io::Error::other("non-canonical scalar"));
|
|
||||||
bytes.as_mut().zeroize();
|
|
||||||
|
|
||||||
|
/// Generate a random non-zero scalar.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn random_nonzero_F<R: RngCore + CryptoRng>(rng: &mut R) -> Self::F {
|
||||||
|
let mut res;
|
||||||
|
while {
|
||||||
|
res = Self::F::random(&mut *rng);
|
||||||
|
res.ct_eq(&Self::F::ZERO).into()
|
||||||
|
} {}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a canonical point from something implementing `std::io::Read`.
|
/// Read a canonical scalar from something implementing std::io::Read.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn read_F<R: Read>(reader: &mut R) -> io::Result<Self::F> {
|
||||||
|
let mut encoding = <Self::F as PrimeField>::Repr::default();
|
||||||
|
reader.read_exact(encoding.as_mut())?;
|
||||||
|
|
||||||
|
// ff mandates this is canonical
|
||||||
|
let res = Option::<Self::F>::from(Self::F::from_repr(encoding))
|
||||||
|
.ok_or_else(|| io::Error::other("non-canonical scalar"));
|
||||||
|
encoding.as_mut().zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a canonical point from something implementing std::io::Read.
|
||||||
|
///
|
||||||
|
/// The provided implementation is safe so long as `GroupEncoding::to_bytes` always returns a
|
||||||
|
/// canonical serialization.
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
|
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
|
||||||
let mut bytes = <Self::G as GroupEncoding>::Repr::default();
|
let mut encoding = <Self::G as GroupEncoding>::Repr::default();
|
||||||
reader.read_exact(bytes.as_mut())?;
|
reader.read_exact(encoding.as_mut())?;
|
||||||
|
|
||||||
let res = Option::<Self::G>::from(Self::from_canonical_bytes(&bytes))
|
let point = Option::<Self::G>::from(Self::G::from_bytes(&encoding))
|
||||||
.ok_or_else(|| io::Error::other("invalid point"))?;
|
.ok_or_else(|| io::Error::other("invalid point"))?;
|
||||||
bytes.as_mut().zeroize();
|
if point.to_bytes().as_ref() != encoding.as_ref() {
|
||||||
|
Err(io::Error::other("non-canonical point"))?;
|
||||||
Ok(res)
|
}
|
||||||
|
Ok(point)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<Gr: GroupCanonicalEncoding> GroupIo for Gr {}
|
|
||||||
|
|
||||||
/// Unified trait defining a ciphersuite around an elliptic curve.
|
|
||||||
pub trait Ciphersuite: Id + WithPreferredHash + GroupCanonicalEncoding {}
|
|
||||||
impl<C: Id + WithPreferredHash + GroupCanonicalEncoding> Ciphersuite for C {}
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dalek-ff-group"
|
name = "dalek-ff-group"
|
||||||
version = "0.5.0"
|
version = "0.4.1"
|
||||||
description = "ff/group bindings around curve25519-dalek"
|
description = "ff/group bindings around curve25519-dalek"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group"
|
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["curve25519", "ed25519", "ristretto", "dalek", "group"]
|
keywords = ["curve25519", "ed25519", "ristretto", "dalek", "group"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85"
|
rust-version = "1.71"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -17,25 +17,26 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rustversion = "1"
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||||
subtle = { version = "^2.4", default-features = false }
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
|
||||||
rand_core = { version = "0.6", default-features = false }
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
sha2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
|
digest = { version = "0.10", default-features = false }
|
||||||
blake2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
|
|
||||||
|
|
||||||
prime-field = { path = "../prime-field", default-features = false }
|
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||||
ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
|
|
||||||
curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["zeroize", "digest", "group-bits", "precomputed-tables"] }
|
crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] }
|
||||||
|
|
||||||
|
curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["alloc", "zeroize", "digest", "group", "precomputed-tables"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||||
ff-group-tests = { path = "../ff-group-tests" }
|
ff-group-tests = { path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["zeroize/alloc", "prime-field/alloc", "ciphersuite/alloc", "curve25519-dalek/alloc"]
|
std = ["zeroize/std", "subtle/std", "rand_core/std", "digest/std"]
|
||||||
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "prime-field/std", "ciphersuite/std"]
|
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022-2025 Luke Parker
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
use sha2::Sha512;
|
|
||||||
use blake2::Blake2b512;
|
|
||||||
|
|
||||||
use ::ciphersuite::{group::Group, *};
|
|
||||||
|
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// Ciphersuite for Ristretto.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
|
||||||
pub struct Ristretto;
|
|
||||||
impl WrappedGroup for Ristretto {
|
|
||||||
type F = Scalar;
|
|
||||||
type G = RistrettoPoint;
|
|
||||||
fn generator() -> Self::G {
|
|
||||||
<RistrettoPoint as Group>::generator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Id for Ristretto {
|
|
||||||
const ID: &[u8] = b"ristretto";
|
|
||||||
}
|
|
||||||
impl WithPreferredHash for Ristretto {
|
|
||||||
type H = Blake2b512;
|
|
||||||
}
|
|
||||||
impl GroupCanonicalEncoding for Ristretto {
|
|
||||||
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
|
|
||||||
Self::G::from_bytes(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ciphersuite for Ed25519.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
|
||||||
pub struct Ed25519;
|
|
||||||
impl WrappedGroup for Ed25519 {
|
|
||||||
type F = Scalar;
|
|
||||||
type G = EdwardsPoint;
|
|
||||||
fn generator() -> Self::G {
|
|
||||||
<EdwardsPoint as Group>::generator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Id for Ed25519 {
|
|
||||||
const ID: &[u8] = b"ed25519";
|
|
||||||
}
|
|
||||||
impl WithPreferredHash for Ed25519 {
|
|
||||||
type H = Sha512;
|
|
||||||
}
|
|
||||||
impl GroupCanonicalEncoding for Ed25519 {}
|
|
||||||
359
crypto/dalek-ff-group/src/field.rs
Normal file
359
crypto/dalek-ff-group/src/field.rs
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
use core::{
|
||||||
|
ops::{Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
||||||
|
iter::{Sum, Product},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use subtle::{
|
||||||
|
Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallyNegatable,
|
||||||
|
ConditionallySelectable,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crypto_bigint::{
|
||||||
|
Integer, NonZero, Encoding, U256, U512,
|
||||||
|
modular::constant_mod::{ResidueParams, Residue},
|
||||||
|
impl_modulus,
|
||||||
|
};
|
||||||
|
|
||||||
|
use group::ff::{Field, PrimeField, FieldBits, PrimeFieldBits};
|
||||||
|
|
||||||
|
use crate::{u8_from_bool, constant_time, math_op, math};
|
||||||
|
|
||||||
|
// 2 ** 255 - 19
|
||||||
|
// Uses saturating_sub because checked_sub isn't available at compile time
|
||||||
|
const MODULUS: U256 = U256::from_u8(1).shl_vartime(255).saturating_sub(&U256::from_u8(19));
|
||||||
|
const WIDE_MODULUS: U512 = U256::ZERO.concat(&MODULUS);
|
||||||
|
|
||||||
|
impl_modulus!(
|
||||||
|
FieldModulus,
|
||||||
|
U256,
|
||||||
|
// 2 ** 255 - 19
|
||||||
|
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
|
||||||
|
);
|
||||||
|
type ResidueType = Residue<FieldModulus, { FieldModulus::LIMBS }>;
|
||||||
|
|
||||||
|
/// A constant-time implementation of the Ed25519 field.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
|
||||||
|
pub struct FieldElement(ResidueType);
|
||||||
|
|
||||||
|
// Square root of -1.
|
||||||
|
// Formula from RFC-8032 (modp_sqrt_m1/sqrt8k5 z)
|
||||||
|
// 2 ** ((MODULUS - 1) // 4) % MODULUS
|
||||||
|
const SQRT_M1: FieldElement = FieldElement(
|
||||||
|
ResidueType::new(&U256::from_u8(2))
|
||||||
|
.pow(&MODULUS.saturating_sub(&U256::ONE).wrapping_div(&U256::from_u8(4))),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Constant useful in calculating square roots (RFC-8032 sqrt8k5's exponent used to calculate y)
|
||||||
|
const MOD_3_8: FieldElement = FieldElement(ResidueType::new(
|
||||||
|
&MODULUS.saturating_add(&U256::from_u8(3)).wrapping_div(&U256::from_u8(8)),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Constant useful in sqrt_ratio_i (sqrt(u / v))
|
||||||
|
const MOD_5_8: FieldElement = FieldElement(ResidueType::sub(&MOD_3_8.0, &ResidueType::ONE));
|
||||||
|
|
||||||
|
fn reduce(x: U512) -> ResidueType {
|
||||||
|
ResidueType::new(&U256::from_le_slice(
|
||||||
|
&x.rem(&NonZero::new(WIDE_MODULUS).unwrap()).to_le_bytes()[.. 32],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_time!(FieldElement, ResidueType);
|
||||||
|
math!(
|
||||||
|
FieldElement,
|
||||||
|
FieldElement,
|
||||||
|
|x: ResidueType, y: ResidueType| x.add(&y),
|
||||||
|
|x: ResidueType, y: ResidueType| x.sub(&y),
|
||||||
|
|x: ResidueType, y: ResidueType| x.mul(&y)
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! from_wrapper {
|
||||||
|
($uint: ident) => {
|
||||||
|
impl From<$uint> for FieldElement {
|
||||||
|
fn from(a: $uint) -> FieldElement {
|
||||||
|
Self(ResidueType::new(&U256::from(a)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
from_wrapper!(u8);
|
||||||
|
from_wrapper!(u16);
|
||||||
|
from_wrapper!(u32);
|
||||||
|
from_wrapper!(u64);
|
||||||
|
from_wrapper!(u128);
|
||||||
|
|
||||||
|
impl Neg for FieldElement {
|
||||||
|
type Output = Self;
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Self(self.0.neg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for &FieldElement {
|
||||||
|
type Output = FieldElement;
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
(*self).neg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for FieldElement {
|
||||||
|
const ZERO: Self = Self(ResidueType::ZERO);
|
||||||
|
const ONE: Self = Self(ResidueType::ONE);
|
||||||
|
|
||||||
|
fn random(mut rng: impl RngCore) -> Self {
|
||||||
|
let mut bytes = [0; 64];
|
||||||
|
rng.fill_bytes(&mut bytes);
|
||||||
|
FieldElement(reduce(U512::from_le_bytes(bytes)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&self) -> Self {
|
||||||
|
FieldElement(self.0.square())
|
||||||
|
}
|
||||||
|
fn double(&self) -> Self {
|
||||||
|
FieldElement(self.0.add(&self.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invert(&self) -> CtOption<Self> {
|
||||||
|
const NEG_2: FieldElement =
|
||||||
|
FieldElement(ResidueType::new(&MODULUS.saturating_sub(&U256::from_u8(2))));
|
||||||
|
CtOption::new(self.pow(NEG_2), !self.is_zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC-8032 sqrt8k5
|
||||||
|
fn sqrt(&self) -> CtOption<Self> {
|
||||||
|
let tv1 = self.pow(MOD_3_8);
|
||||||
|
let tv2 = tv1 * SQRT_M1;
|
||||||
|
let candidate = Self::conditional_select(&tv2, &tv1, tv1.square().ct_eq(self));
|
||||||
|
CtOption::new(candidate, candidate.square().ct_eq(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
|
||||||
|
let i = SQRT_M1;
|
||||||
|
|
||||||
|
let u = *u;
|
||||||
|
let v = *v;
|
||||||
|
|
||||||
|
let v3 = v.square() * v;
|
||||||
|
let v7 = v3.square() * v;
|
||||||
|
let mut r = (u * v3) * (u * v7).pow(MOD_5_8);
|
||||||
|
|
||||||
|
let check = v * r.square();
|
||||||
|
let correct_sign = check.ct_eq(&u);
|
||||||
|
let flipped_sign = check.ct_eq(&(-u));
|
||||||
|
let flipped_sign_i = check.ct_eq(&((-u) * i));
|
||||||
|
|
||||||
|
r.conditional_assign(&(r * i), flipped_sign | flipped_sign_i);
|
||||||
|
|
||||||
|
let r_is_negative = r.is_odd();
|
||||||
|
r.conditional_negate(r_is_negative);
|
||||||
|
|
||||||
|
(correct_sign | flipped_sign, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeField for FieldElement {
|
||||||
|
type Repr = [u8; 32];
|
||||||
|
|
||||||
|
// Big endian representation of the modulus
|
||||||
|
const MODULUS: &'static str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed";
|
||||||
|
|
||||||
|
const NUM_BITS: u32 = 255;
|
||||||
|
const CAPACITY: u32 = 254;
|
||||||
|
|
||||||
|
const TWO_INV: Self = FieldElement(ResidueType::new(&U256::from_u8(2)).invert().0);
|
||||||
|
|
||||||
|
// This was calculated with the method from the ff crate docs
|
||||||
|
// SageMath GF(modulus).primitive_element()
|
||||||
|
const MULTIPLICATIVE_GENERATOR: Self = Self(ResidueType::new(&U256::from_u8(2)));
|
||||||
|
// This was set per the specification in the ff crate docs
|
||||||
|
// The number of leading zero bits in the little-endian bit representation of (modulus - 1)
|
||||||
|
const S: u32 = 2;
|
||||||
|
|
||||||
|
// This was calculated via the formula from the ff crate docs
|
||||||
|
// Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S)
|
||||||
|
const ROOT_OF_UNITY: Self = FieldElement(ResidueType::new(&U256::from_be_hex(
|
||||||
|
"2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0",
|
||||||
|
)));
|
||||||
|
// Self::ROOT_OF_UNITY.invert()
|
||||||
|
const ROOT_OF_UNITY_INV: Self = FieldElement(Self::ROOT_OF_UNITY.0.invert().0);
|
||||||
|
|
||||||
|
// This was calculated via the formula from the ff crate docs
|
||||||
|
// Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S)
|
||||||
|
const DELTA: Self = FieldElement(ResidueType::new(&U256::from_be_hex(
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000010",
|
||||||
|
)));
|
||||||
|
|
||||||
|
fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
|
||||||
|
let res = U256::from_le_bytes(bytes);
|
||||||
|
CtOption::new(Self(ResidueType::new(&res)), res.ct_lt(&MODULUS))
|
||||||
|
}
|
||||||
|
fn to_repr(&self) -> [u8; 32] {
|
||||||
|
self.0.retrieve().to_le_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_odd(&self) -> Choice {
|
||||||
|
self.0.retrieve().is_odd()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u128(num: u128) -> Self {
|
||||||
|
Self::from(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeFieldBits for FieldElement {
|
||||||
|
type ReprBits = [u8; 32];
|
||||||
|
|
||||||
|
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
||||||
|
self.to_repr().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||||
|
MODULUS.to_le_bytes().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldElement {
|
||||||
|
/// Interpret the value as a little-endian integer, square it, and reduce it into a FieldElement.
|
||||||
|
pub fn from_square(value: [u8; 32]) -> FieldElement {
|
||||||
|
let value = U256::from_le_bytes(value);
|
||||||
|
FieldElement(reduce(U512::from(value.mul_wide(&value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform an exponentiation.
|
||||||
|
pub fn pow(&self, other: FieldElement) -> FieldElement {
|
||||||
|
let mut table = [FieldElement::ONE; 16];
|
||||||
|
table[1] = *self;
|
||||||
|
for i in 2 .. 16 {
|
||||||
|
table[i] = table[i - 1] * self;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = FieldElement::ONE;
|
||||||
|
let mut bits = 0;
|
||||||
|
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||||
|
bits <<= 1;
|
||||||
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
|
bits |= bit;
|
||||||
|
bit.zeroize();
|
||||||
|
|
||||||
|
if ((i + 1) % 4) == 0 {
|
||||||
|
if i != 3 {
|
||||||
|
for _ in 0 .. 4 {
|
||||||
|
res *= res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut scale_by = FieldElement::ONE;
|
||||||
|
#[allow(clippy::needless_range_loop)]
|
||||||
|
for i in 0 .. 16 {
|
||||||
|
#[allow(clippy::cast_possible_truncation)] // Safe since 0 .. 16
|
||||||
|
{
|
||||||
|
scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res *= scale_by;
|
||||||
|
bits = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The square root of u/v, as used for Ed25519 point decoding (RFC 8032 5.1.3) and within
|
||||||
|
/// Ristretto (5.1 Extracting an Inverse Square Root).
|
||||||
|
///
|
||||||
|
/// The result is only a valid square root if the Choice is true.
|
||||||
|
/// RFC 8032 simply fails if there isn't a square root, leaving any return value undefined.
|
||||||
|
/// Ristretto explicitly returns 0 or sqrt((SQRT_M1 * u) / v).
|
||||||
|
pub fn sqrt_ratio_i(u: FieldElement, v: FieldElement) -> (Choice, FieldElement) {
|
||||||
|
let i = SQRT_M1;
|
||||||
|
|
||||||
|
let v3 = v.square() * v;
|
||||||
|
let v7 = v3.square() * v;
|
||||||
|
// Candidate root
|
||||||
|
let mut r = (u * v3) * (u * v7).pow(MOD_5_8);
|
||||||
|
|
||||||
|
// 8032 3.1
|
||||||
|
let check = v * r.square();
|
||||||
|
let correct_sign = check.ct_eq(&u);
|
||||||
|
// 8032 3.2 conditional
|
||||||
|
let neg_u = -u;
|
||||||
|
let flipped_sign = check.ct_eq(&neg_u);
|
||||||
|
// Ristretto Step 5
|
||||||
|
let flipped_sign_i = check.ct_eq(&(neg_u * i));
|
||||||
|
|
||||||
|
// 3.2 set
|
||||||
|
r.conditional_assign(&(r * i), flipped_sign | flipped_sign_i);
|
||||||
|
|
||||||
|
// Always return the even root, per Ristretto
|
||||||
|
// This doesn't break Ed25519 point decoding as that doesn't expect these steps to return a
|
||||||
|
// specific root
|
||||||
|
// Ed25519 points include a dedicated sign bit to determine which root to use, so at worst
|
||||||
|
// this is a pointless inefficiency
|
||||||
|
r.conditional_negate(r.is_odd());
|
||||||
|
|
||||||
|
(correct_sign | flipped_sign, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sum<FieldElement> for FieldElement {
|
||||||
|
fn sum<I: Iterator<Item = FieldElement>>(iter: I) -> FieldElement {
|
||||||
|
let mut res = FieldElement::ZERO;
|
||||||
|
for item in iter {
|
||||||
|
res += item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Sum<&'a FieldElement> for FieldElement {
|
||||||
|
fn sum<I: Iterator<Item = &'a FieldElement>>(iter: I) -> FieldElement {
|
||||||
|
iter.copied().sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Product<FieldElement> for FieldElement {
|
||||||
|
fn product<I: Iterator<Item = FieldElement>>(iter: I) -> FieldElement {
|
||||||
|
let mut res = FieldElement::ONE;
|
||||||
|
for item in iter {
|
||||||
|
res *= item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Product<&'a FieldElement> for FieldElement {
|
||||||
|
fn product<I: Iterator<Item = &'a FieldElement>>(iter: I) -> FieldElement {
|
||||||
|
iter.copied().product()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wide_modulus() {
|
||||||
|
let mut wide = [0; 64];
|
||||||
|
wide[.. 32].copy_from_slice(&MODULUS.to_le_bytes());
|
||||||
|
assert_eq!(wide, WIDE_MODULUS.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sqrt_m1() {
|
||||||
|
// Test equivalence against the known constant value
|
||||||
|
const SQRT_M1_MAGIC: U256 =
|
||||||
|
U256::from_be_hex("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0");
|
||||||
|
assert_eq!(SQRT_M1.0.retrieve(), SQRT_M1_MAGIC);
|
||||||
|
|
||||||
|
// Also test equivalence against the result of the formula from RFC-8032 (modp_sqrt_m1/sqrt8k5 z)
|
||||||
|
// 2 ** ((MODULUS - 1) // 4) % MODULUS
|
||||||
|
assert_eq!(
|
||||||
|
SQRT_M1,
|
||||||
|
FieldElement::from(2u8).pow(FieldElement(ResidueType::new(
|
||||||
|
&(FieldElement::ZERO - FieldElement::ONE).0.retrieve().wrapping_div(&U256::from(4u8))
|
||||||
|
)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_field() {
|
||||||
|
ff_group_tests::prime_field::test_prime_field_bits::<_, FieldElement>(&mut rand_core::OsRng);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
use core::{
|
use core::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
||||||
iter::{Iterator, Sum},
|
iter::{Iterator, Sum, Product},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,21 +15,36 @@ use zeroize::Zeroize;
|
|||||||
use subtle::{ConstantTimeEq, ConditionallySelectable};
|
use subtle::{ConstantTimeEq, ConditionallySelectable};
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
|
use digest::{consts::U64, Digest, HashMarker};
|
||||||
|
|
||||||
use subtle::{Choice, CtOption};
|
use subtle::{Choice, CtOption};
|
||||||
|
|
||||||
use curve25519_dalek::{
|
pub use curve25519_dalek as dalek;
|
||||||
edwards::{EdwardsPoint as DEdwardsPoint, CompressedEdwardsY},
|
|
||||||
ristretto::{RistrettoPoint as DRistrettoPoint, CompressedRistretto},
|
use dalek::{
|
||||||
|
constants::{self, BASEPOINT_ORDER},
|
||||||
|
scalar::Scalar as DScalar,
|
||||||
|
edwards::{EdwardsPoint as DEdwardsPoint, EdwardsBasepointTable, CompressedEdwardsY},
|
||||||
|
ristretto::{RistrettoPoint as DRistrettoPoint, RistrettoBasepointTable, CompressedRistretto},
|
||||||
};
|
};
|
||||||
pub use curve25519_dalek::Scalar;
|
pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE};
|
||||||
|
|
||||||
use ::ciphersuite::group::{Group, GroupEncoding, prime::PrimeGroup};
|
use group::{
|
||||||
|
ff::{Field, PrimeField, FieldBits, PrimeFieldBits},
|
||||||
|
Group, GroupEncoding,
|
||||||
|
prime::PrimeGroup,
|
||||||
|
};
|
||||||
|
|
||||||
mod ciphersuite;
|
mod field;
|
||||||
pub use crate::ciphersuite::{Ed25519, Ristretto};
|
pub use field::FieldElement;
|
||||||
|
|
||||||
|
// Use black_box when possible
|
||||||
|
#[rustversion::since(1.66)]
|
||||||
use core::hint::black_box;
|
use core::hint::black_box;
|
||||||
|
#[rustversion::before(1.66)]
|
||||||
|
fn black_box<T>(val: T) -> T {
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||||
let bit_ref = black_box(bit_ref);
|
let bit_ref = black_box(bit_ref);
|
||||||
@@ -88,41 +103,7 @@ macro_rules! constant_time {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use constant_time;
|
||||||
macro_rules! math_op_without_wrapping {
|
|
||||||
(
|
|
||||||
$Value: ident,
|
|
||||||
$Other: ident,
|
|
||||||
$Op: ident,
|
|
||||||
$op_fn: ident,
|
|
||||||
$Assign: ident,
|
|
||||||
$assign_fn: ident,
|
|
||||||
$function: expr
|
|
||||||
) => {
|
|
||||||
impl $Op<$Other> for $Value {
|
|
||||||
type Output = $Value;
|
|
||||||
fn $op_fn(self, other: $Other) -> Self::Output {
|
|
||||||
Self($function(self.0, other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl $Assign<$Other> for $Value {
|
|
||||||
fn $assign_fn(&mut self, other: $Other) {
|
|
||||||
self.0 = $function(self.0, other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> $Op<&'a $Other> for $Value {
|
|
||||||
type Output = $Value;
|
|
||||||
fn $op_fn(self, other: &'a $Other) -> Self::Output {
|
|
||||||
Self($function(self.0, other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> $Assign<&'a $Other> for $Value {
|
|
||||||
fn $assign_fn(&mut self, other: &'a $Other) {
|
|
||||||
self.0 = $function(self.0, other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! math_op {
|
macro_rules! math_op {
|
||||||
(
|
(
|
||||||
@@ -158,12 +139,20 @@ macro_rules! math_op {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use math_op;
|
||||||
|
|
||||||
macro_rules! math_neg {
|
macro_rules! math {
|
||||||
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
|
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
|
||||||
math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
|
math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
|
||||||
math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
|
math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
|
||||||
math_op_without_wrapping!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
|
math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use math;
|
||||||
|
|
||||||
|
macro_rules! math_neg {
|
||||||
|
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
|
||||||
|
math!($Value, $Factor, $add, $sub, $mul);
|
||||||
|
|
||||||
impl Neg for $Value {
|
impl Neg for $Value {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
@@ -174,6 +163,181 @@ macro_rules! math_neg {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper around the dalek Scalar type.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
|
||||||
|
pub struct Scalar(pub DScalar);
|
||||||
|
deref_borrow!(Scalar, DScalar);
|
||||||
|
constant_time!(Scalar, DScalar);
|
||||||
|
math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul);
|
||||||
|
|
||||||
|
macro_rules! from_wrapper {
|
||||||
|
($uint: ident) => {
|
||||||
|
impl From<$uint> for Scalar {
|
||||||
|
fn from(a: $uint) -> Scalar {
|
||||||
|
Scalar(DScalar::from(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
from_wrapper!(u8);
|
||||||
|
from_wrapper!(u16);
|
||||||
|
from_wrapper!(u32);
|
||||||
|
from_wrapper!(u64);
|
||||||
|
from_wrapper!(u128);
|
||||||
|
|
||||||
|
impl Scalar {
|
||||||
|
pub fn pow(&self, other: Scalar) -> Scalar {
|
||||||
|
let mut table = [Scalar::ONE; 16];
|
||||||
|
table[1] = *self;
|
||||||
|
for i in 2 .. 16 {
|
||||||
|
table[i] = table[i - 1] * self;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = Scalar::ONE;
|
||||||
|
let mut bits = 0;
|
||||||
|
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||||
|
bits <<= 1;
|
||||||
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
|
bits |= bit;
|
||||||
|
bit.zeroize();
|
||||||
|
|
||||||
|
if ((i + 1) % 4) == 0 {
|
||||||
|
if i != 3 {
|
||||||
|
for _ in 0 .. 4 {
|
||||||
|
res *= res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut scale_by = Scalar::ONE;
|
||||||
|
#[allow(clippy::needless_range_loop)]
|
||||||
|
for i in 0 .. 16 {
|
||||||
|
#[allow(clippy::cast_possible_truncation)] // Safe since 0 .. 16
|
||||||
|
{
|
||||||
|
scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res *= scale_by;
|
||||||
|
bits = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform wide reduction on a 64-byte array to create a Scalar without bias.
|
||||||
|
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
|
||||||
|
Self(DScalar::from_bytes_mod_order_wide(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive a Scalar without bias from a digest via wide reduction.
|
||||||
|
pub fn from_hash<D: Digest<OutputSize = U64> + HashMarker>(hash: D) -> Scalar {
|
||||||
|
let mut output = [0u8; 64];
|
||||||
|
output.copy_from_slice(&hash.finalize());
|
||||||
|
let res = Scalar(DScalar::from_bytes_mod_order_wide(&output));
|
||||||
|
output.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Scalar {
|
||||||
|
const ZERO: Scalar = Scalar(DScalar::ZERO);
|
||||||
|
const ONE: Scalar = Scalar(DScalar::ONE);
|
||||||
|
|
||||||
|
fn random(rng: impl RngCore) -> Self {
|
||||||
|
Self(<DScalar as Field>::random(rng))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&self) -> Self {
|
||||||
|
Self(self.0.square())
|
||||||
|
}
|
||||||
|
fn double(&self) -> Self {
|
||||||
|
Self(self.0.double())
|
||||||
|
}
|
||||||
|
fn invert(&self) -> CtOption<Self> {
|
||||||
|
<DScalar as Field>::invert(&self.0).map(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(&self) -> CtOption<Self> {
|
||||||
|
self.0.sqrt().map(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
|
||||||
|
let (choice, res) = DScalar::sqrt_ratio(num, div);
|
||||||
|
(choice, Self(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeField for Scalar {
|
||||||
|
type Repr = [u8; 32];
|
||||||
|
|
||||||
|
const MODULUS: &'static str = <DScalar as PrimeField>::MODULUS;
|
||||||
|
|
||||||
|
const NUM_BITS: u32 = <DScalar as PrimeField>::NUM_BITS;
|
||||||
|
const CAPACITY: u32 = <DScalar as PrimeField>::CAPACITY;
|
||||||
|
|
||||||
|
const TWO_INV: Scalar = Scalar(<DScalar as PrimeField>::TWO_INV);
|
||||||
|
|
||||||
|
const MULTIPLICATIVE_GENERATOR: Scalar =
|
||||||
|
Scalar(<DScalar as PrimeField>::MULTIPLICATIVE_GENERATOR);
|
||||||
|
const S: u32 = <DScalar as PrimeField>::S;
|
||||||
|
|
||||||
|
const ROOT_OF_UNITY: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY);
|
||||||
|
const ROOT_OF_UNITY_INV: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY_INV);
|
||||||
|
|
||||||
|
const DELTA: Scalar = Scalar(<DScalar as PrimeField>::DELTA);
|
||||||
|
|
||||||
|
fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
|
||||||
|
<DScalar as PrimeField>::from_repr(bytes).map(Scalar)
|
||||||
|
}
|
||||||
|
fn to_repr(&self) -> [u8; 32] {
|
||||||
|
self.0.to_repr()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_odd(&self) -> Choice {
|
||||||
|
self.0.is_odd()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u128(num: u128) -> Self {
|
||||||
|
Scalar(DScalar::from_u128(num))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeFieldBits for Scalar {
|
||||||
|
type ReprBits = [u8; 32];
|
||||||
|
|
||||||
|
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
||||||
|
self.to_repr().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||||
|
BASEPOINT_ORDER.to_bytes().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sum<Scalar> for Scalar {
|
||||||
|
fn sum<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
||||||
|
Self(DScalar::sum(iter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Sum<&'a Scalar> for Scalar {
|
||||||
|
fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
||||||
|
Self(DScalar::sum(iter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Product<Scalar> for Scalar {
|
||||||
|
fn product<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
||||||
|
Self(DScalar::product(iter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Product<&'a Scalar> for Scalar {
|
||||||
|
fn product<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
||||||
|
Self(DScalar::product(iter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! dalek_group {
|
macro_rules! dalek_group {
|
||||||
(
|
(
|
||||||
$Point: ident,
|
$Point: ident,
|
||||||
@@ -183,19 +347,20 @@ macro_rules! dalek_group {
|
|||||||
$Table: ident,
|
$Table: ident,
|
||||||
|
|
||||||
$DCompressed: ident,
|
$DCompressed: ident,
|
||||||
|
|
||||||
|
$BASEPOINT_POINT: ident,
|
||||||
|
$BASEPOINT_TABLE: ident
|
||||||
) => {
|
) => {
|
||||||
/// Wrapper around the dalek Point type.
|
/// Wrapper around the dalek Point type. For Ed25519, this is restricted to the prime subgroup.
|
||||||
///
|
|
||||||
/// All operations will be restricted to a prime-order subgroup (equivalent to the group itself
|
|
||||||
/// in the case of Ristretto). The exposure of the internal element does allow bypassing this
|
|
||||||
/// however, which may lead to undefined/computationally-unsafe behavior, and is entirely at
|
|
||||||
/// the user's risk.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
pub struct $Point(pub $DPoint);
|
pub struct $Point(pub $DPoint);
|
||||||
deref_borrow!($Point, $DPoint);
|
deref_borrow!($Point, $DPoint);
|
||||||
constant_time!($Point, $DPoint);
|
constant_time!($Point, $DPoint);
|
||||||
math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul);
|
math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul);
|
||||||
|
|
||||||
|
/// The basepoint for this curve.
|
||||||
|
pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT);
|
||||||
|
|
||||||
impl Sum<$Point> for $Point {
|
impl Sum<$Point> for $Point {
|
||||||
fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point {
|
fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point {
|
||||||
Self($DPoint::sum(iter))
|
Self($DPoint::sum(iter))
|
||||||
@@ -226,7 +391,7 @@ macro_rules! dalek_group {
|
|||||||
Self($DPoint::identity())
|
Self($DPoint::identity())
|
||||||
}
|
}
|
||||||
fn generator() -> Self {
|
fn generator() -> Self {
|
||||||
Self(<$DPoint as Group>::generator())
|
$BASEPOINT_POINT
|
||||||
}
|
}
|
||||||
fn is_identity(&self) -> Choice {
|
fn is_identity(&self) -> Choice {
|
||||||
self.0.ct_eq(&$DPoint::identity())
|
self.0.ct_eq(&$DPoint::identity())
|
||||||
@@ -260,6 +425,13 @@ macro_rules! dalek_group {
|
|||||||
|
|
||||||
impl PrimeGroup for $Point {}
|
impl PrimeGroup for $Point {}
|
||||||
|
|
||||||
|
impl Mul<Scalar> for &$Table {
|
||||||
|
type Output = $Point;
|
||||||
|
fn mul(self, b: Scalar) -> $Point {
|
||||||
|
$Point(&b.0 * self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Support being used as a key in a table
|
// Support being used as a key in a table
|
||||||
// While it is expensive as a key, due to the field operations required, there's frequently
|
// While it is expensive as a key, due to the field operations required, there's frequently
|
||||||
// use cases for public key -> value lookups
|
// use cases for public key -> value lookups
|
||||||
@@ -279,14 +451,24 @@ dalek_group!(
|
|||||||
|point: DEdwardsPoint| point.is_torsion_free(),
|
|point: DEdwardsPoint| point.is_torsion_free(),
|
||||||
EdwardsBasepointTable,
|
EdwardsBasepointTable,
|
||||||
CompressedEdwardsY,
|
CompressedEdwardsY,
|
||||||
|
ED25519_BASEPOINT_POINT,
|
||||||
|
ED25519_BASEPOINT_TABLE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl EdwardsPoint {
|
||||||
|
pub fn mul_by_cofactor(&self) -> EdwardsPoint {
|
||||||
|
EdwardsPoint(self.0.mul_by_cofactor())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dalek_group!(
|
dalek_group!(
|
||||||
RistrettoPoint,
|
RistrettoPoint,
|
||||||
DRistrettoPoint,
|
DRistrettoPoint,
|
||||||
|_| true,
|
|_| true,
|
||||||
RistrettoBasepointTable,
|
RistrettoBasepointTable,
|
||||||
CompressedRistretto,
|
CompressedRistretto,
|
||||||
|
RISTRETTO_BASEPOINT_POINT,
|
||||||
|
RISTRETTO_BASEPOINT_TABLE
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -298,12 +480,3 @@ fn test_ed25519_group() {
|
|||||||
fn test_ristretto_group() {
|
fn test_ristretto_group() {
|
||||||
ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng);
|
ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng);
|
||||||
}
|
}
|
||||||
|
|
||||||
type ThirtyTwoArray = [u8; 32];
|
|
||||||
prime_field::odd_prime_field_with_specific_repr!(
|
|
||||||
FieldElement,
|
|
||||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
|
|
||||||
"02",
|
|
||||||
false,
|
|
||||||
crate::ThirtyTwoArray
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dkg"
|
name = "dkg"
|
||||||
version = "0.6.1"
|
version = "0.5.1"
|
||||||
description = "Distributed key generation over ff/group"
|
description = "Distributed key generation over ff/group"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg"
|
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
|
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85"
|
rust-version = "1.81"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -17,25 +17,82 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive", "alloc"] }
|
|
||||||
|
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
|
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false }
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false }
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] }
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
||||||
|
chacha20 = { version = "0.9", default-features = false, features = ["zeroize"] }
|
||||||
|
|
||||||
|
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false }
|
||||||
|
multiexp = { path = "../multiexp", version = "0.4", default-features = false }
|
||||||
|
|
||||||
|
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1", default-features = false }
|
||||||
|
dleq = { path = "../dleq", version = "^0.4.1", default-features = false }
|
||||||
|
|
||||||
|
# eVRF DKG dependencies
|
||||||
|
generic-array = { version = "1", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
blake2 = { version = "0.10", default-features = false, features = ["std"], optional = true }
|
||||||
|
rand_chacha = { version = "0.3", default-features = false, features = ["std"], optional = true }
|
||||||
|
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", default-features = false, optional = true }
|
||||||
|
ec-divisors = { path = "../evrf/divisors", default-features = false, optional = true }
|
||||||
|
generalized-bulletproofs-circuit-abstraction = { path = "../evrf/circuit-abstraction", optional = true }
|
||||||
|
generalized-bulletproofs-ec-gadgets = { path = "../evrf/ec-gadgets", optional = true }
|
||||||
|
|
||||||
|
secq256k1 = { path = "../evrf/secq256k1", optional = true }
|
||||||
|
embedwards25519 = { path = "../evrf/embedwards25519", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||||
|
rand = { version = "0.8", default-features = false, features = ["std"] }
|
||||||
|
ciphersuite = { path = "../ciphersuite", default-features = false, features = ["ristretto"] }
|
||||||
|
generalized-bulletproofs = { path = "../evrf/generalized-bulletproofs", features = ["tests"] }
|
||||||
|
ec-divisors = { path = "../evrf/divisors", features = ["pasta"] }
|
||||||
|
pasta_curves = { git = "https://github.com/kayabaNerve/pasta_curves", rev = "a46b5be95cacbff54d06aad8d3bbcba42e05d616" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
"thiserror/std",
|
"thiserror/std",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
|
||||||
"std-shims/std",
|
"std-shims/std",
|
||||||
|
|
||||||
"borsh?/std",
|
"borsh?/std",
|
||||||
|
|
||||||
|
"transcript/std",
|
||||||
|
"chacha20/std",
|
||||||
|
|
||||||
"ciphersuite/std",
|
"ciphersuite/std",
|
||||||
|
"multiexp/std",
|
||||||
|
"multiexp/batch",
|
||||||
|
|
||||||
|
"schnorr/std",
|
||||||
|
"dleq/std",
|
||||||
|
"dleq/serialize"
|
||||||
]
|
]
|
||||||
borsh = ["dep:borsh"]
|
borsh = ["dep:borsh"]
|
||||||
|
evrf = [
|
||||||
|
"std",
|
||||||
|
|
||||||
|
"dep:generic-array",
|
||||||
|
|
||||||
|
"dep:blake2",
|
||||||
|
"dep:rand_chacha",
|
||||||
|
|
||||||
|
"dep:generalized-bulletproofs",
|
||||||
|
"dep:ec-divisors",
|
||||||
|
"dep:generalized-bulletproofs-circuit-abstraction",
|
||||||
|
"dep:generalized-bulletproofs-ec-gadgets",
|
||||||
|
]
|
||||||
|
evrf-secp256k1 = ["evrf", "ciphersuite/secp256k1", "secq256k1"]
|
||||||
|
evrf-ed25519 = ["evrf", "ciphersuite/ed25519", "embedwards25519"]
|
||||||
|
evrf-ristretto = ["evrf", "ciphersuite/ristretto", "embedwards25519"]
|
||||||
|
tests = ["rand_core/getrandom"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021-2025 Luke Parker
|
Copyright (c) 2021-2023 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
# Distributed Key Generation
|
# Distributed Key Generation
|
||||||
|
|
||||||
A crate implementing a type for keys, presumably the result of a distributed
|
A collection of implementations of various distributed key generation protocols.
|
||||||
key generation protocol, and utilities from there.
|
|
||||||
|
|
||||||
This crate used to host implementations of distributed key generation protocols
|
All included protocols resolve into the provided `Threshold` types, intended to
|
||||||
as well (hence the name). Those have been smashed into their own crates, such
|
enable their modularity. Additional utilities around these types, such as
|
||||||
as [`dkg-musig`](https://docs.rs/dkg-musig) and
|
promotion from one generator to another, are also provided.
|
||||||
[`dkg-pedpop`](https://docs.rs/dkg-pedpop).
|
|
||||||
|
|
||||||
Before being smashed, this crate was [audited by Cypher Stack in March 2023](
|
Currently, the only included protocol is the two-round protocol from the
|
||||||
https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf
|
[FROST paper](https://eprint.iacr.org/2020/852).
|
||||||
), culminating in commit [669d2dbffc1dafb82a09d9419ea182667115df06](
|
|
||||||
https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06
|
This library was
|
||||||
). Any subsequent changes have not undergone auditing.
|
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf),
|
||||||
|
culminating in commit
|
||||||
|
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06).
|
||||||
|
Any subsequent changes have not undergone auditing.
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "dkg-dealer"
|
|
||||||
version = "0.6.0"
|
|
||||||
description = "Produce dkg::ThresholdKeys with a dealer key generation"
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg/dealer"
|
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|
||||||
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.85"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
zeroize = { version = "^1.5", default-features = false }
|
|
||||||
rand_core = { version = "0.6", default-features = false }
|
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false }
|
|
||||||
|
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false }
|
|
||||||
dkg = { path = "../", version = "0.6", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = [
|
|
||||||
"zeroize/std",
|
|
||||||
"rand_core/std",
|
|
||||||
"std-shims/std",
|
|
||||||
"ciphersuite/std",
|
|
||||||
"dkg/std",
|
|
||||||
]
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# Distributed Key Generation - Dealer
|
|
||||||
|
|
||||||
This crate implements a dealer key generation protocol for the
|
|
||||||
[`dkg`](https://docs.rs/dkg) crate's types. This provides a single point of
|
|
||||||
failure when the key is being generated and is NOT recommended for use outside
|
|
||||||
of tests.
|
|
||||||
|
|
||||||
This crate was originally part of (in some form) the `dkg` crate, which was
|
|
||||||
[audited by Cypher Stack in March 2023](
|
|
||||||
https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf
|
|
||||||
), culminating in commit [669d2dbffc1dafb82a09d9419ea182667115df06](
|
|
||||||
https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06
|
|
||||||
). Any subsequent changes have not undergone auditing.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user