Compare commits

..

30 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 288baea49f cargo fmt 2024-02-12 18:37:46 +00:00
Jędrzej Stuczyński 4a0e623e3d removing redundant epoch_id field 2024-02-12 17:29:57 +00:00
Jędrzej Stuczyński 3ecf835a45 clippy 2024-02-12 17:29:57 +00:00
Jędrzej Stuczyński 7258f83b47 nym-cli commands for issuing free passes 2024-02-12 17:29:57 +00:00
Jędrzej Stuczyński e7c04863d1 clippy 2024-02-12 17:29:44 +00:00
Jędrzej Stuczyński 1a0424d57d cargo fmt 2024-02-12 17:01:08 +00:00
Jędrzej Stuczyński f0666b33d2 validating request attributes 2024-02-09 17:37:41 +00:00
Jędrzej Stuczyński b4880db840 storage implementation 2024-02-09 17:37:41 +00:00
Jędrzej Stuczyński 370cc36fe3 nym-api logic for issuing free passes (minus storage impl) 2024-02-09 17:37:41 +00:00
Jędrzej Stuczyński 9cc533b672 request type for obtaining free pass 2024-02-09 17:37:41 +00:00
benedettadavico 5b22671144 cargo fmt 2024-02-09 16:41:01 +01:00
benedettadavico f3f8326022 add return statement 2024-02-09 16:32:53 +01:00
Jędrzej Stuczyński 114228e24a ibid 2024-02-09 14:57:49 +00:00
benedettadavico dab29401be running cargo fmt 2024-02-09 15:32:35 +01:00
Jędrzej Stuczyński 3c13eef452 gateway downgrading advertised protocol for incompatible clients 2024-02-09 14:17:20 +00:00
Jędrzej Stuczyński bb3a7f40b6 fixed SQL type for epoch_id 2024-02-09 13:57:35 +00:00
Jędrzej Stuczyński 1e99358885 restored OldV1Credential::as_bytes to be available to non-test code 2024-02-09 10:14:34 +00:00
Jędrzej Stuczyński c5e9c3fbb9 reintroduced handling of old v1 credentials 2024-02-09 10:07:18 +00:00
Jędrzej Stuczyński 5e08135a68 clippy and fixing tests 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński e1c9ca5e20 missing serialization 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński d9e1257d9b persisting the issued credentials 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński 7598c951ed reintroduced recovery of vouchers 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński 62663d6d12 removed nym-api placeholders 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński 6ca9adcf0d gateway handling of both credential types 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński 7ecd4bc05e removed usage of coconut-interface crate 2024-02-09 10:00:23 +00:00
Jędrzej Stuczyński 67ce8a98a5 wip in removing the Credential type for more strongly typed alternative 2024-02-09 10:00:22 +00:00
Jędrzej Stuczyński b50468b566 wip 2024-02-09 10:00:22 +00:00
Jędrzej Stuczyński 85d404b225 using bincode serialization 2024-02-09 10:00:22 +00:00
Jędrzej Stuczyński f05998323a serde for 'IssuanceBandwidthCredential' 2024-02-09 10:00:22 +00:00
Jędrzej Stuczyński 279db3cc83 revamped BandwidthVoucher to allow for different kinds of bandwidth credentials 2024-02-09 10:00:22 +00:00
257 changed files with 6857 additions and 8020 deletions
+8 -21
View File
@@ -9,17 +9,10 @@ on:
default: false
type: boolean
enable_wireguard:
description: "Add --features wireguard"
description: 'Add --features wireguard'
required: true
default: false
type: boolean
enable_deb:
description: "True to enable cargo-deb installation and .deb package building"
required: false
default: false
type: boolean
schedule:
- cron: "14 0 * * *"
pull_request:
paths:
- "clients/**"
@@ -35,7 +28,6 @@ on:
- "sdk/rust/nym-sdk/**"
- "service-providers/**"
- "tools/**"
- "nymvisor/**"
jobs:
publish-nym:
@@ -70,9 +62,7 @@ jobs:
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
if: >
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true)
if: github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -90,12 +80,12 @@ jobs:
with:
command: install
args: cargo-deb
if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true
- name: Build deb packages
shell: bash
run: make deb
if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true
# If this was a manual workflow_dispatch, publish binaries.
- name: Upload Artifact
if: github.event_name == 'workflow_dispatch'
@@ -111,13 +101,12 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
retention-days: 30
# If this was a pull_request or nightly, upload to build server
# If this was a pull_request, upload to build server
- name: Prepare build output
# if: github.event_name == 'schedule' || github.event_name == 'pull_request'
if: github.event_name == 'pull_request'
shell: bash
env:
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
@@ -129,14 +118,12 @@ jobs:
cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then
cp target/debian/*.deb $OUTPUT_DIR
fi
cp target/debian/*.deb $OUTPUT_DIR
- name: Deploy branch to CI www
if: github.event_name == 'pull_request'
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
+1 -5
View File
@@ -1,4 +1,4 @@
name: publish-nym-binaries
name: Publish Nym binaries
on:
workflow_dispatch:
@@ -29,7 +29,6 @@ jobs:
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
@@ -37,7 +36,6 @@ jobs:
client_version: ${{ steps.binary-versions.outputs.client_version }}
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
@@ -80,7 +78,6 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
retention-days: 30
- id: create-release
@@ -98,7 +95,6 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
@@ -1,4 +1,4 @@
name: publish-nym-connect-macos
name: Publish Nym Connect - desktop (MacOS)
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: publish-nym-connect-ubuntu
name: Publish Nym Connect - desktop (Ubuntu)
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: publish-nym-connect-win10
name: Publish Nym Connect - desktop (Windows 10)
on:
workflow_dispatch:
release:
+1 -1
View File
@@ -1,4 +1,4 @@
name: publish-nym-contracts
name: Build release of Nym smart contracts
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: publish-nym-wallet-macos
name: Publish Nym Wallet (MacOS)
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: publish-nym-wallet-ubuntu
name: Publish Nym Wallet (Ubuntu)
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: publish-nym-wallet-win10
name: Publish Nym Wallet (Windows 10)
on:
workflow_dispatch:
release:
+1 -1
View File
@@ -1,4 +1,4 @@
name: publish-sdk-npm
name: Publish Typescript SDK
on:
workflow_dispatch:
+1 -1
View File
@@ -1,4 +1,4 @@
name: release-calculate-hash
name: Releases - calculate file hashes
on:
workflow_call:
-17
View File
@@ -4,23 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.1-marabou] (2024-02-15)
**New Features:**
- Introduced nymvisor support for nym-api, gateway, and mixnode binaries ([#4158])
- Revamped nym-api execution with the addition of init and run commands ([#4225])
**Enhancements:**
- Implemented internal improvements for gateways to optimize internal packet routing
- Improved routing score calculation
**Bug Fixes:**
- Resolved various bugs to enhance overall stability
[#4158]: https://github.com/nymtech/nym/pull/4158
[#4225]: https://github.com/nymtech/nym/pull/4225
## [2023.5-rolo] (2023-11-28)
- Gateway won't open websocket listener until embedded Network Requester becomes available ([#4166])
Generated
+2194 -815
View File
File diff suppressed because it is too large Load Diff
+5 -8
View File
@@ -32,7 +32,7 @@ members = [
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
# "common/cosmwasm-smart-contracts/ephemera",
"common/cosmwasm-smart-contracts/ephemera",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
@@ -56,7 +56,6 @@ members = [
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
"common/nymcoconut",
"common/nym-id",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -105,9 +104,8 @@ members = [
"nym-outfox",
"nym-validator-rewarder",
"tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump",
"tools/internal/sdk-version-bump",
"tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query",
"tools/nymvisor",
"tools/ts-rs-cli",
@@ -145,7 +143,6 @@ anyhow = "1.0.71"
async-trait = "0.1.68"
axum = "0.6.20"
base64 = "0.21.4"
bs58 = "0.5.0"
bip39 = { version = "2.0.0", features = ["zeroize"] }
clap = "4.4.7"
cfg-if = "1.0.0"
@@ -161,7 +158,7 @@ log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = { version = "0.11.22", default_features = false, features = ["rustls-tls"] }
reqwest = "0.11.22"
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
@@ -171,9 +168,9 @@ time = "0.3.30"
thiserror = "1.0.48"
tokio = "1.33.0"
tokio-util = "0.7.10"
tokio-tungstenite = { version = "0.20.1", features = ["rustls"] }
tokio-tungstenite = "0.20.1"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false, features = ["rustls"] }
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5"
+3 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.33"
version = "1.1.32"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -21,19 +21,18 @@ futures = { workspace = true } # bunch of futures stuff, however, now that I thi
# and the single instance of abortable we have should really be refactored anyway
url = { workspace = true }
bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] }
dirs = "4.0"
lazy_static = "1.4.0"
log = { workspace = true } # self explanatory
pretty_env_logger = "0.4" # for formatting log messages
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
serde_json = { workspace = true }
thiserror = { workspace = true }
tap = "1.0.1"
time = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = { workspace = true }
zeroize = { workspace = true }
## internal
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
@@ -51,6 +50,5 @@ nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
nym-client-websocket-requests = { path = "websocket-requests" }
nym-id = { path = "../../common/nym-id" }
[dev-dependencies]
@@ -2160,9 +2160,9 @@
}
},
"node_modules/ip": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==",
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true
},
"node_modules/ipaddr.js": {
@@ -6157,9 +6157,9 @@
"dev": true
},
"ip": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==",
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true
},
"ipaddr.js": {
@@ -1,54 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::error::ClientError;
use clap::ArgGroup;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Id of client that is going to import the credential
#[clap(long)]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
let config = try_load_current_config(&args.id)?;
let credentials_store = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
+7 -8
View File
@@ -8,6 +8,7 @@ use crate::client::config::{BaseClientConfig, Config};
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
@@ -20,16 +21,18 @@ use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
pub(crate) mod build_info;
pub(crate) mod import_credential;
pub(crate) mod init;
pub(crate) mod run;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print())
&PRETTY_BUILD_INFORMATION
}
#[derive(Parser)]
@@ -55,9 +58,6 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(import_credential::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -86,7 +86,6 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+1 -6
View File
@@ -1,13 +1,11 @@
use nym_client_core::error::ClientCoreError;
use nym_id::NymIdError;
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error(transparent)]
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
#[error("Failed to load config for: {0}")]
@@ -22,7 +20,4 @@ pub enum ClientError {
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
+3 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.33"
version = "1.1.32"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -8,18 +8,17 @@ rust-version = "1.56"
license.workspace = true
[dependencies]
bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] }
lazy_static = "1.4.0"
log = { workspace = true }
pretty_env_logger = "0.4"
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
serde_json = { workspace = true }
tap = "1.0.1"
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
rand = "0.7.3"
time = { workspace = true }
url = { workspace = true }
zeroize = { workspace = true }
# internal
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
@@ -35,7 +34,6 @@ nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" }
nym-topology = { path = "../../common/topology" }
nym-socks5-client-core = { path = "../../common/socks5-client-core" }
nym-id = { path = "../../common/nym-id" }
[features]
default = []
@@ -1,54 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::error::Socks5ClientError;
use clap::ArgGroup;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Id of client that is going to import the credential
#[clap(long)]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let config = try_load_current_config(&args.id)?;
let credentials_store = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
+7 -8
View File
@@ -9,6 +9,7 @@ use crate::config::{BaseClientConfig, Config, SocksClientPaths};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
@@ -23,16 +24,18 @@ use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
pub(crate) mod build_info;
mod import_credential;
pub mod init;
pub(crate) mod run;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print())
&PRETTY_BUILD_INFORMATION
}
#[derive(Parser)]
@@ -58,9 +61,6 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(import_credential::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -92,7 +92,6 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+1 -6
View File
@@ -1,7 +1,5 @@
use nym_client_core::error::ClientCoreError;
use nym_id::NymIdError;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
#[error("I/O error: {0}")]
@@ -20,9 +18,6 @@ pub enum Socks5ClientError {
#[error("Fail to bind address")]
FailToBindAddress,
#[error(transparent)]
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
-3
View File
@@ -21,9 +21,6 @@ pub enum BandwidthControllerError {
#[error("There was a credential storage error - {0}")]
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
#[error("the credential storage does not contain any usable credentials")]
NoCredentialsAvailable,
// this should really be fully incorporated into the above, but messing with coconut is the last thing I want to do now
#[error(transparent)]
StorageError(#[from] StorageError),
+21 -66
View File
@@ -3,12 +3,10 @@
use crate::error::BandwidthControllerError;
use crate::utils::stored_credential_to_issued_bandwidth;
use log::{debug, error, warn};
use log::{error, warn};
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use nym_credentials::coconut::utils::obtain_aggregate_verification_key;
use nym_credentials::IssuedBandwidthCredential;
use nym_credentials_interface::VerificationKey;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nym_api::EpochId;
@@ -35,67 +33,11 @@ pub struct PreparedCredential {
pub credential_id: i64,
}
pub struct RetrievedCredential {
pub credential: IssuedBandwidthCredential,
pub credential_id: i64,
}
impl<C, St: Storage> BandwidthController<C, St> {
pub fn new(storage: St, client: C) -> Self {
BandwidthController { storage, client }
}
/// Tries to retrieve one of the stored, unused credentials that hasn't yet expired.
/// It marks any retrieved intermediate credentials as expired.
pub async fn get_next_usable_credential(
&self,
) -> Result<RetrievedCredential, BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
{
loop {
let Some(maybe_next) = self
.storage
.get_next_unspent_credential()
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?
else {
return Err(BandwidthControllerError::NoCredentialsAvailable);
};
let id = maybe_next.id;
// try to deserialize it
let valid_credential = match stored_credential_to_issued_bandwidth(maybe_next) {
// check if it has already expired
Ok(credential) => match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(_) => {
debug!("credential {id} is a bandwidth voucher");
credential
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("credential {id} is a free pass");
if freepass_info.expired() {
warn!("the free pass (id: {id}) has already expired! The expiration was set to {}", freepass_info.expiry_date());
self.storage.mark_expired(id).await.map_err(|err| {
BandwidthControllerError::CredentialStorageError(Box::new(err))
})?;
continue;
}
credential
}
},
Err(err) => {
error!("failed to deserialize credential with id {id}: {err}. it may need to be manually removed from the storage");
return Err(err);
}
};
return Ok(RetrievedCredential {
credential: valid_credential,
credential_id: id,
});
}
}
pub fn storage(&self) -> &St {
&self.storage
}
@@ -119,16 +61,29 @@ impl<C, St: Storage> BandwidthController<C, St> {
C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static,
{
let retrieved_credential = self.get_next_usable_credential().await?;
let retrieved_credential = self
.storage
.get_next_unspent_credential()
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?;
let epoch_id = retrieved_credential.credential.epoch_id();
let credential_id = retrieved_credential.credential_id;
let epoch_id = retrieved_credential.epoch_id as EpochId;
let credential_id = retrieved_credential.id;
let verification_key = self.get_aggregate_verification_key(epoch_id).await?;
let issued_bandwidth = stored_credential_to_issued_bandwidth(retrieved_credential)?;
let spend_request = retrieved_credential
.credential
.prepare_for_spending(&verification_key)?;
let verification_key = match self.get_aggregate_verification_key(epoch_id).await {
Ok(key) => key,
Err(err) => {
warn!("failed to obtain master verification key: {err}. Putting the credential back into the database");
// TODO: ERROR RECOVERY:
error!("unimplemented: putting the credential back into the database");
return Err(err);
}
};
let spend_request = issued_bandwidth.prepare_for_spending(&verification_key)?;
Ok(PreparedCredential {
data: spend_request,
@@ -69,35 +69,6 @@ impl BinaryBuildInformation {
}
}
// Varient where we want to use the metadata generated by vergen in the consuming crate.
pub const fn new_with_local_vergen(
binary_name: &'static str,
build_timestamp: &'static str,
build_version: &'static str,
commit_sha: &'static str,
commit_timestamp: &'static str,
commit_branch: &'static str,
) -> Self {
let cargo_debug = env!("VERGEN_CARGO_DEBUG");
let cargo_profile = if const_str::equal!(cargo_debug, "true") {
"debug"
} else {
"release"
};
BinaryBuildInformation {
binary_name,
build_timestamp,
build_version,
commit_sha,
commit_timestamp,
commit_branch,
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
}
}
pub fn to_owned(&self) -> BinaryBuildInformationOwned {
BinaryBuildInformationOwned {
binary_name: self.binary_name.to_owned(),
@@ -216,33 +187,3 @@ macro_rules! bin_info_owned {
.to_owned()
};
}
// variant that picks up the vergen build information generated by the build.rs in the consumer
// crate.
#[macro_export]
macro_rules! bin_info_local_vergen {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
#[macro_export]
macro_rules! bin_info_local_vergen_owned {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
@@ -52,7 +52,6 @@ use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use std::path::Path;
use std::sync::Arc;
use url::Url;
@@ -104,12 +103,6 @@ pub struct ClientState {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub reply_controller_sender: ReplyControllerSender,
pub topology_accessor: TopologyAccessor,
pub gateway_connection: GatewayConnection,
}
#[derive(Clone, Copy, Debug)]
pub struct GatewayConnection {
pub gateway_ws_fd: Option<RawFd>,
}
pub enum ClientInputStatus {
@@ -673,7 +666,6 @@ where
shutdown.fork("gateway_transceiver"),
)
.await?;
let gateway_ws_fd = gateway_transceiver.ws_fd();
let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend,
@@ -767,7 +759,6 @@ where
shared_lane_queue_lengths,
reply_controller_sender,
topology_accessor: shared_topology_accessor,
gateway_connection: GatewayConnection { gateway_ws_fd },
},
task_handle: shutdown,
})
@@ -8,7 +8,6 @@ use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_sphinx::forwarding::packet::MixPacket;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))]
@@ -26,7 +25,6 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
/// This combines combines the functionalities of being able to send and receive mix packets.
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
fn gateway_identity(&self) -> identity::PublicKey;
fn ws_fd(&self) -> Option<RawFd>;
}
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
@@ -68,9 +66,6 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
fn gateway_identity(&self) -> identity::PublicKey {
(**self).gateway_identity()
}
fn ws_fd(&self) -> Option<RawFd> {
(**self).ws_fd()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -117,9 +112,6 @@ where
fn gateway_identity(&self) -> identity::PublicKey {
self.gateway_client.gateway_identity()
}
fn ws_fd(&self) -> Option<RawFd> {
self.gateway_client.ws_fd()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -195,9 +187,6 @@ mod nonwasm_sealed {
fn gateway_identity(&self) -> identity::PublicKey {
self.local_identity
}
fn ws_fd(&self) -> Option<RawFd> {
None
}
}
#[async_trait]
@@ -270,7 +259,4 @@ impl GatewayTransceiver for MockGateway {
fn gateway_identity(&self) -> identity::PublicKey {
self.dummy_identity
}
fn ws_fd(&self) -> Option<RawFd> {
None
}
}
@@ -266,11 +266,11 @@ impl std::ops::Div<f64> for PacketRates {
impl PacketRates {
fn summary(&self) -> String {
format!(
"down: {}/s, up: {}/s (cover down: {}/s, cover up: {}/s)",
"rx: {}/s (real: {}/s), tx: {}/s (real: {}/s)",
bibytes2(self.real_packets_received_size + self.cover_packets_received_size),
bibytes2(self.real_packets_received_size),
bibytes2(self.real_packets_sent_size + self.cover_packets_sent_size),
bibytes2(self.real_packets_sent_size),
bibytes2(self.cover_packets_received_size),
bibytes2(self.cover_packets_sent_size),
)
}
@@ -288,7 +288,6 @@ impl PacketRates {
}
}
#[derive(Debug)]
pub(crate) enum PacketStatisticsEvent {
// The real packets sent. Recall that acks are sent by the gateway, so it's not included here.
RealPacketSent(usize),
@@ -444,11 +443,7 @@ impl PacketStatisticsControl {
// Check what the number of retransmissions was during the recording window
if let Some((_, start_stats)) = self.history.front() {
let delta = self.stats.clone() - start_stats.clone();
log::info!(
"mix packet retransmissions/real mix packets: {}/{}",
delta.retransmissions_queued,
delta.real_packets_queued,
);
log::info!("retransmissions: {}", delta.retransmissions_queued,);
} else {
log::warn!("Unable to check retransmissions during recording window");
}
+1 -1
View File
@@ -48,7 +48,7 @@ features = ["net", "sync", "time"]
workspace = true
# the choice of this particular tls feature was arbitrary;
# if you reckon a different one would be more appropriate, feel free to change it
# features = ["native-tls"]
features = ["native-tls"]
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
@@ -6,7 +6,7 @@ use crate::packet_router::PacketRouter;
pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
use crate::socket_state::{ws_fd, PartiallyDelegated, SocketState};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt};
@@ -20,8 +20,7 @@ use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressByt
use nym_gateway_requests::iv::IV;
use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use nym_gateway_requests::{
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION,
CURRENT_PROTOCOL_VERSION,
BinaryRequest, ClientControlRequest, ServerResponse, CURRENT_PROTOCOL_VERSION,
};
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_sphinx::forwarding::packet::MixPacket;
@@ -33,15 +32,11 @@ use std::sync::Arc;
use std::time::Duration;
use tungstenite::protocol::Message;
#[cfg(unix)]
use std::os::fd::RawFd;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(unix))]
use std::os::raw::c_int as RawFd;
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")]
@@ -157,14 +152,6 @@ impl<C, St> GatewayClient<C, St> {
self.gateway_identity
}
pub fn ws_fd(&self) -> Option<RawFd> {
match &self.connection {
SocketState::Available(conn) => ws_fd(conn.as_ref()),
SocketState::PartiallyDelegated(conn) => conn.ws_fd(),
_ => None,
}
}
pub fn remaining_bandwidth(&self) -> i64 {
self.bandwidth_remaining
}
@@ -395,8 +382,6 @@ impl<C, St> GatewayClient<C, St> {
&self,
gateway_protocol: Option<u8>,
) -> Result<(), GatewayClientError> {
debug!("gateway protocol: {gateway_protocol:?}, ours: {CURRENT_PROTOCOL_VERSION}");
// right now there are no failure cases here, but this might change in the future
match gateway_protocol {
None => {
@@ -506,7 +491,6 @@ impl<C, St> GatewayClient<C, St> {
self.check_gateway_protocol(protocol_version)?;
self.authenticated = status;
self.bandwidth_remaining = bandwidth_remaining;
self.negotiated_protocol = protocol_version;
Ok(())
}
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
@@ -593,18 +577,6 @@ impl<C, St> GatewayClient<C, St> {
return self.try_claim_testnet_bandwidth().await;
}
let Some(gateway_protocol) = self.negotiated_protocol else {
return Err(GatewayClientError::OutdatedGatewayCredentialVersion {
negotiated_protocol: None,
});
};
if gateway_protocol < CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION {
return Err(GatewayClientError::OutdatedGatewayCredentialVersion {
negotiated_protocol: Some(gateway_protocol),
});
}
let prepared_credential = self
.bandwidth_controller
.as_ref()
@@ -47,9 +47,6 @@ pub enum GatewayClientError {
#[error("Credential could not be serialized")]
SerializeCredential,
#[error("can not spend bandwidth credential with the gateway as it's using outdated protocol (version: {negotiated_protocol:?})")]
OutdatedGatewayCredentialVersion { negotiated_protocol: Option<u8> },
#[error("Client is not authenticated")]
NotAuthenticated,
@@ -11,12 +11,9 @@ use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_task::TaskClient;
use std::os::raw::c_int as RawFd;
use std::sync::Arc;
use tungstenite::Message;
#[cfg(unix)]
use std::os::fd::AsRawFd;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))]
@@ -40,22 +37,9 @@ type WsConn = JSWebsocket;
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)]
match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
&_ => unreachable!(
"If tls features are enabled, the inner stream needs to be unpacked into raw fd"
),
}
#[cfg(not(unix))]
None
}
pub(crate) struct PartiallyDelegated {
sink_half: SplitSink<WsConn, Message>,
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
ws_fd: Option<RawFd>,
}
impl PartiallyDelegated {
@@ -108,8 +92,6 @@ impl PartiallyDelegated {
let (notify_sender, notify_receiver) = oneshot::channel();
let (stream_sender, stream_receiver) = oneshot::channel();
let ws_fd = ws_fd(&conn);
let (sink, mut stream) = conn.split();
let mixnet_receiver_future = async move {
@@ -159,16 +141,11 @@ impl PartiallyDelegated {
tokio::spawn(mixnet_receiver_future);
PartiallyDelegated {
ws_fd,
sink_half: sink,
delegated_stream: (stream_receiver, notify_sender),
}
}
pub(crate) fn ws_fd(&self) -> Option<RawFd> {
self.ws_fd
}
// if we want to send a message and don't care about response, we can don't need to reunite the split,
// the sink itself is enough
pub(crate) async fn send_without_response(
@@ -31,6 +31,7 @@ log = { workspace = true }
url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["sync", "time"] }
futures = { workspace = true }
openssl = { version = "^0.10.55", features = ["vendored"], optional = true }
nym-coconut = { path = "../../nymcoconut" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -89,7 +90,7 @@ required-features = ["http-client"]
[features]
default = ["http-client"]
http-client = ["cosmrs/rpc"]
http-client = ["cosmrs/rpc", "openssl"]
generate-ts = []
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
@@ -397,7 +397,7 @@ pub trait NymApiClientExt: ApiClient {
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_FREE_PASS,
routes::COCONUT_FREE_PASS_NONCE,
],
NO_PARAMS,
request,
@@ -7,20 +7,19 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use cosmwasm_std::Addr;
use log::trace;
use nym_coconut_dkg_common::types::{ChunkIndex, NodeIndex, StateAdvanceResponse};
use nym_coconut_dkg_common::types::ChunkIndex;
use serde::Deserialize;
use nym_coconut_dkg_common::dealer::RegisteredDealerDetails;
pub use nym_coconut_dkg_common::{
dealer::{DealerDetailsResponse, PagedDealerIndexResponse, PagedDealerResponse},
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
DealingMetadataResponse, DealingStatusResponse,
},
msg::QueryMsg as DkgQueryMsg,
types::{DealerDetails, DealingIndex, Epoch, EpochId, EpochState, State},
types::{
DealerDetails, DealingIndex, Epoch, EpochId, EpochState, InitialReplacementData, State,
},
verification_key::{ContractVKShare, PagedVKSharesResponse, VkShareResponse},
};
@@ -41,25 +40,13 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn can_advance_state(&self) -> Result<StateAdvanceResponse, NyxdError> {
let request = DkgQueryMsg::CanAdvanceState {};
self.query_dkg_contract(request).await
}
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NyxdError> {
let request = DkgQueryMsg::GetCurrentEpochThreshold {};
self.query_dkg_contract(request).await
}
async fn get_registered_dealer_details(
&self,
address: &AccountId,
epoch_id: Option<EpochId>,
) -> Result<RegisteredDealerDetails, NyxdError> {
let request = DkgQueryMsg::GetRegisteredDealer {
dealer_address: address.to_string(),
epoch_id,
};
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>, NyxdError> {
let request = DkgQueryMsg::GetInitialDealers {};
self.query_dkg_contract(request).await
}
@@ -82,12 +69,12 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn get_dealer_indices_paged(
async fn get_past_dealers_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealerIndexResponse, NyxdError> {
let request = DkgQueryMsg::GetDealerIndices { start_after, limit };
) -> Result<PagedDealerResponse, NyxdError> {
let request = DkgQueryMsg::GetPastDealers { start_after, limit };
self.query_dkg_contract(request).await
}
@@ -203,8 +190,8 @@ pub trait PagedDkgQueryClient: DkgQueryClient {
collect_paged!(self, get_current_dealers_paged, dealers)
}
async fn get_all_dealer_indices(&self) -> Result<Vec<(Addr, NodeIndex)>, NyxdError> {
collect_paged!(self, get_dealer_indices_paged, indices)
async fn get_all_past_dealers(&self) -> Result<Vec<DealerDetails>, NyxdError> {
collect_paged!(self, get_past_dealers_paged, dealers)
}
async fn get_all_verification_key_shares(
@@ -231,7 +218,6 @@ where
let dkg_contract_address = &self
.dkg_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?;
trace!("using the following dkg contract: {dkg_contract_address}");
self.query_contract_smart(dkg_contract_address, &query)
.await
}
@@ -252,24 +238,18 @@ mod tests {
match msg {
DkgQueryMsg::GetState {} => client.get_state().ignore(),
DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(),
DkgQueryMsg::CanAdvanceState {} => client.can_advance_state().ignore(),
DkgQueryMsg::GetCurrentEpochThreshold {} => {
client.get_current_epoch_threshold().ignore()
}
DkgQueryMsg::GetRegisteredDealer {
dealer_address,
epoch_id,
} => client
.get_registered_dealer_details(&dealer_address.parse().unwrap(), epoch_id)
.ignore(),
DkgQueryMsg::GetInitialDealers {} => client.get_initial_dealers().ignore(),
DkgQueryMsg::GetDealerDetails { dealer_address } => client
.get_dealer_details(&dealer_address.parse().unwrap())
.ignore(),
DkgQueryMsg::GetCurrentDealers { limit, start_after } => client
.get_current_dealers_paged(start_after, limit)
.ignore(),
DkgQueryMsg::GetDealerIndices { limit, start_after } => {
client.get_dealer_indices_paged(start_after, limit).ignore()
DkgQueryMsg::GetPastDealers { limit, start_after } => {
client.get_past_dealers_paged(start_after, limit).ignore()
}
DkgQueryMsg::GetDealingStatus {
epoch_id,
@@ -39,6 +39,13 @@ pub trait DkgSigningClient {
.await
}
async fn surpass_threshold(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::SurpassedThreshold {};
self.execute_dkg_contract(fee, req, "surpass DKG threshold".to_string(), vec![])
.await
}
async fn register_dealer(
&self,
bte_key: EncodedBTEPublicKeyWithProof,
@@ -78,9 +85,10 @@ pub trait DkgSigningClient {
async fn submit_dealing_chunk(
&self,
chunk: PartialContractDealing,
resharing: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::CommitDealingsChunk { chunk };
let req = DkgExecuteMsg::CommitDealingsChunk { chunk, resharing };
self.execute_dkg_contract(fee, req, "dealing chunk commitment".to_string(), vec![])
.await
@@ -122,20 +130,6 @@ pub trait DkgSigningClient {
)
.await
}
async fn trigger_dkg_reset(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::TriggerReset {};
self.execute_dkg_contract(fee, req, "trigger DKG reset".to_string(), vec![])
.await
}
async fn trigger_dkg_resharing(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::TriggerResharing {};
self.execute_dkg_contract(fee, req, "trigger DKG resharing".to_string(), vec![])
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -198,8 +192,8 @@ mod tests {
} => client
.submit_dealing_metadata(dealing_index, chunks, resharing, None)
.ignore(),
DkgExecuteMsg::CommitDealingsChunk { chunk } => {
client.submit_dealing_chunk(chunk, None).ignore()
DkgExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
client.submit_dealing_chunk(chunk, resharing, None).ignore()
}
DkgExecuteMsg::CommitVerificationKeyShare { share, resharing } => client
.submit_verification_key_share(share, resharing, None)
@@ -207,9 +201,8 @@ mod tests {
DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => client
.verify_verification_key_share(&owner.parse().unwrap(), resharing, None)
.ignore(),
DkgExecuteMsg::SurpassedThreshold {} => client.surpass_threshold(None).ignore(),
DkgExecuteMsg::AdvanceEpochState {} => client.advance_dkg_epoch_state(None).ignore(),
DkgExecuteMsg::TriggerReset {} => client.trigger_dkg_reset(None).ignore(),
DkgExecuteMsg::TriggerResharing {} => client.trigger_dkg_resharing(None).ignore(),
};
}
}
@@ -16,6 +16,7 @@ use crate::signing::tx_signer::TxSigner;
use crate::signing::AccountData;
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
use async_trait::async_trait;
use cosmrs::cosmwasm;
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
use cosmrs::tx::{Raw, SignDoc};
use cosmwasm_std::Addr;
@@ -39,7 +40,7 @@ pub use crate::rpc::TendermintRpcClient;
pub use coin::Coin;
pub use cosmrs::{
bank::MsgSend,
bip32, cosmwasm,
bip32,
crypto::PublicKey,
query::{PageRequest, PageResponse},
tendermint::{
+1 -2
View File
@@ -9,7 +9,7 @@ license.workspace = true
anyhow = { workspace = true }
base64 = "0.13.0"
bip39 = { workspace = true }
bs58 = { workspace = true }
bs58 = "0.4"
comfy-table = "6.0.0"
cfg-if = "1.0.0"
clap = { workspace = true, features = ["derive"] }
@@ -55,7 +55,6 @@ nym-credentials = { path = "../../common/credentials" }
nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -35,12 +35,10 @@ fn parse_rfc3339_expiration_date(raw: &str) -> Result<OffsetDateTime, time::erro
#[clap(group(ArgGroup::new("expiration").required(true)))]
pub struct Args {
/// Specifies the expiration date of the free pass(es)
/// Can't be set to more than a week into the future.
/// Requires
#[clap(long, group = "expiration", value_parser = parse_rfc3339_expiration_date)]
pub(crate) expiration_date: Option<OffsetDateTime>,
/// The expiration of the free pass(es) expresses as unix timestamp.
/// Can't be set to more than a week into the future.
#[clap(long, group = "expiration")]
pub(crate) expiration_timestamp: Option<i64>,
@@ -1,64 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
-2
View File
@@ -4,7 +4,6 @@
use clap::{Args, Subcommand};
pub mod generate_freepass;
pub mod import_credential;
pub mod issue_credentials;
pub mod recover_credentials;
@@ -20,5 +19,4 @@ pub enum CoconutCommands {
GenerateFreepass(generate_freepass::Args),
IssueCredentials(issue_credentials::Args),
RecoverCredentials(recover_credentials::Args),
ImportCredential(import_credential::Args),
}
@@ -14,32 +14,20 @@ pub struct DealerDetails {
pub assigned_index: NodeIndex,
}
#[cw_serde]
pub struct DealerRegistrationDetails {
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
pub ed25519_identity: String,
pub announce_address: String,
}
#[cw_serde]
#[derive(Copy)]
pub enum DealerType {
Current { assigned_index: NodeIndex },
Past { assigned_index: NodeIndex },
Current,
Past,
Unknown,
}
impl DealerType {
pub fn is_current(&self) -> bool {
matches!(&self, DealerType::Current { .. })
matches!(&self, DealerType::Current)
}
}
#[cw_serde]
pub struct RegisteredDealerDetails {
pub details: Option<DealerRegistrationDetails>,
}
#[cw_serde]
pub struct DealerDetailsResponse {
pub details: Option<DealerDetails>,
@@ -77,20 +65,3 @@ impl PagedDealerResponse {
}
}
}
#[cw_serde]
pub struct PagedDealerIndexResponse {
pub indices: Vec<(Addr, NodeIndex)>,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<Addr>,
}
impl PagedDealerIndexResponse {
pub fn new(indices: Vec<(Addr, NodeIndex)>, start_next_after: Option<Addr>) -> Self {
PagedDealerIndexResponse {
indices,
start_next_after,
}
}
}
@@ -11,15 +11,12 @@ use cosmwasm_schema::cw_serde;
#[cfg(feature = "schema")]
use crate::{
dealer::{
DealerDetailsResponse, PagedDealerIndexResponse, PagedDealerResponse,
RegisteredDealerDetails,
},
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
DealingMetadataResponse, DealingStatusResponse,
},
types::{Epoch, State, StateAdvanceResponse},
types::{Epoch, InitialReplacementData, State},
verification_key::{PagedVKSharesResponse, VkShareResponse},
};
#[cfg(feature = "schema")]
@@ -56,6 +53,7 @@ pub enum ExecuteMsg {
CommitDealingsChunk {
chunk: PartialContractDealing,
resharing: bool,
},
CommitVerificationKeyShare {
@@ -68,11 +66,9 @@ pub enum ExecuteMsg {
resharing: bool,
},
SurpassedThreshold {},
AdvanceEpochState {},
TriggerReset {},
TriggerResharing {},
}
#[cw_serde]
@@ -87,14 +83,8 @@ pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(u64))]
GetCurrentEpochThreshold {},
#[cfg_attr(feature = "schema", returns(StateAdvanceResponse))]
CanAdvanceState {},
#[cfg_attr(feature = "schema", returns(RegisteredDealerDetails))]
GetRegisteredDealer {
dealer_address: String,
epoch_id: Option<EpochId>,
},
#[cfg_attr(feature = "schema", returns(Option<InitialReplacementData>))]
GetInitialDealers {},
#[cfg_attr(feature = "schema", returns(DealerDetailsResponse))]
GetDealerDetails { dealer_address: String },
@@ -105,8 +95,8 @@ pub enum QueryMsg {
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(PagedDealerIndexResponse))]
GetDealerIndices {
#[cfg_attr(feature = "schema", returns(PagedDealerResponse))]
GetPastDealers {
limit: Option<u32>,
start_after: Option<String>,
},
@@ -5,7 +5,7 @@ use cosmwasm_schema::cw_serde;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
pub use crate::dealer::{DealerDetails, DealerRegistrationDetails, PagedDealerResponse};
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
pub use contracts_common::dealings::ContractSafeBytes;
pub use cosmwasm_std::{Addr, Coin, Timestamp};
pub use cw4::Cw4Contract;
@@ -22,19 +22,9 @@ pub type ChunkIndex = u16;
pub type PartialContractDealingData = ContractSafeBytes;
#[cw_serde]
#[derive(Copy, Default)]
pub struct StateAdvanceResponse {
pub current_state: EpochState,
pub progress: StateProgress,
pub deadline: Option<Timestamp>,
pub reached_deadline: bool,
pub is_complete: bool,
}
impl StateAdvanceResponse {
pub fn can_advance(&self) -> bool {
self.reached_deadline || self.is_complete
}
pub struct InitialReplacementData {
pub initial_dealers: Vec<Addr>,
pub initial_height: u64,
}
#[cw_serde]
@@ -50,26 +40,6 @@ pub struct TimeConfiguration {
pub in_progress_time_secs: u64,
}
impl TimeConfiguration {
pub fn state_duration(&self, state: EpochState) -> Option<u64> {
match state {
EpochState::WaitingInitialisation => None,
EpochState::PublicKeySubmission { .. } => Some(self.public_key_submission_time_secs),
EpochState::DealingExchange { .. } => Some(self.dealing_exchange_time_secs),
EpochState::VerificationKeySubmission { .. } => {
Some(self.verification_key_submission_time_secs)
}
EpochState::VerificationKeyValidation { .. } => {
Some(self.verification_key_validation_time_secs)
}
EpochState::VerificationKeyFinalization { .. } => {
Some(self.verification_key_finalization_time_secs)
}
EpochState::InProgress => Some(self.in_progress_time_secs),
}
}
}
impl FromStr for TimeConfiguration {
type Err = String;
@@ -117,41 +87,13 @@ pub struct State {
pub key_size: u32,
}
#[cw_serde]
#[derive(Copy, Default)]
pub struct StateProgress {
/// Counts the number of dealers that have registered in this epoch.
// ideally we want to have here all group members
pub registered_dealers: u32,
/// Counts the number of resharing dealers that have registered in this epoch.
/// This field is only populated during a resharing exchange.
/// It is always <= registered_dealers.
pub registered_resharing_dealers: u32,
/// Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.
// we expect registered_dealers * state.key_size number of dealings here (each dealer has to submit key_size number of dealings)
pub submitted_dealings: u32,
/// Counts the number of submitted verification key shared from the dealers.
// we expect registered_dealers number of keys here
pub submitted_key_shares: u32,
/// Counts the number of verified key shares.
// we expect submitted_key_shares number of verified keys here
pub verified_keys: u32,
}
#[cw_serde]
#[derive(Copy, Default)]
pub struct Epoch {
pub state: EpochState,
pub epoch_id: EpochId,
pub state_progress: StateProgress,
pub time_configuration: TimeConfiguration,
#[serde(alias = "finish_timestamp")]
pub deadline: Option<Timestamp>,
pub finish_timestamp: Option<Timestamp>,
}
impl Epoch {
@@ -161,45 +103,35 @@ impl Epoch {
time_configuration: TimeConfiguration,
current_timestamp: Timestamp,
) -> Self {
let duration = time_configuration.state_duration(state);
let duration = match state {
EpochState::WaitingInitialisation => None,
EpochState::PublicKeySubmission { .. } => {
Some(time_configuration.public_key_submission_time_secs)
}
EpochState::DealingExchange { .. } => {
Some(time_configuration.dealing_exchange_time_secs)
}
EpochState::VerificationKeySubmission { .. } => {
Some(time_configuration.verification_key_submission_time_secs)
}
EpochState::VerificationKeyValidation { .. } => {
Some(time_configuration.verification_key_validation_time_secs)
}
EpochState::VerificationKeyFinalization { .. } => {
Some(time_configuration.verification_key_finalization_time_secs)
}
EpochState::InProgress => Some(time_configuration.in_progress_time_secs),
};
Epoch {
state,
epoch_id,
state_progress: Default::default(),
time_configuration,
deadline: duration.map(|d| current_timestamp.plus_seconds(d)),
finish_timestamp: duration.map(|d| current_timestamp.plus_seconds(d)),
}
}
pub fn update(mut self, next_state: EpochState, current_timestamp: Timestamp) -> Self {
self.state = next_state;
let duration = self.time_configuration.state_duration(next_state);
self.deadline = duration.map(|d| current_timestamp.plus_seconds(d));
self
}
pub fn next_reset(self, current_timestamp: Timestamp) -> Self {
Epoch::new(
EpochState::PublicKeySubmission { resharing: false },
self.epoch_id + 1,
self.time_configuration,
current_timestamp,
)
}
pub fn next_resharing(self, current_timestamp: Timestamp) -> Self {
Epoch::new(
EpochState::PublicKeySubmission { resharing: true },
self.epoch_id + 1,
self.time_configuration,
current_timestamp,
)
}
pub fn final_timestamp_secs(&self) -> Option<u64> {
let mut finish = self.deadline?.seconds();
let mut finish = self.finish_timestamp?.seconds();
let time_configuration = self.time_configuration;
let mut curr_epoch_state = self.state;
while let Some(state) = curr_epoch_state.next() {
@@ -324,8 +256,4 @@ impl EpochState {
pub fn is_in_progress(&self) -> bool {
matches!(self, EpochState::InProgress)
}
pub fn is_dealing_exchange(&self) -> bool {
matches!(self, EpochState::DealingExchange { .. })
}
}
@@ -8,7 +8,7 @@ license = { workspace = true }
repository = { workspace = true }
[dependencies]
bs58 = { workspace = true }
bs58 = "0.4.0"
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
schemars = "0.8"
@@ -13,6 +13,5 @@ CREATE TABLE coconut_credentials
credential_type TEXT NOT NULL,
credential_data BLOB NOT NULL,
epoch_id INTEGER NOT NULL,
consumed BOOLEAN NOT NULL,
expired BOOLEAN NOT NULL
consumed BOOLEAN NOT NULL
);
@@ -48,18 +48,13 @@ impl CoconutCredentialManager {
credential_type,
epoch_id,
consumed: false,
expired: false,
})
}
/// Tries to retrieve one of the stored, unused credentials.
pub async fn get_next_unspent_credential(&self) -> Option<StoredIssuedCredential> {
let creds = self.inner.read().await;
creds
.data
.iter()
.find(|c| !c.consumed && !c.expired)
.cloned()
creds.data.iter().find(|c| !c.consumed).cloned()
}
/// Consumes in the database the specified credential.
@@ -73,16 +68,4 @@ impl CoconutCredentialManager {
cred.consumed = true;
}
}
/// Marks the specified credential as expired
///
/// # Arguments
///
/// * `id`: Id of the credential to mark as expired.
pub async fn mark_expired(&self, id: i64) {
let mut creds = self.inner.write().await;
if let Some(cred) = creds.data.get_mut(id as usize) {
cred.expired = true;
}
}
}
@@ -27,8 +27,8 @@ impl CoconutCredentialManager {
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, consumed, expired)
VALUES (?, ?, ?, ?, false, false)
INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, consumed)
VALUES (?, ?, ?, ?, false)
"#,
serialization_revision, credential_type, credential_data, epoch_id
).execute(&self.connection_pool).await?;
@@ -38,11 +38,9 @@ impl CoconutCredentialManager {
pub async fn get_next_unspent_credential(
&self,
) -> Result<Option<StoredIssuedCredential>, sqlx::Error> {
sqlx::query_as(
"SELECT * FROM coconut_credentials WHERE NOT consumed AND NOT expired LIMIT 1",
)
.fetch_optional(&self.connection_pool)
.await
sqlx::query_as("SELECT * FROM coconut_credentials WHERE NOT consumed LIMIT 1")
.fetch_optional(&self.connection_pool)
.await
}
/// Consumes in the database the specified credential.
@@ -59,19 +57,4 @@ impl CoconutCredentialManager {
.await?;
Ok(())
}
/// Marks the specified credential as expired
///
/// # Arguments
///
/// * `id`: Id of the credential to mark as expired.
pub async fn mark_expired(&self, id: i64) -> Result<(), sqlx::Error> {
sqlx::query!(
"UPDATE coconut_credentials SET expired = TRUE WHERE id = ?",
id
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
}
@@ -44,11 +44,14 @@ impl Storage for EphemeralStorage {
async fn get_next_unspent_credential(
&self,
) -> Result<Option<StoredIssuedCredential>, Self::StorageError> {
Ok(self
) -> Result<StoredIssuedCredential, Self::StorageError> {
let credential = self
.coconut_credential_manager
.get_next_unspent_credential()
.await)
.await
.ok_or(StorageError::NoCredential)?;
Ok(credential)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
@@ -58,10 +61,4 @@ impl Storage for EphemeralStorage {
Ok(())
}
async fn mark_expired(&self, id: i64) -> Result<(), Self::StorageError> {
self.coconut_credential_manager.mark_expired(id).await;
Ok(())
}
}
-1
View File
@@ -27,7 +27,6 @@ pub struct StoredIssuedCredential {
pub epoch_id: u32,
pub consumed: bool,
pub expired: bool,
}
pub struct StorableIssuedCredential<'a> {
@@ -76,11 +76,14 @@ impl Storage for PersistentStorage {
async fn get_next_unspent_credential(
&self,
) -> Result<Option<StoredIssuedCredential>, Self::StorageError> {
Ok(self
) -> Result<StoredIssuedCredential, Self::StorageError> {
let credential = self
.coconut_credential_manager
.get_next_unspent_credential()
.await?)
.await?
.ok_or(StorageError::NoCredential)?;
Ok(credential)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
@@ -90,10 +93,4 @@ impl Storage for PersistentStorage {
Ok(())
}
async fn mark_expired(&self, id: i64) -> Result<(), Self::StorageError> {
self.coconut_credential_manager.mark_expired(id).await?;
Ok(())
}
}
+2 -10
View File
@@ -14,11 +14,10 @@ pub trait Storage: Send + Sync {
bandwidth_credential: StorableIssuedCredential<'a>,
) -> Result<(), Self::StorageError>;
/// Tries to retrieve one of the stored, unused credentials,
/// that is also not marked as expired
/// Tries to retrieve one of the stored, unused credentials.
async fn get_next_unspent_credential(
&self,
) -> Result<Option<StoredIssuedCredential>, Self::StorageError>;
) -> Result<StoredIssuedCredential, Self::StorageError>;
/// Marks as consumed in the database the specified credential.
///
@@ -26,11 +25,4 @@ pub trait Storage: Send + Sync {
///
/// * `id`: Id of the credential to be consumed.
async fn consume_coconut_credential(&self, id: i64) -> Result<(), Self::StorageError>;
/// Marks the specified credential as expired
///
/// # Arguments
///
/// * `id`: Id of the credential to mark as expired.
async fn mark_expired(&self, id: i64) -> Result<(), Self::StorageError>;
}
@@ -53,7 +53,7 @@ impl RecoveryStorage {
pub fn voucher_filename(voucher: &IssuanceBandwidthCredential) -> String {
let prefix = voucher.typ().to_string();
let suffix = voucher.blinded_serial_number_bs58();
let suffix = voucher.blinded_g1_serial_number_bs58();
format!("{prefix}-{suffix}.{DUMPED_VOUCHER_EXTENSION}")
}
+1 -1
View File
@@ -92,7 +92,7 @@ where
.as_secs();
if epoch.state.is_final() {
if let Some(finish_timestamp) = epoch.deadline {
if let Some(finish_timestamp) = epoch.finish_timestamp {
if current_timestamp_secs + SAFETY_BUFFER_SECS >= finish_timestamp.seconds() {
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
exit(0);
+3 -7
View File
@@ -10,9 +10,9 @@ use thiserror::Error;
pub use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, keygen,
prepare_blind_sign, prove_bandwidth_credential, verify_credential, Attribute, Base58,
BlindSignRequest, BlindedSerialNumber, BlindedSignature, Bytable, CoconutError, KeyPair,
Parameters, PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare,
VerificationKey, VerifyCredentialRequest,
BlindSignRequest, BlindedSignature, Bytable, CoconutError, KeyPair, Parameters,
PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare, VerificationKey,
VerifyCredentialRequest,
};
pub const VOUCHER_INFO_TYPE: &str = "BandwidthVoucher";
@@ -129,8 +129,4 @@ impl CredentialSpendingData {
// the first attribute is variant specific bandwidth encoding, the second one should be the type
self.public_attributes_plain.first()
}
pub fn blinded_serial_number(&self) -> BlindedSerialNumber {
self.verify_credential_request.blinded_serial_number()
}
}
@@ -14,7 +14,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub const MAX_FREE_PASS_VALIDITY: Duration = Duration::WEEK; // 1 week
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct FreePassIssuedData {
/// the plain validity value of this credential expressed as unix timestamp
#[zeroize(skip)]
@@ -30,14 +30,6 @@ impl<'a> From<&'a FreePassIssuanceData> for FreePassIssuedData {
}
impl FreePassIssuedData {
pub fn expired(&self) -> bool {
self.expiry_date <= OffsetDateTime::now_utc()
}
pub fn expiry_date(&self) -> OffsetDateTime {
self.expiry_date
}
pub fn expiry_date_plain(&self) -> String {
self.expiry_date.unix_timestamp().to_string()
}
@@ -93,7 +85,7 @@ impl FreePassIssuanceData {
pub async fn obtain_free_pass_nonce(
&self,
client: &nym_validator_client::client::NymApiClient,
) -> Result<[u8; 16], Error> {
) -> Result<u32, Error> {
let server_response = client.free_pass_nonce().await?;
Ok(server_response.current_nonce)
}
@@ -102,11 +94,12 @@ impl FreePassIssuanceData {
&self,
signing_request: &CredentialSigningData,
account_data: &AccountData,
issuer_nonce: [u8; 16],
issuer_nonce: u32,
) -> Result<FreePassRequest, Error> {
let plaintext = issuer_nonce.to_be_bytes();
let nonce_signature = account_data
.private_key()
.sign(&issuer_nonce)
.sign(&plaintext)
.map_err(|_| Error::Secp256k1SignFailure)?;
Ok(FreePassRequest {
@@ -9,10 +9,10 @@ use crate::coconut::bandwidth::{
};
use crate::coconut::utils::scalar_serde_helper;
use crate::error::Error;
use bls12_381::G1Projective;
use nym_credentials_interface::{
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSerialNumber,
BlindedSignature, Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare,
VerificationKey,
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSignature,
Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey,
};
use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nym_api::EpochId;
@@ -140,14 +140,15 @@ impl IssuanceBandwidthCredential {
Self::new(FreePassIssuanceData::new(expiry_date))
}
pub fn blind_serial_number(&self) -> BlindedSerialNumber {
(bandwidth_credential_params().gen2() * self.serial_number).into()
pub fn blind_serial_number_in_g1subgroup(&self) -> G1Projective {
bandwidth_credential_params().gen1() * self.serial_number
}
pub fn blinded_serial_number_bs58(&self) -> String {
// NOT TO BE CONFUSED WITH BLINDED SERIAL NUMBER IN CREDENTIAL ITSELF
pub fn blinded_g1_serial_number_bs58(&self) -> String {
use nym_credentials_interface::Base58;
self.blind_serial_number().to_bs58()
self.blind_serial_number_in_g1subgroup().to_bs58()
}
pub fn typ(&self) -> CredentialType {
@@ -315,12 +316,9 @@ impl IssuanceBandwidthCredential {
}
// TODO: is that actually needed?
// idea: make it consistent with the issued credential and its vX serde
pub fn try_from_recovered_bytes(bytes: &[u8]) -> Result<Self, Error> {
use bincode::Options;
make_recovery_bincode_serializer()
.deserialize(bytes)
.map_err(|source| Error::RecoveryCredentialDeserializationFailure { source })
Ok(make_recovery_bincode_serializer().deserialize(bytes)?)
}
}
@@ -330,18 +328,3 @@ fn make_recovery_bincode_serializer() -> impl bincode::Options {
.with_big_endian()
.with_varint_encoding()
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
fn assert_zeroize<T: Zeroize>() {}
#[test]
fn credential_is_zeroized() {
assert_zeroize::<IssuanceBandwidthCredential>();
assert_zeroize_on_drop::<IssuanceBandwidthCredential>();
}
}
@@ -20,7 +20,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub const CURRENT_SERIALIZATION_REVISION: u8 = 1;
#[derive(Debug, Zeroize, Serialize, Deserialize)]
#[derive(Zeroize, Serialize, Deserialize)]
pub enum BandwidthCredentialIssuedDataVariant {
Voucher(BandwidthVoucherIssuedData),
FreePass(FreePassIssuedData),
@@ -116,23 +116,6 @@ impl IssuedBandwidthCredential {
}
}
pub fn try_unpack(bytes: &[u8], revision: impl Into<Option<u8>>) -> Result<Self, Error> {
let revision = revision.into().unwrap_or(CURRENT_SERIALIZATION_REVISION);
match revision {
1 => Self::unpack_v1(bytes),
_ => Err(Error::UnknownSerializationRevision { revision }),
}
}
pub fn epoch_id(&self) -> EpochId {
self.epoch_id
}
pub fn variant_data(&self) -> &BandwidthCredentialIssuedDataVariant {
&self.variant_data
}
pub fn current_serialization_revision(&self) -> u8 {
CURRENT_SERIALIZATION_REVISION
}
@@ -147,12 +130,7 @@ impl IssuedBandwidthCredential {
/// Unpack (deserialize) the credential data from the given bytes using v1 serializer.
pub fn unpack_v1(bytes: &[u8]) -> Result<Self, Error> {
use bincode::Options;
make_storable_bincode_serializer()
.deserialize(bytes)
.map_err(|source| Error::SerializationFailure {
source,
revision: 1,
})
Ok(make_storable_bincode_serializer().deserialize(bytes)?)
}
pub fn randomise_signature(&mut self) {
@@ -205,18 +183,3 @@ fn make_storable_bincode_serializer() -> impl bincode::Options {
.with_big_endian()
.with_varint_encoding()
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
fn assert_zeroize<T: Zeroize>() {}
#[test]
fn credential_is_zeroized() {
assert_zeroize::<IssuedBandwidthCredential>();
assert_zeroize_on_drop::<IssuedBandwidthCredential>();
}
}
@@ -13,7 +13,7 @@ use nym_validator_client::nyxd::{Coin, Hash};
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct BandwidthVoucherIssuedData {
/// the plain value (e.g., bandwidth) encoded in this voucher
// note: for legacy reasons we're only using the value of the coin and ignoring the denom
@@ -30,10 +30,6 @@ impl<'a> From<&'a BandwidthVoucherIssuanceData> for BandwidthVoucherIssuedData {
}
impl BandwidthVoucherIssuedData {
pub fn value(&self) -> &Coin {
&self.value
}
pub fn value_plain(&self) -> String {
self.value.amount.to_string()
}
+2 -13
View File
@@ -5,7 +5,6 @@ use nym_credentials_interface::CoconutError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_validator_client::ValidatorClientError;
use crate::coconut::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -13,18 +12,8 @@ pub enum Error {
#[error("IO error")]
IOError(#[from] std::io::Error),
#[error("failed to deserialize a recovery credential: {source}")]
RecoveryCredentialDeserializationFailure { source: bincode::Error },
#[error("failed to (de)serialize provided credential using revision {revision}: {source}")]
SerializationFailure {
#[source]
source: bincode::Error,
revision: u8,
},
#[error("unknown credential serializatio revision {revision}. the current (and max supported) version is {CURRENT_SERIALIZATION_REVISION}")]
UnknownSerializationRevision { revision: u8 },
#[error("failed to (de)serialize credential structure: {0}")]
SerializationFailure(#[from] bincode::Error),
#[error("The detailed description is yet to be determined")]
BandwidthCredentialError,
-1
View File
@@ -9,4 +9,3 @@ pub use coconut::bandwidth::{
IssuedBandwidthCredential,
};
pub use coconut::utils::{obtain_aggregate_signature, obtain_aggregate_verification_key};
pub use error::Error;
+1 -1
View File
@@ -9,7 +9,7 @@ repository = { workspace = true }
[dependencies]
aes = { version = "0.8.1", optional = true }
bs58 = { workspace = true }
bs58 = "0.4.0"
blake3 = { version = "1.3.1", features = ["traits-preview"], optional = true }
ctr = { version = "0.9.1", optional = true }
digest = { version = "0.10.3", optional = true }
+1 -1
View File
@@ -14,7 +14,7 @@ bitvec = "1.0.0"
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
bls12_381 = { workspace = true, default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common", optional = true }
bs58 = { workspace = true }
bs58 = "0.4"
lazy_static = "1.4.0"
-1
View File
@@ -11,7 +11,6 @@ license.workspace = true
[dependencies]
bincode = "1.3.3"
bytes = "1.5.0"
nym-bin-common = { path = "../bin-common" }
nym-sphinx = { path = "../nymsphinx" }
rand = "0.8.5"
serde = { workspace = true, features = ["derive"] }
+1 -25
View File
@@ -1,32 +1,8 @@
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
pub mod codec;
pub mod request;
pub mod response;
// version 3: initial version
// version 4: IPv6 support
pub const CURRENT_VERSION: u8 = 4;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
pub ipv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
}
impl IpPair {
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
IpPair { ipv4, ipv6 }
}
}
impl Display for IpPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "IPv4: {}, IPV6: {}", self.ipv4, self.ipv6)
}
}
pub const CURRENT_VERSION: u8 = 2;
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
+15 -63
View File
@@ -1,7 +1,9 @@
use std::net::IpAddr;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION};
use crate::{make_bincode_serializer, CURRENT_VERSION};
fn generate_random() -> u64 {
use rand::RngCore;
@@ -17,11 +19,10 @@ pub struct IpPacketRequest {
impl IpPacketRequest {
pub fn new_static_connect_request(
ips: IpPair,
ip: IpAddr,
reply_to: Recipient,
reply_to_hops: Option<u8>,
reply_to_avg_mix_delays: Option<f64>,
buffer_timeout: Option<u64>,
) -> (Self, u64) {
let request_id = generate_random();
(
@@ -29,11 +30,10 @@ impl IpPacketRequest {
version: CURRENT_VERSION,
data: IpPacketRequestData::StaticConnect(StaticConnectRequest {
request_id,
ips,
ip,
reply_to,
reply_to_hops,
reply_to_avg_mix_delays,
buffer_timeout,
}),
},
request_id,
@@ -44,7 +44,6 @@ impl IpPacketRequest {
reply_to: Recipient,
reply_to_hops: Option<u8>,
reply_to_avg_mix_delays: Option<f64>,
buffer_timeout: Option<u64>,
) -> (Self, u64) {
let request_id = generate_random();
(
@@ -55,7 +54,6 @@ impl IpPacketRequest {
reply_to,
reply_to_hops,
reply_to_avg_mix_delays,
buffer_timeout,
}),
},
request_id,
@@ -76,10 +74,10 @@ impl IpPacketRequest {
)
}
pub fn new_data_request(ip_packets: bytes::Bytes) -> Self {
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketRequestData::Data(DataRequest { ip_packets }),
data: IpPacketRequestData::Data(DataRequest { ip_packet }),
}
}
@@ -89,8 +87,6 @@ impl IpPacketRequest {
IpPacketRequestData::DynamicConnect(request) => Some(request.request_id),
IpPacketRequestData::Disconnect(request) => Some(request.request_id),
IpPacketRequestData::Data(_) => None,
IpPacketRequestData::Ping(request) => Some(request.request_id),
IpPacketRequestData::Health(request) => Some(request.request_id),
}
}
@@ -100,8 +96,6 @@ impl IpPacketRequest {
IpPacketRequestData::DynamicConnect(request) => Some(&request.reply_to),
IpPacketRequestData::Disconnect(request) => Some(&request.reply_to),
IpPacketRequestData::Data(_) => None,
IpPacketRequestData::Ping(request) => Some(&request.reply_to),
IpPacketRequestData::Health(request) => Some(&request.reply_to),
}
}
@@ -125,58 +119,35 @@ pub enum IpPacketRequestData {
DynamicConnect(DynamicConnectRequest),
Disconnect(DisconnectRequest),
Data(DataRequest),
Ping(PingRequest),
Health(HealthRequest),
}
// A static connect request is when the client provides the internal IP address it will use on the
// ip packet router.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct StaticConnectRequest {
pub request_id: u64,
pub ips: IpPair,
pub ip: IpAddr,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
// The number of mix node hops that responses should take, in addition to the entry and exit
// node. Zero means only client -> entry -> exit -> client.
pub reply_to_hops: Option<u8>,
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
// ip packet router.
pub reply_to_avg_mix_delays: Option<f64>,
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
// with ip packets.
pub buffer_timeout: Option<u64>,
}
// A dynamic connect request is when the client does not provide the internal IP address it will use
// on the ip packet router, and instead requests one to be assigned to it.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DynamicConnectRequest {
pub request_id: u64,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
// The number of mix node hops that responses should take, in addition to the entry and exit
// node. Zero means only client -> entry -> exit -> client.
pub reply_to_hops: Option<u8>,
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
// ip packet router.
pub reply_to_avg_mix_delays: Option<f64>,
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
// with ip packets.
pub buffer_timeout: Option<u64>,
}
// A disconnect request is when the client wants to disconnect from the ip packet router and free
// up the allocated IP address.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DisconnectRequest {
pub request_id: u64,
@@ -184,32 +155,14 @@ pub struct DisconnectRequest {
pub reply_to: Recipient,
}
// A data request is when the client wants to send an IP packet to a destination.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DataRequest {
pub ip_packets: bytes::Bytes,
}
// A ping request is when the client wants to check if the ip packet router is still alive.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PingRequest {
pub request_id: u64,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct HealthRequest {
pub request_id: u64,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
pub ip_packet: bytes::Bytes,
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[test]
fn check_size_of_request() {
@@ -218,15 +171,14 @@ mod tests {
data: IpPacketRequestData::StaticConnect(
StaticConnectRequest {
request_id: 123,
ips: IpPair::new(Ipv4Addr::from_str("10.0.0.1").unwrap(), Ipv6Addr::from_str("2001:db8:a160::1").unwrap()),
ip: IpAddr::from([10, 0, 0, 1]),
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
reply_to_hops: None,
reply_to_avg_mix_delays: None,
buffer_timeout: None,
},
),
)
};
assert_eq!(connect.to_bytes().unwrap().len(), 123);
assert_eq!(connect.to_bytes().unwrap().len(), 107);
}
#[test]
@@ -234,7 +186,7 @@ mod tests {
let data = IpPacketRequest {
version: 4,
data: IpPacketRequestData::Data(DataRequest {
ip_packets: bytes::Bytes::from(vec![1u8; 32]),
ip_packet: bytes::Bytes::from(vec![1u8; 32]),
}),
};
assert_eq!(data.to_bytes().unwrap().len(), 35);
@@ -245,7 +197,7 @@ mod tests {
let data = IpPacketRequest {
version: 4,
data: IpPacketRequestData::Data(DataRequest {
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
ip_packet: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
}),
};
@@ -262,7 +214,7 @@ mod tests {
assert_eq!(
deserialized.data,
IpPacketRequestData::Data(DataRequest {
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
ip_packet: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
})
);
}
+6 -104
View File
@@ -1,7 +1,9 @@
use std::net::IpAddr;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION};
use crate::{make_bincode_serializer, CURRENT_VERSION};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpPacketResponse {
@@ -36,13 +38,13 @@ impl IpPacketResponse {
}
}
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ips: IpPair) -> Self {
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ip: IpAddr) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
request_id,
reply_to,
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ips }),
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ip }),
}),
}
}
@@ -62,45 +64,6 @@ impl IpPacketResponse {
}
}
pub fn new_disconnect_success(request_id: u64, reply_to: Recipient) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Disconnect(DisconnectResponse {
request_id,
reply_to,
reply: DisconnectResponseReply::Success,
}),
}
}
pub fn new_disconnect_failure(
request_id: u64,
reply_to: Recipient,
reason: DisconnectFailureReason,
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Disconnect(DisconnectResponse {
request_id,
reply_to,
reply: DisconnectResponseReply::Failure(reason),
}),
}
}
pub fn new_unrequested_disconnect(
reply_to: Recipient,
reason: UnrequestedDisconnectReason,
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::UnrequestedDisconnect(UnrequestedDisconnect {
reply_to,
reason,
}),
}
}
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
Self {
version: CURRENT_VERSION,
@@ -143,10 +106,7 @@ impl IpPacketResponse {
IpPacketResponseData::StaticConnect(response) => Some(response.request_id),
IpPacketResponseData::DynamicConnect(response) => Some(response.request_id),
IpPacketResponseData::Disconnect(response) => Some(response.request_id),
IpPacketResponseData::UnrequestedDisconnect(_) => None,
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(response.request_id),
IpPacketResponseData::Health(response) => Some(response.request_id),
IpPacketResponseData::Error(response) => Some(response.request_id),
}
}
@@ -156,10 +116,7 @@ impl IpPacketResponse {
IpPacketResponseData::StaticConnect(response) => Some(&response.reply_to),
IpPacketResponseData::DynamicConnect(response) => Some(&response.reply_to),
IpPacketResponseData::Disconnect(response) => Some(&response.reply_to),
IpPacketResponseData::UnrequestedDisconnect(response) => Some(&response.reply_to),
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(&response.reply_to),
IpPacketResponseData::Health(response) => Some(&response.reply_to),
IpPacketResponseData::Error(response) => Some(&response.reply_to),
}
}
@@ -180,28 +137,10 @@ impl IpPacketResponse {
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum IpPacketResponseData {
// Response for a static connect request
StaticConnect(StaticConnectResponse),
// Response for a dynamic connect request
DynamicConnect(DynamicConnectResponse),
// Response for a disconnect initiqated by the client
Disconnect(DisconnectResponse),
// Message from the server that the client got disconnected without the client initiating it
UnrequestedDisconnect(UnrequestedDisconnect),
// Response to a data request
Data(DataResponse),
// Response to ping request
Pong(PongResponse),
// Response for a health request
Health(HealthResponse),
// Error response
Error(ErrorResponse),
}
@@ -261,7 +200,7 @@ impl DynamicConnectResponseReply {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DynamicConnectSuccess {
pub ips: IpPair,
pub ip: IpAddr,
}
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
@@ -295,48 +234,11 @@ pub enum DisconnectFailureReason {
Other(String),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UnrequestedDisconnect {
pub reply_to: Recipient,
pub reason: UnrequestedDisconnectReason,
}
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
pub enum UnrequestedDisconnectReason {
#[error("client mixnet traffic timeout")]
ClientMixnetTrafficTimeout,
#[error("client tun traffic timeout")]
ClientTunTrafficTimeout,
#[error("{0}")]
Other(String),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DataResponse {
pub ip_packet: bytes::Bytes,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PongResponse {
pub request_id: u64,
pub reply_to: Recipient,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HealthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: HealthResponseReply,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HealthResponseReply {
// Return the binary build information of the IPR
pub build_info: nym_bin_common::build_information::BinaryBuildInformationOwned,
// Return if the IPR has performed a successful routing test.
pub routable: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ErrorResponse {
pub request_id: u64,
-20
View File
@@ -1,20 +0,0 @@
[package]
name = "nym-id"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror.workspace = true
time.workspace = true
tracing.workspace = true
zeroize.workspace = true
nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" }
-20
View File
@@ -1,20 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use thiserror::Error;
use time::OffsetDateTime;
#[derive(Debug, Error)]
pub enum NymIdError {
#[error("failed to deserialize provided credential: {source}")]
CredentialDeserializationFailure { source: nym_credentials::Error },
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
#[error("failed to store credential in the provided store: {source}")]
StorageError {
source: Box<dyn Error + Send + Sync>,
},
}
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::NymIdError;
use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use tracing::{debug, warn};
use zeroize::Zeroizing;
pub async fn import_credential<S>(
credentials_store: S,
raw_credential: Vec<u8>,
credential_version: impl Into<Option<u8>>,
) -> Result<(), NymIdError>
where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
{
let raw_credential = Zeroizing::new(raw_credential);
// note: the type itself implements ZeroizeOnDrop
let credential = IssuedBandwidthCredential::try_unpack(&raw_credential, credential_version)
.map_err(|source| NymIdError::CredentialDeserializationFailure { source })?;
debug!(
"attempting to import credential of type {}",
credential.typ()
);
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
debug!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
warn!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(NymIdError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
// SAFETY:
// for the epoch to run over u32::MAX, we'd have to advance it for few centuries every block...
// the alternative is a very particularly malformed serialized data, but at that point blowing up is the right call
// because we can't rely on it anyway
#[allow(clippy::expect_used)]
let storable = StorableIssuedCredential {
serialization_revision: credential.current_serialization_revision(),
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store
.insert_issued_credential(storable)
.await
.map_err(|source| NymIdError::StorageError {
source: Box::new(source),
})?;
Ok(())
}
-11
View File
@@ -1,11 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub mod error;
pub mod import_credential;
pub use error::NymIdError;
pub use import_credential::import_credential;
+1 -1
View File
@@ -15,7 +15,7 @@ rand = "0.8"
thiserror = { workspace = true }
serde = { workspace = true }
serde_derive = "1.0"
bs58 = { workspace = true }
bs58 = "0.4.0"
sha2 = "0.9"
zeroize = { workspace = true, optional = true }
-1
View File
@@ -24,7 +24,6 @@ pub use scheme::setup::Parameters;
pub use scheme::verification::check_vk_pairing;
pub use scheme::verification::prove_bandwidth_credential;
pub use scheme::verification::verify_credential;
pub use scheme::verification::BlindedSerialNumber;
pub use scheme::verification::VerifyCredentialRequest;
pub use scheme::BlindedSignature;
pub use scheme::Signature;
+10 -39
View File
@@ -1,46 +1,17 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bls12_381::G2Projective;
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::error::{CoconutError, Result};
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
use bls12_381::{G2Affine, G2Projective};
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct BlindedSerialNumber(G2Projective);
// use custom Debug implementation to show base58 encoding (rather than raw curve elements)
impl Debug for BlindedSerialNumber {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("BlindedSerialNumber")
.field(&self.to_bs58())
.finish()
}
}
impl From<G2Projective> for BlindedSerialNumber {
fn from(value: G2Projective) -> Self {
BlindedSerialNumber(value)
}
}
impl From<G2Affine> for BlindedSerialNumber {
fn from(value: G2Affine) -> Self {
BlindedSerialNumber(value.into())
}
}
impl Deref for BlindedSerialNumber {
type Target = G2Projective;
fn deref(&self) -> &Self::Target {
&self.0
}
pub struct BlindedSerialNumber {
pub(crate) inner: G2Projective,
}
impl TryFrom<&[u8]> for BlindedSerialNumber {
@@ -63,13 +34,13 @@ impl TryFrom<&[u8]> for BlindedSerialNumber {
),
)?;
Ok(BlindedSerialNumber(inner))
Ok(BlindedSerialNumber { inner })
}
}
impl Bytable for BlindedSerialNumber {
fn to_byte_vec(&self) -> Vec<u8> {
self.0.to_affine().to_compressed().to_vec()
self.inner.to_affine().to_compressed().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
+25 -20
View File
@@ -1,21 +1,22 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use core::ops::Neg;
use std::convert::TryFrom;
use std::convert::TryInto;
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
use group::{Curve, Group};
use crate::error::{CoconutError, Result};
use crate::proofs::ProofKappaZeta;
use crate::scheme::double_use::BlindedSerialNumber;
use crate::scheme::setup::Parameters;
use crate::scheme::Signature;
use crate::scheme::VerificationKey;
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
use crate::Attribute;
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
use core::ops::Neg;
use group::{Curve, Group};
use std::convert::TryFrom;
use std::convert::TryInto;
pub use crate::scheme::double_use::BlindedSerialNumber;
// TODO NAMING: this whole thing
// Theta
@@ -24,7 +25,7 @@ pub struct VerifyCredentialRequest {
// blinded_message (kappa)
pub blinded_message: G2Projective,
// blinded serial number (zeta)
pub blinded_serial_number: BlindedSerialNumber,
pub blinded_serial_number: G2Projective,
// sigma
pub credential: Signature,
// pi_v
@@ -52,10 +53,15 @@ impl TryFrom<&[u8]> for VerifyCredentialRequest {
),
)?;
let blinded_serial_number_bytes = &bytes[96..192];
let blinded_serial_number =
BlindedSerialNumber::try_from_byte_slice(blinded_serial_number_bytes)?;
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::unwrap_used)]
let blinded_serial_number_bytes = bytes[96..192].try_into().unwrap();
let blinded_serial_number = try_deserialize_g2_projective(
&blinded_serial_number_bytes,
CoconutError::Deserialization(
"failed to deserialize the blinded serial number (zeta)".to_string(),
),
)?;
let credential = Signature::try_from(&bytes[192..288])?;
let pi_v = ProofKappaZeta::from_bytes(&bytes[288..])?;
@@ -81,7 +87,7 @@ impl VerifyCredentialRequest {
pub fn has_blinded_serial_number(&self, blinded_serial_number_bs58: &str) -> Result<bool> {
let blinded_serial_number = BlindedSerialNumber::try_from_bs58(blinded_serial_number_bs58)?;
let ret = self.blinded_serial_number.eq(&blinded_serial_number);
let ret = self.blinded_serial_number.eq(&blinded_serial_number.inner);
Ok(ret)
}
@@ -105,12 +111,11 @@ impl VerifyCredentialRequest {
VerifyCredentialRequest::try_from(bytes)
}
pub fn blinded_serial_number(&self) -> BlindedSerialNumber {
self.blinded_serial_number
}
pub fn blinded_serial_number_bs58(&self) -> String {
self.blinded_serial_number.to_bs58()
let blinded_serial_nuumber = BlindedSerialNumber {
inner: self.blinded_serial_number,
};
blinded_serial_nuumber.to_bs58()
}
}
@@ -193,7 +198,7 @@ pub fn prove_bandwidth_credential(
Ok(VerifyCredentialRequest {
blinded_message,
blinded_serial_number: blinded_serial_number.into(),
blinded_serial_number,
credential: signature_prime,
pi_v,
})
@@ -9,7 +9,7 @@ repository = { workspace = true }
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
bs58 = { workspace = true }
bs58 = "0.4"
serde = { workspace = true }
thiserror = { workspace = true }
-1
View File
@@ -21,7 +21,6 @@ sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros
tendermint.workspace = true
tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] }
thiserror.workspace = true
time = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tokio-stream = "0.1.14"
tokio-util = { version = "0.7.10", features = ["rt"]}
+1 -1
View File
@@ -84,7 +84,7 @@ pub enum ScraperError {
EmptyBlockData { query: String },
#[error("reached maximum number of allowed errors for subscription events")]
MaximumWebSocketFailures,
MaximumSubscriptionFailures,
#[error("failed to begin storage tx: {source}")]
StorageTxBeginFailure {
+22 -17
View File
@@ -6,10 +6,11 @@ use crate::block_requester::BlockRequester;
use crate::error::ScraperError;
use crate::modules::{BlockModule, MsgModule, TxModule};
use crate::rpc_client::RpcClient;
use crate::scraper::subscriber::ChainSubscriber;
use crate::scraper::subscriber::{run_websocket_driver, ChainSubscriber};
use crate::storage::ScraperStorage;
use std::path::PathBuf;
use std::sync::Arc;
use tendermint_rpc::WebSocketClientDriver;
use tokio::sync::mpsc::{channel, unbounded_channel};
use tokio::sync::Notify;
use tokio_util::sync::CancellationToken;
@@ -66,15 +67,20 @@ impl NyxdScraperBuilder {
block_processor.set_tx_modules(self.tx_modules);
block_processor.set_msg_modules(self.msg_modules);
let chain_subscriber = ChainSubscriber::new(
let mut chain_subscriber = ChainSubscriber::new(
&scraper.config.websocket_url,
scraper.cancel_token.clone(),
scraper.task_tracker.clone(),
processing_tx,
)
.await?;
let ws_driver = chain_subscriber.ws_driver();
scraper.start_tasks(block_requester, block_processor, chain_subscriber);
scraper.start_tasks(
block_requester,
block_processor,
chain_subscriber,
ws_driver,
);
Ok(scraper)
}
@@ -135,6 +141,7 @@ impl NyxdScraper {
mut block_requester: BlockRequester,
mut block_processor: BlockProcessor,
mut chain_subscriber: ChainSubscriber,
ws_driver: WebSocketClientDriver,
) {
self.task_tracker
.spawn(async move { block_requester.run().await });
@@ -142,7 +149,8 @@ impl NyxdScraper {
.spawn(async move { block_processor.run().await });
self.task_tracker
.spawn(async move { chain_subscriber.run().await });
self.task_tracker
.spawn(run_websocket_driver(ws_driver, self.cancel_token.clone()));
self.task_tracker.close();
}
@@ -168,16 +176,21 @@ impl NyxdScraper {
rpc_client,
)
.await?;
let chain_subscriber = ChainSubscriber::new(
let mut chain_subscriber = ChainSubscriber::new(
&self.config.websocket_url,
self.cancel_token.clone(),
self.task_tracker.clone(),
processing_tx,
)
.await?;
let ws_driver = chain_subscriber.ws_driver();
// spawn them
self.start_tasks(block_requester, block_processor, chain_subscriber);
self.start_tasks(
block_requester,
block_processor,
chain_subscriber,
ws_driver,
);
Ok(())
}
@@ -188,18 +201,10 @@ impl NyxdScraper {
}
pub async fn stop(self) {
info!("stopping the chain scraper");
info!("stopping the chain scrapper");
assert!(self.task_tracker.is_closed());
self.cancel_token.cancel();
self.task_tracker.wait().await
}
pub fn cancel_token(&self) -> CancellationToken {
self.cancel_token.clone()
}
pub fn is_cancelled(&self) -> bool {
self.cancel_token.is_cancelled()
}
}
+17 -129
View File
@@ -6,25 +6,18 @@ use crate::error::ScraperError;
use tendermint_rpc::event::Event;
use tendermint_rpc::query::EventType;
use tendermint_rpc::{SubscriptionClient, WebSocketClient, WebSocketClientDriver};
use time::{Duration, OffsetDateTime};
use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::StreamExt;
use tokio_util::sync::CancellationToken;
use tokio_util::task::TaskTracker;
use tracing::{error, info, warn};
use url::Url;
const MAX_FAILURES: usize = 10;
const MAX_RECONNECTION_ATTEMPTS: usize = 8;
const SOCKET_FAILURE_RESET: Duration = Duration::hours(2);
pub struct ChainSubscriber {
cancel: CancellationToken,
task_tracker: TaskTracker,
block_sender: UnboundedSender<BlockToProcess>,
websocket_endpoint: Url,
websocket_client: WebSocketClient,
websocket_driver: Option<WebSocketClientDriver>,
}
@@ -33,7 +26,6 @@ impl ChainSubscriber {
pub async fn new(
websocket_endpoint: &Url,
cancel: CancellationToken,
task_tracker: TaskTracker,
block_sender: UnboundedSender<BlockToProcess>,
) -> Result<Self, ScraperError> {
// sure, we could have just used websocket client entirely, but let's keep the logic for
@@ -47,9 +39,7 @@ impl ChainSubscriber {
Ok(ChainSubscriber {
cancel,
task_tracker,
block_sender,
websocket_endpoint: websocket_endpoint.clone(),
websocket_client: client,
websocket_driver: Some(driver),
})
@@ -63,48 +53,8 @@ impl ChainSubscriber {
Ok(())
}
async fn remake_connection(&mut self) -> Result<(), ScraperError> {
info!(
"attempting to reestablish connection to {}",
self.websocket_endpoint
);
let (client, driver) = WebSocketClient::new(self.websocket_endpoint.as_str())
.await
.map_err(|source| ScraperError::WebSocketConnectionFailure {
url: self.websocket_endpoint.to_string(),
source,
})?;
self.websocket_client = client;
self.websocket_driver = Some(driver);
info!(
"managed to reestablish the websocket connection to {}",
self.websocket_endpoint
);
Ok(())
}
/// Returns whether the method exited due to the cancellation
async fn run_chain_subscription(&mut self) -> Result<bool, ScraperError> {
let Some(ws_driver) = self.websocket_driver.take() else {
error!("the websocket driver hasn't been created - we probably failed to establish the connection");
return Ok(false);
};
let driver_cancel = CancellationToken::new();
let _driver_guard = driver_cancel.clone().drop_guard();
// spawn the websocket driver task
let driver_handle = {
self.task_tracker.reopen();
let handle = self
.task_tracker
.spawn(run_websocket_driver(ws_driver, driver_cancel));
self.task_tracker.close();
handle
};
tokio::pin!(driver_handle);
pub(crate) async fn run(&mut self) -> Result<(), ScraperError> {
let _drop_guard = self.cancel.clone().drop_guard();
info!("creating chain subscription");
let mut subs = self
@@ -120,17 +70,12 @@ impl ChainSubscriber {
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
// note: `_driver_guard` will get dropped here thus causing cancellation of the driver task
return Ok(true)
}
_ = &mut driver_handle => {
error!("our websocket driver has finished execution");
return Ok(self.cancel.is_cancelled())
break
}
maybe_event = subs.next() => {
let Some(maybe_event) = maybe_event else {
warn!("stopped receiving new events");
return Ok(false)
break;
};
match maybe_event {
Ok(event) => {
@@ -147,95 +92,38 @@ impl ChainSubscriber {
}
}
if failures >= MAX_FAILURES {
return Ok(false)
// note: the drop_guard will get dropped and thus cause a shutdown
return Err(ScraperError::MaximumSubscriptionFailures);
}
}
}
}
Ok(())
}
async fn websocket_backoff(&mut self, failure_count: usize) -> bool {
const MINIMUM_WAIT_MS: u64 = 10_000;
const INCREMENTAL_WAIT_MS: u64 = 30_000;
let backoff_duration_ms = MINIMUM_WAIT_MS + INCREMENTAL_WAIT_MS * failure_count as u64;
info!("going to wait {backoff_duration_ms} ms before re-attempting the reconnection");
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
true
}
_ = tokio::time::sleep(std::time::Duration::from_millis(backoff_duration_ms)) => false,
}
}
pub(crate) async fn run(&mut self) -> Result<(), ScraperError> {
let _drop_guard = self.cancel.clone().drop_guard();
let mut socket_failures = 0;
let mut last_failure = OffsetDateTime::now_utc();
loop {
if self.cancel.is_cancelled() {
return Ok(());
}
match self.run_chain_subscription().await {
Ok(cancelled) => {
if cancelled {
// we're in the middle of a shutdown
return Ok(());
}
socket_failures += 1;
}
Err(err) => {
error!("failed to create chain subscription: {err}");
socket_failures += 1;
}
}
warn!("current socket failure count: {socket_failures}. the last failure was at {last_failure}");
let now = OffsetDateTime::now_utc();
// if it's been a while since the last failure, reset the count
if now - last_failure > SOCKET_FAILURE_RESET {
warn!("resetting the failure count to 1");
socket_failures = 1;
}
last_failure = now;
if socket_failures >= MAX_RECONNECTION_ATTEMPTS {
error!("reached the maximum allowed failure count");
return Err(ScraperError::MaximumWebSocketFailures);
}
// BACKOFF
let cancelled = self.websocket_backoff(socket_failures).await;
if cancelled {
return Ok(());
}
if let Err(err) = self.remake_connection().await {
error!("failed to re-establish the websocket connection: {err}");
}
}
pub(crate) fn ws_driver(&mut self) -> WebSocketClientDriver {
#[allow(clippy::expect_used)]
self.websocket_driver
.take()
.expect("websocket driver has already been started!")
}
}
pub async fn run_websocket_driver(driver: WebSocketClientDriver, driver_cancel: CancellationToken) {
pub async fn run_websocket_driver(driver: WebSocketClientDriver, cancel: CancellationToken) {
info!("starting websocket driver");
tokio::select! {
_ = driver_cancel.cancelled() => {
_ = cancel.cancelled() => {
info!("received cancellation token")
}
res = driver.run() => {
match res {
Ok(_) => info!("our websocket driver has finished execution"),
Err(err) => {
error!("our websocket driver has errored out: {err}");
// TODO: in the future just attempt to reconnect
error!("our websocket driver has errored out: {err}")
}
}
cancel.cancel()
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ documentation = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bs58 = { workspace = true }
bs58 = "0.4"
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = { workspace = true }
+14 -38
View File
@@ -1,4 +1,3 @@
use std::net::Ipv6Addr;
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
@@ -20,12 +19,6 @@ const TUN_WRITE_TIMEOUT_MS: u64 = 1000;
#[derive(thiserror::Error, Debug)]
pub enum TunDeviceError {
#[error("{0}")]
IO(#[from] std::io::Error),
#[error("{0}")]
TokioTun(#[from] tokio_tun::Error),
#[error("timeout writing to tun device, dropping packet")]
TunWriteTimeout,
@@ -51,18 +44,14 @@ pub enum TunDeviceError {
FailedToLockPeer,
}
fn setup_tokio_tun_device(
name: &str,
address: Ipv4Addr,
netmask: Ipv4Addr,
) -> Result<tokio_tun::Tun, TunDeviceError> {
fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
log::info!("Creating TUN device with: address={address}, netmask={netmask}");
// Read MTU size from env variable NYM_MTU_SIZE, else default to 1420.
let mtu = std::env::var("NYM_MTU_SIZE")
.map(|mtu| mtu.parse().expect("NYM_MTU_SIZE must be a valid integer"))
.unwrap_or(1420);
log::info!("Using MTU size: {mtu}");
Ok(tokio_tun::Tun::builder()
tokio_tun::Tun::builder()
.name(name)
.tap(false)
.packet_info(false)
@@ -70,7 +59,8 @@ fn setup_tokio_tun_device(
.up()
.address(address)
.netmask(netmask)
.try_build()?)
.try_build()
.expect("Failed to setup tun device, do you have permission?")
}
pub struct TunDevice {
@@ -113,18 +103,16 @@ pub struct NatInner {
pub struct TunDeviceConfig {
pub base_name: String,
pub ipv4: Ipv4Addr,
pub netmaskv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
pub netmaskv6: String,
pub ip: Ipv4Addr,
pub netmask: Ipv4Addr,
}
impl TunDevice {
pub fn new(
routing_mode: RoutingMode,
config: TunDeviceConfig,
) -> Result<(Self, TunTaskTx, TunTaskResponseRx), TunDeviceError> {
let tun = Self::new_device_only(config)?;
) -> (Self, TunTaskTx, TunTaskResponseRx) {
let tun = Self::new_device_only(config);
// Channels to communicate with the other tasks
let (tun_task_tx, tun_task_rx) = tun_task_channel();
@@ -137,32 +125,20 @@ impl TunDevice {
routing_mode,
};
Ok((tun_device, tun_task_tx, tun_task_response_rx))
(tun_device, tun_task_tx, tun_task_response_rx)
}
pub fn new_device_only(config: TunDeviceConfig) -> Result<tokio_tun::Tun, TunDeviceError> {
pub fn new_device_only(config: TunDeviceConfig) -> tokio_tun::Tun {
let TunDeviceConfig {
base_name,
ipv4,
netmaskv4,
ipv6,
netmaskv6,
ip,
netmask,
} = config;
let name = format!("{base_name}%d");
let tun = setup_tokio_tun_device(&name, ipv4, netmaskv4)?;
let tun = setup_tokio_tun_device(&name, ip, netmask);
log::info!("Created TUN device: {}", tun.name());
std::process::Command::new("ip")
.args([
"-6",
"addr",
"add",
&format!("{}/{}", ipv6, netmaskv6),
"dev",
&tun.name(),
])
.output()?;
Ok(tun)
tun
}
// Send outbound packets out on the wild internet
+23 -34
View File
@@ -116,15 +116,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
[[package]]
name = "bs58"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896"
dependencies = [
"tinyvec",
]
[[package]]
name = "bumpalo"
version = "3.12.1"
@@ -187,7 +178,6 @@ dependencies = [
name = "coconut-test"
version = "0.1.0"
dependencies = [
"bs58 0.4.0",
"cosmwasm-std",
"cosmwasm-storage",
"cw-controllers",
@@ -204,10 +194,8 @@ dependencies = [
"nym-coconut-dkg-common",
"nym-group-contract-common",
"nym-multisig-contract-common",
"rand_chacha 0.2.2",
"schemars",
"serde",
"subtle-encoding",
"thiserror",
]
@@ -831,7 +819,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.32",
]
[[package]]
@@ -1237,6 +1225,7 @@ dependencies = [
"cw4-group",
"nym-coconut-dkg-common",
"nym-group-contract-common",
"rusty-fork",
"semver",
"serde",
"thiserror",
@@ -1259,7 +1248,7 @@ dependencies = [
name = "nym-contracts-common"
version = "0.5.0"
dependencies = [
"bs58 0.5.0",
"bs58",
"cosmwasm-schema",
"cosmwasm-std",
"schemars",
@@ -1271,7 +1260,7 @@ dependencies = [
name = "nym-crypto"
version = "0.4.0"
dependencies = [
"bs58 0.5.0",
"bs58",
"ed25519-dalek",
"nym-pemstore",
"nym-sphinx-types",
@@ -1327,7 +1316,7 @@ dependencies = [
name = "nym-mixnet-contract"
version = "1.5.1"
dependencies = [
"bs58 0.4.0",
"bs58",
"cosmwasm-derive",
"cosmwasm-schema",
"cosmwasm-std",
@@ -1350,7 +1339,7 @@ dependencies = [
name = "nym-mixnet-contract-common"
version = "0.6.0"
dependencies = [
"bs58 0.4.0",
"bs58",
"cosmwasm-schema",
"cosmwasm-std",
"cw2",
@@ -1385,7 +1374,7 @@ name = "nym-name-service"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.4.0",
"bs58",
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
@@ -1432,7 +1421,7 @@ name = "nym-service-provider-directory"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.4.0",
"bs58",
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
@@ -1619,9 +1608,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.78"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
@@ -1657,9 +1646,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
dependencies = [
"proc-macro2",
]
@@ -1899,9 +1888,9 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
version = "1.0.196"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
@@ -1917,13 +1906,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.196"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.32",
]
[[package]]
@@ -1956,7 +1945,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.32",
]
[[package]]
@@ -2011,7 +2000,7 @@ dependencies = [
"aes",
"arrayref",
"blake2",
"bs58 0.4.0",
"bs58",
"byteorder",
"chacha",
"curve25519-dalek",
@@ -2070,9 +2059,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.49"
version = "2.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
dependencies = [
"proc-macro2",
"quote",
@@ -2109,7 +2098,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.32",
]
[[package]]
@@ -2458,5 +2447,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.32",
]
-2
View File
@@ -34,7 +34,6 @@ incremental = false
overflow-checks = true
[workspace.dependencies]
bs58 = "0.4.0"
cosmwasm-crypto = "=1.3.0"
cosmwasm-derive = "=1.3.0"
cosmwasm-schema = "=1.3.0"
@@ -50,6 +49,5 @@ cw3-fixed-multisig = "=1.1.0"
cw4 = "=1.1.0"
cw20 = "=1.1.0"
semver = "1.0.21"
serde = "1.0.196"
thiserror = "1.0.48"
+1
View File
@@ -30,6 +30,7 @@ thiserror = { workspace = true }
cw-multi-test = { workspace = true }
cw4-group = { path = "../multisig/cw4-group" }
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
rusty-fork = "0.3"
[features]
schema-gen = ["nym-coconut-dkg-common/schema", "cosmwasm-schema"]
+107 -426
View File
@@ -180,11 +180,15 @@
"commit_dealings_chunk": {
"type": "object",
"required": [
"chunk"
"chunk",
"resharing"
],
"properties": {
"chunk": {
"$ref": "#/definitions/PartialContractDealing"
},
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -242,6 +246,19 @@
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"surpassed_threshold"
],
"properties": {
"surpassed_threshold": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
@@ -254,32 +271,6 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"trigger_reset"
],
"properties": {
"trigger_reset": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"trigger_resharing"
],
"properties": {
"trigger_resharing": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
@@ -377,45 +368,16 @@
{
"type": "object",
"required": [
"can_advance_state"
"get_initial_dealers"
],
"properties": {
"can_advance_state": {
"get_initial_dealers": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_registered_dealer"
],
"properties": {
"get_registered_dealer": {
"type": "object",
"required": [
"dealer_address"
],
"properties": {
"dealer_address": {
"type": "string"
},
"epoch_id": {
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
@@ -469,10 +431,10 @@
{
"type": "object",
"required": [
"get_dealer_indices"
"get_past_dealers"
],
"properties": {
"get_dealer_indices": {
"get_past_dealers": {
"type": "object",
"properties": {
"limit": {
@@ -754,215 +716,6 @@
},
"sudo": null,
"responses": {
"can_advance_state": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StateAdvanceResponse",
"type": "object",
"required": [
"current_state",
"is_complete",
"progress",
"reached_deadline"
],
"properties": {
"current_state": {
"$ref": "#/definitions/EpochState"
},
"deadline": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
},
{
"type": "null"
}
]
},
"is_complete": {
"type": "boolean"
},
"progress": {
"$ref": "#/definitions/StateProgress"
},
"reached_deadline": {
"type": "boolean"
}
},
"additionalProperties": false,
"definitions": {
"EpochState": {
"oneOf": [
{
"type": "string",
"enum": [
"waiting_initialisation",
"in_progress"
]
},
{
"type": "object",
"required": [
"public_key_submission"
],
"properties": {
"public_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"dealing_exchange"
],
"properties": {
"dealing_exchange": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_submission"
],
"properties": {
"verification_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_validation"
],
"properties": {
"verification_key_validation": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_finalization"
],
"properties": {
"verification_key_finalization": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
},
"get_c_w2_contract_version": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ContractVersion",
@@ -1060,11 +813,15 @@
"required": [
"epoch_id",
"state",
"state_progress",
"time_configuration"
],
"properties": {
"deadline": {
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"finish_timestamp": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
@@ -1074,17 +831,9 @@
}
]
},
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"state": {
"$ref": "#/definitions/EpochState"
},
"state_progress": {
"$ref": "#/definitions/StateProgress"
},
"time_configuration": {
"$ref": "#/definitions/TimeConfiguration"
}
@@ -1207,49 +956,6 @@
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"TimeConfiguration": {
"type": "object",
"required": [
@@ -1448,109 +1154,15 @@
"additionalProperties": false
},
"DealerType": {
"oneOf": [
{
"type": "string",
"enum": [
"unknown"
]
},
{
"type": "object",
"required": [
"current"
],
"properties": {
"current": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"past"
],
"properties": {
"past": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
"type": "string",
"enum": [
"current",
"past",
"unknown"
]
}
}
},
"get_dealer_indices": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedDealerIndexResponse",
"type": "object",
"required": [
"indices"
],
"properties": {
"indices": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"anyOf": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
}
}
},
"get_dealing_chunk": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DealingChunkResponse",
@@ -1843,15 +1455,70 @@
}
}
},
"get_registered_dealer": {
"get_initial_dealers": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RegisteredDealerDetails",
"title": "Nullable_InitialReplacementData",
"anyOf": [
{
"$ref": "#/definitions/InitialReplacementData"
},
{
"type": "null"
}
],
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"InitialReplacementData": {
"type": "object",
"required": [
"initial_dealers",
"initial_height"
],
"properties": {
"initial_dealers": {
"type": "array",
"items": {
"$ref": "#/definitions/Addr"
}
},
"initial_height": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
}
},
"get_past_dealers": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedDealerResponse",
"type": "object",
"required": [
"dealers",
"per_page"
],
"properties": {
"details": {
"dealers": {
"type": "array",
"items": {
"$ref": "#/definitions/DealerDetails"
}
},
"per_page": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"anyOf": [
{
"$ref": "#/definitions/DealerRegistrationDetails"
"$ref": "#/definitions/Addr"
},
{
"type": "null"
@@ -1861,17 +1528,31 @@
},
"additionalProperties": false,
"definitions": {
"DealerRegistrationDetails": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"DealerDetails": {
"type": "object",
"required": [
"address",
"announce_address",
"assigned_index",
"bte_public_key_with_proof",
"ed25519_identity"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
},
"announce_address": {
"type": "string"
},
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"bte_public_key_with_proof": {
"type": "string"
},
+18 -27
View File
@@ -91,11 +91,15 @@
"commit_dealings_chunk": {
"type": "object",
"required": [
"chunk"
"chunk",
"resharing"
],
"properties": {
"chunk": {
"$ref": "#/definitions/PartialContractDealing"
},
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -153,6 +157,19 @@
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"surpassed_threshold"
],
"properties": {
"surpassed_threshold": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
@@ -165,32 +182,6 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"trigger_reset"
],
"properties": {
"trigger_reset": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"trigger_resharing"
],
"properties": {
"trigger_resharing": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
+4 -33
View File
@@ -44,45 +44,16 @@
{
"type": "object",
"required": [
"can_advance_state"
"get_initial_dealers"
],
"properties": {
"can_advance_state": {
"get_initial_dealers": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_registered_dealer"
],
"properties": {
"get_registered_dealer": {
"type": "object",
"required": [
"dealer_address"
],
"properties": {
"dealer_address": {
"type": "string"
},
"epoch_id": {
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
@@ -136,10 +107,10 @@
{
"type": "object",
"required": [
"get_dealer_indices"
"get_past_dealers"
],
"properties": {
"get_dealer_indices": {
"get_past_dealers": {
"type": "object",
"properties": {
"limit": {
@@ -1,209 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StateAdvanceResponse",
"type": "object",
"required": [
"current_state",
"is_complete",
"progress",
"reached_deadline"
],
"properties": {
"current_state": {
"$ref": "#/definitions/EpochState"
},
"deadline": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
},
{
"type": "null"
}
]
},
"is_complete": {
"type": "boolean"
},
"progress": {
"$ref": "#/definitions/StateProgress"
},
"reached_deadline": {
"type": "boolean"
}
},
"additionalProperties": false,
"definitions": {
"EpochState": {
"oneOf": [
{
"type": "string",
"enum": [
"waiting_initialisation",
"in_progress"
]
},
{
"type": "object",
"required": [
"public_key_submission"
],
"properties": {
"public_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"dealing_exchange"
],
"properties": {
"dealing_exchange": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_submission"
],
"properties": {
"verification_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_validation"
],
"properties": {
"verification_key_validation": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_finalization"
],
"properties": {
"verification_key_finalization": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
}
@@ -5,11 +5,15 @@
"required": [
"epoch_id",
"state",
"state_progress",
"time_configuration"
],
"properties": {
"deadline": {
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"finish_timestamp": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
@@ -19,17 +23,9 @@
}
]
},
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"state": {
"$ref": "#/definitions/EpochState"
},
"state_progress": {
"$ref": "#/definitions/StateProgress"
},
"time_configuration": {
"$ref": "#/definitions/TimeConfiguration"
}
@@ -152,49 +148,6 @@
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"TimeConfiguration": {
"type": "object",
"required": [
@@ -57,59 +57,11 @@
"additionalProperties": false
},
"DealerType": {
"oneOf": [
{
"type": "string",
"enum": [
"unknown"
]
},
{
"type": "object",
"required": [
"current"
],
"properties": {
"current": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"past"
],
"properties": {
"past": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
"type": "string",
"enum": [
"current",
"past",
"unknown"
]
}
}
@@ -1,46 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedDealerIndexResponse",
"type": "object",
"required": [
"indices"
],
"properties": {
"indices": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"anyOf": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
}
}
}
@@ -1,40 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RegisteredDealerDetails",
"type": "object",
"properties": {
"details": {
"anyOf": [
{
"$ref": "#/definitions/DealerRegistrationDetails"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"DealerRegistrationDetails": {
"type": "object",
"required": [
"announce_address",
"bte_public_key_with_proof",
"ed25519_identity"
],
"properties": {
"announce_address": {
"type": "string"
},
"bte_public_key_with_proof": {
"type": "string"
},
"ed25519_identity": {
"type": "string"
}
},
"additionalProperties": false
}
}
}
+11 -21
View File
@@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::queries::{
query_current_dealers_paged, query_dealer_details, query_dealers_indices_paged,
query_registered_dealer_details,
query_current_dealers_paged, query_dealer_details, query_past_dealers_paged,
};
use crate::dealers::transactions::try_add_dealer;
use crate::dealings::queries::{
@@ -12,11 +11,11 @@ use crate::dealings::queries::{
};
use crate::dealings::transactions::{try_commit_dealings_chunk, try_submit_dealings_metadata};
use crate::epoch_state::queries::{
query_can_advance_state, query_current_epoch, query_current_epoch_threshold,
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::transactions::{
try_advance_epoch_state, try_initiate_dkg, try_trigger_reset, try_trigger_resharing,
advance_epoch_state, try_initiate_dkg, try_surpassed_threshold,
};
use crate::error::ContractError;
use crate::state::queries::query_state;
@@ -109,8 +108,8 @@ pub fn execute(
chunks,
resharing,
} => try_submit_dealings_metadata(deps, info, dealing_index, chunks, resharing),
ExecuteMsg::CommitDealingsChunk { chunk } => {
try_commit_dealings_chunk(deps, env, info, chunk)
ExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
try_commit_dealings_chunk(deps, env, info, chunk, resharing)
}
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
try_commit_verification_key_share(deps, env, info, share, resharing)
@@ -118,37 +117,28 @@ pub fn execute(
ExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => {
try_verify_verification_key_share(deps, info, owner, resharing)
}
ExecuteMsg::AdvanceEpochState {} => try_advance_epoch_state(deps, env),
ExecuteMsg::TriggerReset {} => try_trigger_reset(deps, env, info),
ExecuteMsg::TriggerResharing {} => try_trigger_resharing(deps, env, info),
ExecuteMsg::SurpassedThreshold {} => try_surpassed_threshold(deps, env),
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env),
}
}
#[entry_point]
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let response = match msg {
QueryMsg::GetState {} => to_binary(&query_state(deps.storage)?)?,
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?,
QueryMsg::CanAdvanceState {} => to_binary(&query_can_advance_state(deps.storage, env)?)?,
QueryMsg::GetCurrentEpochThreshold {} => {
to_binary(&query_current_epoch_threshold(deps.storage)?)?
}
QueryMsg::GetRegisteredDealer {
dealer_address,
epoch_id,
} => to_binary(&query_registered_dealer_details(
deps,
dealer_address,
epoch_id,
)?)?,
QueryMsg::GetInitialDealers {} => to_binary(&query_initial_dealers(deps.storage)?)?,
QueryMsg::GetDealerDetails { dealer_address } => {
to_binary(&query_dealer_details(deps, dealer_address)?)?
}
QueryMsg::GetCurrentDealers { limit, start_after } => {
to_binary(&query_current_dealers_paged(deps, start_after, limit)?)?
}
QueryMsg::GetDealerIndices { limit, start_after } => {
to_binary(&query_dealers_indices_paged(deps, start_after, limit)?)?
QueryMsg::GetPastDealers { limit, start_after } => {
to_binary(&query_past_dealers_paged(deps, start_after, limit)?)?
}
QueryMsg::GetDealingsMetadata {
epoch_id,
+127 -166
View File
@@ -1,34 +1,36 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage::{
self, get_dealer_details, get_dealer_index, get_registration_details, DEALERS_INDICES,
EPOCH_DEALERS_MAP,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::dealers::storage::{self, IndexedDealersMap};
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use nym_coconut_dkg_common::dealer::{
DealerDetailsResponse, DealerType, PagedDealerIndexResponse, PagedDealerResponse,
RegisteredDealerDetails,
};
use nym_coconut_dkg_common::types::{DealerDetails, EpochId};
use nym_coconut_dkg_common::dealer::{DealerDetailsResponse, DealerType, PagedDealerResponse};
pub fn query_registered_dealer_details(
fn query_dealers(
deps: Deps<'_>,
dealer_address: String,
epoch_id: Option<EpochId>,
) -> StdResult<RegisteredDealerDetails> {
let addr = deps.api.addr_validate(&dealer_address)?;
start_after: Option<String>,
limit: Option<u32>,
underlying_map: &IndexedDealersMap<'_>,
) -> StdResult<PagedDealerResponse> {
let limit = limit
.unwrap_or(storage::DEALERS_PAGE_DEFAULT_LIMIT)
.min(storage::DEALERS_PAGE_MAX_LIMIT) as usize;
let epoch_id = match epoch_id {
Some(epoch_id) => epoch_id,
None => CURRENT_EPOCH.load(deps.storage)?.epoch_id,
};
let addr = start_after
.map(|addr| deps.api.addr_validate(&addr))
.transpose()?;
Ok(RegisteredDealerDetails {
details: get_registration_details(deps.storage, &addr, epoch_id).ok(),
})
let start = addr.as_ref().map(Bound::exclusive);
let dealers = underlying_map
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = dealers.last().map(|dealer| dealer.address.clone());
Ok(PagedDealerResponse::new(dealers, limit, start_next_after))
}
pub fn query_dealer_details(
@@ -36,93 +38,32 @@ pub fn query_dealer_details(
dealer_address: String,
) -> StdResult<DealerDetailsResponse> {
let addr = deps.api.addr_validate(&dealer_address)?;
let current_epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
// if the address has registration data for the current epoch, it means it's an active dealer
if let Ok(dealer_details) = get_dealer_details(deps.storage, &addr, current_epoch_id) {
let assigned_index = dealer_details.assigned_index;
if let Some(current) = storage::current_dealers().may_load(deps.storage, &addr)? {
return Ok(DealerDetailsResponse::new(
Some(dealer_details),
DealerType::Current { assigned_index },
Some(current),
DealerType::Current,
));
}
// and if has had an assigned index it must have been a dealer at some point in the past
if let Ok(assigned_index) = get_dealer_index(deps.storage, &addr, current_epoch_id) {
return Ok(DealerDetailsResponse::new(
None,
DealerType::Past { assigned_index },
));
if let Some(past) = storage::past_dealers().may_load(deps.storage, &addr)? {
return Ok(DealerDetailsResponse::new(Some(past), DealerType::Past));
}
Ok(DealerDetailsResponse::new(None, DealerType::Unknown))
}
pub fn query_dealers_indices_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedDealerIndexResponse> {
let limit = limit
.unwrap_or(storage::DEALER_INDICES_PAGE_DEFAULT_LIMIT)
.min(storage::DEALER_INDICES_PAGE_MAX_LIMIT) as usize;
let addr = start_after
.map(|addr| deps.api.addr_validate(&addr))
.transpose()?;
let start = addr.as_ref().map(Bound::exclusive);
let dealers = DEALERS_INDICES
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = dealers.last().map(|dealer| dealer.0.clone());
Ok(PagedDealerIndexResponse::new(dealers, start_next_after))
}
pub fn query_current_dealers_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedDealerResponse> {
let limit = limit
.unwrap_or(storage::DEALERS_PAGE_DEFAULT_LIMIT)
.min(storage::DEALERS_PAGE_MAX_LIMIT) as usize;
let addr = start_after
.map(|addr| deps.api.addr_validate(&addr))
.transpose()?;
query_dealers(deps, start_after, limit, &storage::current_dealers())
}
let start = addr.as_ref().map(Bound::exclusive);
let current_epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
let dealers = EPOCH_DEALERS_MAP
.prefix(current_epoch_id)
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|res| {
res.map(|(address, details)| {
// SAFETY: if we have DealerRegistrationDetails saved, it means we MUST also have its node index
// otherwise some serious invariants have been broken in the contract, and we're in trouble
#[allow(clippy::expect_used)]
let assigned_index = get_dealer_index(deps.storage, &address, current_epoch_id)
.expect("could not retrieve dealer index for a registered dealer");
DealerDetails {
address,
bte_public_key_with_proof: details.bte_public_key_with_proof,
ed25519_identity: details.ed25519_identity,
announce_address: details.announce_address,
assigned_index,
}
})
})
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = dealers.last().map(|dealer| dealer.address.clone());
Ok(PagedDealerResponse::new(dealers, limit, start_next_after))
pub fn query_past_dealers_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedDealerResponse> {
query_dealers(deps, start_after, limit, &storage::past_dealers())
}
#[cfg(test)]
@@ -130,22 +71,24 @@ pub(crate) mod tests {
use super::*;
use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT};
use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::helpers::{init_contract, insert_dealer};
use crate::support::tests::helpers::init_contract;
use cosmwasm_std::DepsMut;
fn fill_dealers(mut deps: DepsMut<'_>, epoch_id: EpochId, size: usize) {
for assigned_index in 0..size {
let dealer_details = dealer_details_fixture(assigned_index as u64);
insert_dealer(deps.branch(), epoch_id, &dealer_details);
fn fill_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
for n in 0..size {
let dealer_details = dealer_details_fixture(n as u64);
mapping
.save(deps.storage, &dealer_details.address, &dealer_details)
.unwrap();
}
}
fn remove_dealers(deps: DepsMut<'_>, epoch_id: EpochId, size: usize) {
for assigned_index in 0..size {
let dealer_details = dealer_details_fixture(assigned_index as u64);
DEALERS_INDICES.remove(deps.storage, &dealer_details.address);
EPOCH_DEALERS_MAP.remove(deps.storage, (epoch_id, &dealer_details.address));
fn remove_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
for n in 0..size {
let dealer_details = dealer_details_fixture(n as u64);
mapping
.remove(deps.storage, &dealer_details.address)
.unwrap();
}
}
@@ -153,8 +96,10 @@ pub(crate) mod tests {
fn dealers_empty_on_init() {
let deps = init_contract();
let page1 = query_current_dealers_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(0, page1.dealers.len() as u32);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
assert_eq!(0, page1.dealers.len() as u32);
}
}
#[test]
@@ -162,26 +107,30 @@ pub(crate) mod tests {
let mut deps = init_contract();
let limit = 2;
fill_dealers(deps.as_mut(), 0, 1000);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1000);
let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
assert_eq!(limit, page1.dealers.len() as u32);
let page1 = query_dealers(deps.as_ref(), None, Option::from(limit), &mapping).unwrap();
assert_eq!(limit, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), 0, 1000);
remove_dealers(deps.as_mut(), &mapping, 1000);
}
}
#[test]
fn dealers_paged_retrieval_has_default_limit() {
let mut deps = init_contract();
fill_dealers(deps.as_mut(), 0, 1000);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1000);
// query without explicitly setting a limit
let page1 = query_current_dealers_paged(deps.as_ref(), None, None).unwrap();
// query without explicitly setting a limit
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32);
assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), 0, 1000);
remove_dealers(deps.as_mut(), &mapping, 1000);
}
}
#[test]
@@ -191,16 +140,18 @@ pub(crate) mod tests {
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT;
fill_dealers(deps.as_mut(), 0, 1000);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1000);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
let page1 =
query_dealers(deps.as_ref(), None, Option::from(crazy_limit), &mapping).unwrap();
// we default to a decent sized upper bound instead
let expected_limit = DEALERS_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.dealers.len() as u32);
// we default to a decent sized upper bound instead
let expected_limit = DEALERS_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), 0, 1000);
remove_dealers(deps.as_mut(), &mapping, 1000);
}
}
#[test]
@@ -209,52 +160,62 @@ pub(crate) mod tests {
let per_page = 2;
fill_dealers(deps.as_mut(), 0, 1);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1);
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.dealers.len());
remove_dealers(deps.as_mut(), 0, 1);
// page should have 1 result on it
assert_eq!(1, page1.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 1);
}
fill_dealers(deps.as_mut(), 0, 2);
// page1 should have 2 results on it
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.dealers.len());
remove_dealers(deps.as_mut(), 0, 2);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 2);
// page1 should have 2 results on it
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
assert_eq!(2, page1.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 2);
}
fill_dealers(deps.as_mut(), 0, 3);
// page1 still has 2 results
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.dealers.len());
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 3);
// page1 still has 2 results
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
assert_eq!(2, page1.dealers.len());
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_current_dealers_paged(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
)
.unwrap();
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_dealers(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
&mapping,
)
.unwrap();
assert_eq!(1, page2.dealers.len());
remove_dealers(deps.as_mut(), 0, 3);
assert_eq!(1, page2.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 3);
}
fill_dealers(deps.as_mut(), 0, 4);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
let start_after = page1.start_next_after.unwrap();
let page2 = query_current_dealers_paged(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
)
.unwrap();
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 4);
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
let start_after = page1.start_next_after.unwrap();
let page2 = query_dealers(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
&mapping,
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.dealers.len());
remove_dealers(deps.as_mut(), 0, 4);
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 4);
}
}
}
+26 -84
View File
@@ -1,102 +1,44 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::ContractError;
use crate::Dealer;
use cosmwasm_std::{StdResult, Storage};
use cw_storage_plus::{Item, Map};
use nym_coconut_dkg_common::types::{DealerDetails, DealerRegistrationDetails, EpochId, NodeIndex};
use cosmwasm_std::{Addr, StdResult, Storage};
use cw_storage_plus::{Index, IndexList, IndexedMap, Item, UniqueIndex};
use nym_coconut_dkg_common::types::{DealerDetails, NodeIndex};
pub(crate) const DEALER_INDICES_PAGE_MAX_LIMIT: u32 = 80;
pub(crate) const DEALER_INDICES_PAGE_DEFAULT_LIMIT: u32 = 40;
const CURRENT_DEALERS_PK: &str = "crd";
const PAST_DEALERS_PK: &str = "ptd";
const DEALERS_NODE_INDEX_IDX_NAMESPACE: &str = "dni";
pub(crate) const DEALERS_PAGE_MAX_LIMIT: u32 = 25;
pub(crate) const DEALERS_PAGE_DEFAULT_LIMIT: u32 = 10;
pub(crate) const DEALERS_PAGE_MAX_LIMIT: u32 = 75;
pub(crate) const DEALERS_PAGE_DEFAULT_LIMIT: u32 = 50;
pub(crate) const NODE_INDEX_COUNTER: Item<NodeIndex> = Item::new("node_index_counter");
pub(crate) const DEALERS_INDICES: Map<Dealer, NodeIndex> = Map::new("dealer_index");
pub(crate) type IndexedDealersMap<'a> = IndexedMap<'a, &'a Addr, DealerDetails, DealersIndex<'a>>;
pub(crate) const EPOCH_DEALERS_MAP: Map<(EpochId, Dealer), DealerRegistrationDetails> =
Map::new("epoch_dealers");
pub(crate) struct DealersIndex<'a> {
pub(crate) node_index: UniqueIndex<'a, NodeIndex, DealerDetails>,
}
/// Attempts to retrieve a pre-assign node index associated with given dealer.
/// If one doesn't exist, a new one is assigned.
pub(crate) fn get_or_assign_index(
storage: &mut dyn Storage,
dealer: Dealer,
) -> StdResult<NodeIndex> {
if let Some(index) = DEALERS_INDICES.may_load(storage, dealer)? {
return Ok(index);
impl<'a> IndexList<DealerDetails> for DealersIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<DealerDetails>> + '_> {
let v: Vec<&dyn Index<DealerDetails>> = vec![&self.node_index];
Box::new(v.into_iter())
}
let index = next_node_index(storage)?;
DEALERS_INDICES.save(storage, dealer, &index)?;
Ok(index)
}
pub(crate) fn save_dealer_details_if_not_a_dealer(
storage: &mut dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
details: DealerRegistrationDetails,
) -> Result<(), ContractError> {
if EPOCH_DEALERS_MAP.has(storage, (epoch_id, dealer)) {
return Err(ContractError::AlreadyADealer);
}
EPOCH_DEALERS_MAP.save(storage, (epoch_id, dealer), &details)?;
Ok(())
pub(crate) fn current_dealers<'a>() -> IndexedDealersMap<'a> {
let indexes = DealersIndex {
node_index: UniqueIndex::new(|d| d.assigned_index, DEALERS_NODE_INDEX_IDX_NAMESPACE),
};
IndexedMap::new(CURRENT_DEALERS_PK, indexes)
}
pub(crate) fn ensure_dealer(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<(), ContractError> {
if !is_dealer(storage, dealer, epoch_id) {
return Err(ContractError::NotADealer { epoch_id });
}
Ok(())
}
pub(crate) fn is_dealer(storage: &dyn Storage, dealer: Dealer, epoch_id: EpochId) -> bool {
EPOCH_DEALERS_MAP.has(storage, (epoch_id, dealer))
}
// note: `epoch_id` is provided purely for the error message. it has nothing to do with storage retrieval
pub(crate) fn get_dealer_index(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<NodeIndex, ContractError> {
DEALERS_INDICES
.may_load(storage, dealer)?
.ok_or(ContractError::NotADealer { epoch_id })
}
pub(crate) fn get_registration_details(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<DealerRegistrationDetails, ContractError> {
EPOCH_DEALERS_MAP
.may_load(storage, (epoch_id, dealer))?
.ok_or(ContractError::NotADealer { epoch_id })
}
pub(crate) fn get_dealer_details(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<DealerDetails, ContractError> {
let registration_details = get_registration_details(storage, dealer, epoch_id)?;
let assigned_index = get_dealer_index(storage, dealer, epoch_id)?;
Ok(DealerDetails {
address: dealer.to_owned(),
bte_public_key_with_proof: registration_details.bte_public_key_with_proof,
ed25519_identity: registration_details.ed25519_identity,
announce_address: registration_details.announce_address,
assigned_index,
})
pub(crate) fn past_dealers<'a>() -> IndexedDealersMap<'a> {
let indexes = DealersIndex {
node_index: UniqueIndex::new(|d| d.assigned_index, DEALERS_NODE_INDEX_IDX_NAMESPACE),
};
IndexedMap::new(PAST_DEALERS_PK, indexes)
}
pub(crate) fn next_node_index(store: &mut dyn Storage) -> StdResult<NodeIndex> {
@@ -1,24 +1,34 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage::{
get_or_assign_index, is_dealer, save_dealer_details_if_not_a_dealer,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::dealers::storage as dealers_storage;
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::storage::STATE;
use crate::Dealer;
use cosmwasm_std::{Deps, DepsMut, MessageInfo, Response, StdResult};
use nym_coconut_dkg_common::dealer::DealerRegistrationDetails;
use nym_coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
use nym_coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState};
fn ensure_group_member(deps: Deps, dealer: Dealer) -> Result<(), ContractError> {
// currently we only require that
// a) it's part of the signer group
// b) it isn't already a dealer
fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<(), ContractError> {
if dealers_storage::current_dealers()
.may_load(deps.storage, dealer)?
.is_some()
{
return Err(ContractError::AlreadyADealer);
}
let state = STATE.load(deps.storage)?;
let height = if resharing {
Some(INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height)
} else {
None
};
state
.group_addr
.is_voting_member(&deps.querier, dealer, None)?
.is_voting_member(&deps.querier, dealer, height)?
.ok_or(ContractError::Unauthorized {})?;
Ok(())
@@ -27,57 +37,42 @@ fn ensure_group_member(deps: Deps, dealer: Dealer) -> Result<(), ContractError>
// future optimisation:
// for a recurring dealer just let it refresh the keys without having to do all the storage operations
pub fn try_add_dealer(
deps: DepsMut<'_>,
mut deps: DepsMut<'_>,
info: MessageInfo,
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
identity_key: String,
announce_address: String,
resharing: bool,
) -> Result<Response, ContractError> {
let epoch = CURRENT_EPOCH.load(deps.storage)?;
check_epoch_state(deps.storage, EpochState::PublicKeySubmission { resharing })?;
// make sure this potential dealer actually belong to the group
ensure_group_member(deps.as_ref(), &info.sender)?;
verify_dealer(deps.branch(), &info.sender, resharing)?;
let node_index = get_or_assign_index(deps.storage, &info.sender)?;
// if it was already a dealer in the past, assign the same node index
let node_index = if let Some(prior_details) =
dealers_storage::past_dealers().may_load(deps.storage, &info.sender)?
{
// since this dealer is going to become active now, remove it from the past dealers
dealers_storage::past_dealers().replace(
deps.storage,
&info.sender,
None,
Some(&prior_details),
)?;
prior_details.assigned_index
} else {
dealers_storage::next_node_index(deps.storage)?
};
// save the dealer into the storage (if it hasn't already been saved)
let dealer_details = DealerRegistrationDetails {
// save the dealer into the storage
let dealer_details = DealerDetails {
address: info.sender.clone(),
bte_public_key_with_proof: bte_key_with_proof,
ed25519_identity: identity_key,
announce_address,
assigned_index: node_index,
};
save_dealer_details_if_not_a_dealer(
deps.storage,
&info.sender,
epoch.epoch_id,
dealer_details,
)?;
// check if it's a resharing dealer
let is_resharing_dealer = resharing
&& is_dealer(
deps.storage,
&info.sender,
epoch
.epoch_id
.checked_sub(1)
.expect("epoch invariant broken: resharing during 0th epoch"),
);
// increment the number of registered dealers
CURRENT_EPOCH.update(deps.storage, |epoch| -> StdResult<_> {
let mut updated_epoch = epoch;
updated_epoch.state_progress.registered_dealers += 1;
if is_resharing_dealer {
updated_epoch.state_progress.registered_resharing_dealers += 1;
}
Ok(updated_epoch)
})?;
dealers_storage::current_dealers().save(deps.storage, &info.sender, &dealer_details)?;
Ok(Response::new().add_attribute("node_index", node_index.to_string()))
}
@@ -85,12 +80,63 @@ pub fn try_add_dealer(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
use crate::dealers::storage::current_dealers;
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, GROUP_MEMBERS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::types::TimeConfiguration;
use cw4::Member;
use nym_coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
use rusty_fork::rusty_fork_test;
rusty_fork_test! {
#[test]
fn verification() {
let mut deps = helpers::init_contract();
let new_dealer = Addr::unchecked("new_dealer");
let details1 = dealer_details_fixture(1);
let details2 = dealer_details_fixture(2);
let details3 = dealer_details_fixture(3);
current_dealers()
.save(deps.as_mut().storage, &details1.address, &details1)
.unwrap();
let err = verify_dealer(deps.as_mut(), &details1.address, false).unwrap_err();
assert_eq!(err, ContractError::AlreadyADealer);
INITIAL_REPLACEMENT_DATA
.save(
deps.as_mut().storage,
&InitialReplacementData {
initial_dealers: vec![details1.address, details2.address, details3.address],
initial_height: 1,
},
)
.unwrap();
let err = verify_dealer(deps.as_mut(), &new_dealer, false).unwrap_err();
assert_eq!(err, ContractError::Unauthorized);
GROUP_MEMBERS.lock().unwrap().push((
Member {
addr: new_dealer.to_string(),
weight: 10,
},
2,
));
verify_dealer(deps.as_mut(), &new_dealer, false).unwrap();
let err = verify_dealer(deps.as_mut(), &new_dealer, true).unwrap_err();
assert_eq!(err, ContractError::Unauthorized);
INITIAL_REPLACEMENT_DATA
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
data.initial_height = 2;
Ok(data)
})
.unwrap();
verify_dealer(deps.as_mut(), &new_dealer, true).unwrap();
}
}
#[test]
fn invalid_state() {
@@ -110,7 +156,7 @@ pub(crate) mod tests {
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
try_advance_epoch_state(deps.as_mut(), env).unwrap();
advance_epoch_state(deps.as_mut(), env).unwrap();
let ret = try_add_dealer(
deps.as_mut(),
@@ -2,14 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::ContractError;
use crate::Dealer;
use cosmwasm_std::Storage;
use cosmwasm_std::{Addr, Storage};
use cw_storage_plus::{Key, Map, Path, PrimaryKey};
use nym_coconut_dkg_common::dealing::{DealingMetadata, PartialContractDealing};
use nym_coconut_dkg_common::types::{
ChunkIndex, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealingData,
};
type Dealer<'a> = &'a Addr;
/// Metadata for a dealing for given `EpochId`, submitted by particular `Dealer` for given `DealingIndex`.
pub(crate) const DEALINGS_METADATA: Map<(EpochId, Dealer, DealingIndex), DealingMetadata> =
Map::new("dealings_metadata");
@@ -179,7 +180,7 @@ impl StoredDealing {
pub(crate) fn unchecked_all_entries(
storage: &dyn Storage,
) -> Vec<(
(EpochId, cosmwasm_std::Addr, (DealingIndex, ChunkIndex)),
(EpochId, Addr, (DealingIndex, ChunkIndex)),
PartialContractDealingData,
)> {
use cw_storage_plus::KeyDeserialize;
@@ -209,7 +210,6 @@ impl StoredDealing {
mod tests {
use super::*;
use crate::support::tests::helpers::init_contract;
use cosmwasm_std::Addr;
use cw_storage_plus::Bound;
use std::collections::HashMap;
@@ -1,11 +1,11 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage::ensure_dealer;
use crate::dealers::storage as dealers_storage;
use crate::dealings::storage::{
metadata_exists, must_read_metadata, store_metadata, StoredDealing,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA};
use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::storage::STATE;
@@ -13,25 +13,31 @@ use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Storage};
use nym_coconut_dkg_common::dealing::{
DealingChunkInfo, DealingMetadata, PartialContractDealing, MAX_DEALING_CHUNKS,
};
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId, EpochState};
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochState};
// make sure the epoch is in the dealing exchange and the message sender is a valid dealer for this epoch
fn ensure_permission(
storage: &dyn Storage,
sender: &Addr,
current_epoch_id: EpochId,
resharing: bool,
) -> Result<(), ContractError> {
check_epoch_state(storage, EpochState::DealingExchange { resharing })?;
// ensure the sender is a dealer for this epoch
ensure_dealer(storage, sender, current_epoch_id)?;
// if we're in resharing, make sure this sender has also been a dealer in the previous epoch
if resharing {
ensure_dealer(storage, sender, current_epoch_id.saturating_sub(1))?;
// ensure the sender is a dealer
if dealers_storage::current_dealers()
.may_load(storage, sender)?
.is_none()
{
return Err(ContractError::NotADealer);
}
if resharing
&& !INITIAL_REPLACEMENT_DATA
.load(storage)?
.initial_dealers
.contains(sender)
{
return Err(ContractError::NotAnInitialDealer);
}
Ok(())
}
@@ -42,10 +48,10 @@ pub fn try_submit_dealings_metadata(
chunks: Vec<DealingChunkInfo>,
resharing: bool,
) -> Result<Response, ContractError> {
let epoch = CURRENT_EPOCH.load(deps.storage)?;
let state = STATE.load(deps.storage)?;
ensure_permission(deps.storage, &info.sender, resharing)?;
ensure_permission(deps.storage, &info.sender, epoch.epoch_id, resharing)?;
let state = STATE.load(deps.storage)?;
let epoch = CURRENT_EPOCH.load(deps.storage)?;
// don't allow overwriting existing metadata
if metadata_exists(deps.storage, epoch.epoch_id, &info.sender, dealing_index) {
@@ -133,11 +139,11 @@ pub fn try_commit_dealings_chunk(
env: Env,
info: MessageInfo,
chunk: PartialContractDealing,
resharing: bool,
) -> Result<Response, ContractError> {
// note: checking permissions is implicit as if the metadata exists,
// the sender must have been allowed to submit it
ensure_permission(deps.storage, &info.sender, resharing)?;
let mut epoch = CURRENT_EPOCH.load(deps.storage)?;
let epoch = CURRENT_EPOCH.load(deps.storage)?;
// read meta
let mut metadata = must_read_metadata(
@@ -193,13 +199,6 @@ pub fn try_commit_dealings_chunk(
// store the dealing
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, chunk);
// this is less than ideal since we have to iterate through all the chunks, but realistically,
// there won't be a lot of them
if metadata.is_complete() {
epoch.state_progress.submitted_dealings += 1;
CURRENT_EPOCH.save(deps.storage, &epoch)?;
}
Ok(Response::new())
}
@@ -207,14 +206,18 @@ pub fn try_commit_dealings_chunk(
pub(crate) mod tests {
use super::*;
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
use crate::support::tests::fixtures::{dealing_metadata_fixture, partial_dealing_fixture};
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
use crate::support::tests::fixtures::{
dealer_details_fixture, dealing_metadata_fixture, partial_dealing_fixture,
};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_current_dealer, re_register_dealer, ADMIN_ADDRESS};
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{ContractSafeBytes, TimeConfiguration};
use nym_coconut_dkg_common::types::{
ContractSafeBytes, InitialReplacementData, TimeConfiguration,
};
#[test]
fn invalid_commit_dealing_chunk() {
@@ -224,27 +227,41 @@ pub(crate) mod tests {
let owner = Addr::unchecked("owner1");
let info = mock_info(owner.as_str(), &[]);
let chunk = partial_dealing_fixture();
let dealing = partial_dealing_fixture();
// no dealing metadata
let ret =
try_commit_dealings_chunk(deps.as_mut(), env.clone(), info.clone(), chunk.clone())
.unwrap_err();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
)
.unwrap_err();
assert_eq!(
ret,
ContractError::UnavailableDealingMetadata {
epoch_id: 0,
dealer: info.sender.clone(),
dealing_index: chunk.dealing_index,
ContractError::IncorrectEpochState {
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
}
);
// add dealing metadata
env.block.time = env
.block
.time
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
)
.unwrap_err();
assert_eq!(ret, ContractError::NotADealer);
let dealer_details = DealerDetails {
address: owner.clone(),
bte_public_key_with_proof: String::new(),
@@ -252,12 +269,60 @@ pub(crate) mod tests {
announce_address: String::new(),
assigned_index: 1,
};
add_current_dealer(deps.as_mut(), &dealer_details);
dealers_storage::current_dealers()
.save(deps.as_mut().storage, &owner, &dealer_details)
.unwrap();
// assume we're in resharing mode
CURRENT_EPOCH
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
epoch.state = EpochState::DealingExchange { resharing: true };
Ok(epoch)
})
.unwrap();
INITIAL_REPLACEMENT_DATA
.save(
deps.as_mut().storage,
&InitialReplacementData {
initial_dealers: vec![],
initial_height: 1,
},
)
.unwrap();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
true,
)
.unwrap_err();
assert_eq!(ret, ContractError::NotAnInitialDealer);
INITIAL_REPLACEMENT_DATA
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
data.initial_dealers = vec![dealer_details_fixture(1).address];
Ok(data)
})
.unwrap();
// back to 'normal' mode
CURRENT_EPOCH
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
epoch.state = EpochState::DealingExchange { resharing: false };
Ok(epoch)
})
.unwrap();
// TODO: test case: no metadata
//
//
// add dealing metadata
try_submit_dealings_metadata(
deps.as_mut(),
info.clone(),
chunk.dealing_index,
0,
dealing_metadata_fixture(),
false,
)
@@ -273,6 +338,7 @@ pub(crate) mod tests {
chunk_index: 42,
data: ContractSafeBytes(vec![1, 2, 3]),
},
false,
)
.unwrap_err();
assert_eq!(
@@ -286,14 +352,24 @@ pub(crate) mod tests {
);
// 'good' dealing
let ret =
try_commit_dealings_chunk(deps.as_mut(), env.clone(), info.clone(), chunk.clone());
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
);
assert!(ret.is_ok());
// duplicate dealing
let ret =
try_commit_dealings_chunk(deps.as_mut(), env.clone(), info.clone(), chunk.clone())
.unwrap_err();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
)
.unwrap_err();
assert_eq!(
ret,
ContractError::DealingChunkAlreadyCommitted {
@@ -312,7 +388,6 @@ pub(crate) mod tests {
Ok(epoch)
})
.unwrap();
re_register_dealer(deps.as_mut(), &info.sender);
try_submit_dealings_metadata(
deps.as_mut(),
@@ -323,7 +398,7 @@ pub(crate) mod tests {
)
.unwrap();
let ret = try_commit_dealings_chunk(deps.as_mut(), env, info, chunk.clone());
let ret = try_commit_dealings_chunk(deps.as_mut(), env, info, dealing.clone(), false);
assert!(ret.is_ok());
}
}

Some files were not shown because too many files have changed in this diff Show More