mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 13:39:25 +00:00
Compare commits
50 Commits
d304cd97e1
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca93c82156 | ||
|
|
5b1875dae6 | ||
|
|
bcd68441be | ||
|
|
4ebf9ad9c7 | ||
|
|
807572199c | ||
|
|
3cdc1536c5 | ||
|
|
9e13e5ebff | ||
|
|
9b2c254eee | ||
|
|
0883479068 | ||
|
|
c5480c63be | ||
|
|
4280ee6987 | ||
|
|
91673d7ae3 | ||
|
|
927f07b62b | ||
|
|
7e774d6d2d | ||
|
|
fccd06b376 | ||
|
|
e3edc0a7fc | ||
|
|
9c47ef2658 | ||
|
|
e1b6b638c6 | ||
|
|
c24768f922 | ||
|
|
87ee879dea | ||
|
|
b5603560e8 | ||
|
|
5818f1a41c | ||
|
|
1b781b4b57 | ||
|
|
94faf098b6 | ||
|
|
03e45f73cd | ||
|
|
63f7e220c0 | ||
|
|
7d49366373 | ||
|
|
55ed33d2d1 | ||
|
|
138a0e9b40 | ||
|
|
4fc7263ac3 | ||
|
|
f27fd59fa6 | ||
|
|
437f0e9a93 | ||
|
|
cc5d38f1ce | ||
|
|
0ce025e0c2 | ||
|
|
224cf4ea21 | ||
|
|
a9b1e5293c | ||
|
|
80009ab67f | ||
|
|
df9fda2971 | ||
|
|
ca8afb83a1 | ||
|
|
18a9cf2535 | ||
|
|
10c126ad92 | ||
|
|
19305aebc9 | ||
|
|
be68e27551 | ||
|
|
d6d96fe8ff | ||
|
|
95909d83a4 | ||
|
|
3bd48974f3 | ||
|
|
29093715e3 | ||
|
|
87b4dfc8f3 | ||
|
|
4db78b1787 | ||
|
|
02a5f15535 |
2
.github/actions/bitcoin/action.yml
vendored
2
.github/actions/bitcoin/action.yml
vendored
@@ -5,7 +5,7 @@ inputs:
|
|||||||
version:
|
version:
|
||||||
description: "Version to download and run"
|
description: "Version to download and run"
|
||||||
required: false
|
required: false
|
||||||
default: "29.1"
|
default: "30.0"
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
|
|||||||
21
.github/actions/build-dependencies/action.yml
vendored
21
.github/actions/build-dependencies/action.yml
vendored
@@ -7,6 +7,10 @@ runs:
|
|||||||
- name: Remove unused packages
|
- name: Remove unused packages
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
# Ensure the repositories are synced
|
||||||
|
sudo apt update -y
|
||||||
|
|
||||||
|
# Actually perform the removals
|
||||||
sudo apt remove -y "*powershell*" "*nuget*" "*bazel*" "*ansible*" "*terraform*" "*heroku*" "*aws*" azure-cli
|
sudo apt remove -y "*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*"
|
||||||
@@ -14,8 +18,9 @@ runs:
|
|||||||
sudo apt remove -y --allow-remove-essential -f shim-signed *python3*
|
sudo apt remove -y --allow-remove-essential -f shim-signed *python3*
|
||||||
# This removal command requires the prior removals due to unmet dependencies otherwise
|
# 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
|
# Reinstall python3 as a general dependency of a functional operating system
|
||||||
sudo apt install python3
|
sudo apt install -y python3 --fix-missing
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
- name: Remove unused packages
|
- name: Remove unused packages
|
||||||
@@ -33,19 +38,23 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||||
sudo apt install -y ca-certificates protobuf-compiler
|
sudo apt install -y ca-certificates protobuf-compiler libclang-dev
|
||||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||||
choco install protoc
|
choco install protoc
|
||||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||||
brew install protobuf
|
brew install protobuf llvm
|
||||||
|
HOMEBREW_ROOT_PATH=/opt/homebrew # Apple Silicon
|
||||||
|
if [ $(uname -m) = "x86_64" ]; then HOMEBREW_ROOT_PATH=/usr/local; fi # Intel
|
||||||
|
ls $HOMEBREW_ROOT_PATH/opt/llvm/lib | grep "libclang.dylib" # Make sure this installed `libclang`
|
||||||
|
echo "DYLD_LIBRARY_PATH=$HOMEBREW_ROOT_PATH/opt/llvm/lib:$DYLD_LIBRARY_PATH" >> "$GITHUB_ENV"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Install solc
|
- name: Install solc
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cargo +1.89 install svm-rs --version =0.5.18
|
cargo +1.91 install svm-rs --version =0.5.19
|
||||||
svm install 0.8.26
|
svm install 0.8.29
|
||||||
svm use 0.8.26
|
svm use 0.8.29
|
||||||
|
|
||||||
- name: Remove preinstalled Docker
|
- name: Remove preinstalled Docker
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
2
.github/actions/monero-wallet-rpc/action.yml
vendored
2
.github/actions/monero-wallet-rpc/action.yml
vendored
@@ -5,7 +5,7 @@ inputs:
|
|||||||
version:
|
version:
|
||||||
description: "Version to download and run"
|
description: "Version to download and run"
|
||||||
required: false
|
required: false
|
||||||
default: v0.18.3.4
|
default: v0.18.4.3
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
|
|||||||
2
.github/actions/monero/action.yml
vendored
2
.github/actions/monero/action.yml
vendored
@@ -5,7 +5,7 @@ inputs:
|
|||||||
version:
|
version:
|
||||||
description: "Version to download and run"
|
description: "Version to download and run"
|
||||||
required: false
|
required: false
|
||||||
default: v0.18.3.4
|
default: v0.18.4.3
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
|
|||||||
4
.github/actions/test-dependencies/action.yml
vendored
4
.github/actions/test-dependencies/action.yml
vendored
@@ -5,12 +5,12 @@ inputs:
|
|||||||
monero-version:
|
monero-version:
|
||||||
description: "Monero version to download and run as a regtest node"
|
description: "Monero version to download and run as a regtest node"
|
||||||
required: false
|
required: false
|
||||||
default: v0.18.3.4
|
default: v0.18.4.3
|
||||||
|
|
||||||
bitcoin-version:
|
bitcoin-version:
|
||||||
description: "Bitcoin version to download and run as a regtest node"
|
description: "Bitcoin version to download and run as a regtest node"
|
||||||
required: false
|
required: false
|
||||||
default: "29.1"
|
default: "30.0"
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
|
|||||||
2
.github/nightly-version
vendored
2
.github/nightly-version
vendored
@@ -1 +1 @@
|
|||||||
nightly-2025-09-01
|
nightly-2025-11-11
|
||||||
|
|||||||
2
.github/workflows/daily-deny.yml
vendored
2
.github/workflows/daily-deny.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
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 +1.91 install cargo-deny --version =0.18.5
|
||||||
|
|
||||||
- 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 --hide-inclusion-graph
|
||||||
|
|||||||
38
.github/workflows/lint.yml
vendored
38
.github/workflows/lint.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
clippy:
|
clippy:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-13, macos-14, windows-latest]
|
os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -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 wasm32v1-none -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
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
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 +1.91 install cargo-deny --version =0.18.5
|
||||||
|
|
||||||
- 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 --hide-inclusion-graph
|
||||||
@@ -88,8 +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 +1.91 install cargo-machete --version =0.9.1
|
||||||
cargo +1.89 machete
|
cargo +1.91 machete
|
||||||
|
|
||||||
msrv:
|
msrv:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
- name: Verify claimed `rust-version`
|
- name: Verify claimed `rust-version`
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cargo +1.89 install cargo-msrv --version =0.18.4
|
cargo +1.91 install cargo-msrv --version =0.18.4
|
||||||
|
|
||||||
function check_msrv {
|
function check_msrv {
|
||||||
# We `cd` into the directory passed as the first argument, but will return to the
|
# We `cd` into the directory passed as the first argument, but will return to the
|
||||||
@@ -144,18 +144,17 @@ jobs:
|
|||||||
function check_workspace {
|
function check_workspace {
|
||||||
# Get the members array from the workspace's `Cargo.toml`
|
# Get the members array from the workspace's `Cargo.toml`
|
||||||
cargo_toml_lines=$(cat ./Cargo.toml | wc -l)
|
cargo_toml_lines=$(cat ./Cargo.toml | wc -l)
|
||||||
|
# Keep all lines after the start of the array, then keep all lines before the next "]"
|
||||||
members=$(cat Cargo.toml | grep "members\ \=\ \[" -m1 -A$cargo_toml_lines | grep "]" -m1 -B$cargo_toml_lines)
|
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","}')
|
# Parse out any comments, whitespace, including comments post-fixed on the same line as an entry
|
||||||
# Prune `members = [` to `[` by replacing the first line with just `[`
|
# We accomplish the latter by pruning all characters after the entry's ","
|
||||||
|
members=$(echo "$members" | grep -Ev "^[[:space:]]*(#|$)" | awk -F',' '{print $1","}')
|
||||||
|
# Replace the first line, which was "members = [" and is now "members = [,", with "["
|
||||||
members=$(echo "$members" | sed "1s/.*/\[/")
|
members=$(echo "$members" | sed "1s/.*/\[/")
|
||||||
# Remove the trailing comma by replacing the last line's "," with ""
|
# Correct the last line, which was malleated to "],"
|
||||||
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/\]\,/\]/")
|
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
|
# Don't check the following
|
||||||
# Most of these are binaries, with the exception of the Substrate runtime which has a
|
# Most of these are binaries, with the exception of the Substrate runtime which has a
|
||||||
# bespoke build pipeline
|
# bespoke build pipeline
|
||||||
@@ -174,6 +173,9 @@ jobs:
|
|||||||
members=$(echo "$members" | grep -v "mini\"")
|
members=$(echo "$members" | grep -v "mini\"")
|
||||||
members=$(echo "$members" | grep -v "tests/")
|
members=$(echo "$members" | grep -v "tests/")
|
||||||
|
|
||||||
|
# Remove the trailing comma by replacing the last line's "," with ""
|
||||||
|
members=$(echo "$members" | sed "$(($(echo "$members" | wc -l) - 1))s/\,//")
|
||||||
|
|
||||||
echo $members | jq -r ".[]" | while read -r member; do
|
echo $members | jq -r ".[]" | while read -r member; do
|
||||||
check_msrv $member
|
check_msrv $member
|
||||||
correct=$?
|
correct=$?
|
||||||
@@ -188,12 +190,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||||
|
|
||||||
|
- name: Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
- name: Slither
|
- name: Slither
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install solc-select
|
|
||||||
solc-select install 0.8.26
|
|
||||||
solc-select use 0.8.26
|
|
||||||
|
|
||||||
python3 -m pip install slither-analyzer
|
python3 -m pip install slither-analyzer
|
||||||
|
|
||||||
slither --include-paths ./networks/ethereum/schnorr/contracts/Schnorr.sol
|
slither --include-paths ./networks/ethereum/schnorr/contracts/Schnorr.sol
|
||||||
|
|||||||
2
.github/workflows/pages.yml
vendored
2
.github/workflows/pages.yml
vendored
@@ -70,7 +70,7 @@ jobs:
|
|||||||
- 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
|
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32v1-none -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 --no-deps --all-features
|
||||||
mv target/doc docs/_site/rust
|
mv target/doc docs/_site/rust
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,14 @@
|
|||||||
target
|
target
|
||||||
|
|
||||||
|
# Don't commit any `Cargo.lock` which aren't the workspace's
|
||||||
|
Cargo.lock
|
||||||
|
!./Cargo.lock
|
||||||
|
|
||||||
|
# Don't commit any `Dockerfile`, as they're auto-generated, except the only one which isn't
|
||||||
Dockerfile
|
Dockerfile
|
||||||
Dockerfile.fast-epoch
|
Dockerfile.fast-epoch
|
||||||
!orchestration/runtime/Dockerfile
|
!orchestration/runtime/Dockerfile
|
||||||
|
|
||||||
.test-logs
|
.test-logs
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
6301
Cargo.lock
generated
6301
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
61
Cargo.toml
61
Cargo.toml
@@ -1,26 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
# Version patches
|
|
||||||
"patches/parking_lot",
|
|
||||||
"patches/zstd",
|
|
||||||
"patches/rocksdb",
|
|
||||||
|
|
||||||
# std patches
|
|
||||||
"patches/matches",
|
|
||||||
"patches/is-terminal",
|
|
||||||
|
|
||||||
# Rewrites/redirects
|
|
||||||
"patches/option-ext",
|
|
||||||
"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",
|
||||||
@@ -192,9 +172,15 @@ panic = "unwind"
|
|||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
# Point to empty crates for unused crates in our tree
|
||||||
|
ark-ff-3 = { package = "ark-ff", path = "patches/ethereum/ark-ff-0.3" }
|
||||||
|
ark-ff-4 = { package = "ark-ff", path = "patches/ethereum/ark-ff-0.4" }
|
||||||
|
c-kzg = { path = "patches/ethereum/c-kzg" }
|
||||||
|
secp256k1-30 = { package = "secp256k1", path = "patches/ethereum/secp256k1-30" }
|
||||||
|
|
||||||
# Dependencies from monero-oxide which originate from within our own tree
|
# Dependencies from monero-oxide which originate from within our own tree
|
||||||
std-shims = { path = "common/std-shims" }
|
std-shims = { path = "patches/std-shims" }
|
||||||
simple-request = { path = "common/request" }
|
simple-request = { path = "patches/simple-request" }
|
||||||
multiexp = { path = "crypto/multiexp" }
|
multiexp = { path = "crypto/multiexp" }
|
||||||
flexible-transcript = { path = "crypto/transcript" }
|
flexible-transcript = { path = "crypto/transcript" }
|
||||||
ciphersuite = { path = "patches/ciphersuite" }
|
ciphersuite = { path = "patches/ciphersuite" }
|
||||||
@@ -202,20 +188,16 @@ dalek-ff-group = { path = "patches/dalek-ff-group" }
|
|||||||
minimal-ed448 = { path = "crypto/ed448" }
|
minimal-ed448 = { path = "crypto/ed448" }
|
||||||
modular-frost = { path = "crypto/frost" }
|
modular-frost = { path = "crypto/frost" }
|
||||||
|
|
||||||
|
# This has a non-deprecated `std` alternative since Rust's 2024 edition
|
||||||
|
home = { path = "patches/home" }
|
||||||
|
|
||||||
|
# Updates to the latest version
|
||||||
|
darling = { path = "patches/darling" }
|
||||||
|
thiserror = { path = "patches/thiserror" }
|
||||||
|
|
||||||
# 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 = { 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
|
||||||
@@ -224,10 +206,16 @@ matches = { path = "patches/matches" }
|
|||||||
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
|
# Patch from a fork back to upstream
|
||||||
|
parity-bip39 = { path = "patches/parity-bip39" }
|
||||||
|
|
||||||
|
# Patch to include `FromUniformBytes<64>` over `Scalar`
|
||||||
k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
|
k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
|
||||||
p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
|
p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
|
||||||
|
|
||||||
|
# `jemalloc` conflicts with `mimalloc`, so patch to a `rocksdb` which never uses `jemalloc`
|
||||||
|
librocksdb-sys = { path = "patches/librocksdb-sys" }
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
unwrap_or_default = "allow"
|
unwrap_or_default = "allow"
|
||||||
map_unwrap_or = "allow"
|
map_unwrap_or = "allow"
|
||||||
@@ -272,7 +260,7 @@ redundant_closure_for_method_calls = "deny"
|
|||||||
redundant_else = "deny"
|
redundant_else = "deny"
|
||||||
string_add_assign = "deny"
|
string_add_assign = "deny"
|
||||||
string_slice = "deny"
|
string_slice = "deny"
|
||||||
unchecked_duration_subtraction = "deny"
|
unchecked_time_subtraction = "deny"
|
||||||
uninlined_format_args = "deny"
|
uninlined_format_args = "deny"
|
||||||
unnecessary_box_returns = "deny"
|
unnecessary_box_returns = "deny"
|
||||||
unnecessary_join = "deny"
|
unnecessary_join = "deny"
|
||||||
@@ -281,3 +269,6 @@ unnested_or_patterns = "deny"
|
|||||||
unused_async = "deny"
|
unused_async = "deny"
|
||||||
unused_self = "deny"
|
unused_self = "deny"
|
||||||
zero_sized_map_values = "deny"
|
zero_sized_map_values = "deny"
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
unused = "allow" # TODO: https://github.com/rust-lang/rust/issues/147648
|
||||||
|
|||||||
50
audits/crypto/dkg/evrf/README.md
Normal file
50
audits/crypto/dkg/evrf/README.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# eVRF DKG
|
||||||
|
|
||||||
|
In 2024, the [eVRF paper](https://eprint.iacr.org/2024/397) was published to
|
||||||
|
the IACR preprint server. Within it was a one-round unbiased DKG and a
|
||||||
|
one-round unbiased threshold DKG. Unfortunately, both simply describe
|
||||||
|
communication of the secret shares as 'Alice sends $s_b$ to Bob'. This causes,
|
||||||
|
in practice, the need for an additional round of communication to occur where
|
||||||
|
all participants confirm they received their secret shares.
|
||||||
|
|
||||||
|
Within Serai, it was posited to use the same premises as the DDH eVRF itself to
|
||||||
|
achieve a verifiable encryption scheme. This allows the secret shares to be
|
||||||
|
posted to any 'bulletin board' (such as a blockchain) and for all observers to
|
||||||
|
confirm:
|
||||||
|
|
||||||
|
- A participant participated
|
||||||
|
- The secret shares sent can be received by the intended recipient so long as
|
||||||
|
they can access the bulletin board
|
||||||
|
|
||||||
|
Additionally, Serai desired a robust scheme (albeit with an biased key as the
|
||||||
|
output, which is fine for our purposes). Accordingly, our implementation
|
||||||
|
instantiates the threshold eVRF DKG from the eVRF paper, with our own proposal
|
||||||
|
for verifiable encryption, with the caller allowed to decide the set of
|
||||||
|
participants. They may:
|
||||||
|
|
||||||
|
- Select everyone, collapsing to the non-threshold unbiased DKG from the eVRF
|
||||||
|
paper
|
||||||
|
- Select a pre-determined set, collapsing to the threshold unbaised DKG from
|
||||||
|
the eVRF paper
|
||||||
|
- Select a post-determined set (with any solution for the Common Subset
|
||||||
|
problem), allowing achieving a robust threshold biased DKG
|
||||||
|
|
||||||
|
Note that the eVRF paper proposes using the eVRF to sample coefficients yet
|
||||||
|
this is unnecessary when the resulting key will be biased. Any proof of
|
||||||
|
knowledge for the coefficients, as necessary for their extraction within the
|
||||||
|
security proofs, would be sufficient.
|
||||||
|
|
||||||
|
MAGIC Grants contracted HashCloak to formalize Serai's proposal for a DKG and
|
||||||
|
provide proofs for its security. This resulted in
|
||||||
|
[this paper](<./Security Proofs.pdf>).
|
||||||
|
|
||||||
|
Our implementation itself is then built on top of the audited
|
||||||
|
[`generalized-bulletproofs`](https://github.com/kayabaNerve/monero-oxide/tree/generalized-bulletproofs/audits/crypto/generalized-bulletproofs)
|
||||||
|
and
|
||||||
|
[`generalized-bulletproofs-ec-gadgets`](https://github.com/monero-oxide/monero-oxide/tree/fcmp%2B%2B/audits/fcmps).
|
||||||
|
|
||||||
|
Note we do not use the originally premised DDH eVRF yet the one premised on
|
||||||
|
elliptic curve divisors, the methodology of which is commented on
|
||||||
|
[here](https://github.com/monero-oxide/monero-oxide/tree/fcmp%2B%2B/audits/divisors).
|
||||||
|
|
||||||
|
Our implementation itself is unaudited at this time however.
|
||||||
BIN
audits/crypto/dkg/evrf/Security Proofs.pdf
Normal file
BIN
audits/crypto/dkg/evrf/Security Proofs.pdf
Normal file
Binary file not shown.
@@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-db = { version = "0.4", default-features = false, optional = true }
|
parity-db = { version = "0.5", default-features = false, optional = true }
|
||||||
rocksdb = { version = "0.24", default-features = false, features = ["zstd"], optional = true }
|
rocksdb = { version = "0.24", default-features = false, features = ["zstd"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
2
common/env/src/lib.rs
vendored
2
common/env/src/lib.rs
vendored
@@ -1,5 +1,5 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
// Obtain a variable from the Serai environment/secret store.
|
// Obtain a variable from the Serai environment/secret store.
|
||||||
pub fn var(variable: &str) -> Option<String> {
|
pub fn var(variable: &str) -> Option<String> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "simple-request"
|
name = "simple-request"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
description = "A simple HTTP(S) request library"
|
description = "A simple HTTP(S) request library"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/common/simple-request"
|
repository = "https://github.com/serai-dex/serai/tree/develop/common/request"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["http", "https", "async", "request", "ssl"]
|
keywords = ["http", "https", "async", "request", "ssl"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -19,9 +19,10 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tower-service = { version = "0.3", default-features = false }
|
tower-service = { version = "0.3", default-features = false }
|
||||||
hyper = { version = "1", default-features = false, features = ["http1", "client"] }
|
hyper = { version = "1", default-features = false, features = ["http1", "client"] }
|
||||||
hyper-util = { version = "0.1", default-features = false, features = ["http1", "client-legacy", "tokio"] }
|
hyper-util = { version = "0.1", default-features = false, features = ["http1", "client-legacy"] }
|
||||||
http-body-util = { version = "0.1", default-features = false }
|
http-body-util = { version = "0.1", default-features = false }
|
||||||
tokio = { version = "1", default-features = false }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
|
tokio = { version = "1", default-features = false, features = ["sync"] }
|
||||||
|
|
||||||
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "ring", "rustls-native-certs", "native-tokio"], optional = true }
|
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "ring", "rustls-native-certs", "native-tokio"], optional = true }
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ zeroize = { version = "1", optional = true }
|
|||||||
base64ct = { version = "1", features = ["alloc"], optional = true }
|
base64ct = { version = "1", features = ["alloc"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tls = ["hyper-rustls"]
|
tokio = ["hyper-util/tokio"]
|
||||||
|
tls = ["tokio", "hyper-rustls"]
|
||||||
|
webpki-roots = ["tls", "hyper-rustls/webpki-roots"]
|
||||||
basic-auth = ["zeroize", "base64ct"]
|
basic-auth = ["zeroize", "base64ct"]
|
||||||
default = ["tls"]
|
default = ["tls"]
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
use core::{pin::Pin, future::Future};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use tokio::sync::Mutex;
|
use futures_util::FutureExt;
|
||||||
|
use ::tokio::sync::Mutex;
|
||||||
|
|
||||||
use tower_service::Service as TowerService;
|
use tower_service::Service as TowerService;
|
||||||
|
use hyper::{Uri, header::HeaderValue, body::Bytes, client::conn::http1::SendRequest, rt::Executor};
|
||||||
|
pub use hyper;
|
||||||
|
|
||||||
|
use hyper_util::client::legacy::{Client as HyperClient, connect::HttpConnector};
|
||||||
|
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
use hyper_rustls::{HttpsConnectorBuilder, HttpsConnector};
|
use hyper_rustls::{HttpsConnectorBuilder, HttpsConnector};
|
||||||
use hyper::{Uri, header::HeaderValue, body::Bytes, client::conn::http1::SendRequest};
|
|
||||||
use hyper_util::{
|
|
||||||
rt::tokio::TokioExecutor,
|
|
||||||
client::legacy::{Client as HyperClient, connect::HttpConnector},
|
|
||||||
};
|
|
||||||
pub use hyper;
|
|
||||||
|
|
||||||
mod request;
|
mod request;
|
||||||
pub use request::*;
|
pub use request::*;
|
||||||
@@ -37,52 +38,86 @@ type Connector = HttpConnector;
|
|||||||
type Connector = HttpsConnector<HttpConnector>;
|
type Connector = HttpsConnector<HttpConnector>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Connection {
|
enum Connection<
|
||||||
|
E: 'static + Send + Sync + Clone + Executor<Pin<Box<dyn Send + Future<Output = ()>>>>,
|
||||||
|
> {
|
||||||
ConnectionPool(HyperClient<Connector, Full<Bytes>>),
|
ConnectionPool(HyperClient<Connector, Full<Bytes>>),
|
||||||
Connection {
|
Connection {
|
||||||
|
executor: E,
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
host: Uri,
|
host: Uri,
|
||||||
connection: Arc<Mutex<Option<SendRequest<Full<Bytes>>>>>,
|
connection: Arc<Mutex<Option<SendRequest<Full<Bytes>>>>>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An HTTP client.
|
||||||
|
///
|
||||||
|
/// `tls` is only guaranteed to work when using the `tokio` executor. Instantiating a client when
|
||||||
|
/// the `tls` feature is active without using the `tokio` executor will cause errors.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Client {
|
pub struct Client<
|
||||||
connection: Connection,
|
E: 'static + Send + Sync + Clone + Executor<Pin<Box<dyn Send + Future<Output = ()>>>>,
|
||||||
|
> {
|
||||||
|
connection: Connection<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl<E: 'static + Send + Sync + Clone + Executor<Pin<Box<dyn Send + Future<Output = ()>>>>>
|
||||||
fn connector() -> Connector {
|
Client<E>
|
||||||
|
{
|
||||||
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
|
fn connector() -> Result<Connector, Error> {
|
||||||
let mut res = HttpConnector::new();
|
let mut res = HttpConnector::new();
|
||||||
res.set_keepalive(Some(core::time::Duration::from_secs(60)));
|
res.set_keepalive(Some(core::time::Duration::from_secs(60)));
|
||||||
res.set_nodelay(true);
|
res.set_nodelay(true);
|
||||||
res.set_reuse_address(true);
|
res.set_reuse_address(true);
|
||||||
|
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
if core::any::TypeId::of::<E>() !=
|
||||||
|
core::any::TypeId::of::<hyper_util::rt::tokio::TokioExecutor>()
|
||||||
|
{
|
||||||
|
Err(Error::ConnectionError(
|
||||||
|
"`tls` feature enabled but not using the `tokio` executor".into(),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
res.enforce_http(false);
|
res.enforce_http(false);
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
let res = HttpsConnectorBuilder::new()
|
let https = HttpsConnectorBuilder::new().with_native_roots();
|
||||||
.with_native_roots()
|
#[cfg(all(feature = "tls", not(feature = "webpki-roots")))]
|
||||||
.expect("couldn't fetch system's SSL roots")
|
let https = https.map_err(|e| {
|
||||||
.https_or_http()
|
Error::ConnectionError(
|
||||||
.enable_http1()
|
format!("couldn't load system's SSL root certificates and webpki-roots unavilable: {e:?}")
|
||||||
.wrap_connector(res);
|
.into(),
|
||||||
res
|
)
|
||||||
|
})?;
|
||||||
|
// Fallback to `webpki-roots` if present
|
||||||
|
#[cfg(all(feature = "tls", feature = "webpki-roots"))]
|
||||||
|
let https = https.unwrap_or(HttpsConnectorBuilder::new().with_webpki_roots());
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
let res = https.https_or_http().enable_http1().wrap_connector(res);
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_connection_pool() -> Client {
|
pub fn with_executor_and_connection_pool(executor: E) -> Result<Client<E>, Error> {
|
||||||
Client {
|
Ok(Client {
|
||||||
connection: Connection::ConnectionPool(
|
connection: Connection::ConnectionPool(
|
||||||
HyperClient::builder(TokioExecutor::new())
|
HyperClient::builder(executor)
|
||||||
.pool_idle_timeout(core::time::Duration::from_secs(60))
|
.pool_idle_timeout(core::time::Duration::from_secs(60))
|
||||||
.build(Self::connector()),
|
.build(Self::connector()?),
|
||||||
),
|
),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn without_connection_pool(host: &str) -> Result<Client, Error> {
|
pub fn with_executor_and_without_connection_pool(
|
||||||
|
executor: E,
|
||||||
|
host: &str,
|
||||||
|
) -> Result<Client<E>, Error> {
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
connection: Connection::Connection {
|
connection: Connection::Connection {
|
||||||
connector: Self::connector(),
|
executor,
|
||||||
|
connector: Self::connector()?,
|
||||||
host: {
|
host: {
|
||||||
let uri: Uri = host.parse().map_err(|_| Error::InvalidUri)?;
|
let uri: Uri = host.parse().map_err(|_| Error::InvalidUri)?;
|
||||||
if uri.host().is_none() {
|
if uri.host().is_none() {
|
||||||
@@ -95,9 +130,9 @@ impl Client {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request<R: Into<Request>>(&self, request: R) -> Result<Response<'_>, Error> {
|
pub async fn request<R: Into<Request>>(&self, request: R) -> Result<Response<'_, E>, Error> {
|
||||||
let request: Request = request.into();
|
let request: Request = request.into();
|
||||||
let mut request = request.0;
|
let Request { mut request, response_size_limit } = request;
|
||||||
if let Some(header_host) = request.headers().get(hyper::header::HOST) {
|
if let Some(header_host) = request.headers().get(hyper::header::HOST) {
|
||||||
match &self.connection {
|
match &self.connection {
|
||||||
Connection::ConnectionPool(_) => {}
|
Connection::ConnectionPool(_) => {}
|
||||||
@@ -131,7 +166,7 @@ impl Client {
|
|||||||
Connection::ConnectionPool(client) => {
|
Connection::ConnectionPool(client) => {
|
||||||
client.request(request).await.map_err(Error::HyperUtil)?
|
client.request(request).await.map_err(Error::HyperUtil)?
|
||||||
}
|
}
|
||||||
Connection::Connection { connector, host, connection } => {
|
Connection::Connection { executor, connector, host, connection } => {
|
||||||
let mut connection_lock = connection.lock().await;
|
let mut connection_lock = connection.lock().await;
|
||||||
|
|
||||||
// If there's not a connection...
|
// If there's not a connection...
|
||||||
@@ -143,28 +178,46 @@ impl Client {
|
|||||||
let call_res = call_res.map_err(Error::ConnectionError);
|
let call_res = call_res.map_err(Error::ConnectionError);
|
||||||
let (requester, connection) =
|
let (requester, connection) =
|
||||||
hyper::client::conn::http1::handshake(call_res?).await.map_err(Error::Hyper)?;
|
hyper::client::conn::http1::handshake(call_res?).await.map_err(Error::Hyper)?;
|
||||||
// This will die when we drop the requester, so we don't need to track an AbortHandle
|
// This task will die when we drop the requester
|
||||||
// for it
|
executor.execute(Box::pin(connection.map(|_| ())));
|
||||||
tokio::spawn(connection);
|
|
||||||
*connection_lock = Some(requester);
|
*connection_lock = Some(requester);
|
||||||
}
|
}
|
||||||
|
|
||||||
let connection = connection_lock.as_mut().unwrap();
|
let connection = connection_lock.as_mut().expect("lock over the connection was poisoned");
|
||||||
let mut err = connection.ready().await.err();
|
let mut err = connection.ready().await.err();
|
||||||
if err.is_none() {
|
if err.is_none() {
|
||||||
// Send the request
|
// Send the request
|
||||||
let res = connection.send_request(request).await;
|
let response = connection.send_request(request).await;
|
||||||
if let Ok(res) = res {
|
if let Ok(response) = response {
|
||||||
return Ok(Response(res, self));
|
return Ok(Response { response, size_limit: response_size_limit, client: self });
|
||||||
}
|
}
|
||||||
err = res.err();
|
err = response.err();
|
||||||
}
|
}
|
||||||
// Since this connection has been put into an error state, drop it
|
// Since this connection has been put into an error state, drop it
|
||||||
*connection_lock = None;
|
*connection_lock = None;
|
||||||
Err(Error::Hyper(err.unwrap()))?
|
Err(Error::Hyper(err.expect("only here if `err` is some yet no error")))?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Response(response, self))
|
Ok(Response { response, size_limit: response_size_limit, client: self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
mod tokio {
|
||||||
|
use hyper_util::rt::tokio::TokioExecutor;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub type TokioClient = Client<TokioExecutor>;
|
||||||
|
impl Client<TokioExecutor> {
|
||||||
|
pub fn with_connection_pool() -> Result<Self, Error> {
|
||||||
|
Self::with_executor_and_connection_pool(TokioExecutor::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn without_connection_pool(host: &str) -> Result<Self, Error> {
|
||||||
|
Self::with_executor_and_without_connection_pool(TokioExecutor::new(), host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
pub use tokio::TokioClient;
|
||||||
|
|||||||
@@ -7,11 +7,15 @@ pub use http_body_util::Full;
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Request(pub(crate) hyper::Request<Full<Bytes>>);
|
pub struct Request {
|
||||||
|
pub(crate) request: hyper::Request<Full<Bytes>>,
|
||||||
|
pub(crate) response_size_limit: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
#[cfg(feature = "basic-auth")]
|
#[cfg(feature = "basic-auth")]
|
||||||
fn username_password_from_uri(&self) -> Result<(String, String), Error> {
|
fn username_password_from_uri(&self) -> Result<(String, String), Error> {
|
||||||
if let Some(authority) = self.0.uri().authority() {
|
if let Some(authority) = self.request.uri().authority() {
|
||||||
let authority = authority.as_str();
|
let authority = authority.as_str();
|
||||||
if authority.contains('@') {
|
if authority.contains('@') {
|
||||||
// Decode the username and password from the URI
|
// Decode the username and password from the URI
|
||||||
@@ -36,9 +40,10 @@ impl Request {
|
|||||||
let mut formatted = format!("{username}:{password}");
|
let mut formatted = format!("{username}:{password}");
|
||||||
let mut encoded = Base64::encode_string(formatted.as_bytes());
|
let mut encoded = Base64::encode_string(formatted.as_bytes());
|
||||||
formatted.zeroize();
|
formatted.zeroize();
|
||||||
self.0.headers_mut().insert(
|
self.request.headers_mut().insert(
|
||||||
hyper::header::AUTHORIZATION,
|
hyper::header::AUTHORIZATION,
|
||||||
HeaderValue::from_str(&format!("Basic {encoded}")).unwrap(),
|
HeaderValue::from_str(&format!("Basic {encoded}"))
|
||||||
|
.expect("couldn't form header from base64-encoded string"),
|
||||||
);
|
);
|
||||||
encoded.zeroize();
|
encoded.zeroize();
|
||||||
}
|
}
|
||||||
@@ -59,9 +64,17 @@ impl Request {
|
|||||||
pub fn with_basic_auth(&mut self) {
|
pub fn with_basic_auth(&mut self) {
|
||||||
let _ = self.basic_auth_from_uri();
|
let _ = self.basic_auth_from_uri();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl From<hyper::Request<Full<Bytes>>> for Request {
|
/// Set a size limit for the response.
|
||||||
fn from(request: hyper::Request<Full<Bytes>>) -> Request {
|
///
|
||||||
Request(request)
|
/// This may be exceeded by a single HTTP frame and accordingly isn't perfect.
|
||||||
|
pub fn set_response_size_limit(&mut self, response_size_limit: Option<usize>) {
|
||||||
|
self.response_size_limit = response_size_limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<hyper::Request<Full<Bytes>>> for Request {
|
||||||
|
fn from(request: hyper::Request<Full<Bytes>>) -> Request {
|
||||||
|
Request { request, response_size_limit: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,54 @@
|
|||||||
|
use core::{pin::Pin, future::Future};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
use hyper::{
|
use hyper::{
|
||||||
StatusCode,
|
StatusCode,
|
||||||
header::{HeaderValue, HeaderMap},
|
header::{HeaderValue, HeaderMap},
|
||||||
body::{Buf, Incoming},
|
body::Incoming,
|
||||||
|
rt::Executor,
|
||||||
};
|
};
|
||||||
use http_body_util::BodyExt;
|
use http_body_util::BodyExt;
|
||||||
|
|
||||||
|
use futures_util::{Stream, StreamExt};
|
||||||
|
|
||||||
use crate::{Client, Error};
|
use crate::{Client, Error};
|
||||||
|
|
||||||
// Borrows the client so its async task lives as long as this response exists.
|
// Borrows the client so its async task lives as long as this response exists.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Response<'a>(pub(crate) hyper::Response<Incoming>, pub(crate) &'a Client);
|
pub struct Response<
|
||||||
impl Response<'_> {
|
'a,
|
||||||
|
E: 'static + Send + Sync + Clone + Executor<Pin<Box<dyn Send + Future<Output = ()>>>>,
|
||||||
|
> {
|
||||||
|
pub(crate) response: hyper::Response<Incoming>,
|
||||||
|
pub(crate) size_limit: Option<usize>,
|
||||||
|
pub(crate) client: &'a Client<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: 'static + Send + Sync + Clone + Executor<Pin<Box<dyn Send + Future<Output = ()>>>>>
|
||||||
|
Response<'_, E>
|
||||||
|
{
|
||||||
pub fn status(&self) -> StatusCode {
|
pub fn status(&self) -> StatusCode {
|
||||||
self.0.status()
|
self.response.status()
|
||||||
}
|
}
|
||||||
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
|
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
|
||||||
self.0.headers()
|
self.response.headers()
|
||||||
}
|
}
|
||||||
pub async fn body(self) -> Result<impl std::io::Read, Error> {
|
pub async fn body(self) -> Result<impl std::io::Read, Error> {
|
||||||
Ok(self.0.into_body().collect().await.map_err(Error::Hyper)?.aggregate().reader())
|
let mut body = self.response.into_body().into_data_stream();
|
||||||
|
let mut res: Vec<u8> = vec![];
|
||||||
|
loop {
|
||||||
|
if let Some(size_limit) = self.size_limit {
|
||||||
|
let (lower, upper) = body.size_hint();
|
||||||
|
if res.len().wrapping_add(upper.unwrap_or(lower)) > size_limit.min(usize::MAX - 1) {
|
||||||
|
Err(Error::ConnectionError("response exceeded size limit".into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(part) = body.next().await else { break };
|
||||||
|
let part = part.map_err(Error::Hyper)?;
|
||||||
|
res.extend(part.as_ref());
|
||||||
|
}
|
||||||
|
Ok(io::Cursor::new(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "std-shims"
|
name = "std-shims"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
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"
|
||||||
@@ -18,9 +18,10 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustversion = { version = "1", default-features = false }
|
rustversion = { version = "1", default-features = false }
|
||||||
spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "once", "lazy"] }
|
spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "fair_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"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = []
|
alloc = ["hashbrown"]
|
||||||
|
std = ["alloc", "spin/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,11 +1,28 @@
|
|||||||
# std shims
|
# `std` shims
|
||||||
|
|
||||||
A crate which passes through to std when the default `std` feature is enabled,
|
`std-shims` is a Rust crate with two purposes:
|
||||||
yet provides a series of shims when it isn't.
|
- Expand the functionality of `core` and `alloc`
|
||||||
|
- Polyfill functionality only available on newer version of Rust
|
||||||
|
|
||||||
No guarantee of one-to-one parity is provided. The shims provided aim to be sufficient for the
|
The goal is to make supporting no-`std` environments, and older versions of
|
||||||
average case.
|
Rust, as simple as possible. For most use cases, replacing `std::` with
|
||||||
|
`std_shims::` and adding `use std_shims::prelude::*` is sufficient to take full
|
||||||
|
advantage of `std-shims`.
|
||||||
|
|
||||||
`HashSet` and `HashMap` are provided via `hashbrown`. Synchronization primitives are provided via
|
# API Surface
|
||||||
`spin` (avoiding a requirement on `critical-section`).
|
|
||||||
types are not guaranteed to be
|
`std-shims` only aims to have items _mutually available_ between `alloc` (with
|
||||||
|
extra dependencies) and `std` publicly exposed. Items exclusive to `std`, with
|
||||||
|
no shims available, will not be exported by `std-shims`.
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
`HashSet` and `HashMap` are provided via `hashbrown`. Synchronization
|
||||||
|
primitives are provided via `spin` (avoiding a requirement on
|
||||||
|
`critical-section`). Sections of `std::io` are independently matched as
|
||||||
|
possible. `rustversion` is used to detect when to provide polyfills.
|
||||||
|
|
||||||
|
# Disclaimer
|
||||||
|
|
||||||
|
No guarantee of one-to-one parity is provided. The shims provided aim to be
|
||||||
|
sufficient for the average case. Pull requests are _welcome_.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
|
pub use extern_alloc::collections::*;
|
||||||
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
|
pub use hashbrown::{HashSet, HashMap};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::collections::*;
|
pub use std::collections::*;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use alloc::collections::*;
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use hashbrown::{HashSet, HashMap};
|
|
||||||
|
|||||||
@@ -1,42 +1,74 @@
|
|||||||
#[cfg(feature = "std")]
|
|
||||||
pub use std::io::*;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
mod shims {
|
mod shims {
|
||||||
use core::fmt::{Debug, Formatter};
|
use core::fmt::{self, Debug, Display, Formatter};
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
#[cfg(feature = "alloc")]
|
||||||
|
use extern_alloc::{boxed::Box, vec::Vec};
|
||||||
|
use crate::error::Error as CoreError;
|
||||||
|
|
||||||
|
/// The kind of error.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
UnexpectedEof,
|
UnexpectedEof,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
kind: ErrorKind,
|
kind: ErrorKind,
|
||||||
error: Box<dyn Send + Sync>,
|
#[cfg(feature = "alloc")]
|
||||||
|
error: Box<dyn Send + Sync + CoreError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
fmt.debug_struct("Error").field("kind", &self.kind).finish_non_exhaustive()
|
<Self as Debug>::fmt(self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CoreError for Error {}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
pub trait IntoBoxSendSyncError {}
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
impl<I> IntoBoxSendSyncError for I {}
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub trait IntoBoxSendSyncError: Into<Box<dyn Send + Sync + CoreError>> {}
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<I: Into<Box<dyn Send + Sync + CoreError>>> IntoBoxSendSyncError for I {}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn new<E: 'static + Send + Sync>(kind: ErrorKind, error: E) -> Error {
|
/// Create a new error.
|
||||||
Error { kind, error: Box::new(error) }
|
///
|
||||||
|
/// The error object itself is silently dropped when `alloc` is not enabled.
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn new<E: 'static + IntoBoxSendSyncError>(kind: ErrorKind, error: E) -> Error {
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
let res = Error { kind };
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
let res = Error { kind, error: error.into() };
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn other<E: 'static + Send + Sync>(error: E) -> Error {
|
/// Create a new error with `io::ErrorKind::Other` as its kind.
|
||||||
Error { kind: ErrorKind::Other, error: Box::new(error) }
|
///
|
||||||
|
/// The error object itself is silently dropped when `alloc` is not enabled.
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn other<E: 'static + IntoBoxSendSyncError>(error: E) -> Error {
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
let res = Error { kind: ErrorKind::Other };
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
let res = Error { kind: ErrorKind::Other, error: error.into() };
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of error.
|
||||||
pub fn kind(&self) -> ErrorKind {
|
pub fn kind(&self) -> ErrorKind {
|
||||||
self.kind
|
self.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> Option<Box<dyn Send + Sync>> {
|
/// Retrieve the inner error.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub fn into_inner(self) -> Option<Box<dyn Send + Sync + CoreError>> {
|
||||||
Some(self.error)
|
Some(self.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,6 +96,12 @@ mod shims {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R: Read> Read for &mut R {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
R::read(*self, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait BufRead: Read {
|
pub trait BufRead: Read {
|
||||||
fn fill_buf(&mut self) -> Result<&[u8]>;
|
fn fill_buf(&mut self) -> Result<&[u8]>;
|
||||||
fn consume(&mut self, amt: usize);
|
fn consume(&mut self, amt: usize);
|
||||||
@@ -88,6 +126,7 @@ mod shims {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
impl Write for Vec<u8> {
|
impl Write for Vec<u8> {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||||
self.extend(buf);
|
self.extend(buf);
|
||||||
@@ -95,6 +134,8 @@ mod shims {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
pub use shims::*;
|
pub use shims::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::io::{ErrorKind, Error, Result, Read, BufRead, Write};
|
||||||
|
|||||||
@@ -1,18 +1,45 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
pub extern crate alloc;
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
pub use core::*;
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
pub use core::{alloc, borrow, ffi, fmt, slice, str, task};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[rustversion::before(1.81)]
|
||||||
|
pub mod error {
|
||||||
|
use core::fmt::Debug::Display;
|
||||||
|
pub trait Error: Debug + Display {}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[rustversion::since(1.81)]
|
||||||
|
pub use core::error;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
extern crate alloc as extern_alloc;
|
||||||
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
|
pub use extern_alloc::{alloc, borrow, boxed, ffi, fmt, rc, slice, str, string, task, vec, format};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::{alloc, borrow, boxed, error, ffi, fmt, rc, slice, str, string, task, vec, format};
|
||||||
|
|
||||||
pub mod sync;
|
|
||||||
pub mod collections;
|
pub mod collections;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
|
pub mod sync;
|
||||||
pub use alloc::vec;
|
|
||||||
pub use alloc::str;
|
|
||||||
pub use alloc::string;
|
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
// Shim the `std` prelude
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub use extern_alloc::{
|
||||||
|
format, vec,
|
||||||
|
borrow::ToOwned,
|
||||||
|
boxed::Box,
|
||||||
|
vec::Vec,
|
||||||
|
string::{String, ToString},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shim `div_ceil`
|
||||||
#[rustversion::before(1.73)]
|
#[rustversion::before(1.73)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait StdShimsDivCeil {
|
pub trait StdShimsDivCeil {
|
||||||
@@ -53,6 +80,7 @@ pub mod prelude {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shim `io::Error::other`
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[rustversion::before(1.74)]
|
#[rustversion::before(1.74)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|||||||
@@ -1,19 +1,28 @@
|
|||||||
pub use core::sync::*;
|
pub use core::sync::atomic;
|
||||||
pub use alloc::sync::*;
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
|
pub use extern_alloc::sync::{Arc, Weak};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
mod mutex_shim {
|
mod mutex_shim {
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use std::sync::*;
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
pub use spin::*;
|
pub use spin::{Mutex, MutexGuard};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
|
/// A shimmed `Mutex` with an API mutual to `spin` and `std`.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ShimMutex<T>(Mutex<T>);
|
pub struct ShimMutex<T>(Mutex<T>);
|
||||||
impl<T> ShimMutex<T> {
|
impl<T> ShimMutex<T> {
|
||||||
|
/// Construct a new `Mutex`.
|
||||||
pub const fn new(value: T) -> Self {
|
pub const fn new(value: T) -> Self {
|
||||||
Self(Mutex::new(value))
|
Self(Mutex::new(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Acquire a lock on the contents of the `Mutex`.
|
||||||
|
///
|
||||||
|
/// On no-`std` environments, this may spin until the lock is acquired. On `std` environments,
|
||||||
|
/// this may panic if the `Mutex` was poisoned.
|
||||||
pub fn lock(&self) -> MutexGuard<'_, T> {
|
pub fn lock(&self) -> MutexGuard<'_, T> {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
let res = self.0.lock().unwrap();
|
let res = self.0.lock().unwrap();
|
||||||
@@ -25,10 +34,11 @@ 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)]
|
#[rustversion::before(1.80)]
|
||||||
#[cfg(feature = "std")]
|
pub use spin::Lazy as LazyLock;
|
||||||
|
|
||||||
|
#[rustversion::since(1.80)]
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
pub use spin::Lazy as LazyLock;
|
pub use spin::Lazy as LazyLock;
|
||||||
#[rustversion::since(1.80)]
|
#[rustversion::since(1.80)]
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![cfg_attr(all(zalloc_rustc_nightly, feature = "allocator"), feature(allocator_api))]
|
#![cfg_attr(all(zalloc_rustc_nightly, feature = "allocator"), feature(allocator_api))]
|
||||||
|
|
||||||
//! Implementation of a Zeroizing Allocator, enabling zeroizing memory on deallocation.
|
//! Implementation of a Zeroizing Allocator, enabling zeroizing memory on deallocation.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ tributary-sdk = { path = "../../tributary-sdk" }
|
|||||||
|
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
tokio = { version = "1", default-features = false, features = ["sync"] }
|
tokio = { version = "1", default-features = false, features = ["sync"] }
|
||||||
libp2p = { version = "0.54", default-features = false, features = ["tokio", "tcp", "noise", "yamux", "ping", "request-response", "gossipsub", "macros"] }
|
libp2p = { version = "0.56", default-features = false, features = ["tokio", "tcp", "noise", "yamux", "ping", "request-response", "gossipsub", "macros"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
serai-task = { path = "../../../common/task", version = "0.1" }
|
serai-task = { path = "../../../common/task", version = "0.1" }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,8 @@ impl SwarmTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
gossip::Event::Subscribed { .. } | gossip::Event::Unsubscribed { .. } => {}
|
gossip::Event::Subscribed { .. } | gossip::Event::Unsubscribed { .. } => {}
|
||||||
gossip::Event::GossipsubNotSupported { peer_id } => {
|
gossip::Event::GossipsubNotSupported { peer_id } |
|
||||||
|
gossip::Event::SlowPeer { peer_id, .. } => {
|
||||||
let _: Result<_, _> = self.swarm.disconnect_peer_id(peer_id);
|
let _: Result<_, _> = self.swarm.disconnect_peer_id(peer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ mod _internal_db {
|
|||||||
// Tributary transactions to publish from the DKG confirmation task
|
// Tributary transactions to publish from the DKG confirmation task
|
||||||
TributaryTransactionsFromDkgConfirmation: (set: ExternalValidatorSet) -> Transaction,
|
TributaryTransactionsFromDkgConfirmation: (set: ExternalValidatorSet) -> Transaction,
|
||||||
// Participants to remove
|
// Participants to remove
|
||||||
RemoveParticipant: (set: ExternalValidatorSet) -> Participant,
|
RemoveParticipant: (set: ExternalValidatorSet) -> u16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,10 +139,11 @@ impl RemoveParticipant {
|
|||||||
pub(crate) fn send(txn: &mut impl DbTxn, set: ExternalValidatorSet, participant: Participant) {
|
pub(crate) fn send(txn: &mut impl DbTxn, set: ExternalValidatorSet, participant: Participant) {
|
||||||
// If this set has yet to be retired, send this transaction
|
// If this set has yet to be retired, send this transaction
|
||||||
if RetiredTributary::get(txn, set.network).map(|session| session.0) < Some(set.session.0) {
|
if RetiredTributary::get(txn, set.network).map(|session| session.0) < Some(set.session.0) {
|
||||||
_internal_db::RemoveParticipant::send(txn, set, &participant);
|
_internal_db::RemoveParticipant::send(txn, set, &u16::from(participant));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn try_recv(txn: &mut impl DbTxn, set: ExternalValidatorSet) -> Option<Participant> {
|
pub(crate) fn try_recv(txn: &mut impl DbTxn, set: ExternalValidatorSet) -> Option<Participant> {
|
||||||
_internal_db::RemoveParticipant::try_recv(txn, set)
|
_internal_db::RemoveParticipant::try_recv(txn, set)
|
||||||
|
.map(|i| Participant::new(i).expect("sent invalid participant index for removal"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ async fn handle_network(
|
|||||||
&mut txn,
|
&mut txn,
|
||||||
ExternalValidatorSet { network, session },
|
ExternalValidatorSet { network, session },
|
||||||
slash_report,
|
slash_report,
|
||||||
Signature(signature),
|
Signature::from(signature),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use rand::{RngCore, CryptoRng, rngs::OsRng};
|
|||||||
use blake2::{Digest, Blake2s256};
|
use blake2::{Digest, Blake2s256};
|
||||||
|
|
||||||
use dalek_ff_group::Ristretto;
|
use dalek_ff_group::Ristretto;
|
||||||
use ciphersuite::{group::Group, *};
|
use ciphersuite::*;
|
||||||
use schnorr::SchnorrSignature;
|
use schnorr::SchnorrSignature;
|
||||||
|
|
||||||
use scale::Encode;
|
use scale::Encode;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![expect(clippy::cast_possible_truncation)]
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![expect(clippy::cast_possible_truncation)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use scale::Encode;
|
use scale::Encode;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ 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.4", 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 }
|
||||||
@@ -33,7 +33,7 @@ hex = { version = "0.4", 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 = ["zeroize/alloc", "digest/alloc", "ff/alloc"]
|
||||||
std = [
|
std = [
|
||||||
"alloc",
|
"alloc",
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("lib.md")]
|
#![doc = include_str!("lib.md")]
|
||||||
#![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")]
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std_shims::prelude::*;
|
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 subtle::{CtOption, ConstantTimeEq, ConditionallySelectable};
|
||||||
@@ -112,7 +110,6 @@ pub trait GroupCanonicalEncoding: WrappedGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `std::io` extensions for `GroupCanonicalEncoding.`
|
/// `std::io` extensions for `GroupCanonicalEncoding.`
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub trait GroupIo: GroupCanonicalEncoding {
|
pub trait GroupIo: GroupCanonicalEncoding {
|
||||||
/// Read a canonical field element from something implementing `std::io::Read`.
|
/// Read a canonical field element from something implementing `std::io::Read`.
|
||||||
@@ -129,8 +126,6 @@ pub trait GroupIo: GroupCanonicalEncoding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read a canonical point from something implementing `std::io::Read`.
|
/// Read a canonical point from something implementing `std::io::Read`.
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[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 bytes = <Self::G as GroupEncoding>::Repr::default();
|
||||||
reader.read_exact(bytes.as_mut())?;
|
reader.read_exact(bytes.as_mut())?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std
|
#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![allow(clippy::redundant_closure_call)]
|
#![allow(clippy::redundant_closure_call)]
|
||||||
|
|||||||
@@ -21,21 +21,14 @@ zeroize = { version = "^1.5", default-features = false, features = ["zeroize_der
|
|||||||
|
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false }
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
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"] }
|
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
"thiserror/std",
|
"thiserror/std",
|
||||||
|
|
||||||
"std-shims/std",
|
"std-shims/std",
|
||||||
|
|
||||||
"borsh?/std",
|
|
||||||
|
|
||||||
"ciphersuite/std",
|
"ciphersuite/std",
|
||||||
]
|
]
|
||||||
borsh = ["dep:borsh"]
|
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ workspace = true
|
|||||||
zeroize = { version = "^1.5", default-features = false }
|
zeroize = { version = "^1.5", default-features = false }
|
||||||
rand_core = { version = "0.6", default-features = false }
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false }
|
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false }
|
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false }
|
||||||
dkg = { path = "../", version = "0.6", default-features = false }
|
dkg = { path = "../", version = "0.6", default-features = false }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ rand_core = { version = "0.6", default-features = false, features = ["alloc"] }
|
|||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["alloc", "zeroize_derive"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["alloc", "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, features = ["alloc"] }
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
transcript = { package = "flexible-transcript", path = "../../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
||||||
|
|
||||||
@@ -34,10 +34,10 @@ generic-array = { version = "1", default-features = false, features = ["alloc"]
|
|||||||
blake2 = { version = "0.11.0-rc.2", default-features = false }
|
blake2 = { version = "0.11.0-rc.2", default-features = false }
|
||||||
rand_chacha = { version = "0.3", default-features = false }
|
rand_chacha = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
|
generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false }
|
||||||
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
|
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false }
|
||||||
generalized-bulletproofs-circuit-abstraction = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
|
generalized-bulletproofs-circuit-abstraction = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false }
|
||||||
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
|
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false }
|
||||||
|
|
||||||
dkg = { path = "..", default-features = false }
|
dkg = { path = "..", default-features = false }
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ rand = { version = "0.8", default-features = false, features = ["std"] }
|
|||||||
ciphersuite = { path = "../../ciphersuite", default-features = false, features = ["std"] }
|
ciphersuite = { path = "../../ciphersuite", default-features = false, features = ["std"] }
|
||||||
embedwards25519 = { path = "../../embedwards25519", default-features = false, features = ["std"] }
|
embedwards25519 = { path = "../../embedwards25519", default-features = false, features = ["std"] }
|
||||||
dalek-ff-group = { path = "../../dalek-ff-group", default-features = false, features = ["std"] }
|
dalek-ff-group = { path = "../../dalek-ff-group", default-features = false, features = ["std"] }
|
||||||
generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", features = ["tests"] }
|
generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", features = ["tests"] }
|
||||||
dkg-recovery = { path = "../recovery" }
|
dkg-recovery = { path = "../recovery" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -26,21 +26,9 @@ presented in section 4.2 is extended, with the following changes:
|
|||||||
just one round.
|
just one round.
|
||||||
|
|
||||||
For a gist of the verifiable encryption scheme, please see
|
For a gist of the verifiable encryption scheme, please see
|
||||||
https://gist.github.com/kayabaNerve/cfbde74b0660dfdf8dd55326d6ec33d7. Security
|
https://gist.github.com/kayabaNerve/cfbde74b0660dfdf8dd55326d6ec33d7. For
|
||||||
proofs are currently being worked on.
|
security proofs and audit information, please see
|
||||||
|
[here](../../../audits/crypto/dkg/evrf).
|
||||||
---
|
|
||||||
|
|
||||||
This library relies on an implementation of Bulletproofs and various
|
|
||||||
zero-knowledge gadgets. This library uses
|
|
||||||
[`generalized-bulletproofs`](https://docs.rs/generalized-bulletproofs),
|
|
||||||
[`generalized-bulletproofs-circuit-abstraction`](https://docs.rs/generalized-bulletproofs-circuit-abstraction),
|
|
||||||
and
|
|
||||||
[`generalized-bulletproofs-ec-gadgets`](https://docs.rs/generalized-bulletproofs-ec-gadgets)
|
|
||||||
from the Monero project's FCMP++ codebase. These libraries have received the
|
|
||||||
following audits in the past:
|
|
||||||
- https://github.com/kayabaNerve/monero-oxide/tree/fcmp++/audits/generalized-bulletproofs
|
|
||||||
- https://github.com/kayabaNerve/monero-oxide/tree/fcmp++/audits/fcmps
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ rand_core = { version = "0.6", default-features = false }
|
|||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
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, features = ["alloc"] }
|
||||||
|
|
||||||
multiexp = { path = "../../multiexp", version = "0.4", default-features = false }
|
multiexp = { path = "../../multiexp", version = "0.4", default-features = false }
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false }
|
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
@@ -22,7 +22,6 @@ use ciphersuite::{
|
|||||||
|
|
||||||
/// The ID of a participant, defined as a non-zero u16.
|
/// The ID of a participant, defined as a non-zero u16.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Zeroize)]
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))]
|
|
||||||
pub struct Participant(u16);
|
pub struct Participant(u16);
|
||||||
impl Participant {
|
impl Participant {
|
||||||
/// Create a new Participant identifier from a u16.
|
/// Create a new Participant identifier from a u16.
|
||||||
@@ -129,18 +128,8 @@ pub enum DkgError {
|
|||||||
NotParticipating,
|
NotParticipating,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually implements BorshDeserialize so we can enforce it's a valid index
|
|
||||||
#[cfg(feature = "borsh")]
|
|
||||||
impl borsh::BorshDeserialize for Participant {
|
|
||||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
|
||||||
Participant::new(u16::deserialize_reader(reader)?)
|
|
||||||
.ok_or_else(|| io::Error::other("invalid participant"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parameters for a multisig.
|
/// Parameters for a multisig.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))]
|
|
||||||
pub struct ThresholdParams {
|
pub struct ThresholdParams {
|
||||||
/// Participants needed to sign on behalf of the group.
|
/// Participants needed to sign on behalf of the group.
|
||||||
t: u16,
|
t: u16,
|
||||||
@@ -210,16 +199,6 @@ impl ThresholdParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "borsh")]
|
|
||||||
impl borsh::BorshDeserialize for ThresholdParams {
|
|
||||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
|
||||||
let t = u16::deserialize_reader(reader)?;
|
|
||||||
let n = u16::deserialize_reader(reader)?;
|
|
||||||
let i = Participant::deserialize_reader(reader)?;
|
|
||||||
ThresholdParams::new(t, n, i).map_err(|e| io::Error::other(format!("{e:?}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A method of interpolation.
|
/// A method of interpolation.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||||
pub enum Interpolation<F: Zeroize + PrimeField> {
|
pub enum Interpolation<F: Zeroize + PrimeField> {
|
||||||
|
|||||||
@@ -33,6 +33,6 @@ 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", "sha3/alloc", "crypto-bigint/alloc", "prime-field/alloc", "ciphersuite/alloc"]
|
alloc = ["zeroize/alloc", "sha3/alloc", "prime-field/alloc", "ciphersuite/alloc"]
|
||||||
std = ["alloc", "zeroize/std", "prime-field/std", "ciphersuite/std"]
|
std = ["alloc", "zeroize/std", "prime-field/std", "ciphersuite/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
hex-literal = { version = "1", default-features = false }
|
hex-literal = { version = "1", default-features = false }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true }
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false }
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ curve25519-dalek = { version = "4", default-features = false, features = ["legac
|
|||||||
blake2 = { version = "0.11.0-rc.2", default-features = false }
|
blake2 = { version = "0.11.0-rc.2", default-features = false }
|
||||||
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
|
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
|
||||||
|
|
||||||
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false, optional = true }
|
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
@@ -39,6 +39,6 @@ rand_core = { version = "0.6", features = ["std"] }
|
|||||||
ff-group-tests = { path = "../ff-group-tests" }
|
ff-group-tests = { path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["std-shims", "zeroize/alloc", "prime-field/alloc", "short-weierstrass/alloc", "curve25519-dalek/alloc", "blake2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"]
|
alloc = ["zeroize/alloc", "prime-field/alloc", "short-weierstrass/alloc", "curve25519-dalek/alloc", "blake2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"]
|
||||||
std = ["alloc", "std-shims/std", "zeroize/std", "prime-field/std", "short-weierstrass/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"]
|
std = ["alloc", "std-shims/std", "zeroize/std", "prime-field/std", "short-weierstrass/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std_shims::prelude::*;
|
use std_shims::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
/// Tests for the Field trait.
|
/// Tests for the Field trait.
|
||||||
|
|||||||
@@ -17,33 +17,35 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = { version = "2", default-features = false, features = ["std"] }
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
thiserror = { version = "2", default-features = false }
|
||||||
rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] }
|
rand_core = { version = "0.6", default-features = false, features = ["alloc"] }
|
||||||
subtle = { version = "^2.4", default-features = false, features = ["std"] }
|
rand_chacha = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"], optional = true }
|
zeroize = { version = "^1.5", default-features = false, features = ["alloc", "zeroize_derive"] }
|
||||||
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["std", "recommended"] }
|
hex = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
|
||||||
dalek-ff-group = { path = "../dalek-ff-group", version = "0.5", default-features = false, features = ["std"], optional = true }
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
||||||
minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, features = ["std"], optional = true }
|
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["std"] }
|
dalek-ff-group = { path = "../dalek-ff-group", version = "0.5", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
|
||||||
|
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] }
|
||||||
sha2 = { version = "0.10.0", default-features = false, optional = true }
|
sha2 = { version = "0.10.0", default-features = false, optional = true }
|
||||||
elliptic-curve = { version = "0.13", default-features = false, features = ["hash2curve"], optional = true }
|
elliptic-curve = { version = "0.13", default-features = false, features = ["hash2curve"], optional = true }
|
||||||
ciphersuite-kp256 = { path = "../ciphersuite/kp256", version = "0.4", default-features = false, features = ["std"], optional = true }
|
ciphersuite-kp256 = { path = "../ciphersuite/kp256", version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
|
||||||
multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["std", "batch"] }
|
multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["alloc", "batch"] }
|
||||||
|
|
||||||
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1", default-features = false, features = ["std"] }
|
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
dkg = { path = "../dkg", version = "0.6.1", default-features = false, features = ["std"] }
|
dkg = { path = "../dkg", version = "0.6.1", default-features = false }
|
||||||
dkg-recovery = { path = "../dkg/recovery", version = "0.6", default-features = false, features = ["std"], optional = true }
|
dkg-recovery = { path = "../dkg/recovery", version = "0.6", default-features = false, optional = true }
|
||||||
dkg-dealer = { path = "../dkg/dealer", version = "0.6", default-features = false, features = ["std"], optional = true }
|
dkg-dealer = { path = "../dkg/dealer", version = "0.6", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
@@ -54,6 +56,38 @@ dkg-recovery = { path = "../dkg/recovery", default-features = false, features =
|
|||||||
dkg-dealer = { path = "../dkg/dealer", default-features = false, features = ["std"] }
|
dkg-dealer = { path = "../dkg/dealer", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
std = [
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
|
"thiserror/std",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
"rand_chacha/std",
|
||||||
|
|
||||||
|
"zeroize/std",
|
||||||
|
"subtle/std",
|
||||||
|
|
||||||
|
"hex?/std",
|
||||||
|
|
||||||
|
"transcript/std",
|
||||||
|
|
||||||
|
"dalek-ff-group?/std",
|
||||||
|
"minimal-ed448?/std",
|
||||||
|
|
||||||
|
"ciphersuite/std",
|
||||||
|
"sha2?/std",
|
||||||
|
"elliptic-curve?/std",
|
||||||
|
"ciphersuite-kp256?/std",
|
||||||
|
|
||||||
|
"multiexp/std",
|
||||||
|
|
||||||
|
"schnorr/std",
|
||||||
|
|
||||||
|
"dkg/std",
|
||||||
|
"dkg-recovery?/std",
|
||||||
|
"dkg-dealer?/std",
|
||||||
|
]
|
||||||
|
|
||||||
ed25519 = ["dalek-ff-group"]
|
ed25519 = ["dalek-ff-group"]
|
||||||
ristretto = ["dalek-ff-group"]
|
ristretto = ["dalek-ff-group"]
|
||||||
|
|
||||||
@@ -63,3 +97,5 @@ p256 = ["sha2", "elliptic-curve", "ciphersuite-kp256"]
|
|||||||
ed448 = ["minimal-ed448"]
|
ed448 = ["minimal-ed448"]
|
||||||
|
|
||||||
tests = ["hex", "rand_core/getrandom", "dkg-dealer", "dkg-recovery"]
|
tests = ["hex", "rand_core/getrandom", "dkg-dealer", "dkg-recovery"]
|
||||||
|
|
||||||
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use core::{marker::PhantomData, fmt::Debug};
|
use core::{marker::PhantomData, fmt::Debug};
|
||||||
use std::io::{self, Read, Write};
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read, Write};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
@@ -26,8 +28,10 @@ impl<A: Send + Sync + Clone + PartialEq + Debug + WriteAddendum> Addendum for A
|
|||||||
|
|
||||||
/// Algorithm trait usable by the FROST signing machine to produce signatures..
|
/// Algorithm trait usable by the FROST signing machine to produce signatures..
|
||||||
pub trait Algorithm<C: Curve>: Send + Sync {
|
pub trait Algorithm<C: Curve>: Send + Sync {
|
||||||
/// The transcript format this algorithm uses. This likely should NOT be the IETF-compatible
|
/// The transcript format this algorithm uses.
|
||||||
/// transcript included in this crate.
|
///
|
||||||
|
/// This MUST NOT be the IETF-compatible transcript included in this crate UNLESS this is an
|
||||||
|
/// IETF-specified ciphersuite.
|
||||||
type Transcript: Sync + Clone + Debug + Transcript;
|
type Transcript: Sync + Clone + Debug + Transcript;
|
||||||
/// Serializable addendum, used in algorithms requiring more data than just the nonces.
|
/// Serializable addendum, used in algorithms requiring more data than just the nonces.
|
||||||
type Addendum: Addendum;
|
type Addendum: Addendum;
|
||||||
@@ -67,8 +71,10 @@ pub trait Algorithm<C: Curve>: Send + Sync {
|
|||||||
) -> Result<(), FrostError>;
|
) -> Result<(), FrostError>;
|
||||||
|
|
||||||
/// Sign a share with the given secret/nonce.
|
/// Sign a share with the given secret/nonce.
|
||||||
|
///
|
||||||
/// The secret will already have been its lagrange coefficient applied so it is the necessary
|
/// The secret will already have been its lagrange coefficient applied so it is the necessary
|
||||||
/// key share.
|
/// key share.
|
||||||
|
///
|
||||||
/// The nonce will already have been processed into the combined form d + (e * p).
|
/// The nonce will already have been processed into the combined form d + (e * p).
|
||||||
fn sign_share(
|
fn sign_share(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -83,6 +89,7 @@ pub trait Algorithm<C: Curve>: Send + Sync {
|
|||||||
fn verify(&self, group_key: C::G, nonces: &[Vec<C::G>], sum: C::F) -> Option<Self::Signature>;
|
fn verify(&self, group_key: C::G, nonces: &[Vec<C::G>], sum: C::F) -> Option<Self::Signature>;
|
||||||
|
|
||||||
/// Verify a specific share given as a response.
|
/// Verify a specific share given as a response.
|
||||||
|
///
|
||||||
/// This function should return a series of pairs whose products should sum to zero for a valid
|
/// This function should return a series of pairs whose products should sum to zero for a valid
|
||||||
/// share. Any error raised is treated as the share being invalid.
|
/// share. Any error raised is treated as the share being invalid.
|
||||||
#[allow(clippy::type_complexity, clippy::result_unit_err)]
|
#[allow(clippy::type_complexity, clippy::result_unit_err)]
|
||||||
@@ -97,8 +104,10 @@ pub trait Algorithm<C: Curve>: Send + Sync {
|
|||||||
mod sealed {
|
mod sealed {
|
||||||
pub use super::*;
|
pub use super::*;
|
||||||
|
|
||||||
/// IETF-compliant transcript. This is incredibly naive and should not be used within larger
|
/// IETF-compliant transcript.
|
||||||
/// protocols.
|
///
|
||||||
|
/// This is incredibly naive and MUST NOT be used within larger protocols. No guarantees are made
|
||||||
|
/// about its safety EXCEPT as used with the IETF-specified FROST ciphersuites.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct IetfTranscript(pub(crate) Vec<u8>);
|
pub struct IetfTranscript(pub(crate) Vec<u8>);
|
||||||
impl Transcript for IetfTranscript {
|
impl Transcript for IetfTranscript {
|
||||||
@@ -129,6 +138,7 @@ pub(crate) use sealed::IetfTranscript;
|
|||||||
/// HRAm usable by the included Schnorr signature algorithm to generate challenges.
|
/// HRAm usable by the included Schnorr signature algorithm to generate challenges.
|
||||||
pub trait Hram<C: Curve>: Send + Sync + Clone {
|
pub trait Hram<C: Curve>: Send + Sync + Clone {
|
||||||
/// HRAm function to generate a challenge.
|
/// HRAm function to generate a challenge.
|
||||||
|
///
|
||||||
/// H2 from the IETF draft, despite having a different argument set (not being pre-formatted).
|
/// H2 from the IETF draft, despite having a different argument set (not being pre-formatted).
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F;
|
fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use core::{ops::Deref, convert::AsRef};
|
use core::{ops::Deref, convert::AsRef};
|
||||||
use std::io::{self, Read};
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use std::collections::HashMap;
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::collections::HashMap;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
// Each nonce remains of the form (d, e) and made into a proper nonce with d + (e * b)
|
// Each nonce remains of the form (d, e) and made into a proper nonce with d + (e * b)
|
||||||
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::{
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::{
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use core::{ops::Deref, fmt::Debug};
|
use core::{ops::Deref, fmt::Debug};
|
||||||
use std::{
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::{
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
@@ -100,6 +102,7 @@ pub trait PreprocessMachine: Send {
|
|||||||
type SignMachine: SignMachine<Self::Signature, Preprocess = Self::Preprocess>;
|
type SignMachine: SignMachine<Self::Signature, Preprocess = Self::Preprocess>;
|
||||||
|
|
||||||
/// Perform the preprocessing round required in order to sign.
|
/// Perform the preprocessing round required in order to sign.
|
||||||
|
///
|
||||||
/// Returns a preprocess message to be broadcast to all participants, over an authenticated
|
/// Returns a preprocess message to be broadcast to all participants, over an authenticated
|
||||||
/// channel.
|
/// channel.
|
||||||
fn preprocess<R: RngCore + CryptoRng>(self, rng: &mut R)
|
fn preprocess<R: RngCore + CryptoRng>(self, rng: &mut R)
|
||||||
@@ -233,6 +236,8 @@ pub trait SignMachine<S>: Send + Sync + Sized {
|
|||||||
/// Takes in the participants' preprocess messages. Returns the signature share to be broadcast
|
/// Takes in the participants' preprocess messages. Returns the signature share to be broadcast
|
||||||
/// to all participants, over an authenticated channel. The parties who participate here will
|
/// to all participants, over an authenticated channel. The parties who participate here will
|
||||||
/// become the signing set for this session.
|
/// become the signing set for this session.
|
||||||
|
///
|
||||||
|
/// The caller MUST only use preprocesses obtained via this machine's `read_preprocess` function.
|
||||||
fn sign(
|
fn sign(
|
||||||
self,
|
self,
|
||||||
commitments: HashMap<Participant, Self::Preprocess>,
|
commitments: HashMap<Participant, Self::Preprocess>,
|
||||||
@@ -419,7 +424,10 @@ pub trait SignatureMachine<S>: Send + Sync {
|
|||||||
fn read_share<R: Read>(&self, reader: &mut R) -> io::Result<Self::SignatureShare>;
|
fn read_share<R: Read>(&self, reader: &mut R) -> io::Result<Self::SignatureShare>;
|
||||||
|
|
||||||
/// Complete signing.
|
/// Complete signing.
|
||||||
|
///
|
||||||
/// Takes in everyone elses' shares. Returns the signature.
|
/// Takes in everyone elses' shares. Returns the signature.
|
||||||
|
///
|
||||||
|
/// The caller MUST only use shares obtained via this machine's `read_shares` function.
|
||||||
fn complete(self, shares: HashMap<Participant, Self::SignatureShare>) -> Result<S, FrostError>;
|
fn complete(self, shares: HashMap<Participant, Self::SignatureShare>) -> Result<S, FrostError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std_shims::collections::HashMap;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::io::{self, Read};
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std_shims::collections::HashMap;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustversion = "1"
|
|
||||||
|
|
||||||
std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-features = false }
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||||
|
|
||||||
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||||
@@ -35,8 +31,9 @@ k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic"
|
|||||||
dalek-ff-group = { path = "../dalek-ff-group" }
|
dalek-ff-group = { path = "../dalek-ff-group" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = ["std-shims/std", "zeroize/std", "ff/std", "rand_core?/std"]
|
alloc = ["zeroize/alloc"]
|
||||||
|
std = ["alloc", "zeroize/std", "ff/std", "rand_core?/std"]
|
||||||
|
|
||||||
batch = ["rand_core"]
|
batch = ["alloc", "rand_core"]
|
||||||
|
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ culminating in commit
|
|||||||
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06).
|
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06).
|
||||||
Any subsequent changes have not undergone auditing.
|
Any subsequent changes have not undergone auditing.
|
||||||
|
|
||||||
This library is usable under no_std, via alloc, when the default features are
|
This library is usable under no-`std` and no-`alloc`. With the `alloc` feature,
|
||||||
disabled.
|
the library is fully functional. Without the `alloc` feature, the `multiexp`
|
||||||
|
function is shimmed with a serial implementation.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std_shims::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,201 +1,178 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
#[macro_use]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
#[allow(unused_imports)]
|
|
||||||
use std_shims::prelude::*;
|
|
||||||
use std_shims::vec::Vec;
|
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use ff::PrimeFieldBits;
|
use ff::PrimeFieldBits;
|
||||||
use group::Group;
|
use group::Group;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
mod straus;
|
mod straus;
|
||||||
use straus::*;
|
#[cfg(feature = "alloc")]
|
||||||
|
|
||||||
mod pippenger;
|
mod pippenger;
|
||||||
use pippenger::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "batch")]
|
#[cfg(feature = "batch")]
|
||||||
mod batch;
|
mod batch;
|
||||||
#[cfg(feature = "batch")]
|
|
||||||
pub use batch::BatchVerifier;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "alloc"))]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
// Use black_box when possible
|
#[cfg(feature = "alloc")]
|
||||||
#[rustversion::since(1.66)]
|
mod underlying {
|
||||||
use core::hint::black_box;
|
use super::*;
|
||||||
#[rustversion::before(1.66)]
|
|
||||||
fn black_box<T>(val: T) -> T {
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
use core::hint::black_box;
|
||||||
let bit_ref = black_box(bit_ref);
|
use alloc::{vec, vec::Vec};
|
||||||
|
|
||||||
let mut bit = black_box(*bit_ref);
|
pub(crate) use straus::*;
|
||||||
#[allow(clippy::cast_lossless)]
|
|
||||||
let res = black_box(bit as u8);
|
|
||||||
bit.zeroize();
|
|
||||||
debug_assert!((res | 1) == 1);
|
|
||||||
|
|
||||||
bit_ref.zeroize();
|
pub(crate) use pippenger::*;
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert scalars to `window`-sized bit groups, as needed to index a table
|
#[cfg(feature = "batch")]
|
||||||
// This algorithm works for `window <= 8`
|
pub use batch::BatchVerifier;
|
||||||
pub(crate) fn prep_bits<G: Group<Scalar: PrimeFieldBits>>(
|
|
||||||
pairs: &[(G::Scalar, G)],
|
|
||||||
window: u8,
|
|
||||||
) -> Vec<Vec<u8>> {
|
|
||||||
let w_usize = usize::from(window);
|
|
||||||
|
|
||||||
let mut groupings = vec![];
|
fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||||
for pair in pairs {
|
let bit_ref = black_box(bit_ref);
|
||||||
let p = groupings.len();
|
|
||||||
let mut bits = pair.0.to_le_bits();
|
|
||||||
groupings.push(vec![0; bits.len().div_ceil(w_usize)]);
|
|
||||||
|
|
||||||
for (i, mut bit) in bits.iter_mut().enumerate() {
|
let mut bit = black_box(*bit_ref);
|
||||||
let mut bit = u8_from_bool(&mut bit);
|
#[allow(clippy::cast_lossless)]
|
||||||
groupings[p][i / w_usize] |= bit << (i % w_usize);
|
let res = black_box(bit as u8);
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
|
debug_assert!((res | 1) == 1);
|
||||||
|
|
||||||
|
bit_ref.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert scalars to `window`-sized bit groups, as needed to index a table
|
||||||
|
// This algorithm works for `window <= 8`
|
||||||
|
pub(crate) fn prep_bits<G: Group<Scalar: PrimeFieldBits>>(
|
||||||
|
pairs: &[(G::Scalar, G)],
|
||||||
|
window: u8,
|
||||||
|
) -> Vec<Vec<u8>> {
|
||||||
|
let w_usize = usize::from(window);
|
||||||
|
|
||||||
|
let mut groupings = vec![];
|
||||||
|
for pair in pairs {
|
||||||
|
let p = groupings.len();
|
||||||
|
let mut bits = pair.0.to_le_bits();
|
||||||
|
groupings.push(vec![0; bits.len().div_ceil(w_usize)]);
|
||||||
|
|
||||||
|
for (i, mut bit) in bits.iter_mut().enumerate() {
|
||||||
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
|
groupings[p][i / w_usize] |= bit << (i % w_usize);
|
||||||
|
bit.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupings
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
enum Algorithm {
|
||||||
|
Null,
|
||||||
|
Single,
|
||||||
|
Straus(u8),
|
||||||
|
Pippenger(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are 'rule of thumb's obtained via benchmarking `k256` and `curve25519-dalek`
|
||||||
|
fn algorithm(len: usize) -> Algorithm {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
if len == 0 {
|
||||||
|
Algorithm::Null
|
||||||
|
} else if len == 1 {
|
||||||
|
Algorithm::Single
|
||||||
|
} else if len < 10 {
|
||||||
|
// Straus 2 never showed a performance benefit, even with just 2 elements
|
||||||
|
Algorithm::Straus(3)
|
||||||
|
} else if len < 20 {
|
||||||
|
Algorithm::Straus(4)
|
||||||
|
} else if len < 50 {
|
||||||
|
Algorithm::Straus(5)
|
||||||
|
} else if len < 100 {
|
||||||
|
Algorithm::Pippenger(4)
|
||||||
|
} else if len < 125 {
|
||||||
|
Algorithm::Pippenger(5)
|
||||||
|
} else if len < 275 {
|
||||||
|
Algorithm::Pippenger(6)
|
||||||
|
} else if len < 400 {
|
||||||
|
Algorithm::Pippenger(7)
|
||||||
|
} else {
|
||||||
|
Algorithm::Pippenger(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if len == 0 {
|
||||||
|
Algorithm::Null
|
||||||
|
} else if len == 1 {
|
||||||
|
Algorithm::Single
|
||||||
|
} else if len < 10 {
|
||||||
|
Algorithm::Straus(3)
|
||||||
|
} else if len < 80 {
|
||||||
|
Algorithm::Straus(4)
|
||||||
|
} else if len < 100 {
|
||||||
|
Algorithm::Straus(5)
|
||||||
|
} else if len < 125 {
|
||||||
|
Algorithm::Pippenger(4)
|
||||||
|
} else if len < 275 {
|
||||||
|
Algorithm::Pippenger(5)
|
||||||
|
} else if len < 475 {
|
||||||
|
Algorithm::Pippenger(6)
|
||||||
|
} else if len < 750 {
|
||||||
|
Algorithm::Pippenger(7)
|
||||||
|
} else {
|
||||||
|
Algorithm::Pippenger(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
groupings
|
/// Performs a multiexponentiation, automatically selecting the optimal algorithm based on the
|
||||||
}
|
/// amount of pairs.
|
||||||
|
pub fn multiexp<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
pairs: &[(G::Scalar, G)],
|
||||||
enum Algorithm {
|
) -> G {
|
||||||
Null,
|
match algorithm(pairs.len()) {
|
||||||
Single,
|
Algorithm::Null => Group::identity(),
|
||||||
Straus(u8),
|
Algorithm::Single => pairs[0].1 * pairs[0].0,
|
||||||
Pippenger(u8),
|
// These functions panic if called without any pairs
|
||||||
}
|
Algorithm::Straus(window) => straus(pairs, window),
|
||||||
|
Algorithm::Pippenger(window) => pippenger(pairs, window),
|
||||||
/*
|
}
|
||||||
Release (with runs 20, so all of these are off by 20x):
|
|
||||||
|
|
||||||
k256
|
|
||||||
Straus 3 is more efficient at 5 with 678µs per
|
|
||||||
Straus 4 is more efficient at 10 with 530µs per
|
|
||||||
Straus 5 is more efficient at 35 with 467µs per
|
|
||||||
|
|
||||||
Pippenger 5 is more efficient at 125 with 431µs per
|
|
||||||
Pippenger 6 is more efficient at 275 with 349µs per
|
|
||||||
Pippenger 7 is more efficient at 375 with 360µs per
|
|
||||||
|
|
||||||
dalek
|
|
||||||
Straus 3 is more efficient at 5 with 519µs per
|
|
||||||
Straus 4 is more efficient at 10 with 376µs per
|
|
||||||
Straus 5 is more efficient at 170 with 330µs per
|
|
||||||
|
|
||||||
Pippenger 5 is more efficient at 125 with 305µs per
|
|
||||||
Pippenger 6 is more efficient at 275 with 250µs per
|
|
||||||
Pippenger 7 is more efficient at 450 with 205µs per
|
|
||||||
Pippenger 8 is more efficient at 800 with 213µs per
|
|
||||||
|
|
||||||
Debug (with runs 5, so...):
|
|
||||||
|
|
||||||
k256
|
|
||||||
Straus 3 is more efficient at 5 with 2532µs per
|
|
||||||
Straus 4 is more efficient at 10 with 1930µs per
|
|
||||||
Straus 5 is more efficient at 80 with 1632µs per
|
|
||||||
|
|
||||||
Pippenger 5 is more efficient at 150 with 1441µs per
|
|
||||||
Pippenger 6 is more efficient at 300 with 1235µs per
|
|
||||||
Pippenger 7 is more efficient at 475 with 1182µs per
|
|
||||||
Pippenger 8 is more efficient at 625 with 1170µs per
|
|
||||||
|
|
||||||
dalek:
|
|
||||||
Straus 3 is more efficient at 5 with 971µs per
|
|
||||||
Straus 4 is more efficient at 10 with 782µs per
|
|
||||||
Straus 5 is more efficient at 75 with 778µs per
|
|
||||||
Straus 6 is more efficient at 165 with 867µs per
|
|
||||||
|
|
||||||
Pippenger 5 is more efficient at 125 with 677µs per
|
|
||||||
Pippenger 6 is more efficient at 250 with 655µs per
|
|
||||||
Pippenger 7 is more efficient at 475 with 500µs per
|
|
||||||
Pippenger 8 is more efficient at 875 with 499µs per
|
|
||||||
*/
|
|
||||||
fn algorithm(len: usize) -> Algorithm {
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
if len == 0 {
|
|
||||||
Algorithm::Null
|
|
||||||
} else if len == 1 {
|
|
||||||
Algorithm::Single
|
|
||||||
} else if len < 10 {
|
|
||||||
// Straus 2 never showed a performance benefit, even with just 2 elements
|
|
||||||
Algorithm::Straus(3)
|
|
||||||
} else if len < 20 {
|
|
||||||
Algorithm::Straus(4)
|
|
||||||
} else if len < 50 {
|
|
||||||
Algorithm::Straus(5)
|
|
||||||
} else if len < 100 {
|
|
||||||
Algorithm::Pippenger(4)
|
|
||||||
} else if len < 125 {
|
|
||||||
Algorithm::Pippenger(5)
|
|
||||||
} else if len < 275 {
|
|
||||||
Algorithm::Pippenger(6)
|
|
||||||
} else if len < 400 {
|
|
||||||
Algorithm::Pippenger(7)
|
|
||||||
} else {
|
|
||||||
Algorithm::Pippenger(8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
/// Performs a multiexponentiation in variable time, automatically selecting the optimal algorithm
|
||||||
if len == 0 {
|
/// based on the amount of pairs.
|
||||||
Algorithm::Null
|
pub fn multiexp_vartime<G: Group<Scalar: PrimeFieldBits>>(pairs: &[(G::Scalar, G)]) -> G {
|
||||||
} else if len == 1 {
|
match algorithm(pairs.len()) {
|
||||||
Algorithm::Single
|
Algorithm::Null => Group::identity(),
|
||||||
} else if len < 10 {
|
Algorithm::Single => pairs[0].1 * pairs[0].0,
|
||||||
Algorithm::Straus(3)
|
Algorithm::Straus(window) => straus_vartime(pairs, window),
|
||||||
} else if len < 80 {
|
Algorithm::Pippenger(window) => pippenger_vartime(pairs, window),
|
||||||
Algorithm::Straus(4)
|
}
|
||||||
} else if len < 100 {
|
|
||||||
Algorithm::Straus(5)
|
|
||||||
} else if len < 125 {
|
|
||||||
Algorithm::Pippenger(4)
|
|
||||||
} else if len < 275 {
|
|
||||||
Algorithm::Pippenger(5)
|
|
||||||
} else if len < 475 {
|
|
||||||
Algorithm::Pippenger(6)
|
|
||||||
} else if len < 750 {
|
|
||||||
Algorithm::Pippenger(7)
|
|
||||||
} else {
|
|
||||||
Algorithm::Pippenger(8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a multiexponentiation, automatically selecting the optimal algorithm based on the
|
#[cfg(not(feature = "alloc"))]
|
||||||
/// amount of pairs.
|
mod underlying {
|
||||||
pub fn multiexp<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(
|
use super::*;
|
||||||
pairs: &[(G::Scalar, G)],
|
|
||||||
) -> G {
|
/// Performs a multiexponentiation, automatically selecting the optimal algorithm based on the
|
||||||
match algorithm(pairs.len()) {
|
/// amount of pairs.
|
||||||
Algorithm::Null => Group::identity(),
|
pub fn multiexp<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(
|
||||||
Algorithm::Single => pairs[0].1 * pairs[0].0,
|
pairs: &[(G::Scalar, G)],
|
||||||
// These functions panic if called without any pairs
|
) -> G {
|
||||||
Algorithm::Straus(window) => straus(pairs, window),
|
pairs.iter().map(|(scalar, point)| *point * scalar).sum()
|
||||||
Algorithm::Pippenger(window) => pippenger(pairs, window),
|
}
|
||||||
|
|
||||||
|
/// Performs a multiexponentiation in variable time, automatically selecting the optimal algorithm
|
||||||
|
/// based on the amount of pairs.
|
||||||
|
pub fn multiexp_vartime<G: Group<Scalar: PrimeFieldBits>>(pairs: &[(G::Scalar, G)]) -> G {
|
||||||
|
pairs.iter().map(|(scalar, point)| *point * scalar).sum()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a multiexponentiation in variable time, automatically selecting the optimal algorithm
|
pub use underlying::*;
|
||||||
/// based on the amount of pairs.
|
|
||||||
pub fn multiexp_vartime<G: Group<Scalar: PrimeFieldBits>>(pairs: &[(G::Scalar, G)]) -> G {
|
|
||||||
match algorithm(pairs.len()) {
|
|
||||||
Algorithm::Null => Group::identity(),
|
|
||||||
Algorithm::Single => pairs[0].1 * pairs[0].0,
|
|
||||||
Algorithm::Straus(window) => straus_vartime(pairs, window),
|
|
||||||
Algorithm::Pippenger(window) => pippenger_vartime(pairs, window),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use alloc::vec;
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use ff::PrimeFieldBits;
|
use ff::PrimeFieldBits;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std_shims::vec::Vec;
|
use alloc::{vec, vec::Vec};
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ ff = { version = "0.13", default-features = false, features = ["bits"] }
|
|||||||
ff-group-tests = { version = "0.13", path = "../ff-group-tests", optional = true }
|
ff-group-tests = { version = "0.13", path = "../ff-group-tests", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["zeroize/alloc", "crypto-bigint/alloc", "ff/alloc"]
|
alloc = ["zeroize/alloc", "ff/alloc"]
|
||||||
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "ff/std", "ff-group-tests"]
|
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "ff/std", "ff-group-tests"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ digest = { version = "0.11.0-rc.1", default-features = false, features = ["block
|
|||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, optional = true }
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, optional = true }
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] }
|
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false }
|
||||||
multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["batch"] }
|
multiexp = { path = "../multiexp", version = "0.4", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
@@ -40,6 +40,7 @@ dalek-ff-group = { path = "../dalek-ff-group" }
|
|||||||
ciphersuite = { path = "../ciphersuite" }
|
ciphersuite = { path = "../ciphersuite" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
aggregate = ["transcript"]
|
alloc = ["zeroize/alloc", "digest/alloc", "ciphersuite/alloc", "multiexp/alloc", "multiexp/batch"]
|
||||||
std = ["std-shims/std", "rand_core/std", "zeroize/std", "transcript?/std", "ciphersuite/std", "multiexp/std"]
|
aggregate = ["alloc", "transcript"]
|
||||||
|
std = ["alloc", "std-shims/std", "rand_core/std", "zeroize/std", "transcript?/std", "ciphersuite/std", "multiexp/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
#[macro_use]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use std_shims::{
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
vec::Vec,
|
use alloc::vec::Vec;
|
||||||
io::{self, Read, Write},
|
#[allow(unused_imports)]
|
||||||
};
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read, Write};
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
@@ -22,7 +23,9 @@ use ciphersuite::{
|
|||||||
},
|
},
|
||||||
GroupIo,
|
GroupIo,
|
||||||
};
|
};
|
||||||
use multiexp::{multiexp_vartime, BatchVerifier};
|
use multiexp::multiexp_vartime;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use multiexp::BatchVerifier;
|
||||||
|
|
||||||
/// Half-aggregation from <https://eprint.iacr.org/2021/350>.
|
/// Half-aggregation from <https://eprint.iacr.org/2021/350>.
|
||||||
#[cfg(feature = "aggregate")]
|
#[cfg(feature = "aggregate")]
|
||||||
@@ -59,6 +62,7 @@ impl<C: GroupIo> SchnorrSignature<C> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a SchnorrSignature, returning a `Vec<u8>`.
|
/// Serialize a SchnorrSignature, returning a `Vec<u8>`.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
self.write(&mut buf).unwrap();
|
self.write(&mut buf).unwrap();
|
||||||
@@ -114,6 +118,7 @@ impl<C: GroupIo> SchnorrSignature<C> {
|
|||||||
/// This challenge must be properly crafted, which means being binding to the public key, nonce,
|
/// This challenge must be properly crafted, which means being binding to the public key, nonce,
|
||||||
/// and any message. Failure to do so will let a malicious adversary to forge signatures for
|
/// and any message. Failure to do so will let a malicious adversary to forge signatures for
|
||||||
/// different keys/messages.
|
/// different keys/messages.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub fn batch_verify<R: RngCore + CryptoRng, I: Copy + Zeroize>(
|
pub fn batch_verify<R: RngCore + CryptoRng, I: Copy + Zeroize>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
|
|||||||
@@ -17,18 +17,35 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand_core = "0.6"
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||||
zeroize = "^1.5"
|
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", features = ["merlin"] }
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
zeroize = { version = "1.5", default-features = false, features = ["zeroize_derive", "alloc"] }
|
||||||
|
|
||||||
group = "0.13"
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "0.3.2", default-features = false, features = ["merlin"] }
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", features = ["std"] }
|
ciphersuite = { path = "../ciphersuite", version = "0.4.1", default-features = false, features = ["alloc"] }
|
||||||
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1" }
|
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "0.5.1", default-features = false, features = ["alloc"] }
|
||||||
frost = { path = "../frost", package = "modular-frost", version = "0.11.0", features = ["ristretto"] }
|
frost = { path = "../frost", package = "modular-frost", version = "0.11.0", default-features = false, features = ["ristretto"] }
|
||||||
|
|
||||||
schnorrkel = { version = "0.11" }
|
schnorrkel = { version = "0.11", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
frost = { path = "../frost", package = "modular-frost", features = ["tests"] }
|
frost = { path = "../frost", package = "modular-frost", features = ["tests"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
"zeroize/std",
|
||||||
|
|
||||||
|
"transcript/std",
|
||||||
|
|
||||||
|
"ciphersuite/std",
|
||||||
|
"schnorr/std",
|
||||||
|
"frost/std",
|
||||||
|
|
||||||
|
"schnorrkel/std",
|
||||||
|
]
|
||||||
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use std::io::{self, Read};
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use group::GroupEncoding;
|
use ciphersuite::group::GroupEncoding;
|
||||||
use frost::{
|
use frost::{
|
||||||
Participant,
|
Participant,
|
||||||
tests::{key_gen, algorithm_machines, sign},
|
tests::{key_gen, algorithm_machines, sign},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
hex-literal = { version = "1", default-features = false }
|
hex-literal = { version = "1", default-features = false }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true }
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false }
|
||||||
|
|
||||||
sha2 = { version = "0.11.0-rc.0", default-features = false }
|
sha2 = { version = "0.11.0-rc.0", default-features = false }
|
||||||
k256 = { version = "0.13", default-features = false, features = ["arithmetic", "expose-field"] }
|
k256 = { version = "0.13", default-features = false, features = ["arithmetic", "expose-field"] }
|
||||||
@@ -24,7 +24,7 @@ prime-field = { path = "../prime-field", default-features = false }
|
|||||||
short-weierstrass = { path = "../short-weierstrass", default-features = false }
|
short-weierstrass = { path = "../short-weierstrass", default-features = false }
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
|
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
|
||||||
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false, optional = true }
|
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
@@ -34,6 +34,6 @@ rand_core = { version = "0.6", features = ["std"] }
|
|||||||
ff-group-tests = { path = "../ff-group-tests" }
|
ff-group-tests = { path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["std-shims", "k256/alloc", "prime-field/alloc", "short-weierstrass/alloc", "sha2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"]
|
alloc = ["k256/alloc", "prime-field/alloc", "short-weierstrass/alloc", "sha2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"]
|
||||||
std = ["alloc", "std-shims/std", "k256/std", "prime-field/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"]
|
std = ["alloc", "std-shims/std", "k256/std", "prime-field/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std_shims::prelude::*;
|
use std_shims::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ rand_core = { version = "0.6", default-features = false }
|
|||||||
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 }
|
||||||
|
|
||||||
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false, optional = true }
|
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "dc1b3dbe436aae61ec363505052d4715d38ce1df", default-features = false, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc", "ec-divisors"]
|
alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc", "ec-divisors"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
|||||||
124
deny.toml
124
deny.toml
@@ -7,11 +7,10 @@ db-urls = ["https://github.com/rustsec/advisory-db"]
|
|||||||
yanked = "deny"
|
yanked = "deny"
|
||||||
|
|
||||||
ignore = [
|
ignore = [
|
||||||
"RUSTSEC-2020-0168", # mach is unmaintained
|
|
||||||
"RUSTSEC-2021-0139", # https://github.com/serai-dex/serai/228
|
|
||||||
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
|
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
|
||||||
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
|
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
|
||||||
"RUSTSEC-2024-0384", # instant is unmaintained
|
"RUSTSEC-2024-0436", # paste is unmaintained
|
||||||
|
"RUSTSEC-2025-0057", # fxhash is unmaintained, fixed with bytecodealliance/wasmtime/pull/11634
|
||||||
]
|
]
|
||||||
|
|
||||||
[licenses]
|
[licenses]
|
||||||
@@ -30,83 +29,86 @@ allow = [
|
|||||||
"ISC",
|
"ISC",
|
||||||
"Zlib",
|
"Zlib",
|
||||||
"Unicode-3.0",
|
"Unicode-3.0",
|
||||||
"OpenSSL",
|
# "OpenSSL", # Commented as it's not currently in-use within the Serai tree
|
||||||
|
"CDLA-Permissive-2.0",
|
||||||
|
|
||||||
# Non-invasive copyleft
|
# Non-invasive copyleft
|
||||||
"MPL-2.0",
|
# "MPL-2.0", # Commented as it's not currently in-use within the Serai tree
|
||||||
"Apache-2.0",
|
"Apache-2.0",
|
||||||
"Apache-2.0 WITH LLVM-exception",
|
"Apache-2.0 WITH LLVM-exception",
|
||||||
"GPL-3.0 WITH Classpath-exception-2.0",
|
"GPL-3.0-or-later WITH Classpath-exception-2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
exceptions = [
|
exceptions = [
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-env" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-env" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-task" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-task" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "ethereum-schnorr-contract" },
|
{ allow = ["AGPL-3.0-only"], name = "ethereum-schnorr-contract" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-ethereum-relayer" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-ethereum-relayer" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-message-queue" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-message-queue" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-messages" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-messages" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-primitives" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-primitives" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-key-gen" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-key-gen" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-frost-attempt-manager" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-frost-attempt-manager" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-scanner" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-scanner" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-scheduler-primitives" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-scheduler-primitives" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-utxo-scheduler-primitives" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-utxo-scheduler-primitives" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-utxo-scheduler" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-utxo-scheduler" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-transaction-chaining-scheduler" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-transaction-chaining-scheduler" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-smart-contract-scheduler" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-smart-contract-scheduler" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-signers" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-signers" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-bitcoin-processor" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-bitcoin-processor" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-bin" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-bin" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-primitives" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-ethereum-primitives" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-ethereum-test-primitives" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-ethereum-test-primitives" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-deployer" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-ethereum-deployer" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-router" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-ethereum-router" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-erc20" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-ethereum-erc20" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-ethereum-processor" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-ethereum-processor" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-monero-processor" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-monero-processor" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "tributary-sdk" },
|
{ allow = ["AGPL-3.0-only"], name = "tributary-sdk" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-cosign" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-cosign" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coordinator-substrate" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-substrate" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coordinator-tributary" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-tributary" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coordinator-p2p" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-p2p" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coordinator-libp2p-p2p" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-libp2p-p2p" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coordinator" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coins-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "pallet-session" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-dex-pallet" },
|
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-genesis-liquidity-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coins-pallet" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-emissions-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-dex-pallet" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-economic-security-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-genesis-liquidity-pallet" },
|
||||||
|
{ allow = ["AGPL-3.0-only"], name = "serai-emissions-pallet" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-in-instructions-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-economic-security-pallet" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-validator-sets-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-in-instructions-pallet" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-signals-pallet" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-validator-sets-pallet" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-runtime" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-signals-pallet" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-node" },
|
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-orchestrator" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-runtime" },
|
||||||
|
{ allow = ["AGPL-3.0-only"], name = "serai-node" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "mini-serai" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-orchestrator" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-docker-tests" },
|
{ allow = ["AGPL-3.0-only"], name = "mini-serai" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-message-queue-tests" },
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor-tests" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-docker-tests" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-coordinator-tests" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-message-queue-tests" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-full-stack-tests" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-processor-tests" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-reproducible-runtime-tests" },
|
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-tests" },
|
||||||
|
{ allow = ["AGPL-3.0-only"], name = "serai-full-stack-tests" },
|
||||||
|
{ allow = ["AGPL-3.0-only"], name = "serai-reproducible-runtime-tests" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[licenses.clarify]]
|
[[licenses.clarify]]
|
||||||
@@ -124,6 +126,10 @@ highlight = "all"
|
|||||||
deny = [
|
deny = [
|
||||||
{ name = "serde_derive", version = ">=1.0.172, <1.0.185" },
|
{ name = "serde_derive", version = ">=1.0.172, <1.0.185" },
|
||||||
{ name = "hashbrown", version = "=0.15.0" },
|
{ name = "hashbrown", version = "=0.15.0" },
|
||||||
|
# Legacy which _no one_ should use anymore
|
||||||
|
{ name = "is-terminal", version = "*" },
|
||||||
|
# Stop introduction into the tree without realizing it
|
||||||
|
{ name = "once_cell_polyfill", version = "*" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[sources]
|
[sources]
|
||||||
@@ -132,10 +138,10 @@ unknown-git = "deny"
|
|||||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||||
allow-git = [
|
allow-git = [
|
||||||
"https://github.com/rust-lang-nursery/lazy-static.rs",
|
"https://github.com/rust-lang-nursery/lazy-static.rs",
|
||||||
"https://github.com/kayabaNerve/hybrid-array",
|
|
||||||
"https://github.com/kayabaNerve/elliptic-curves",
|
"https://github.com/kayabaNerve/elliptic-curves",
|
||||||
"https://github.com/monero-oxide/monero-oxide",
|
"https://github.com/monero-oxide/monero-oxide",
|
||||||
"https://github.com/serai-dex/substrate-bip39",
|
"https://github.com/kayabaNerve/monero-oxide",
|
||||||
"https://github.com/serai-dex/substrate",
|
"https://github.com/rust-bitcoin/rust-bip39",
|
||||||
|
"https://github.com/rust-rocksdb/rust-rocksdb",
|
||||||
"https://github.com/serai-dex/patch-polkadot-sdk",
|
"https://github.com/serai-dex/patch-polkadot-sdk",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -238,8 +238,7 @@ async fn main() {
|
|||||||
// TODO: Add a magic value with a key at the start of the connection to make this authed
|
// TODO: Add a magic value with a key at the start of the connection to make this authed
|
||||||
let mut db = db.clone();
|
let mut db = db.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
while let Ok(msg_len) = socket.read_u32_le().await {
|
||||||
let Ok(msg_len) = socket.read_u32_le().await else { break };
|
|
||||||
let mut buf = vec![0; usize::try_from(msg_len).unwrap()];
|
let mut buf = vec![0; usize::try_from(msg_len).unwrap()];
|
||||||
let Ok(_) = socket.read_exact(&mut buf).await else { break };
|
let Ok(_) = socket.read_exact(&mut buf).await else { break };
|
||||||
let msg = borsh::from_slice(&buf).unwrap();
|
let msg = borsh::from_slice(&buf).unwrap();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
std-shims = { version = "0.1.1", path = "../../common/std-shims", default-features = false }
|
std-shims = { version = "0.1.1", path = "../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
|
||||||
@@ -27,12 +27,12 @@ rand_core = { version = "0.6", default-features = false }
|
|||||||
bitcoin = { version = "0.32", default-features = false }
|
bitcoin = { version = "0.32", default-features = false }
|
||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
||||||
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.11", default-features = false, features = ["secp256k1"], optional = true }
|
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.11", default-features = false, features = ["secp256k1"] }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, optional = true }
|
hex = { version = "0.4", default-features = false, optional = true }
|
||||||
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
|
core-json-traits = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
serde_json = { version = "1", default-features = false, optional = true }
|
core-json-derive = { version = "0.4", default-features = false, optional = true }
|
||||||
simple-request = { path = "../../common/request", version = "0.1", default-features = false, features = ["tls", "basic-auth"], optional = true }
|
simple-request = { path = "../../common/request", version = "0.3", default-features = false, features = ["tokio", "tls", "basic-auth"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
secp256k1 = { version = "0.29", default-features = false, features = ["std"] }
|
secp256k1 = { version = "0.29", default-features = false, features = ["std"] }
|
||||||
@@ -52,15 +52,16 @@ std = [
|
|||||||
"rand_core/std",
|
"rand_core/std",
|
||||||
|
|
||||||
"bitcoin/std",
|
"bitcoin/std",
|
||||||
"bitcoin/serde",
|
|
||||||
|
|
||||||
"k256/std",
|
"k256/std",
|
||||||
"frost",
|
"frost/std",
|
||||||
|
]
|
||||||
|
rpc = [
|
||||||
|
"std",
|
||||||
"hex/std",
|
"hex/std",
|
||||||
"serde/std",
|
"core-json-traits",
|
||||||
"serde_json/std",
|
"core-json-derive",
|
||||||
"simple-request",
|
"simple-request",
|
||||||
]
|
]
|
||||||
hazmat = []
|
hazmat = []
|
||||||
default = ["std"]
|
default = ["std", "rpc"]
|
||||||
|
|||||||
@@ -1,9 +1,27 @@
|
|||||||
#[cfg(feature = "std")]
|
use core::fmt::Debug;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io;
|
||||||
|
|
||||||
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable};
|
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable};
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use k256::{elliptic_curve::sec1::ToEncodedPoint, ProjectivePoint};
|
use k256::{
|
||||||
|
elliptic_curve::{ops::Reduce, sec1::ToEncodedPoint},
|
||||||
|
U256, Scalar, ProjectivePoint,
|
||||||
|
};
|
||||||
|
|
||||||
use bitcoin::key::XOnlyPublicKey;
|
use bitcoin::{
|
||||||
|
hashes::{HashEngine, Hash, sha256::Hash as Sha256},
|
||||||
|
key::XOnlyPublicKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
use frost::{
|
||||||
|
curve::{WrappedGroup, Secp256k1},
|
||||||
|
Participant, ThresholdKeys, ThresholdView, FrostError,
|
||||||
|
algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr},
|
||||||
|
};
|
||||||
|
|
||||||
/// Get the x coordinate of a non-infinity point.
|
/// Get the x coordinate of a non-infinity point.
|
||||||
///
|
///
|
||||||
@@ -21,142 +39,118 @@ pub(crate) fn x_only(key: &ProjectivePoint) -> XOnlyPublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return if a point must be negated to have an even Y coordinate and be eligible for use.
|
/// Return if a point must be negated to have an even Y coordinate and be eligible for use.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub(crate) fn needs_negation(key: &ProjectivePoint) -> Choice {
|
pub(crate) fn needs_negation(key: &ProjectivePoint) -> Choice {
|
||||||
use k256::elliptic_curve::sec1::Tag;
|
use k256::elliptic_curve::sec1::Tag;
|
||||||
u8::from(key.to_encoded_point(true).tag()).ct_eq(&u8::from(Tag::CompressedOddY))
|
u8::from(key.to_encoded_point(true).tag()).ct_eq(&u8::from(Tag::CompressedOddY))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
|
||||||
mod frost_crypto {
|
///
|
||||||
use core::fmt::Debug;
|
/// If passed an odd nonce, the challenge will be negated.
|
||||||
use std_shims::{vec::Vec, io};
|
///
|
||||||
|
/// If either `R` or `A` is the point at infinity, this will panic.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Hram;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl HramTrait<Secp256k1> for Hram {
|
||||||
|
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
||||||
|
const TAG_HASH: Sha256 = Sha256::const_hash(b"BIP0340/challenge");
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
let mut data = Sha256::engine();
|
||||||
use rand_core::{RngCore, CryptoRng};
|
data.input(TAG_HASH.as_ref());
|
||||||
|
data.input(TAG_HASH.as_ref());
|
||||||
|
data.input(&x(R));
|
||||||
|
data.input(&x(A));
|
||||||
|
data.input(m);
|
||||||
|
|
||||||
use bitcoin::hashes::{HashEngine, Hash, sha256::Hash as Sha256};
|
let c = Scalar::reduce(U256::from_be_slice(Sha256::from_engine(data).as_ref()));
|
||||||
|
// If the nonce was odd, sign `r - cx` instead of `r + cx`, allowing us to negate `s` at the
|
||||||
use k256::{elliptic_curve::ops::Reduce, U256, Scalar};
|
// end to sign as `-r + cx`
|
||||||
|
<_>::conditional_select(&c, &-c, needs_negation(R))
|
||||||
use frost::{
|
}
|
||||||
curve::{WrappedGroup, Secp256k1},
|
}
|
||||||
Participant, ThresholdKeys, ThresholdView, FrostError,
|
|
||||||
algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr},
|
/// BIP-340 Schnorr signature algorithm.
|
||||||
};
|
///
|
||||||
|
/// This may panic if called with nonces/a group key which are the point at infinity (which have
|
||||||
use super::*;
|
/// a negligible probability for a well-reasoned caller, even with malicious participants
|
||||||
|
/// present).
|
||||||
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
|
///
|
||||||
///
|
/// `verify`, `verify_share` MUST be called after `sign_share` is called. Otherwise, this library
|
||||||
/// If passed an odd nonce, the challenge will be negated.
|
/// MAY panic.
|
||||||
///
|
#[derive(Clone)]
|
||||||
/// If either `R` or `A` is the point at infinity, this will panic.
|
pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>);
|
||||||
#[derive(Clone, Copy, Debug)]
|
impl Schnorr {
|
||||||
pub struct Hram;
|
/// Construct a Schnorr algorithm continuing the specified transcript.
|
||||||
#[allow(non_snake_case)]
|
#[allow(clippy::new_without_default)]
|
||||||
impl HramTrait<Secp256k1> for Hram {
|
pub fn new() -> Schnorr {
|
||||||
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
Schnorr(FrostSchnorr::ietf())
|
||||||
const TAG_HASH: Sha256 = Sha256::const_hash(b"BIP0340/challenge");
|
}
|
||||||
|
}
|
||||||
let mut data = Sha256::engine();
|
|
||||||
data.input(TAG_HASH.as_ref());
|
impl Algorithm<Secp256k1> for Schnorr {
|
||||||
data.input(TAG_HASH.as_ref());
|
type Transcript = <FrostSchnorr<Secp256k1, Hram> as Algorithm<Secp256k1>>::Transcript;
|
||||||
data.input(&x(R));
|
type Addendum = ();
|
||||||
data.input(&x(A));
|
type Signature = [u8; 64];
|
||||||
data.input(m);
|
|
||||||
|
fn transcript(&mut self) -> &mut Self::Transcript {
|
||||||
let c = Scalar::reduce(U256::from_be_slice(Sha256::from_engine(data).as_ref()));
|
self.0.transcript()
|
||||||
// If the nonce was odd, sign `r - cx` instead of `r + cx`, allowing us to negate `s` at the
|
}
|
||||||
// end to sign as `-r + cx`
|
|
||||||
<_>::conditional_select(&c, &-c, needs_negation(R))
|
fn nonces(&self) -> Vec<Vec<ProjectivePoint>> {
|
||||||
}
|
self.0.nonces()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// BIP-340 Schnorr signature algorithm.
|
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
||||||
///
|
&mut self,
|
||||||
/// This may panic if called with nonces/a group key which are the point at infinity (which have
|
rng: &mut R,
|
||||||
/// a negligible probability for a well-reasoned caller, even with malicious participants
|
keys: &ThresholdKeys<Secp256k1>,
|
||||||
/// present).
|
) {
|
||||||
///
|
self.0.preprocess_addendum(rng, keys)
|
||||||
/// `verify`, `verify_share` MUST be called after `sign_share` is called. Otherwise, this library
|
}
|
||||||
/// MAY panic.
|
|
||||||
#[derive(Clone)]
|
fn read_addendum<R: io::Read>(&self, reader: &mut R) -> io::Result<Self::Addendum> {
|
||||||
pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>);
|
self.0.read_addendum(reader)
|
||||||
impl Schnorr {
|
}
|
||||||
/// Construct a Schnorr algorithm continuing the specified transcript.
|
|
||||||
#[allow(clippy::new_without_default)]
|
fn process_addendum(
|
||||||
pub fn new() -> Schnorr {
|
&mut self,
|
||||||
Schnorr(FrostSchnorr::ietf())
|
view: &ThresholdView<Secp256k1>,
|
||||||
}
|
i: Participant,
|
||||||
}
|
addendum: (),
|
||||||
|
) -> Result<(), FrostError> {
|
||||||
impl Algorithm<Secp256k1> for Schnorr {
|
self.0.process_addendum(view, i, addendum)
|
||||||
type Transcript = <FrostSchnorr<Secp256k1, Hram> as Algorithm<Secp256k1>>::Transcript;
|
}
|
||||||
type Addendum = ();
|
|
||||||
type Signature = [u8; 64];
|
fn sign_share(
|
||||||
|
&mut self,
|
||||||
fn transcript(&mut self) -> &mut Self::Transcript {
|
params: &ThresholdView<Secp256k1>,
|
||||||
self.0.transcript()
|
nonce_sums: &[Vec<<Secp256k1 as WrappedGroup>::G>],
|
||||||
}
|
nonces: Vec<Zeroizing<<Secp256k1 as WrappedGroup>::F>>,
|
||||||
|
msg: &[u8],
|
||||||
fn nonces(&self) -> Vec<Vec<ProjectivePoint>> {
|
) -> <Secp256k1 as WrappedGroup>::F {
|
||||||
self.0.nonces()
|
self.0.sign_share(params, nonce_sums, nonces, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
fn verify(
|
||||||
&mut self,
|
&self,
|
||||||
rng: &mut R,
|
group_key: ProjectivePoint,
|
||||||
keys: &ThresholdKeys<Secp256k1>,
|
nonces: &[Vec<ProjectivePoint>],
|
||||||
) {
|
sum: Scalar,
|
||||||
self.0.preprocess_addendum(rng, keys)
|
) -> Option<Self::Signature> {
|
||||||
}
|
self.0.verify(group_key, nonces, sum).map(|mut sig| {
|
||||||
|
sig.s = <_>::conditional_select(&sum, &-sum, needs_negation(&sig.R));
|
||||||
fn read_addendum<R: io::Read>(&self, reader: &mut R) -> io::Result<Self::Addendum> {
|
// Convert to a Bitcoin signature by dropping the byte for the point's sign bit
|
||||||
self.0.read_addendum(reader)
|
sig.serialize()[1 ..].try_into().unwrap()
|
||||||
}
|
})
|
||||||
|
}
|
||||||
fn process_addendum(
|
|
||||||
&mut self,
|
fn verify_share(
|
||||||
view: &ThresholdView<Secp256k1>,
|
&self,
|
||||||
i: Participant,
|
verification_share: ProjectivePoint,
|
||||||
addendum: (),
|
nonces: &[Vec<ProjectivePoint>],
|
||||||
) -> Result<(), FrostError> {
|
share: Scalar,
|
||||||
self.0.process_addendum(view, i, addendum)
|
) -> Result<Vec<(Scalar, ProjectivePoint)>, ()> {
|
||||||
}
|
self.0.verify_share(verification_share, nonces, share)
|
||||||
|
|
||||||
fn sign_share(
|
|
||||||
&mut self,
|
|
||||||
params: &ThresholdView<Secp256k1>,
|
|
||||||
nonce_sums: &[Vec<<Secp256k1 as WrappedGroup>::G>],
|
|
||||||
nonces: Vec<Zeroizing<<Secp256k1 as WrappedGroup>::F>>,
|
|
||||||
msg: &[u8],
|
|
||||||
) -> <Secp256k1 as WrappedGroup>::F {
|
|
||||||
self.0.sign_share(params, nonce_sums, nonces, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify(
|
|
||||||
&self,
|
|
||||||
group_key: ProjectivePoint,
|
|
||||||
nonces: &[Vec<ProjectivePoint>],
|
|
||||||
sum: Scalar,
|
|
||||||
) -> Option<Self::Signature> {
|
|
||||||
self.0.verify(group_key, nonces, sum).map(|mut sig| {
|
|
||||||
sig.s = <_>::conditional_select(&sum, &-sum, needs_negation(&sig.R));
|
|
||||||
// Convert to a Bitcoin signature by dropping the byte for the point's sign bit
|
|
||||||
sig.serialize()[1 ..].try_into().unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_share(
|
|
||||||
&self,
|
|
||||||
verification_share: ProjectivePoint,
|
|
||||||
nonces: &[Vec<ProjectivePoint>],
|
|
||||||
share: Scalar,
|
|
||||||
) -> Result<Vec<(Scalar, ProjectivePoint)>, ()> {
|
|
||||||
self.0.verify_share(verification_share, nonces, share)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use frost_crypto::*;
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
/// The bitcoin Rust library.
|
/// The bitcoin Rust library.
|
||||||
pub use bitcoin;
|
pub use bitcoin;
|
||||||
|
|
||||||
@@ -17,7 +14,7 @@ pub(crate) mod crypto;
|
|||||||
/// Wallet functionality to create transactions.
|
/// Wallet functionality to create transactions.
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
/// A minimal asynchronous Bitcoin RPC client.
|
/// A minimal asynchronous Bitcoin RPC client.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "rpc")]
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
use core::fmt::Debug;
|
use core::{str::FromStr, fmt::Debug};
|
||||||
use std::collections::HashSet;
|
use std::{io::Read, collections::HashSet};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use serde::{Deserialize, de::DeserializeOwned};
|
use simple_request::{hyper, Request, TokioClient as Client};
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use simple_request::{hyper, Request, Client};
|
|
||||||
|
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
hashes::{Hash, hex::FromHex},
|
hashes::{Hash, hex::FromHex},
|
||||||
@@ -14,19 +11,12 @@ use bitcoin::{
|
|||||||
Txid, Transaction, BlockHash, Block,
|
Txid, Transaction, BlockHash, Block,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Deserialize)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
code: isize,
|
code: isize,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum RpcResponse<T> {
|
|
||||||
Ok { result: T },
|
|
||||||
Err { error: Error },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A minimal asynchronous Bitcoin RPC client.
|
/// A minimal asynchronous Bitcoin RPC client.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Rpc {
|
pub struct Rpc {
|
||||||
@@ -34,14 +24,14 @@ pub struct Rpc {
|
|||||||
url: String,
|
url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Error)]
|
#[derive(Clone, Debug, Error)]
|
||||||
pub enum RpcError {
|
pub enum RpcError {
|
||||||
#[error("couldn't connect to node")]
|
#[error("couldn't connect to node")]
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
#[error("request had an error: {0:?}")]
|
#[error("request had an error: {0:?}")]
|
||||||
RequestError(Error),
|
RequestError(Error),
|
||||||
#[error("node replied with invalid JSON")]
|
#[error("node replied with invalid JSON")]
|
||||||
InvalidJson(serde_json::error::Category),
|
InvalidJson,
|
||||||
#[error("node sent an invalid response ({0})")]
|
#[error("node sent an invalid response ({0})")]
|
||||||
InvalidResponse(&'static str),
|
InvalidResponse(&'static str),
|
||||||
#[error("node was missing expected methods")]
|
#[error("node was missing expected methods")]
|
||||||
@@ -62,10 +52,11 @@ impl Rpc {
|
|||||||
/// provided to this library, if the RPC has an incompatible argument layout. That is not checked
|
/// provided to this library, if the RPC has an incompatible argument layout. That is not checked
|
||||||
/// at time of RPC creation.
|
/// at time of RPC creation.
|
||||||
pub async fn new(url: String) -> Result<Rpc, RpcError> {
|
pub async fn new(url: String) -> Result<Rpc, RpcError> {
|
||||||
let rpc = Rpc { client: Client::with_connection_pool(), url };
|
let rpc =
|
||||||
|
Rpc { client: Client::with_connection_pool().map_err(|_| RpcError::ConnectionError)?, url };
|
||||||
|
|
||||||
// Make an RPC request to verify the node is reachable and sane
|
// Make an RPC request to verify the node is reachable and sane
|
||||||
let res: String = rpc.rpc_call("help", json!([])).await?;
|
let res: String = rpc.call("help", "[]").await?;
|
||||||
|
|
||||||
// Verify all methods we expect are present
|
// Verify all methods we expect are present
|
||||||
// If we had a more expanded RPC, due to differences in RPC versions, it wouldn't make sense to
|
// If we had a more expanded RPC, due to differences in RPC versions, it wouldn't make sense to
|
||||||
@@ -102,22 +93,21 @@ impl Rpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an arbitrary RPC call.
|
/// Perform an arbitrary RPC call.
|
||||||
pub async fn rpc_call<Response: DeserializeOwned + Debug>(
|
pub async fn call<Response: 'static + Default + core_json_traits::JsonDeserialize>(
|
||||||
&self,
|
&self,
|
||||||
method: &str,
|
method: &str,
|
||||||
params: serde_json::Value,
|
params: &str,
|
||||||
) -> Result<Response, RpcError> {
|
) -> Result<Response, RpcError> {
|
||||||
let mut request = Request::from(
|
let mut request = Request::from(
|
||||||
hyper::Request::post(&self.url)
|
hyper::Request::post(&self.url)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.body(
|
.body(
|
||||||
serde_json::to_vec(&json!({ "jsonrpc": "2.0", "method": method, "params": params }))
|
format!(r#"{{ "method": "{method}", "params": {params} }}"#).as_bytes().to_vec().into(),
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
request.with_basic_auth();
|
request.with_basic_auth();
|
||||||
|
request.set_response_size_limit(Some(100 * 1024 * 1024));
|
||||||
let mut res = self
|
let mut res = self
|
||||||
.client
|
.client
|
||||||
.request(request)
|
.request(request)
|
||||||
@@ -127,11 +117,52 @@ impl Rpc {
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| RpcError::ConnectionError)?;
|
.map_err(|_| RpcError::ConnectionError)?;
|
||||||
|
|
||||||
let res: RpcResponse<Response> =
|
#[derive(Default, core_json_derive::JsonDeserialize)]
|
||||||
serde_json::from_reader(&mut res).map_err(|e| RpcError::InvalidJson(e.classify()))?;
|
struct InternalError {
|
||||||
|
code: Option<i64>,
|
||||||
|
message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(core_json_derive::JsonDeserialize)]
|
||||||
|
struct RpcResponse<T: core_json_traits::JsonDeserialize> {
|
||||||
|
result: Option<T>,
|
||||||
|
error: Option<InternalError>,
|
||||||
|
}
|
||||||
|
impl<T: core_json_traits::JsonDeserialize> Default for RpcResponse<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { result: None, error: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: `core_json::ReadAdapter`
|
||||||
|
let mut res_vec = vec![];
|
||||||
|
res.read_to_end(&mut res_vec).map_err(|_| RpcError::ConnectionError)?;
|
||||||
|
let res = <RpcResponse<Response> as core_json_traits::JsonStructure>::deserialize_structure::<
|
||||||
|
_,
|
||||||
|
core_json_traits::ConstStack<32>,
|
||||||
|
>(res_vec.as_slice())
|
||||||
|
.map_err(|_| RpcError::InvalidJson)?;
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
RpcResponse::Ok { result } => Ok(result),
|
RpcResponse { result: Some(result), error: None } => Ok(result),
|
||||||
RpcResponse::Err { error } => Err(RpcError::RequestError(error)),
|
RpcResponse { result: None, error: Some(error) } => {
|
||||||
|
let code =
|
||||||
|
error.code.ok_or_else(|| RpcError::InvalidResponse("error was missing `code`"))?;
|
||||||
|
let code = isize::try_from(code)
|
||||||
|
.map_err(|_| RpcError::InvalidResponse("error code exceeded isize::MAX"))?;
|
||||||
|
let message =
|
||||||
|
error.message.ok_or_else(|| RpcError::InvalidResponse("error was missing `message`"))?;
|
||||||
|
Err(RpcError::RequestError(Error { code, message }))
|
||||||
|
}
|
||||||
|
// `invalidateblock` yields this edge case
|
||||||
|
RpcResponse { result: None, error: None } => {
|
||||||
|
if core::any::TypeId::of::<Response>() == core::any::TypeId::of::<()>() {
|
||||||
|
Ok(Default::default())
|
||||||
|
} else {
|
||||||
|
Err(RpcError::InvalidResponse("response lacked both a result and an error"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(RpcError::InvalidResponse("response contained both a result and an error")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,16 +175,17 @@ impl Rpc {
|
|||||||
// tip block of the current chain. The "height" of a block is defined as the amount of blocks
|
// tip block of the current chain. The "height" of a block is defined as the amount of blocks
|
||||||
// present when the block was created. Accordingly, the genesis block has height 0, and
|
// present when the block was created. Accordingly, the genesis block has height 0, and
|
||||||
// getblockcount will return 0 when it's only the only block, despite their being one block.
|
// getblockcount will return 0 when it's only the only block, despite their being one block.
|
||||||
self.rpc_call("getblockcount", json!([])).await
|
usize::try_from(self.call::<u64>("getblockcount", "[]").await?)
|
||||||
|
.map_err(|_| RpcError::InvalidResponse("latest block number exceeded usize::MAX"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the hash of a block by the block's number.
|
/// Get the hash of a block by the block's number.
|
||||||
pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> {
|
pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> {
|
||||||
let mut hash = self
|
let mut hash =
|
||||||
.rpc_call::<BlockHash>("getblockhash", json!([number]))
|
BlockHash::from_str(&self.call::<String>("getblockhash", &format!("[{number}]")).await?)
|
||||||
.await?
|
.map_err(|_| RpcError::InvalidResponse("block hash was not valid hex"))?
|
||||||
.as_raw_hash()
|
.as_raw_hash()
|
||||||
.to_byte_array();
|
.to_byte_array();
|
||||||
// bitcoin stores the inner bytes in reverse order.
|
// bitcoin stores the inner bytes in reverse order.
|
||||||
hash.reverse();
|
hash.reverse();
|
||||||
Ok(hash)
|
Ok(hash)
|
||||||
@@ -161,16 +193,25 @@ impl Rpc {
|
|||||||
|
|
||||||
/// Get a block's number by its hash.
|
/// Get a block's number by its hash.
|
||||||
pub async fn get_block_number(&self, hash: &[u8; 32]) -> Result<usize, RpcError> {
|
pub async fn get_block_number(&self, hash: &[u8; 32]) -> Result<usize, RpcError> {
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Default, core_json_derive::JsonDeserialize)]
|
||||||
struct Number {
|
struct Number {
|
||||||
height: usize,
|
height: Option<u64>,
|
||||||
}
|
}
|
||||||
Ok(self.rpc_call::<Number>("getblockheader", json!([hex::encode(hash)])).await?.height)
|
usize::try_from(
|
||||||
|
self
|
||||||
|
.call::<Number>("getblockheader", &format!(r#"["{}"]"#, hex::encode(hash)))
|
||||||
|
.await?
|
||||||
|
.height
|
||||||
|
.ok_or_else(|| {
|
||||||
|
RpcError::InvalidResponse("`getblockheader` did not include `height` field")
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
.map_err(|_| RpcError::InvalidResponse("block number exceeded usize::MAX"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a block by its hash.
|
/// Get a block by its hash.
|
||||||
pub async fn get_block(&self, hash: &[u8; 32]) -> Result<Block, RpcError> {
|
pub async fn get_block(&self, hash: &[u8; 32]) -> Result<Block, RpcError> {
|
||||||
let hex = self.rpc_call::<String>("getblock", json!([hex::encode(hash), 0])).await?;
|
let hex = self.call::<String>("getblock", &format!(r#"["{}", 0]"#, hex::encode(hash))).await?;
|
||||||
let bytes: Vec<u8> = FromHex::from_hex(&hex)
|
let bytes: Vec<u8> = FromHex::from_hex(&hex)
|
||||||
.map_err(|_| RpcError::InvalidResponse("node didn't use hex to encode the block"))?;
|
.map_err(|_| RpcError::InvalidResponse("node didn't use hex to encode the block"))?;
|
||||||
let block: Block = encode::deserialize(&bytes)
|
let block: Block = encode::deserialize(&bytes)
|
||||||
@@ -187,8 +228,13 @@ impl Rpc {
|
|||||||
|
|
||||||
/// Publish a transaction.
|
/// Publish a transaction.
|
||||||
pub async fn send_raw_transaction(&self, tx: &Transaction) -> Result<Txid, RpcError> {
|
pub async fn send_raw_transaction(&self, tx: &Transaction) -> Result<Txid, RpcError> {
|
||||||
let txid = match self.rpc_call("sendrawtransaction", json!([encode::serialize_hex(tx)])).await {
|
let txid = match self
|
||||||
Ok(txid) => txid,
|
.call::<String>("sendrawtransaction", &format!(r#"["{}"]"#, encode::serialize_hex(tx)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(txid) => {
|
||||||
|
Txid::from_str(&txid).map_err(|_| RpcError::InvalidResponse("TXID was not valid hex"))?
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// A const from Bitcoin's bitcoin/src/rpc/protocol.h
|
// A const from Bitcoin's bitcoin/src/rpc/protocol.h
|
||||||
const RPC_VERIFY_ALREADY_IN_CHAIN: isize = -27;
|
const RPC_VERIFY_ALREADY_IN_CHAIN: isize = -27;
|
||||||
@@ -209,7 +255,8 @@ impl Rpc {
|
|||||||
|
|
||||||
/// Get a transaction by its hash.
|
/// Get a transaction by its hash.
|
||||||
pub async fn get_transaction(&self, hash: &[u8; 32]) -> Result<Transaction, RpcError> {
|
pub async fn get_transaction(&self, hash: &[u8; 32]) -> Result<Transaction, RpcError> {
|
||||||
let hex = self.rpc_call::<String>("getrawtransaction", json!([hex::encode(hash)])).await?;
|
let hex =
|
||||||
|
self.call::<String>("getrawtransaction", &format!(r#"["{}"]"#, hex::encode(hash))).await?;
|
||||||
let bytes: Vec<u8> = FromHex::from_hex(&hex)
|
let bytes: Vec<u8> = FromHex::from_hex(&hex)
|
||||||
.map_err(|_| RpcError::InvalidResponse("node didn't use hex to encode the transaction"))?;
|
.map_err(|_| RpcError::InvalidResponse("node didn't use hex to encode the transaction"))?;
|
||||||
let tx: Transaction = encode::deserialize(&bytes)
|
let tx: Transaction = encode::deserialize(&bytes)
|
||||||
|
|||||||
@@ -1,36 +1,31 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
use std_shims::{
|
use std_shims::{
|
||||||
vec::Vec,
|
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io::{self, Write},
|
io::{self, Read, Write},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::io::{Read, BufReader};
|
|
||||||
|
|
||||||
use k256::{
|
use k256::{
|
||||||
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
||||||
Scalar, ProjectivePoint,
|
Scalar, ProjectivePoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use frost::{
|
use frost::{
|
||||||
curve::{WrappedGroup, GroupIo, Secp256k1},
|
curve::{WrappedGroup, GroupIo, Secp256k1},
|
||||||
ThresholdKeys,
|
ThresholdKeys,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
consensus::encode::serialize, key::TweakedPublicKey, OutPoint, ScriptBuf, TxOut, Transaction,
|
hashes::Hash,
|
||||||
Block,
|
key::TweakedPublicKey,
|
||||||
|
TapTweakHash,
|
||||||
|
consensus::encode::{Decodable, serialize},
|
||||||
|
OutPoint, ScriptBuf, TxOut, Transaction, Block,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use bitcoin::{hashes::Hash, consensus::encode::Decodable, TapTweakHash};
|
|
||||||
|
|
||||||
use crate::crypto::x_only;
|
use crate::crypto::{x_only, needs_negation};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use crate::crypto::needs_negation;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
mod send;
|
mod send;
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use send::*;
|
pub use send::*;
|
||||||
|
|
||||||
/// Tweak keys to ensure they're usable with Bitcoin's Taproot upgrade.
|
/// Tweak keys to ensure they're usable with Bitcoin's Taproot upgrade.
|
||||||
@@ -42,7 +37,6 @@ pub use send::*;
|
|||||||
/// After adding an unspendable script path, the key is negated if odd.
|
/// After adding an unspendable script path, the key is negated if odd.
|
||||||
///
|
///
|
||||||
/// This has a neligible probability of returning keys whose group key is the point at infinity.
|
/// This has a neligible probability of returning keys whose group key is the point at infinity.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
||||||
// Adds the unspendable script path per
|
// Adds the unspendable script path per
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23
|
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23
|
||||||
@@ -118,18 +112,23 @@ impl ReceivedOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read a ReceivedOutput from a generic satisfying Read.
|
/// Read a ReceivedOutput from a generic satisfying Read.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
||||||
let offset = Secp256k1::read_F(r)?;
|
let offset = Secp256k1::read_F(r)?;
|
||||||
let output;
|
|
||||||
let outpoint;
|
struct BitcoinRead<R: Read>(R);
|
||||||
{
|
impl<R: Read> bitcoin::io::Read for BitcoinRead<R> {
|
||||||
let mut buf_r = BufReader::with_capacity(0, r);
|
fn read(&mut self, buf: &mut [u8]) -> bitcoin::io::Result<usize> {
|
||||||
output =
|
self
|
||||||
TxOut::consensus_decode(&mut buf_r).map_err(|_| io::Error::other("invalid TxOut"))?;
|
.0
|
||||||
outpoint =
|
.read(buf)
|
||||||
OutPoint::consensus_decode(&mut buf_r).map_err(|_| io::Error::other("invalid OutPoint"))?;
|
.map_err(|e| bitcoin::io::Error::new(bitcoin::io::ErrorKind::Other, e.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let mut r = BitcoinRead(r);
|
||||||
|
|
||||||
|
let output = TxOut::consensus_decode(&mut r).map_err(|_| io::Error::other("invalid TxOut"))?;
|
||||||
|
let outpoint =
|
||||||
|
OutPoint::consensus_decode(&mut r).map_err(|_| io::Error::other("invalid OutPoint"))?;
|
||||||
Ok(ReceivedOutput { offset, output, outpoint })
|
Ok(ReceivedOutput { offset, output, outpoint })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
use std_shims::{
|
use std_shims::{
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ pub(crate) async fn rpc() -> Rpc {
|
|||||||
// If this node has already been interacted with, clear its chain
|
// If this node has already been interacted with, clear its chain
|
||||||
if rpc.get_latest_block_number().await.unwrap() > 0 {
|
if rpc.get_latest_block_number().await.unwrap() > 0 {
|
||||||
rpc
|
rpc
|
||||||
.rpc_call(
|
.call(
|
||||||
"invalidateblock",
|
"invalidateblock",
|
||||||
serde_json::json!([hex::encode(rpc.get_block_hash(1).await.unwrap())]),
|
&format!(r#"["{}"]"#, hex::encode(rpc.get_block_hash(1).await.unwrap())),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@@ -41,21 +41,21 @@ async fn send_and_get_output(rpc: &Rpc, scanner: &Scanner, key: ProjectivePoint)
|
|||||||
let block_number = rpc.get_latest_block_number().await.unwrap() + 1;
|
let block_number = rpc.get_latest_block_number().await.unwrap() + 1;
|
||||||
|
|
||||||
rpc
|
rpc
|
||||||
.rpc_call::<Vec<String>>(
|
.call::<Vec<String>>(
|
||||||
"generatetoaddress",
|
"generatetoaddress",
|
||||||
serde_json::json!([
|
&format!(
|
||||||
1,
|
r#"[1, "{}"]"#,
|
||||||
Address::from_script(&p2tr_script_buf(key).unwrap(), Network::Regtest).unwrap()
|
Address::from_script(&p2tr_script_buf(key).unwrap(), Network::Regtest).unwrap()
|
||||||
]),
|
),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Mine until maturity
|
// Mine until maturity
|
||||||
rpc
|
rpc
|
||||||
.rpc_call::<Vec<String>>(
|
.call::<Vec<String>>(
|
||||||
"generatetoaddress",
|
"generatetoaddress",
|
||||||
serde_json::json!([100, Address::p2sh(Script::new(), Network::Regtest).unwrap()]),
|
&format!(r#"[100, "{}"]"#, Address::p2sh(Script::new(), Network::Regtest).unwrap()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user