Compare commits

..

4 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 2cc99b486e being VERY explicit about loading coconut keys 2023-12-14 10:09:52 +00:00
Jędrzej Stuczyński 556d980c01 fixed node indexing 2023-12-14 09:27:39 +00:00
Jędrzej Stuczyński b86ae7520e hardcoded migration data 2023-12-14 09:05:47 +00:00
Jędrzej Stuczyński 43da0e3aa7 ignore dkg 2023-12-14 08:30:49 +00:00
998 changed files with 34189 additions and 39537 deletions
@@ -0,0 +1,61 @@
name: build-upload-binaries
on:
workflow_dispatch:
inputs:
add_tokio_unstable:
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
required: true
default: false
type: boolean
env:
NETWORK: mainnet
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
- name: Sets env vars for tokio if set in manual dispatch inputs
run: |
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-binaries-artifacts
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
retention-days: 30
+13 -72
View File
@@ -2,34 +2,20 @@ name: ci-build-upload-binaries
on:
workflow_dispatch:
inputs:
add_tokio_unstable:
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
required: true
default: false
type: boolean
enable_wireguard:
description: 'Add --features wireguard'
required: true
default: false
type: boolean
schedule:
- cron: '14 0 * * *'
pull_request:
paths:
- "clients/**"
- "common/**"
- "explorer-api/**"
- "gateway/**"
- "integrations/**"
- "mixnode/**"
- "nym-api/**"
- "nym-node/**"
- "nym-outfox/**"
- "nym-validator-rewarder/**"
- "sdk/rust/nym-sdk/**"
- "service-providers/**"
- "tools/**"
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/ts-rs-cli/**'
jobs:
publish-nym:
@@ -56,18 +42,6 @@ jobs:
- name: Install Dependencies (Linux)
run: sudo apt update && sudo apt install libudev-dev
- name: Sets env vars for tokio if set in manual dispatch inputs
run: |
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
- 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)
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -77,40 +51,9 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release ${{ env.CARGO_FEATURES }}
- name: Install cargo-deb
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-deb
- name: Build deb packages
shell: bash
run: make deb
# If this was a manual workflow_dispatch, publish binaries.
- name: Upload Artifact
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v3
with:
name: nym-binaries-artifacts
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
retention-days: 30
# If this was a pull_request or nightly, upload to build server
args: --workspace --release
- name: Prepare build output
# if: github.event_name == 'schedule' || github.event_name == 'pull_request'
shell: bash
env:
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
@@ -124,10 +67,8 @@ jobs:
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
cp target/debian/*.deb $OUTPUT_DIR
- name: Deploy branch to CI www
# if: github.event_name == 'schedule' || github.event_name == 'pull_request'
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
-21
View File
@@ -1,21 +0,0 @@
name: ci-cargo-deny
on:
workflow_dispatch:
pull_request:
jobs:
cargo-deny:
runs-on: ubuntu-22.04
strategy:
matrix:
checks:
# - advisories
- licenses bans sources
steps:
- uses: actions/checkout@v3
- uses: EmbarkStudios/cargo-deny-action@v1
with:
log-level: warn
command: check ${{ matrix.checks }}
argument: --all-features
-3
View File
@@ -17,9 +17,6 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: install yarn in root
run: cd ../.. && yarn install
- name: Install npm
run: npm install
+40
View File
@@ -0,0 +1,40 @@
name: ci-nym-vpn-ui-js
on:
workflow_dispatch:
pull_request:
paths:
- 'nym-vpn/ui/src/**'
- 'nym-vpn/ui/package.json'
- 'nym-vpn/ui/index.html'
jobs:
check:
runs-on: custom-linux
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Yarn
run: npm install -g yarn
- name: Install dependencies
working-directory: nym-vpn/ui
run: yarn
- name: Type-check
working-directory: nym-vpn/ui
run: yarn typecheck
- name: Check lint
working-directory: nym-vpn/ui
run: yarn lint
- name: Check formatting
working-directory: nym-vpn/ui
run: yarn fmt:check
# - name: Run tests
# working-directory: nym-vpn/ui
# run: yarn test
- name: Check build
working-directory: nym-vpn/ui
run: yarn build
+63
View File
@@ -0,0 +1,63 @@
name: ci-nym-vpn-ui-rust
on:
workflow_dispatch:
pull_request:
paths:
- 'nym-vpn/ui/src-tauri/**'
jobs:
build:
runs-on: custom-linux
env:
CARGO_TERM_COLOR: always
CARGOTOML_PATH: ./nym-vpn/ui/src-tauri/Cargo.toml
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
continue-on-error: true
- name: Checkout
uses: actions/checkout@v4
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Prepare build
run: mkdir nym-vpn/ui/dist
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path ${{ env.CARGOTOML_PATH }} --features custom-protocol
# - name: Run all tests
# uses: actions-rs/cargo@v1
# with:
# command: test
# args: --manifest-path ${{ env.CARGOTOML_PATH }}
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all -- --check
- name: Annotate with clippy checks
uses: actions-rs/clippy-check@v1
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features --all-targets -- -D warnings
+1 -1
View File
@@ -1,4 +1,4 @@
name: publish-nym-binaries
name: Publish Nym binaries
on:
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:
-1
View File
@@ -9,7 +9,6 @@
target
.env
.env.dev
envs/devnet.env
/.vscode/settings.json
validator/.vscode
sample-configs/validator-config.toml
Generated
+338 -441
View File
File diff suppressed because it is too large Load Diff
+3 -20
View File
@@ -67,7 +67,6 @@ members = [
"common/nymsphinx/params",
"common/nymsphinx/routing",
"common/nymsphinx/types",
"common/nyxd-scraper",
"common/pemstore",
"common/socks5-client-core",
"common/socks5/proxy-helpers",
@@ -102,7 +101,6 @@ members = [
"nym-node",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
"tools/internal/ssl-inject",
"tools/internal/sdk-version-bump",
"tools/nym-cli",
@@ -125,10 +123,9 @@ default-members = [
"nym-api",
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles", "sdk/ffi/cpp"]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles"]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -162,12 +159,10 @@ reqwest = "0.11.22"
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
sqlx = "0.6.3"
tap = "1.0.1"
time = "0.3.30"
thiserror = "1.0.48"
tokio = "1.33.0"
tokio-util = "0.7.10"
tokio = "1.24.1"
tokio-tungstenite = "0.20.1"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
@@ -177,14 +172,6 @@ utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"
# coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
# 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 = { git = "https://github.com/jstuczyn/bls12_381", branch ="feature/gt-serialization-0.8.0" }
group = "0.13.0"
ff = "0.13.0"
# cosmwasm-related
cosmwasm-derive = "=1.3.0"
cosmwasm-schema = "=1.3.0"
@@ -203,11 +190,7 @@ cw-controllers = { version = "=1.1.0" }
# cosmrs-related
bip32 = "0.5.1"
# temporarily using a fork again (yay.) because we need staking and slashing support
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch ="nym-temp/all-validator-features" }
#cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.34" # same version as used by cosmrs
cosmrs = "=0.15.0"
tendermint-rpc = "0.34" # same version as used by cosmrs
prost = "0.12"
+3 -17
View File
@@ -12,7 +12,6 @@ help:
@echo " clippy: run clippy for all workspaces"
@echo " test: run clippy, unit tests, and formatting."
@echo " test-all: like test, but also includes the expensive tests"
@echo " deb: build debian packages
# -----------------------------------------------------------------------------
# Meta targets
@@ -158,12 +157,6 @@ build-explorer-api:
build-nym-cli:
cargo build -p nym-cli --release
build-nym-gateway:
cargo build -p nym-gateway --release
build-nym-mixnode:
cargo build -p nym-mixnode --release
# -----------------------------------------------------------------------------
# Misc
# -----------------------------------------------------------------------------
@@ -176,13 +169,6 @@ run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
# Build debian package, and update PPA
deb-mixnode: build-nym-mixnode
cargo deb -p nym-mixnode
deb-gateway: build-nym-gateway
cargo deb -p nym-gateway
deb-cli: build-nym-cli
cargo deb -p nym-cli
deb: deb-mixnode deb-gateway deb-cli
# Requires base64 encode GPG key to be set up in environment PPA_SIGNING_KEY
deb:
scripts/ppa.sh
-70
View File
@@ -1,70 +0,0 @@
<html>
<head>
<style>
@media (prefers-color-scheme: dark) {
body {
background: #333;
color: white;
}
a {
color: skyblue;
}
}
.container {
font-family: sans-serif;
max-width: 800px;
margin: 0 auto;
}
.intro {
text-align: center;
}
.licenses-list {
list-style-type: none;
margin: 0;
padding: 0;
}
.license-used-by {
margin-top: -10px;
}
.license-text {
max-height: 200px;
overflow-y: scroll;
white-space: pre-wrap;
}
</style>
</head>
<body>
<main class="container">
<div class="intro">
<h1>Third Party Licenses</h1>
<p>This page lists the licenses of the projects used in cargo-about.</p>
</div>
<h2>Overview of licenses:</h2>
<ul class="licenses-overview">
{{#each overview}}
<li><a href="#{{id}}">{{name}}</a> ({{count}})</li>
{{/each}}
</ul>
<h2>All license text:</h2>
<ul class="licenses-list">
{{#each licenses}}
<li class="license">
<h3 id="{{id}}">{{name}}</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
{{#each used_by}}
<li><a href="{{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}">{{crate.name}} {{crate.version}}</a></li>
{{/each}}
</ul>
<pre class="license-text">{{text}}</pre>
</li>
{{/each}}
</ul>
</main>
</body>
</html>
-19
View File
@@ -1,19 +0,0 @@
private = { ignore = true }
accepted = [
"0BSD",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"CC0-1.0",
"ISC",
"MIT",
"MPL-2.0",
"Unicode-DFS-2016",
"OpenSSL",
]
workarounds = [
"ring",
"rustls",
]
-1
View File
@@ -5,7 +5,6 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej St
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.65"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1667,9 +1667,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"dev": true,
"funding": [
{
@@ -5800,9 +5800,9 @@
}
},
"follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"dev": true
},
"forwarded": {
+1 -1
View File
@@ -51,7 +51,7 @@ impl InitialisableClient for NativeClientInit {
}
}
#[derive(Args, Clone, Debug)]
#[derive(Args, Clone)]
pub(crate) struct Init {
#[command(flatten)]
common_args: CommonClientInitArgs,
@@ -3,7 +3,6 @@ name = "nym-client-websocket-requests"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-1
View File
@@ -5,7 +5,6 @@ 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"
rust-version = "1.56"
license.workspace = true
[dependencies]
clap = { workspace = true, features = ["cargo", "derive"] }
+1 -1
View File
@@ -51,7 +51,7 @@ impl InitialisableClient for Socks5ClientInit {
}
}
#[derive(Args, Clone, Debug)]
#[derive(Args, Clone)]
pub(crate) struct Init {
#[command(flatten)]
common_args: CommonClientInitArgs,
-4
View File
@@ -1,4 +0,0 @@
module.exports = {
ConfigHandler: require('./config/configHandler.ts'),
RestClient: require('./restClient/RestClient.ts')
};
-1
View File
@@ -2,7 +2,6 @@
name = "async-file-watcher"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-2
View File
@@ -2,7 +2,6 @@
name = "nym-bandwidth-controller"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -11,7 +10,6 @@ bip39 = { workspace = true }
rand = "0.7.3"
thiserror = { workspace = true }
url = { workspace = true }
zeroize = { workspace = true }
nym-coconut-interface = { path = "../coconut-interface" }
nym-credential-storage = { path = "../credential-storage" }
+15 -12
View File
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError;
use nym_coconut_interface::Base58;
use nym_coconut_interface::{Base58, Parameters};
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO;
@@ -12,8 +12,10 @@ use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::nyxd::Hash;
use rand::rngs::OsRng;
use state::State;
use state::{KeyPair, State};
use std::str::FromStr;
pub mod state;
@@ -22,29 +24,30 @@ where
C: CoconutBandwidthSigningClient + Sync,
{
let mut rng = OsRng;
let signing_key = identity::PrivateKey::new(&mut rng);
let encryption_key = encryption::PrivateKey::new(&mut rng);
let params = BandwidthVoucher::default_parameters();
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let voucher_value = amount.amount.to_string();
let tx_hash = client
.deposit(
amount,
String::from(VOUCHER_INFO),
signing_key.public_key().to_base58_string(),
encryption_key.public_key().to_base58_string(),
signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(),
None,
)
.await?
.transaction_hash;
.transaction_hash
.to_string();
let voucher = BandwidthVoucher::new(
&params,
voucher_value,
VOUCHER_INFO.to_string(),
tx_hash,
signing_key,
encryption_key,
Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?,
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
);
let state = State { voucher, params };
@@ -2,7 +2,32 @@
// SPDX-License-Identifier: Apache-2.0
use nym_coconut_interface::Parameters;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_crypto::asymmetric::{encryption, identity};
pub(crate) struct KeyPair {
pub public_key: String,
pub private_key: String,
}
impl From<identity::KeyPair> for KeyPair {
fn from(kp: identity::KeyPair) -> Self {
Self {
public_key: kp.public_key().to_base58_string(),
private_key: kp.private_key().to_base58_string(),
}
}
}
impl From<encryption::KeyPair> for KeyPair {
fn from(kp: encryption::KeyPair) -> Self {
Self {
public_key: kp.public_key().to_base58_string(),
private_key: kp.private_key().to_base58_string(),
}
}
}
pub struct State {
pub voucher: BandwidthVoucher,
@@ -13,7 +38,7 @@ impl State {
pub fn new(voucher: BandwidthVoucher) -> Self {
State {
voucher,
params: BandwidthVoucher::default_parameters(),
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
}
}
}
+6 -9
View File
@@ -7,7 +7,6 @@ use nym_credential_storage::storage::Storage;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::str::FromStr;
use zeroize::Zeroizing;
use {
nym_coconut_interface::Base58,
nym_credentials::coconut::{
@@ -47,12 +46,10 @@ impl<C, St: Storage> BandwidthController<C, St> {
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
.map_err(|_| StorageError::InconsistentData)?;
let voucher_info = bandwidth_credential.voucher_info.clone();
let serial_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
bandwidth_credential.serial_number,
)?);
let binding_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
bandwidth_credential.binding_number,
)?);
let serial_number =
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
let binding_number =
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
let signature =
nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
@@ -67,8 +64,8 @@ impl<C, St: Storage> BandwidthController<C, St> {
prepare_for_spending(
voucher_value,
voucher_info,
&serial_number,
&binding_number,
serial_number,
binding_number,
epoch_id,
&signature,
&verification_key,
+1 -3
View File
@@ -9,7 +9,6 @@ repository = { workspace = true }
[dependencies]
atty = "0.2"
const-str = "0.5.6"
clap = { workspace = true, features = ["derive"] }
clap_complete = "4.0"
clap_complete_fig = "4.0"
@@ -36,10 +35,9 @@ opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
[build-dependencies]
vergen = { version = "=8.2.6", default-features = false, features = [
vergen = { version = "=7.4.3", default-features = false, features = [
"build",
"git",
"gitcl",
"rustc",
"cargo",
] }
+7 -8
View File
@@ -1,14 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use vergen::EmitBuilder;
use vergen::{vergen, Config};
fn main() {
EmitBuilder::builder()
.all_build()
.all_git()
.all_rustc()
.all_cargo()
.emit()
.expect("failed to extract build metadata");
let mut config = Config::default();
if std::env::var("DOCS_RS").is_ok() {
// If we don't have access to git information, such as in a docs.rs build, don't error
*config.git_mut().skip_if_error_mut() = true;
}
vergen(config).expect("failed to extract build metadata");
}
+5 -14
View File
@@ -40,22 +40,14 @@ pub struct BinaryBuildInformation {
/// Provides the rustc channel that was used for the build, for example `nightly`.
pub rustc_channel: &'static str,
// VERGEN_CARGO_DEBUG
/// Provides the cargo debug mode that was used for the build.
// NOTE: keep the old name cargo_profile instead of cargo_debug for backwards compatibility
// VERGEN_CARGO_PROFILE
/// Provides the cargo profile that was used for the build, for example `debug`.
pub cargo_profile: &'static str,
}
impl BinaryBuildInformation {
// explicitly require the build_version to be passed as it's binary specific
pub const fn new(binary_name: &'static str, build_version: &'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: env!("VERGEN_BUILD_TIMESTAMP"),
@@ -65,7 +57,7 @@ impl BinaryBuildInformation {
commit_branch: env!("VERGEN_GIT_BRANCH"),
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
cargo_profile: env!("VERGEN_CARGO_PROFILE"),
}
}
@@ -123,9 +115,8 @@ pub struct BinaryBuildInformationOwned {
/// Provides the rustc channel that was used for the build, for example `nightly`.
pub rustc_channel: String,
// VERGEN_CARGO_DEBUG
/// Provides the cargo debug mode that was used for the build.
// NOTE: keep the old name cargo_profile instead of cargo_debug for backwards compatibility
// VERGEN_CARGO_PROFILE
/// Provides the cargo profile that was used for the build, for example `debug`.
pub cargo_profile: String,
}
+2 -4
View File
@@ -4,7 +4,6 @@ version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -46,7 +45,6 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" }
si-scale = "0.2.2"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
@@ -60,7 +58,7 @@ features = ["time"]
version = "0.20.1"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
version = "0.6.2"
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
@@ -91,7 +89,7 @@ tempfile = "3.1.0"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
default = []
@@ -109,8 +109,6 @@ pub async fn initialise_client<C>(
) -> Result<InitResultsWithConfig<C::Config>, C::Error>
where
C: InitialisableClient,
<C as InitialisableClient>::Config: std::fmt::Debug,
<C as InitialisableClient>::InitArgs: std::fmt::Debug,
{
info!("initialising {} client", C::NAME);
@@ -142,32 +140,17 @@ where
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = common_args.gateway;
log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}");
let selection_spec = GatewaySelectionSpecification::new(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(common_args.latency_based_selection),
false,
);
log::debug!("Gateway selection specification: {selection_spec:?}");
// Load and potentially override config
log::debug!("Init arguments: {init_args:#?}");
let config = C::construct_config(&init_args);
log::debug!("Constructed config: {config:#?}");
let paths = config.common_paths();
let core = config.core_config();
log::info!(
"Using nym-api: {}",
core.client
.nym_api_urls
.iter()
.map(|url| url.as_str())
.collect::<Vec<&str>>()
.join(",")
);
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let key_store = OnDiskKeys::new(paths.keys.clone());
@@ -1,7 +1,6 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::packet_statistics_control::PacketStatisticsReporter;
use super::received_buffer::ReceivedBufferMessage;
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
@@ -11,7 +10,6 @@ use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputM
use crate::client::key_manager::persistence::KeyStore;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::packet_statistics_control::PacketStatisticsControl;
use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController;
use crate::client::received_buffer::{
@@ -52,7 +50,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::fd::RawFd;
use std::path::Path;
use std::sync::Arc;
use url::Url;
@@ -257,7 +254,6 @@ where
self_address: Recipient,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
stats_tx: PacketStatisticsReporter,
shutdown: TaskClient,
) {
info!("Starting loop cover traffic stream...");
@@ -270,7 +266,6 @@ where
topology_accessor,
debug_config.traffic,
debug_config.cover_traffic,
stats_tx,
);
stream.start_with_shutdown(shutdown);
@@ -290,7 +285,6 @@ where
client_connection_rx: ConnectionCommandReceiver,
shutdown: TaskClient,
packet_type: PacketType,
stats_tx: PacketStatisticsReporter,
) {
info!("Starting real traffic stream...");
@@ -305,7 +299,6 @@ where
reply_controller_receiver,
lane_queue_lengths,
client_connection_rx,
stats_tx,
)
.start_with_shutdown(shutdown, packet_type);
}
@@ -319,7 +312,6 @@ where
reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender,
shutdown: TaskClient,
packet_statistics_control: PacketStatisticsReporter,
) {
info!("Starting received messages buffer controller...");
let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
@@ -329,7 +321,6 @@ where
mixnet_receiver,
reply_key_storage,
reply_controller_sender,
packet_statistics_control,
);
controller.start_with_shutdown(shutdown)
}
@@ -515,13 +506,6 @@ where
Ok(())
}
fn start_packet_statistics_control(shutdown: TaskClient) -> PacketStatisticsReporter {
info!("Starting packet statistics control...");
let (packet_statistics_control, packet_stats_reporter) = PacketStatisticsControl::new();
packet_statistics_control.start_with_shutdown(shutdown);
packet_stats_reporter
}
fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown: TaskClient,
@@ -649,9 +633,6 @@ where
)
.await?;
let packet_stats_reporter =
Self::start_packet_statistics_control(shutdown.fork("packet_statistics_control"));
let gateway_packet_router = PacketRouter::new(
ack_sender,
mixnet_messages_sender,
@@ -681,10 +662,8 @@ where
reply_storage.key_storage(),
reply_controller_sender.clone(),
shutdown.fork("received_messages_buffer"),
packet_stats_reporter.clone(),
);
let gateway_fd = gateway_transceiver.ws_fd();
// The message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
@@ -721,7 +700,6 @@ where
client_connection_rx,
shutdown.fork("real_traffic_controller"),
self.config.debug.traffic.packet_type,
packet_stats_reporter.clone(),
);
if !self
@@ -736,7 +714,6 @@ where
self_address,
shared_topology_accessor.clone(),
message_sender,
packet_stats_reporter,
shutdown.fork("cover_traffic_stream"),
);
}
@@ -757,7 +734,6 @@ where
received_buffer_request_sender,
},
},
gateway_fd,
client_state: ClientState {
shared_lane_queue_lengths,
reply_controller_sender,
@@ -773,7 +749,6 @@ pub struct BaseClient {
pub client_input: ClientInputStatus,
pub client_output: ClientOutputStatus,
pub client_state: ClientState,
pub gateway_fd: Option<RawFd>,
pub task_handle: TaskHandle,
}
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
use crate::client::topology_control::TopologyAccessor;
use crate::{config, spawn_future};
use futures::task::{Context, Poll};
@@ -62,8 +61,6 @@ where
secondary_packet_size: Option<PacketSize>,
packet_type: PacketType,
stats_tx: PacketStatisticsReporter,
}
impl<R> Stream for LoopCoverTrafficStream<R>
@@ -100,8 +97,7 @@ where
// obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R`
impl LoopCoverTrafficStream<OsRng> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
pub fn new(
ack_key: Arc<AckKey>,
average_ack_delay: Duration,
mix_tx: BatchMixMessageSender,
@@ -109,7 +105,6 @@ impl LoopCoverTrafficStream<OsRng> {
topology_access: TopologyAccessor,
traffic_config: config::Traffic,
cover_config: config::CoverTraffic,
stats_tx: PacketStatisticsReporter,
) -> Self {
let rng = OsRng;
@@ -127,7 +122,6 @@ impl LoopCoverTrafficStream<OsRng> {
primary_packet_size: traffic_config.primary_packet_size,
secondary_packet_size: traffic_config.secondary_packet_size,
packet_type: traffic_config.packet_type,
stats_tx,
}
}
@@ -197,10 +191,6 @@ impl LoopCoverTrafficStream<OsRng> {
log::warn!("Failed to send cover message - channel closed");
}
}
} else {
self.stats_tx.report(PacketStatisticsEvent::CoverPacketSent(
cover_traffic_packet_size.size(),
));
}
// TODO: I'm not entirely sure whether this is really required, because I'm not 100%
@@ -1,8 +1,6 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![allow(unused_imports)]
use std::time::Duration;
pub use wasmtimer::{std::Instant, tokio::*};
@@ -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::fd::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
}
}
-1
View File
@@ -7,7 +7,6 @@ pub(crate) mod helpers;
pub mod inbound_messages;
pub mod key_manager;
pub mod mix_traffic;
pub(crate) mod packet_statistics_control;
pub mod real_messages_control;
pub mod received_buffer;
pub mod replies;
@@ -1,503 +0,0 @@
use std::{
collections::VecDeque,
time::{Duration, Instant},
};
use si_scale::helpers::bibytes2;
use crate::spawn_future;
// Time interval between reporting packet statistics
const PACKET_REPORT_INTERVAL_SECS: u64 = 2;
// Interval for taking snapshots of the packet statistics
const SNAPSHOT_INTERVAL_MS: u64 = 500;
// When computing rates, we include snapshots that are up to this old. We set it to some odd number
// a tad larger than an integer number of snapshot intervals, so that we don't have to worry about
// threshold effects.
// Also, set it larger than the packet report interval so that we don't miss notable singular events
const RECORDING_WINDOW_MS: u64 = 2300;
#[derive(Default, Debug, Clone)]
struct PacketStatistics {
// Sent
real_packets_sent: u64,
real_packets_sent_size: usize,
cover_packets_sent: u64,
cover_packets_sent_size: usize,
// Received
real_packets_received: u64,
real_packets_received_size: usize,
cover_packets_received: u64,
cover_packets_received_size: usize,
// Acks
total_acks_received: u64,
total_acks_received_size: usize,
real_acks_received: u64,
real_acks_received_size: usize,
cover_acks_received: u64,
cover_acks_received_size: usize,
// Types of packets queued
// TODO: track the type sent instead
real_packets_queued: u64,
retransmissions_queued: u64,
reply_surbs_queued: u64,
additional_reply_surbs_queued: u64,
}
impl PacketStatistics {
fn handle_event(&mut self, event: PacketStatisticsEvent) {
match event {
PacketStatisticsEvent::RealPacketSent(packet_size) => {
self.real_packets_sent += 1;
self.real_packets_sent_size += packet_size;
}
PacketStatisticsEvent::CoverPacketSent(packet_size) => {
self.cover_packets_sent += 1;
self.cover_packets_sent_size += packet_size;
}
PacketStatisticsEvent::RealPacketReceived(packet_size) => {
self.real_packets_received += 1;
self.real_packets_received_size += packet_size;
}
PacketStatisticsEvent::CoverPacketReceived(packet_size) => {
self.cover_packets_received += 1;
self.cover_packets_received_size += packet_size;
}
PacketStatisticsEvent::AckReceived(packet_size) => {
self.total_acks_received += 1;
self.total_acks_received_size += packet_size;
}
PacketStatisticsEvent::RealAckReceived(packet_size) => {
self.real_acks_received += 1;
self.real_acks_received_size += packet_size;
}
PacketStatisticsEvent::CoverAckReceived(packet_size) => {
self.cover_acks_received += 1;
self.cover_acks_received_size += packet_size;
}
PacketStatisticsEvent::RealPacketQueued => {
self.real_packets_queued += 1;
}
PacketStatisticsEvent::RetransmissionQueued => {
self.retransmissions_queued += 1;
}
PacketStatisticsEvent::ReplySurbRequestQueued => {
self.reply_surbs_queued += 1;
}
PacketStatisticsEvent::AdditionalReplySurbRequestQueued => {
self.additional_reply_surbs_queued += 1;
}
}
}
fn summary(&self) -> (String, String) {
(
format!(
"packets sent: {} (real: {}, cover: {}, retransmissions: {})",
self.real_packets_sent + self.cover_packets_sent,
self.real_packets_sent,
self.cover_packets_sent,
self.retransmissions_queued,
),
format!(
"packets received: {}, (real: {}, cover: {}, acks: {}, acks for cover: {})",
self.real_packets_received + self.cover_packets_received,
self.real_packets_received,
self.cover_packets_received,
self.real_acks_received,
self.cover_acks_received,
),
)
}
}
impl std::ops::Sub for PacketStatistics {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
real_packets_sent: self.real_packets_sent - rhs.real_packets_sent,
real_packets_sent_size: self.real_packets_sent_size - rhs.real_packets_sent_size,
cover_packets_sent: self.cover_packets_sent - rhs.cover_packets_sent,
cover_packets_sent_size: self.cover_packets_sent_size - rhs.cover_packets_sent_size,
real_packets_received: self.real_packets_received - rhs.real_packets_received,
real_packets_received_size: self.real_packets_received_size
- rhs.real_packets_received_size,
cover_packets_received: self.cover_packets_received - rhs.cover_packets_received,
cover_packets_received_size: self.cover_packets_received_size
- rhs.cover_packets_received_size,
total_acks_received: self.total_acks_received - rhs.total_acks_received,
total_acks_received_size: self.total_acks_received_size - rhs.total_acks_received_size,
real_acks_received: self.real_acks_received - rhs.real_acks_received,
real_acks_received_size: self.real_acks_received_size - rhs.real_acks_received_size,
cover_acks_received: self.cover_acks_received - rhs.cover_acks_received,
cover_acks_received_size: self.cover_acks_received_size - rhs.cover_acks_received_size,
real_packets_queued: self.real_packets_queued - rhs.real_packets_queued,
retransmissions_queued: self.retransmissions_queued - rhs.retransmissions_queued,
reply_surbs_queued: self.reply_surbs_queued - rhs.reply_surbs_queued,
additional_reply_surbs_queued: self.additional_reply_surbs_queued
- rhs.additional_reply_surbs_queued,
}
}
}
#[derive(Debug, Clone)]
struct PacketRates {
real_packets_sent: f64,
real_packets_sent_size: f64,
cover_packets_sent: f64,
cover_packets_sent_size: f64,
real_packets_received: f64,
real_packets_received_size: f64,
cover_packets_received: f64,
cover_packets_received_size: f64,
total_acks_received: f64,
total_acks_received_size: f64,
real_acks_received: f64,
real_acks_received_size: f64,
cover_acks_received: f64,
cover_acks_received_size: f64,
real_packets_queued: f64,
retransmissions_queued: f64,
reply_surbs_queued: f64,
additional_reply_surbs_queued: f64,
}
impl From<PacketStatistics> for PacketRates {
fn from(stats: PacketStatistics) -> Self {
Self {
real_packets_sent: stats.real_packets_sent as f64,
real_packets_sent_size: stats.real_packets_sent_size as f64,
cover_packets_sent: stats.cover_packets_sent as f64,
cover_packets_sent_size: stats.cover_packets_sent_size as f64,
real_packets_received: stats.real_packets_received as f64,
real_packets_received_size: stats.real_packets_received_size as f64,
cover_packets_received: stats.cover_packets_received as f64,
cover_packets_received_size: stats.cover_packets_received_size as f64,
total_acks_received: stats.total_acks_received as f64,
total_acks_received_size: stats.total_acks_received_size as f64,
real_acks_received: stats.real_acks_received as f64,
real_acks_received_size: stats.real_acks_received_size as f64,
cover_acks_received: stats.cover_acks_received as f64,
cover_acks_received_size: stats.cover_acks_received_size as f64,
real_packets_queued: stats.real_packets_queued as f64,
retransmissions_queued: stats.retransmissions_queued as f64,
reply_surbs_queued: stats.reply_surbs_queued as f64,
additional_reply_surbs_queued: stats.additional_reply_surbs_queued as f64,
}
}
}
impl std::ops::Sub for PacketRates {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
real_packets_sent: self.real_packets_sent - rhs.real_packets_sent,
real_packets_sent_size: self.real_packets_sent_size - rhs.real_packets_sent_size,
cover_packets_sent: self.cover_packets_sent - rhs.cover_packets_sent,
cover_packets_sent_size: self.cover_packets_sent_size - rhs.cover_packets_sent_size,
real_packets_received: self.real_packets_received - rhs.real_packets_received,
real_packets_received_size: self.real_packets_received_size
- rhs.real_packets_received_size,
cover_packets_received: self.cover_packets_received - rhs.cover_packets_received,
cover_packets_received_size: self.cover_packets_received_size
- rhs.cover_packets_received_size,
total_acks_received: self.total_acks_received - rhs.total_acks_received,
total_acks_received_size: self.total_acks_received_size - rhs.total_acks_received_size,
real_acks_received: self.real_acks_received - rhs.real_acks_received,
real_acks_received_size: self.real_acks_received_size - rhs.real_acks_received_size,
cover_acks_received: self.cover_acks_received - rhs.cover_acks_received,
cover_acks_received_size: self.cover_acks_received_size - rhs.cover_acks_received_size,
real_packets_queued: self.real_packets_queued - rhs.real_packets_queued,
retransmissions_queued: self.retransmissions_queued - rhs.retransmissions_queued,
reply_surbs_queued: self.reply_surbs_queued - rhs.reply_surbs_queued,
additional_reply_surbs_queued: self.additional_reply_surbs_queued
- rhs.additional_reply_surbs_queued,
}
}
}
impl std::ops::Div<f64> for PacketRates {
type Output = Self;
fn div(self, rhs: f64) -> Self::Output {
Self {
real_packets_sent: self.real_packets_sent / rhs,
real_packets_sent_size: self.real_packets_sent_size / rhs,
cover_packets_sent: self.cover_packets_sent / rhs,
cover_packets_sent_size: self.cover_packets_sent_size / rhs,
real_packets_received: self.real_packets_received / rhs,
real_packets_received_size: self.real_packets_received_size / rhs,
cover_packets_received: self.cover_packets_received / rhs,
cover_packets_received_size: self.cover_packets_received_size / rhs,
total_acks_received: self.total_acks_received / rhs,
total_acks_received_size: self.total_acks_received_size / rhs,
real_acks_received: self.real_acks_received / rhs,
real_acks_received_size: self.real_acks_received_size / rhs,
cover_acks_received: self.cover_acks_received / rhs,
cover_acks_received_size: self.cover_acks_received_size / rhs,
real_packets_queued: self.real_packets_queued / rhs,
retransmissions_queued: self.retransmissions_queued / rhs,
reply_surbs_queued: self.reply_surbs_queued / rhs,
additional_reply_surbs_queued: self.additional_reply_surbs_queued / rhs,
}
}
}
impl PacketRates {
fn summary(&self) -> String {
format!(
"down: {}/s, up: {}/s (cover down: {}/s, cover up: {}/s)",
bibytes2(self.real_packets_received_size),
bibytes2(self.real_packets_sent_size),
bibytes2(self.cover_packets_received_size),
bibytes2(self.cover_packets_sent_size),
)
}
fn detailed_summary(&self) -> String {
format!(
"RX: {:.1} mixpkt/s, {}/s (real: {}/s, acks: {}/s), TX: {:.1} mixpkt/s, {}/s (real: {}/s)",
self.real_packets_received + self.cover_packets_received,
bibytes2(self.real_packets_received_size + self.cover_packets_received_size),
bibytes2(self.real_packets_received_size),
bibytes2(self.total_acks_received_size),
self.real_packets_sent + self.cover_packets_sent,
bibytes2(self.real_packets_sent_size + self.cover_packets_sent_size),
bibytes2(self.real_packets_sent_size),
)
}
}
#[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),
// The cover packets sent
CoverPacketSent(usize),
// Real packets received
RealPacketReceived(usize),
// Cover packets received
CoverPacketReceived(usize),
// Ack of any type received. This is mostly used as a consistency check, and should be the sum
// of real and cover acks received.
AckReceived(usize),
// Out of the total acks received, this is the subset of those that were real
RealAckReceived(usize),
// Out of the total acks received, this is the subset of those that were for cover traffic
CoverAckReceived(usize),
// Types of packets queued
RealPacketQueued,
RetransmissionQueued,
ReplySurbRequestQueued,
AdditionalReplySurbRequestQueued,
}
type PacketStatisticsReceiver = tokio::sync::mpsc::UnboundedReceiver<PacketStatisticsEvent>;
#[derive(Clone)]
pub(crate) struct PacketStatisticsReporter {
stats_tx: tokio::sync::mpsc::UnboundedSender<PacketStatisticsEvent>,
}
impl PacketStatisticsReporter {
pub(crate) fn new(stats_tx: tokio::sync::mpsc::UnboundedSender<PacketStatisticsEvent>) -> Self {
Self { stats_tx }
}
pub(crate) fn report(&self, event: PacketStatisticsEvent) {
self.stats_tx.send(event).unwrap_or_else(|err| {
log::error!("Failed to report packet stat: {:?}", err);
});
}
}
pub(crate) struct PacketStatisticsControl {
// Incoming packet stats events from other tasks
stats_rx: PacketStatisticsReceiver,
// Keep track of packet statistics over time
stats: PacketStatistics,
// We keep snapshots of the statistics over time so we can compute rates, and also keeping the
// full history allows for some more fancy averaging if we want to do that.
history: VecDeque<(Instant, PacketStatistics)>,
// Keep previous rates so that we can detect notable events
rates: VecDeque<(Instant, PacketRates)>,
}
impl PacketStatisticsControl {
pub(crate) fn new() -> (Self, PacketStatisticsReporter) {
let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel();
(
Self {
stats_rx,
stats: PacketStatistics::default(),
history: VecDeque::new(),
rates: VecDeque::new(),
},
PacketStatisticsReporter::new(stats_tx),
)
}
// Add the current stats to the history, and remove old ones.
fn update_history(&mut self) {
// Update latest
self.history.push_back((Instant::now(), self.stats.clone()));
// Filter out old ones
let recording_window = Instant::now() - Duration::from_millis(RECORDING_WINDOW_MS);
while self
.history
.front()
.map_or(false, |&(t, _)| t < recording_window)
{
self.history.pop_front();
}
}
fn compute_rates(&self) -> Option<PacketRates> {
// NOTE: consider changing this to compute rates over the history instead of using current
// stats. Currently it should not make much of a difference since we call this just after
// updating the history, but it seems like it could be more internally consistent to do it
// that way.
// Do basic averaging over the entire history, which just uses the first and last
if let Some((start, start_stats)) = self.history.front() {
let duration_secs = Instant::now().duration_since(*start).as_secs_f64();
let delta = self.stats.clone() - start_stats.clone();
let rates = PacketRates::from(delta) / duration_secs;
Some(rates)
} else {
None
}
}
fn update_rates(&mut self) {
// Update latest
if let Some(rates) = self.compute_rates() {
self.rates.push_back((Instant::now(), rates));
}
// Filter out old ones
let recording_window = Instant::now() - Duration::from_millis(RECORDING_WINDOW_MS);
while self
.rates
.front()
.map_or(false, |&(t, _)| t < recording_window)
{
self.rates.pop_front();
}
}
fn report_rates(&self) {
if let Some((_, rates)) = self.rates.back() {
log::info!("{}", rates.summary());
log::debug!("{}", rates.detailed_summary());
}
}
fn report_counters(&self) {
log::trace!("packet statistics: {:?}", &self.stats);
let (summary_sent, summary_recv) = self.stats.summary();
log::debug!("{}", summary_sent);
log::debug!("{}", summary_recv);
}
fn check_for_notable_events(&self) {
let Some((_, latest_rates)) = self.rates.back() else {
return;
};
// If we get a burst of retransmissions
// TODO: consider making this the number of retransmissions since last report instead.
if latest_rates.retransmissions_queued > 0.0 {
log::debug!(
"retransmissions: {:.2} pkt/s",
latest_rates.retransmissions_queued
);
// 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,
);
} else {
log::warn!("Unable to check retransmissions during recording window");
}
}
// IDEA: if there is a burst of acks, that could indicate tokio task starvation.
}
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
log::debug!("Started PacketStatisticsControl with graceful shutdown support");
let report_interval = Duration::from_secs(PACKET_REPORT_INTERVAL_SECS);
let mut report_interval = tokio::time::interval(report_interval);
let snapshot_interval = Duration::from_millis(SNAPSHOT_INTERVAL_MS);
let mut snapshot_interval = tokio::time::interval(snapshot_interval);
loop {
tokio::select! {
stats_event = self.stats_rx.recv() => match stats_event {
Some(stats_event) => {
log::trace!("PacketStatisticsControl: Received stats event");
self.stats.handle_event(stats_event);
},
None => {
log::trace!("PacketStatisticsControl: stopping since stats channel was closed");
break;
}
},
_ = snapshot_interval.tick() => {
self.update_history();
self.update_rates();
}
_ = report_interval.tick() => {
self.report_rates();
self.check_for_notable_events();
self.report_counters();
}
_ = shutdown.recv_with_delay() => {
log::trace!("PacketStatisticsControl: Received shutdown");
break;
},
}
}
log::debug!("PacketStatisticsControl: Exiting");
}
pub(crate) fn start_with_shutdown(mut self, task_client: nym_task::TaskClient) {
spawn_future(async move {
self.run_with_shutdown(task_client).await;
})
}
}
@@ -1,8 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
use super::action_controller::{AckActionSender, Action};
use futures::StreamExt;
use log::*;
@@ -19,7 +17,6 @@ pub(super) struct AcknowledgementListener {
ack_key: Arc<AckKey>,
ack_receiver: AcknowledgementReceiver,
action_sender: AckActionSender,
stats_tx: PacketStatisticsReporter,
}
impl AcknowledgementListener {
@@ -27,21 +24,16 @@ impl AcknowledgementListener {
ack_key: Arc<AckKey>,
ack_receiver: AcknowledgementReceiver,
action_sender: AckActionSender,
stats_tx: PacketStatisticsReporter,
) -> Self {
AcknowledgementListener {
ack_key,
ack_receiver,
action_sender,
stats_tx,
}
}
async fn on_ack(&mut self, ack_content: Vec<u8>) {
trace!("Received an ack");
self.stats_tx
.report(PacketStatisticsEvent::AckReceived(ack_content.len()));
let frag_id = match recover_identifier(&self.ack_key, &ack_content)
.map(FragmentIdentifier::try_from_bytes)
{
@@ -56,14 +48,11 @@ impl AcknowledgementListener {
// because nothing was inserted in the first place
if frag_id == COVER_FRAG_ID {
trace!("Received an ack for a cover message - no need to do anything");
self.stats_tx
.report(PacketStatisticsEvent::CoverAckReceived(ack_content.len()));
return;
}
trace!("Received {} from the mix network", frag_id);
self.stats_tx
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()));
self.action_sender
.unbounded_send(Action::new_remove(frag_id))
.unwrap();
@@ -127,9 +127,7 @@ impl ActionController {
.insert(frag_id, (Arc::new(pending_ack), None))
.is_some()
{
// This used to be a panic, however since we've seen this actually happen in the
// wild, let's not take the whole client (and possibly gateway) down because of it.
error!("Tried to insert duplicate pending ack! This should not be possible!")
panic!("Tried to insert duplicate pending ack")
}
}
}
@@ -8,7 +8,6 @@ use self::{
sent_notification_listener::SentNotificationListener,
};
use crate::client::inbound_messages::InputMessageReceiver;
use crate::client::packet_statistics_control::PacketStatisticsReporter;
use crate::client::real_messages_control::message_handler::MessageHandler;
use crate::client::replies::reply_controller::ReplyControllerSender;
use crate::spawn_future;
@@ -209,7 +208,6 @@ where
connectors: AcknowledgementControllerConnectors,
message_handler: MessageHandler<R>,
reply_controller_sender: ReplyControllerSender,
stats_tx: PacketStatisticsReporter,
) -> Self {
let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
@@ -226,7 +224,6 @@ where
Arc::clone(&ack_key),
connectors.ack_receiver,
connectors.ack_action_sender.clone(),
stats_tx,
);
// will listen for any new messages from the client
@@ -35,8 +35,6 @@ use crate::client::replies::reply_controller;
use crate::config;
pub(crate) use acknowledgement_control::{AckActionSender, Action};
use super::packet_statistics_control::PacketStatisticsReporter;
pub(crate) mod acknowledgement_control;
pub(crate) mod message_handler;
pub(crate) mod real_traffic_stream;
@@ -145,7 +143,6 @@ impl RealMessagesController<OsRng> {
reply_controller_receiver: ReplyControllerReceiver,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
stats_tx: PacketStatisticsReporter,
) -> Self {
let rng = OsRng;
@@ -184,7 +181,6 @@ impl RealMessagesController<OsRng> {
ack_controller_connectors,
message_handler.clone(),
reply_controller_sender,
stats_tx.clone(),
);
let reply_control = ReplyController::new(
@@ -203,7 +199,6 @@ impl RealMessagesController<OsRng> {
topology_access,
lane_queue_lengths,
client_connection_rx,
stats_tx,
);
RealMessagesController {
@@ -3,7 +3,6 @@
use self::sending_delay_controller::SendingDelayController;
use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
use crate::client::topology_control::TopologyAccessor;
use crate::client::transmission_buffer::TransmissionBuffer;
@@ -114,9 +113,6 @@ where
/// Report queue lengths so that upstream can backoff sending data, and keep connections open.
lane_queue_lengths: LaneQueueLengths,
/// Channel used for sending statistics events to `PacketStatisticsControl`.
stats_tx: PacketStatisticsReporter,
}
#[derive(Debug)]
@@ -175,7 +171,6 @@ where
topology_access: TopologyAccessor,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
stats_tx: PacketStatisticsReporter,
) -> Self {
OutQueueControl {
config,
@@ -189,7 +184,6 @@ where
transmission_buffer: TransmissionBuffer::new(),
client_connection_rx,
lane_queue_lengths,
stats_tx,
}
}
@@ -220,7 +214,7 @@ where
async fn on_message(&mut self, next_message: StreamMessage) {
trace!("created new message");
let (next_message, fragment_id, packet_size) = match next_message {
let (next_message, fragment_id) = match next_message {
StreamMessage::Cover => {
let cover_traffic_packet_size = self.loop_cover_message_size();
trace!("the next loop cover message will be put in a {cover_traffic_packet_size} packet");
@@ -256,28 +250,15 @@ where
"Somehow failed to generate a loop cover message with a valid topology",
),
None,
cover_traffic_packet_size.size(),
)
}
StreamMessage::Real(real_message) => {
let packet_size = real_message.packet_size();
(
real_message.mix_packet,
real_message.fragment_id,
packet_size,
)
(real_message.mix_packet, real_message.fragment_id)
}
};
if let Err(err) = self.mix_tx.send(vec![next_message]).await {
log::error!("Failed to send: {err}");
} else {
let event = if fragment_id.is_some() {
PacketStatisticsEvent::RealPacketSent(packet_size)
} else {
PacketStatisticsEvent::CoverPacketSent(packet_size)
};
self.stats_tx.report(event);
}
// notify ack controller about sending our message only after we actually managed to push it
@@ -359,28 +340,6 @@ where
let lane_length = self.transmission_buffer.lane_length(&lane);
self.lane_queue_lengths.set(&lane, lane_length);
// This is the last step in the pipeline where we know the type of the message, so
// lets count the number of retransmissions and reply surb messages sent here.
let stat_event = match lane {
TransmissionLane::General => None,
TransmissionLane::ConnectionId(_) => None,
TransmissionLane::ReplySurbRequest => {
Some(PacketStatisticsEvent::ReplySurbRequestQueued)
}
TransmissionLane::AdditionalReplySurbs => {
Some(PacketStatisticsEvent::AdditionalReplySurbRequestQueued)
}
TransmissionLane::Retransmission => Some(PacketStatisticsEvent::RetransmissionQueued),
};
if let Some(stat_event) = stat_event {
self.stats_tx.report(stat_event);
}
// To avoid comparing apples to oranges when presenting the fraction of packets that are
// retransmissions, we also need to keep track to the total number of real messages queued,
// even though we also track the actual number of messages sent later in the pipeline.
self.stats_tx
.report(PacketStatisticsEvent::RealPacketQueued);
Some(real_next)
}
@@ -474,13 +433,6 @@ where
Poll::Ready(Some((real_messages, conn_id))) => {
log::trace!("handling real_messages: size: {}", real_messages.len());
// This is the last step in the pipeline where we know the type of the message, so
// lets count the number of retransmissions here.
if conn_id == TransmissionLane::Retransmission {
self.stats_tx
.report(PacketStatisticsEvent::RetransmissionQueued);
}
// First store what we got for the given connection id
self.transmission_buffer.store(&conn_id, real_messages);
let real_next = self.pop_next_message().expect("we just added one");
@@ -519,10 +471,10 @@ where
let mult = self.sending_delay_controller.current_multiplier();
let delay = self.current_average_message_sending_delay().as_millis();
let status_str = if self.config.traffic.disable_main_poisson_packet_distribution {
format!("Packet backlog: {backlog:.2} kiB ({packets}), {lanes} lanes, no delay")
format!("Status: {lanes} lanes, backlog: {backlog:.2} kiB ({packets}), no delay")
} else {
format!(
"Packet backlog: {backlog:.2} kiB ({packets}), {lanes} lanes, avg delay: {delay}ms ({mult})"
"Status: {lanes} lanes, backlog: {backlog:.2} kiB ({packets}), avg delay: {delay}ms ({mult})"
)
};
if packets > 1000 {
@@ -1,10 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::{
packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter},
replies::{reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys},
};
use crate::client::replies::reply_controller::ReplyControllerSender;
use crate::client::replies::reply_storage::SentReplyKeys;
use crate::spawn_future;
use futures::channel::mpsc;
use futures::lock::Mutex;
@@ -45,33 +43,15 @@ struct ReceivedMessagesBufferInner<R: MessageReceiver> {
// but perhaps it should be changed to include timestamps of when the message was reconstructed
// and every now and then remove ids older than X
recently_reconstructed: HashSet<i32>,
stats_tx: PacketStatisticsReporter,
}
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
fn recover_from_fragment(
&mut self,
fragment_data: &[u8],
fragment_data_size: usize,
) -> Option<NymMessage> {
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
if nym_sphinx::cover::is_cover(fragment_data) {
trace!("The message was a loop cover message! Skipping it");
// NOTE: it's important to note that there is quite a bit of difference in size of
// received and sent packets due to the sphinx layers being removed by the exit gateway
// before it reaches the mixnet client.
self.stats_tx
.report(PacketStatisticsEvent::CoverPacketReceived(
fragment_data_size,
));
return None;
}
self.stats_tx
.report(PacketStatisticsEvent::RealPacketReceived(
fragment_data_size,
));
let fragment = match self.message_receiver.recover_fragment(fragment_data) {
Err(err) => {
warn!("failed to recover fragment from raw data: {err}. The whole underlying message might be corrupted and unrecoverable!");
@@ -123,17 +103,15 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
reply_ciphertext: &mut [u8],
reply_key: SurbEncryptionKey,
) -> Result<Option<NymMessage>, MessageRecoveryError> {
let reply_ciphertext_size = reply_ciphertext.len();
// note: this performs decryption IN PLACE without extra allocation
self.message_receiver
.recover_plaintext_from_reply(reply_ciphertext, reply_key)?;
let fragment_data = reply_ciphertext;
Ok(self.recover_from_fragment(fragment_data, reply_ciphertext_size))
Ok(self.recover_from_fragment(fragment_data))
}
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
let raw_fragment_size = raw_fragment.len();
let fragment_data = match self.message_receiver.recover_plaintext_from_regular_packet(
self.local_encryption_keypair.private_key(),
&mut raw_fragment,
@@ -145,7 +123,7 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
Ok(frag_data) => frag_data,
};
self.recover_from_fragment(fragment_data, raw_fragment_size)
self.recover_from_fragment(fragment_data)
}
}
@@ -163,7 +141,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
local_encryption_keypair: Arc<encryption::KeyPair>,
reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender,
stats_tx: PacketStatisticsReporter,
) -> Self {
ReceivedMessagesBuffer {
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
@@ -172,7 +149,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
message_receiver: R::new(),
message_sender: None,
recently_reconstructed: HashSet::new(),
stats_tx,
})),
reply_key_storage,
reply_controller_sender,
@@ -377,7 +353,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
};
if let Some(completed) = completed_message {
debug!("received {completed}");
info!("received {completed}");
completed_messages.push(completed)
}
}
@@ -504,13 +480,11 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
mixnet_packet_receiver: MixnetMessageReceiver,
reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender,
packet_statistics_reporter: PacketStatisticsReporter,
) -> Self {
let received_buffer = ReceivedMessagesBuffer::new(
local_encryption_keypair,
reply_key_storage,
reply_controller_sender,
packet_statistics_reporter,
);
ReceivedMessagesBufferController {
+1 -1
View File
@@ -65,7 +65,7 @@ pub async fn current_gateways<R: Rng>(
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::debug!("Fetching list of gateways from: {nym_api}");
log::trace!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_cached_described_gateways().await?;
log::debug!("Found {} gateways", gateways.len());
+1 -1
View File
@@ -178,7 +178,7 @@ impl<T> From<PersistedGatewayDetails<T>> for GatewayDetails<T> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone)]
pub enum GatewaySelectionSpecification<T = EmptyCustomDetails> {
/// Uniformly choose a random remote gateway.
UniformRemote { must_use_tls: bool },
@@ -3,7 +3,6 @@ name = "nym-gateway-client"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -26,8 +26,6 @@ use nym_task::TaskClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::os::fd::AsRawFd;
use std::os::fd::RawFd;
use std::sync::Arc;
use std::time::Duration;
use tungstenite::protocol::Message;
@@ -36,7 +34,6 @@ use tungstenite::protocol::Message;
use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
use tokio_tungstenite::MaybeTlsStream;
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
@@ -149,21 +146,6 @@ impl<C, St> GatewayClient<C, St> {
self.gateway_identity
}
pub fn ws_fd(&self) -> Option<RawFd> {
match &self.connection {
SocketState::Available(conn) => match conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
MaybeTlsStream::NativeTls(stream) => Some(stream.as_raw_fd()),
&_ => None,
},
SocketState::PartiallyDelegated(conn) => Some(conn.ws_fd()),
_ => {
log::warn!("No fd yet");
None
}
}
}
pub fn remaining_bandwidth(&self) -> i64 {
self.bandwidth_remaining
}
@@ -11,7 +11,6 @@ use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_task::TaskClient;
use std::os::fd::{AsRawFd, RawFd};
use std::sync::Arc;
use tungstenite::Message;
@@ -39,15 +38,11 @@ type WsConn = JSWebsocket;
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
pub(crate) struct PartiallyDelegated {
ws_fd: RawFd,
sink_half: SplitSink<WsConn, Message>,
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
}
impl PartiallyDelegated {
pub fn ws_fd(&self) -> RawFd {
self.ws_fd
}
fn recover_received_plaintexts(ws_msgs: Vec<Message>, shared_key: &SharedKeys) -> Vec<Vec<u8>> {
let mut plaintexts = Vec::with_capacity(ws_msgs.len());
for ws_msg in ws_msgs {
@@ -97,11 +92,6 @@ impl PartiallyDelegated {
let (notify_sender, notify_receiver) = oneshot::channel();
let (stream_sender, stream_receiver) = oneshot::channel();
let ws_fd = match conn.get_ref() {
MaybeTlsStream::Plain(stream) => stream.as_raw_fd(),
MaybeTlsStream::NativeTls(stream) => stream.as_raw_fd(),
_ => 0.into(),
};
let (sink, mut stream) = conn.split();
let mixnet_receiver_future = async move {
@@ -151,7 +141,6 @@ impl PartiallyDelegated {
tokio::spawn(mixnet_receiver_future);
PartiallyDelegated {
ws_fd,
sink_half: sink,
delegated_stream: (stream_receiver, notify_sender),
}
+2 -3
View File
@@ -3,15 +3,14 @@ name = "nym-mixnet-client"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = { workspace = true }
log = { workspace = true }
tokio = { workspace = true, features = ["time", "net", "rt"] }
tokio-util = { workspace = true, features = ["codec"] }
tokio = { version = "1.24.1", features = ["time", "net", "rt"] }
tokio-util = { version = "0.7.4", features = ["codec"] }
# internal
nym-sphinx = { path = "../../nymsphinx" }
@@ -4,7 +4,6 @@ version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.56"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -42,14 +42,6 @@ pub struct Config {
nyxd_config: nyxd::Config,
}
impl TryFrom<NymNetworkDetails> for Config {
type Error = ValidatorClientError;
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
Config::try_from_nym_network_details(&value)
}
}
impl Config {
pub fn try_from_nym_network_details(
details: &NymNetworkDetails,
@@ -5,24 +5,16 @@ use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use async_trait::async_trait;
use http_api_client::{ApiClient, NO_PARAMS};
pub use nym_api_requests::{
coconut::{
models::{
EpochCredentialsResponse, IssuedCredential, IssuedCredentialBody,
IssuedCredentialResponse, IssuedCredentialsResponse,
},
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody,
VerifyCredentialBody, VerifyCredentialResponse,
},
models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
},
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
};
pub use nym_coconut_dkg_common::types::EpochId;
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_name_service_common::response::NamesListResponse;
@@ -407,60 +399,6 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn epoch_credentials(
&self,
dkg_epoch: EpochId,
) -> Result<EpochCredentialsResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_EPOCH_CREDENTIALS,
&dkg_epoch.to_string(),
],
NO_PARAMS,
)
.await
}
async fn issued_credential(
&self,
credential_id: i64,
) -> Result<IssuedCredentialResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_ISSUED_CREDENTIAL,
&credential_id.to_string(),
],
NO_PARAMS,
)
.await
}
async fn issued_credentials(
&self,
credential_ids: Vec<i64>,
) -> Result<IssuedCredentialsResponse, NymAPIError> {
self.post_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_ISSUED_CREDENTIALS,
],
NO_PARAMS,
&CredentialsRequestBody {
credential_ids,
pagination: None,
},
)
.await
}
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
log::trace!("Getting service providers");
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
@@ -17,9 +17,6 @@ pub const BANDWIDTH: &str = "bandwidth";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
pub const COCONUT_EPOCH_CREDENTIALS: &str = "epoch-credentials";
pub const COCONUT_ISSUED_CREDENTIAL: &str = "issued-credential";
pub const COCONUT_ISSUED_CREDENTIALS: &str = "issued-credentials";
pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode";
@@ -8,8 +8,6 @@ use cosmwasm_std::{Fraction, Uint128};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Div;
use std::str::FromStr;
use thiserror::Error;
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct MismatchedDenoms;
@@ -128,37 +126,6 @@ impl From<CosmWasmCoin> for Coin {
}
}
// unfortunately cosmwasm didn't re-export this correct so we just redefine its
#[derive(Error, Debug, PartialEq, Eq)]
pub enum CoinFromStrError {
#[error("Missing denominator")]
MissingDenom,
#[error("Missing amount or non-digit characters in amount")]
MissingAmount,
#[error("Invalid amount: {0}")]
InvalidAmount(#[from] std::num::ParseIntError),
}
impl FromStr for Coin {
type Err = CoinFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let pos = s
.find(|c: char| !c.is_ascii_digit())
.ok_or(CoinFromStrError::MissingDenom)?;
let (amount, denom) = s.split_at(pos);
if amount.is_empty() {
return Err(CoinFromStrError::MissingAmount);
}
Ok(Coin {
amount: amount.parse::<u128>()?,
denom: denom.to_string(),
})
}
}
pub trait CoinConverter {
type Target;
@@ -32,7 +32,7 @@ pub trait CoconutBandwidthSigningClient {
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = CoconutBandwidthExecuteMsg::DepositFunds {
data: DepositData::new(info, verification_key, encryption_key),
data: DepositData::new(info.to_string(), verification_key, encryption_key),
};
self.execute_coconut_bandwidth_contract(
fee,
@@ -1,4 +1,4 @@
// 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::collect_paged;
@@ -7,21 +7,13 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_coconut_dkg_common::types::ChunkIndex;
use serde::Deserialize;
pub use nym_coconut_dkg_common::{
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
DealingMetadataResponse, DealingStatusResponse,
},
msg::QueryMsg as DkgQueryMsg,
types::{
DealerDetails, DealingIndex, Epoch, EpochId, EpochState, InitialReplacementData, State,
},
verification_key::{ContractVKShare, PagedVKSharesResponse, VkShareResponse},
use nym_coconut_dkg_common::dealer::{
ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
};
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData};
use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse};
use serde::Deserialize;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -30,16 +22,10 @@ pub trait DkgQueryClient {
where
for<'a> T: Deserialize<'a>;
async fn get_state(&self) -> Result<State, NyxdError> {
let request = DkgQueryMsg::GetState {};
self.query_dkg_contract(request).await
}
async fn get_current_epoch(&self) -> Result<Epoch, NyxdError> {
let request = DkgQueryMsg::GetCurrentEpochState {};
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
@@ -78,86 +64,17 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn get_dealings_metadata(
async fn get_dealings_paged(
&self,
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
) -> Result<DealingMetadataResponse, NyxdError> {
let request = DkgQueryMsg::GetDealingsMetadata {
epoch_id,
dealer,
dealing_index,
idx: u64,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealingsResponse, NyxdError> {
let request = DkgQueryMsg::GetDealing {
idx,
limit,
start_after,
};
self.query_dkg_contract(request).await
}
async fn get_dealer_dealings_status(
&self,
epoch_id: EpochId,
dealer: String,
) -> Result<DealerDealingsStatusResponse, NyxdError> {
let request = DkgQueryMsg::GetDealerDealingsStatus { epoch_id, dealer };
self.query_dkg_contract(request).await
}
async fn get_dealing_status(
&self,
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
) -> Result<DealingStatusResponse, NyxdError> {
let request = DkgQueryMsg::GetDealingStatus {
epoch_id,
dealer,
dealing_index,
};
self.query_dkg_contract(request).await
}
async fn get_dealing_chunk_status(
&self,
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
) -> Result<DealingChunkStatusResponse, NyxdError> {
let request = DkgQueryMsg::GetDealingChunkStatus {
epoch_id,
dealer,
dealing_index,
chunk_index,
};
self.query_dkg_contract(request).await
}
async fn get_dealing_chunk(
&self,
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
) -> Result<DealingChunkResponse, NyxdError> {
let request = DkgQueryMsg::GetDealingChunk {
epoch_id,
dealer,
dealing_index,
chunk_index,
};
self.query_dkg_contract(request).await
}
async fn get_vk_share(
&self,
epoch_id: EpochId,
owner: String,
) -> Result<VkShareResponse, NyxdError> {
let request = DkgQueryMsg::GetVerificationKey { epoch_id, owner };
self.query_dkg_contract(request).await
}
@@ -174,11 +91,6 @@ pub trait DkgQueryClient {
};
self.query_dkg_contract(request).await
}
async fn get_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
self.query_dkg_contract(DkgQueryMsg::GetCW2ContractVersion {})
.await
}
}
// extension trait to the query client to deal with the paged queries
@@ -194,6 +106,10 @@ pub trait PagedDkgQueryClient: DkgQueryClient {
collect_paged!(self, get_past_dealers_paged, dealers)
}
async fn get_all_epoch_dealings(&self, idx: u64) -> Result<Vec<ContractDealing>, NyxdError> {
collect_paged!(self, get_dealings_paged, dealings, idx)
}
async fn get_all_verification_key_shares(
&self,
epoch_id: EpochId,
@@ -227,7 +143,6 @@ where
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue;
use nym_coconut_dkg_common::msg::QueryMsg;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
@@ -236,7 +151,6 @@ mod tests {
msg: DkgQueryMsg,
) {
match msg {
DkgQueryMsg::GetState {} => client.get_state().ignore(),
DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(),
DkgQueryMsg::GetCurrentEpochThreshold {} => {
client.get_current_epoch_threshold().ignore()
@@ -251,42 +165,11 @@ mod tests {
DkgQueryMsg::GetPastDealers { limit, start_after } => {
client.get_past_dealers_paged(start_after, limit).ignore()
}
DkgQueryMsg::GetDealingStatus {
epoch_id,
dealer,
dealing_index,
} => client
.get_dealing_status(epoch_id, dealer, dealing_index)
.ignore(),
DkgQueryMsg::GetDealingsMetadata {
epoch_id,
dealer,
dealing_index,
} => client
.get_dealings_metadata(epoch_id, dealer, dealing_index)
.ignore(),
QueryMsg::GetDealerDealingsStatus { epoch_id, dealer } => {
client.get_dealer_dealings_status(epoch_id, dealer).ignore()
}
DkgQueryMsg::GetDealingChunkStatus {
epoch_id,
dealer,
dealing_index,
chunk_index,
} => client
.get_dealing_chunk_status(epoch_id, dealer, dealing_index, chunk_index)
.ignore(),
DkgQueryMsg::GetDealingChunk {
epoch_id,
dealer,
dealing_index,
chunk_index,
} => client
.get_dealing_chunk(epoch_id, dealer, dealing_index, chunk_index)
.ignore(),
DkgQueryMsg::GetVerificationKey { epoch_id, owner } => {
client.get_vk_share(epoch_id, owner).ignore()
}
DkgQueryMsg::GetDealing {
idx,
limit,
start_after,
} => client.get_dealings_paged(idx, start_after, limit).ignore(),
DkgQueryMsg::GetVerificationKeys {
epoch_id,
limit,
@@ -294,7 +177,6 @@ mod tests {
} => client
.get_vk_shares_paged(epoch_id, start_after, limit)
.ignore(),
DkgQueryMsg::GetCW2ContractVersion {} => client.get_contract_cw2_version().ignore(),
};
}
}
@@ -8,11 +8,11 @@ use crate::nyxd::{Coin, Fee, SigningCosmWasmClient};
use crate::signing::signer::OfflineSigner;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_coconut_dkg_common::dealing::{DealingChunkInfo, PartialContractDealing};
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
use nym_coconut_dkg_common::types::{DealingIndex, EncodedBTEPublicKeyWithProof};
use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
use nym_coconut_dkg_common::verification_key::VerificationKeyShare;
use nym_contracts_common::IdentityKey;
use nym_contracts_common::dealings::ContractSafeBytes;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -25,13 +25,6 @@ pub trait DkgSigningClient {
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn initiate_dkg(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::InitiateDkg {};
self.execute_dkg_contract(fee, req, "initiating the DKG".to_string(), vec![])
.await
}
async fn advance_dkg_epoch_state(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::AdvanceEpochState {};
@@ -49,14 +42,12 @@ pub trait DkgSigningClient {
async fn register_dealer(
&self,
bte_key: EncodedBTEPublicKeyWithProof,
identity_key: IdentityKey,
announce_address: String,
resharing: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::RegisterDealer {
bte_key_with_proof: bte_key,
identity_key,
announce_address,
resharing,
};
@@ -65,32 +56,18 @@ pub trait DkgSigningClient {
.await
}
async fn submit_dealing_metadata(
async fn submit_dealing_bytes(
&self,
dealing_index: DealingIndex,
chunks: Vec<DealingChunkInfo>,
dealing_bytes: ContractSafeBytes,
resharing: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::CommitDealingsMetadata {
dealing_index,
chunks,
let req = DkgExecuteMsg::CommitDealing {
dealing_bytes,
resharing,
};
self.execute_dkg_contract(fee, req, "dealing metadata commitment".to_string(), vec![])
.await
}
async fn submit_dealing_chunk(
&self,
chunk: PartialContractDealing,
resharing: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::CommitDealingsChunk { chunk, resharing };
self.execute_dkg_contract(fee, req, "dealing chunk commitment".to_string(), vec![])
self.execute_dkg_contract(fee, req, "dealing commitment".to_string(), vec![])
.await
}
@@ -117,10 +94,9 @@ pub trait DkgSigningClient {
resharing: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::VerifyVerificationKeyShare {
owner: owner.to_string(),
resharing,
};
// the call to unchecked is fine as we're converting from pre-validated `AccountId`
let owner = Addr::unchecked(owner.to_string());
let req = DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing };
self.execute_dkg_contract(
fee,
@@ -170,36 +146,28 @@ mod tests {
msg: DkgExecuteMsg,
) {
match msg {
DkgExecuteMsg::InitiateDkg {} => client.initiate_dkg(None).ignore(),
DkgExecuteMsg::RegisterDealer {
bte_key_with_proof,
identity_key,
announce_address,
resharing,
} => client
.register_dealer(
bte_key_with_proof,
identity_key,
announce_address,
resharing,
None,
)
.register_dealer(bte_key_with_proof, announce_address, resharing, None)
.ignore(),
DkgExecuteMsg::CommitDealingsMetadata {
dealing_index,
chunks,
DkgExecuteMsg::CommitDealing {
dealing_bytes,
resharing,
} => client
.submit_dealing_metadata(dealing_index, chunks, resharing, None)
.submit_dealing_bytes(dealing_bytes, resharing, 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)
.ignore(),
DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => client
.verify_verification_key_share(&owner.parse().unwrap(), resharing, None)
.verify_verification_key_share(
&owner.into_string().parse().unwrap(),
resharing,
None,
)
.ignore(),
DkgExecuteMsg::SurpassedThreshold {} => client.surpass_threshold(None).ignore(),
DkgExecuteMsg::AdvanceEpochState {} => client.advance_dkg_epoch_state(None).ignore(),
@@ -8,26 +8,26 @@ use std::str::FromStr;
// TODO: all of those could/should be derived via a macro
// query clients
pub mod coconut_bandwidth_query_client;
pub mod dkg_query_client;
pub mod ephemera_query_client;
pub mod group_query_client;
pub mod mixnet_query_client;
pub mod multisig_query_client;
pub mod name_service_query_client;
pub mod sp_directory_query_client;
pub mod vesting_query_client;
mod coconut_bandwidth_query_client;
mod dkg_query_client;
mod ephemera_query_client;
mod group_query_client;
mod mixnet_query_client;
mod multisig_query_client;
mod name_service_query_client;
mod sp_directory_query_client;
mod vesting_query_client;
// signing clients
pub mod coconut_bandwidth_signing_client;
pub mod dkg_signing_client;
pub mod ephemera_signing_client;
pub mod group_signing_client;
pub mod mixnet_signing_client;
pub mod multisig_signing_client;
pub mod name_service_signing_client;
pub mod sp_directory_signing_client;
pub mod vesting_signing_client;
mod coconut_bandwidth_signing_client;
mod dkg_signing_client;
mod ephemera_signing_client;
mod group_signing_client;
mod mixnet_signing_client;
mod multisig_signing_client;
mod name_service_signing_client;
mod sp_directory_signing_client;
mod vesting_signing_client;
// re-export query traits
pub use coconut_bandwidth_query_client::{
@@ -6,8 +6,8 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cw3::{
ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterDetail,
VoterListResponse, VoterResponse,
ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterListResponse,
VoterResponse,
};
use cw_utils::ThresholdResponse;
use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg;
@@ -114,26 +114,6 @@ pub trait PagedMultisigQueryClient: MultisigQueryClient {
Ok(proposals)
}
async fn get_all_voters(&self) -> Result<Vec<VoterDetail>, NyxdError> {
let mut voters = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self.list_voters(start_after.take(), None).await?;
let last_voter = paged_response.voters.last().map(|prop| prop.addr.clone());
voters.append(&mut paged_response.voters);
if let Some(start_after_res) = last_voter {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(voters)
}
}
#[async_trait]
@@ -52,6 +52,10 @@ use wasmtimer::tokio::sleep;
pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
#[cfg(feature = "http-client")]
#[async_trait]
impl CosmWasmClient for cosmrs::rpc::HttpClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait CosmWasmClient: TendermintRpcClient {
@@ -518,7 +522,3 @@ pub trait CosmWasmClient: TendermintRpcClient {
res.try_into()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<T> CosmWasmClient for T where T: TendermintRpcClient {}
@@ -425,7 +425,7 @@ where
amount: amount.into_iter().map(Into::into).collect(),
}
.to_any()
.map_err(|_| NyxdError::SerializationError("MsgSend".to_owned()))
.map_err(|_| NyxdError::SerializationError("MsgExecuteContract".to_owned()))
})
.collect::<Result<_, _>>()?;
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::client_traits::SigningCosmWasmClient;
use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Config, GasPrice, Hash, Height};
use crate::rpc::TendermintRpcClient;
@@ -26,7 +26,6 @@ use cosmrs::rpc::{HttpClient, HttpClientUrl};
pub mod client_traits;
mod helpers;
pub mod logs;
pub mod module_traits;
pub mod types;
#[derive(Debug)]
@@ -330,6 +329,14 @@ where
}
}
#[async_trait]
impl<C, S> CosmWasmClient for MaybeSigningClient<C, S>
where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
}
#[async_trait]
impl<C, S> SigningCosmWasmClient for MaybeSigningClient<C, S>
where
@@ -1,8 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod slashing;
pub mod staking;
pub use staking::query::StakingQueryClient;
// pub use slashing::query
@@ -1,4 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod query;
@@ -1,2 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
@@ -1,8 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod query;
pub use cosmrs::staking::{
QueryHistoricalInfoResponse, QueryValidatorResponse, QueryValidatorsResponse, Validator,
};
@@ -1,78 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::{QueryHistoricalInfoResponse, QueryValidatorResponse, QueryValidatorsResponse};
use crate::nyxd::error::NyxdError;
use crate::nyxd::{CosmWasmClient, PageRequest};
use async_trait::async_trait;
use cosmrs::proto::cosmos::staking::v1beta1::{
QueryHistoricalInfoRequest as ProtoQueryHistoricalInfoRequest,
QueryHistoricalInfoResponse as ProtoQueryHistoricalInfoResponse,
QueryValidatorRequest as ProtoQueryValidatorRequest,
QueryValidatorResponse as ProtoQueryValidatorResponse,
QueryValidatorsRequest as ProtoQueryValidatorsRequest,
QueryValidatorsResponse as ProtoQueryValidatorsResponse,
};
use cosmrs::staking::{QueryHistoricalInfoRequest, QueryValidatorRequest, QueryValidatorsRequest};
use cosmrs::AccountId;
// TODO: change trait restriction from `CosmWasmClient` to `TendermintRpcClient`
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait StakingQueryClient: CosmWasmClient {
async fn historical_info(&self, height: i64) -> Result<QueryHistoricalInfoResponse, NyxdError> {
let path = Some("/cosmos.staking.v1beta1.Query/HistoricalInfo".to_owned());
let req = QueryHistoricalInfoRequest { height };
let res = self
.make_abci_query::<ProtoQueryHistoricalInfoRequest, ProtoQueryHistoricalInfoResponse>(
path,
req.into(),
)
.await?;
Ok(res.try_into()?)
}
async fn validator(
&self,
validator_addr: AccountId,
) -> Result<QueryValidatorResponse, NyxdError> {
let path = Some("/cosmos.staking.v1beta1.Query/Validator".to_owned());
let req = QueryValidatorRequest { validator_addr };
let res = self
.make_abci_query::<ProtoQueryValidatorRequest, ProtoQueryValidatorResponse>(
path,
req.into(),
)
.await?;
Ok(res.try_into()?)
}
async fn validators(
&self,
status: String,
pagination: Option<PageRequest>,
) -> Result<QueryValidatorsResponse, NyxdError> {
let path = Some("/cosmos.staking.v1beta1.Query/Validators".to_owned());
let req = QueryValidatorsRequest { status, pagination };
let res = self
.make_abci_query::<ProtoQueryValidatorsRequest, ProtoQueryValidatorsResponse>(
path,
req.into(),
)
.await?;
Ok(res.try_into()?)
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<T> StakingQueryClient for T where T: CosmWasmClient {}
@@ -1,13 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::TxResponse;
pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str) -> Option<String> {
let event = tx.tx_result.events.iter().find(|e| e.kind == event_type)?;
let attribute = event
.attributes
.iter()
.find(|attr| attr.key == attribute_key)?;
Some(attribute.value.clone())
}
@@ -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;
@@ -28,41 +29,31 @@ use tendermint_rpc::endpoint::*;
use tendermint_rpc::{Error as TendermintRpcError, Order};
use url::Url;
pub use crate::nyxd::{
cosmwasm_client::{
client_traits::{CosmWasmClient, SigningCosmWasmClient},
module_traits::{self, StakingQueryClient},
},
fee::Fee,
};
pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
pub use crate::nyxd::fee::Fee;
pub use crate::rpc::TendermintRpcClient;
pub use coin::Coin;
pub use cosmrs::{
bank::MsgSend,
bip32, cosmwasm,
crypto::PublicKey,
query::{PageRequest, PageResponse},
tendermint::{
abci::{response::DeliverTx, types::ExecTxResult, Event, EventAttribute},
block::Height,
hash::{self, Algorithm, Hash},
validator::Info as TendermintValidatorInfo,
Time as TendermintTime,
},
tx::{self, Msg},
AccountId, Any, Coin as CosmosCoin, Denom, Gas,
pub use cosmrs::bank::MsgSend;
pub use cosmrs::tendermint::abci::{
response::DeliverTx, types::ExecTxResult, Event, EventAttribute,
};
pub use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::hash::{self, Algorithm, Hash};
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::Msg;
pub use cosmrs::tx::{self};
pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::Gas;
pub use cosmrs::{bip32, AccountId, Denom};
pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use cw2;
pub use cw3;
pub use cw4;
pub use cw_controllers;
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use tendermint_rpc::{
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
query::Query,
Paging, Request, Response, SimpleRequest,
Paging,
};
pub use tendermint_rpc::{Request, Response, SimpleRequest};
#[cfg(feature = "http-client")]
use crate::http_client;
@@ -76,7 +67,6 @@ pub mod contract_traits;
pub mod cosmwasm_client;
pub mod error;
pub mod fee;
pub mod helpers;
#[derive(Debug, Clone)]
pub struct Config {
@@ -102,14 +92,6 @@ impl Config {
}
}
impl TryFrom<NymNetworkDetails> for Config {
type Error = NyxdError;
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
Config::try_from_nym_network_details(&value)
}
}
#[derive(Debug)]
pub struct NyxdClient<C, S = NoSigner> {
client: MaybeSigningClient<C, S>,
@@ -741,7 +723,7 @@ where
where
H: Into<Height> + Send,
{
TendermintRpcClient::validators(&self.client, height, paging).await
self.client.validators(height, paging).await
}
async fn latest_consensus_params(
@@ -816,6 +798,14 @@ where
}
}
#[async_trait]
impl<C, S> CosmWasmClient for NyxdClient<C, S>
where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
}
impl<C, S> OfflineSigner for NyxdClient<C, S>
where
S: OfflineSigner,
-1
View File
@@ -3,7 +3,6 @@ name = "nym-coconut-interface"
version = "0.1.0"
edition = "2021"
description = "Crutch library until there is proper SerDe support for coconut structs"
license.workspace = true
[dependencies]
bs58 = "0.4.0"
+11 -13
View File
@@ -14,21 +14,17 @@ pub use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar,
prepare_blind_sign, prove_bandwidth_credential, Attribute, Base58, BlindSignRequest,
BlindedSignature, Bytable, CoconutError, KeyPair, Parameters, PrivateAttribute,
PublicAttribute, SecretKey, Signature, SignatureShare, Theta, VerificationKey,
PublicAttribute, Signature, SignatureShare, Theta, VerificationKey,
};
#[derive(Debug, Serialize, Deserialize, Getters, CopyGetters, Clone, PartialEq, Eq)]
pub struct Credential {
#[getset(get = "pub")]
n_params: u32,
#[getset(get = "pub")]
theta: Theta,
voucher_value: u64,
voucher_info: String,
#[getset(get = "pub")]
epoch_id: u64,
}
@@ -68,12 +64,14 @@ impl Credential {
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
let params = Parameters::new(self.n_params).unwrap();
let hashed_value = hash_to_scalar(self.voucher_value.to_string());
let hashed_info = hash_to_scalar(&self.voucher_info);
let public_attributes = &[&hashed_value, &hashed_info];
nym_coconut::verify_credential(&params, verification_key, &self.theta, public_attributes)
let public_attributes = [
self.voucher_value.to_string().as_bytes(),
self.voucher_info.as_bytes(),
]
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
nym_coconut::verify_credential(&params, verification_key, &self.theta, &public_attributes)
}
pub fn as_bytes(&self) -> Vec<u8> {
@@ -182,8 +180,8 @@ mod tests {
&params,
&verification_key,
&signature,
&serial_number,
&binding_number,
serial_number,
binding_number,
)
.unwrap();
let credential = Credential::new(4, theta, voucher_value, voucher_info, 42);
-3
View File
@@ -3,7 +3,6 @@ name = "nym-cli-commands"
version = "1.0.0"
authors.workspace = true
edition = "2021"
license.workspace = true
[dependencies]
anyhow = { workspace = true }
@@ -13,11 +12,9 @@ bs58 = "0.4"
comfy-table = "6.0.0"
cfg-if = "1.0.0"
clap = { workspace = true, features = ["derive"] }
csv = "1.3.0"
cw-utils = { workspace = true }
handlebars = "3.0.1"
humantime-serde = "1.0"
inquire = "0.6.2"
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
log = { workspace = true }
rand = {version = "0.6", features = ["std"] }
@@ -1,4 +0,0 @@
n1q85lscptz860j3dx92f8phaeaw08j2l5dt7adq,50,nym
n136ckky0n39eskewg04xhvahq9yp9f7sgtnxytt,50,unym
n1xh7ru0zp67czxhvx0r5e8ur7rvg6n3ymmje0ju,50,nyx
n13mpm4aj03alffnvz2x9ynjy4u3cxemxn2xw34w,50,unyx
1 n1q85lscptz860j3dx92f8phaeaw08j2l5dt7adq 50 nym
2 n136ckky0n39eskewg04xhvahq9yp9f7sgtnxytt 50 unym
3 n1xh7ru0zp67czxhvx0r5e8ur7rvg6n3ymmje0ju 50 nyx
4 n13mpm4aj03alffnvz2x9ynjy4u3cxemxn2xw34w 50 unyx
@@ -26,10 +26,6 @@ pub struct Args {
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
if args.amount == 0 {
bail!("did not specify credential amount")
}
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
+1 -4
View File
@@ -1,4 +1,4 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
@@ -7,7 +7,6 @@ pub mod balance;
pub mod create;
pub mod pubkey;
pub mod send;
pub mod send_multiple;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
@@ -26,6 +25,4 @@ pub enum AccountCommands {
PubKey(crate::validator::account::pubkey::Args),
/// Sends tokens to another account
Send(crate::validator::account::send::Args),
/// Batch multiple token sends
SendMultiple(crate::validator::account::send_multiple::Args),
}
@@ -1,216 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::pretty_coin;
use clap::Parser;
use comfy_table::Table;
use cosmrs::rpc::endpoint::tx::Response;
use log::{error, info};
use nym_validator_client::nyxd::{AccountId, Coin};
use serde_json::json;
use std::str::FromStr;
use std::{fs, io::Write};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub memo: Option<String>,
#[clap(
long,
help = "Input file path (CSV format) with account/amount pairs to send"
)]
pub input: String,
#[clap(
long,
help = "An output file path (CSV format) to create or append a log of results to"
)]
pub output: Option<String>,
}
pub async fn send_multiple(args: Args, client: &SigningClient) {
let memo = args
.memo
.unwrap_or_else(|| "Sending tokens with nym-cli".to_owned());
let rows = InputFileReader::new(&args.input);
if let Err(e) = rows {
error!("Failed to read input file: {}", e);
return;
}
let rows = rows.unwrap();
let mut table = Table::new();
if rows.rows.is_empty() {
error!("No transactions to send");
return;
}
println!(
"The following transfer will be made from account {} to:",
client.address()
);
table.set_header(vec!["Address", "Amount"]);
for row in rows.rows.iter() {
table.add_row(vec![row.address.to_string(), pretty_coin(&row.amount)]);
}
println!("{table}");
let ans = inquire::Confirm::new("Do you want to continue with the transfers?")
.with_default(false)
.with_help_message("You must confirm before the transaction will be sent")
.prompt();
if let Err(e) = ans {
info!("Aborting, {}...", e);
return;
}
if let Ok(false) = ans {
info!("Aborting!");
return;
}
info!("Transferring from {}...", client.address());
let multiple_sends: Vec<(AccountId, Vec<Coin>)> = rows
.rows
.iter()
.map(|row| (row.address.clone(), vec![row.amount.clone()]))
.collect();
let res = client
.send_multiple(multiple_sends, memo, None)
.await
.expect("failed to send tokens!");
info!("Sending result: {}", json!(res));
println!();
println!(
"Nodesguru: https://nym.explorers.guru/transaction/{}",
&res.hash
);
println!("Mintscan: https://www.mintscan.io/nyx/txs/{}", &res.hash);
println!("Transaction result code: {}", &res.tx_result.code.value());
println!("Transaction hash: {}", &res.hash);
if let Some(output_filename) = args.output {
println!("\nWriting output log to {}", output_filename);
if let Err(e) = write_output_file(rows, res, &output_filename) {
error!(
"Failed to write output file {} with error {}",
output_filename, e
);
}
}
}
fn write_output_file(
rows: InputFileReader,
res: Response,
output_filename: &String,
) -> Result<(), anyhow::Error> {
let mut file = fs::OpenOptions::new()
.create(true)
.append(true)
.open(output_filename)?;
let now = time::OffsetDateTime::now_utc();
let now = now.format(&time::format_description::well_known::Rfc3339)?;
let data = rows
.rows
.iter()
.map(|row| {
format!(
"{},{},{},{},{}",
row.address, row.amount.amount, row.amount.denom, now, res.hash
)
})
.collect::<Vec<String>>()
.join("\n");
Ok(file.write_all(format!("{}\n", data).as_bytes())?)
}
#[derive(Debug)]
pub struct InputFileRow {
pub address: AccountId,
pub amount: Coin,
}
pub struct InputFileReader {
pub rows: Vec<InputFileRow>,
}
impl InputFileReader {
pub fn new(path: &str) -> Result<InputFileReader, anyhow::Error> {
let mut rows: Vec<InputFileRow> = vec![];
let file_contents = fs::read_to_string(path)?;
let lines: Vec<String> = file_contents.lines().map(String::from).collect();
for line in lines {
let tokens: Vec<_> = line.split(',').collect();
if tokens.len() < 3 {
return Err(anyhow::anyhow!(
"'{}' does not have enough columns, expecting <address>,<amount>,<denom>",
line
));
}
// try parse amount to u128
let amount = u128::from_str(tokens[1])
.map_err(|_| anyhow::anyhow!("'{}' has an invalid amount", line))?;
let denom: String = tokens[2].into();
// multiply when a whole token amount, e.g. 50nym (50.123456nym is not allowed, that must be input as 50123456unym)
let (amount, denom) = if !denom.starts_with('u') {
(amount * 1_000_000u128, format!("u{}", denom))
} else {
(amount, denom)
};
let address = AccountId::from_str(tokens[0])
.map_err(|e| anyhow::anyhow!("'{}' has an invalid address: {}", line, e))?;
let amount = Coin { amount, denom };
rows.push(InputFileRow { address, amount })
}
Ok(InputFileReader { rows })
}
}
#[cfg(test)]
mod test_multiple_send_input_csv {
use super::*;
use nym_validator_client::nyxd::AccountId;
use std::str::FromStr;
#[test]
fn works_on_happy_path() {
let input_csv = InputFileReader::new("fixtures/test_send_multiple.csv").unwrap();
assert_eq!(
AccountId::from_str("n1q85lscptz860j3dx92f8phaeaw08j2l5dt7adq").unwrap(),
input_csv.rows[0].address
);
println!("{:?}", input_csv.rows);
assert_eq!(50_000_000u128, input_csv.rows[0].amount.amount);
assert_eq!(50u128, input_csv.rows[1].amount.amount);
assert_eq!(50_000_000u128, input_csv.rows[2].amount.amount);
assert_eq!(50u128, input_csv.rows[3].amount.amount);
assert_eq!("unym", input_csv.rows[0].amount.denom);
assert_eq!("unym", input_csv.rows[1].amount.denom);
assert_eq!("unyx", input_csv.rows[2].amount.denom);
assert_eq!("unyx", input_csv.rows[3].amount.denom);
}
}
@@ -3,7 +3,6 @@
use clap::Parser;
use log::{debug, info};
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
use std::str::FromStr;
use nym_coconut_dkg_common::msg::InstantiateMsg;
@@ -94,7 +93,6 @@ pub async fn generate(args: Args) {
multisig_addr: multisig_addr.to_string(),
time_configuration: Some(time_configuration),
mix_denom,
key_size: DEFAULT_DEALINGS as u32,
};
debug!("instantiate_msg: {:?}", instantiate_msg);
+1 -2
View File
@@ -3,7 +3,6 @@ name = "nym-config"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -18,4 +17,4 @@ url = { workspace = true }
nym-network-defaults = { path = "../network-defaults" }
[features]
default = ["dirs"]
default = ["dirs"]
+1 -1
View File
@@ -73,7 +73,7 @@ where
P: AsRef<Path>,
{
let path = path.as_ref();
log::info!("saving config file to {}", path.display());
log::debug!("trying to save config file to {}", path.display());
if let Some(parent) = path.parent() {
create_dir_all(parent)?;
@@ -2,7 +2,6 @@
name = "nym-coconut-bandwidth-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,4 +12,4 @@ cw2 = { workspace = true, optional = true }
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = ["cw2"]
schema = ["cw2"]
@@ -4,9 +4,6 @@
// event types
pub const DEPOSITED_FUNDS_EVENT_TYPE: &str = "deposited-funds";
// a 'wasm-' prefix is added to all cosmwasm events
pub const COSMWASM_DEPOSITED_FUNDS_EVENT_TYPE: &str = "wasm-deposited-funds";
// attributes that are used in multiple places
pub const DEPOSIT_VALUE: &str = "deposit-value";
pub const DEPOSIT_INFO: &str = "deposit-info";
@@ -2,7 +2,6 @@
name = "nym-coconut-dkg-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,11 +9,9 @@ license.workspace = true
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-utils = { workspace = true }
cw2 = { workspace = true }
cw4 = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = []
schema = []
@@ -1,7 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{EncodedBTEPublicKeyWithProof, NodeIndex};
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, NodeIndex};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
@@ -9,7 +9,6 @@ use cosmwasm_std::Addr;
pub struct DealerDetails {
pub address: Addr,
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
pub ed25519_identity: String,
pub announce_address: String,
pub assigned_index: NodeIndex,
}
@@ -65,3 +64,38 @@ impl PagedDealerResponse {
}
}
}
#[cw_serde]
pub struct ContractDealing {
pub dealing: ContractSafeBytes,
pub dealer: Addr,
}
impl ContractDealing {
pub fn new(dealing: ContractSafeBytes, dealer: Addr) -> Self {
ContractDealing { dealing, dealer }
}
}
#[cw_serde]
pub struct PagedDealingsResponse {
pub dealings: Vec<ContractDealing>,
pub per_page: usize,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<Addr>,
}
impl PagedDealingsResponse {
pub fn new(
dealings: Vec<ContractDealing>,
per_page: usize,
start_next_after: Option<Addr>,
) -> Self {
PagedDealingsResponse {
dealings,
per_page,
start_next_after,
}
}
}
@@ -1,290 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{ChunkIndex, DealingIndex, EpochId, PartialContractDealingData};
use contracts_common::dealings::ContractSafeBytes;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use std::collections::{BTreeMap, HashMap};
/// Defines the maximum size of a dealing chunk. Currently set to 2kB
pub const MAX_DEALING_CHUNK_SIZE: usize = 2048;
/// Defines the maximum size of a full dealing.
/// Currently set to 100kB (which is enough for a dealing created for 100 parties)
pub const MAX_DEALING_SIZE: usize = 102400;
pub const MAX_DEALING_CHUNKS: usize = MAX_DEALING_SIZE / MAX_DEALING_CHUNK_SIZE;
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
pub const DEFAULT_DEALINGS: usize = 2 + 2 + 1;
pub fn chunk_dealing(
dealing_index: DealingIndex,
dealing_bytes: Vec<u8>,
chunk_size: usize,
) -> HashMap<ChunkIndex, PartialContractDealing> {
let mut chunks = HashMap::new();
for (chunk_index, chunk) in dealing_bytes.chunks(chunk_size).enumerate() {
let chunk = PartialContractDealing {
dealing_index,
chunk_index: chunk_index as ChunkIndex,
data: ContractSafeBytes(chunk.to_vec()),
};
chunks.insert(chunk_index as ChunkIndex, chunk);
}
chunks
}
#[cw_serde]
#[derive(Copy)]
pub struct DealingChunkInfo {
pub size: u64,
}
impl DealingChunkInfo {
pub fn new(size: usize) -> Self {
DealingChunkInfo { size: size as u64 }
}
pub fn construct(dealing_len: usize, chunk_size: usize) -> Vec<Self> {
let (full_chunks, overflow) = (dealing_len / chunk_size, dealing_len % chunk_size);
let mut chunks = Vec::new();
for _ in 0..full_chunks {
chunks.push(DealingChunkInfo::new(chunk_size));
}
if overflow != 0 {
chunks.push(DealingChunkInfo::new(overflow));
}
chunks
}
}
#[cw_serde]
#[derive(Copy)]
pub struct SubmittedChunk {
pub info: DealingChunkInfo,
pub status: ChunkSubmissionStatus,
}
#[cw_serde]
#[derive(Default, Copy)]
pub struct ChunkSubmissionStatus {
// this field is updated by the contract itself to indicate when this particular chunk has been received
pub submission_height: Option<u64>,
}
impl ChunkSubmissionStatus {
pub fn submitted(&self) -> bool {
self.submission_height.is_some()
}
}
impl From<DealingChunkInfo> for SubmittedChunk {
fn from(value: DealingChunkInfo) -> Self {
SubmittedChunk::new(value)
}
}
impl SubmittedChunk {
pub fn new(info: DealingChunkInfo) -> Self {
SubmittedChunk {
info,
status: Default::default(),
}
}
pub fn submitted(&self) -> bool {
self.status.submitted()
}
}
#[cw_serde]
pub struct DealingMetadata {
pub dealing_index: DealingIndex,
pub submitted_chunks: BTreeMap<ChunkIndex, SubmittedChunk>,
}
impl DealingMetadata {
pub fn new(dealing_index: DealingIndex, chunks: Vec<DealingChunkInfo>) -> Self {
DealingMetadata {
dealing_index,
submitted_chunks: chunks
.into_iter()
.enumerate()
.map(|(id, chunk)| (id as ChunkIndex, chunk.into()))
.collect(),
}
}
pub fn is_complete(&self) -> bool {
self.submitted_chunks.values().all(|c| c.submitted())
}
pub fn total_size(&self) -> usize {
self.submitted_chunks
.values()
.map(|c| c.info.size as usize)
.sum()
}
pub fn submission_statuses(&self) -> BTreeMap<ChunkIndex, ChunkSubmissionStatus> {
self.submitted_chunks
.iter()
.map(|(id, c)| (*id, c.status))
.collect()
}
}
#[cw_serde]
pub struct PartialContractDealing {
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub data: PartialContractDealingData,
}
impl PartialContractDealing {
pub fn new(
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
data: PartialContractDealingData,
) -> Self {
PartialContractDealing {
dealing_index,
chunk_index,
data,
}
}
}
#[cw_serde]
pub struct DealingMetadataResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub metadata: Option<DealingMetadata>,
}
#[cw_serde]
pub struct DealingChunkResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub chunk: Option<PartialContractDealingData>,
}
#[cw_serde]
pub struct DealingChunkStatusResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub status: ChunkSubmissionStatus,
}
#[cw_serde]
pub struct DealingStatusResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub status: DealingStatus,
}
#[cw_serde]
pub struct DealingStatus {
pub has_metadata: bool,
pub fully_submitted: bool,
pub chunk_submission_status: BTreeMap<ChunkIndex, ChunkSubmissionStatus>,
}
impl From<Option<DealingMetadata>> for DealingStatus {
fn from(metadata: Option<DealingMetadata>) -> Self {
DealingStatus {
has_metadata: metadata.is_some(),
fully_submitted: metadata
.as_ref()
.map(|m| m.is_complete())
.unwrap_or_default(),
chunk_submission_status: metadata
.map(|m| m.submission_statuses())
.unwrap_or_default(),
}
}
}
#[cw_serde]
pub struct DealerDealingsStatusResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub all_dealings_fully_submitted: bool,
pub dealing_submission_status: BTreeMap<DealingIndex, DealingStatus>,
}
impl DealerDealingsStatusResponse {
pub fn full_dealings(&self) -> usize {
self.dealing_submission_status
.values()
.filter(|s| s.fully_submitted)
.count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunking_dealings() {
const CHUNK_SIZE: usize = 512;
let test_cases = [
(CHUNK_SIZE - 10, CHUNK_SIZE, 1),
(CHUNK_SIZE, CHUNK_SIZE, 1),
(CHUNK_SIZE + 10, CHUNK_SIZE, 2),
(CHUNK_SIZE * 2, CHUNK_SIZE, 2),
(CHUNK_SIZE * 2 + 1, CHUNK_SIZE, 3),
(CHUNK_SIZE * 10 + 42, CHUNK_SIZE, 11),
];
for (dealing_len, chunk_size, expected_chunks) in test_cases {
let chunks = DealingChunkInfo::construct(dealing_len, chunk_size);
assert_eq!(expected_chunks, chunks.len());
assert_eq!(
dealing_len as u64,
chunks.iter().map(|c| c.size).sum::<u64>()
);
let mut expected_last = dealing_len % chunk_size;
if expected_last == 0 {
expected_last = chunk_size;
}
assert_eq!(chunks.last().unwrap().size, expected_last as u64);
}
}
}
@@ -1,8 +1,4 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod dealer;
pub mod dealing;
pub mod event_attributes;
pub mod msg;
pub mod types;
@@ -1,23 +1,16 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealing::{DealingChunkInfo, PartialContractDealing};
use crate::types::{
ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration,
};
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration};
use crate::verification_key::VerificationKeyShare;
use contracts_common::IdentityKey;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
#[cfg(feature = "schema")]
use crate::{
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
DealingMetadataResponse, DealingStatusResponse,
},
types::{Epoch, InitialReplacementData, State},
verification_key::{PagedVKSharesResponse, VkShareResponse},
dealer::{DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse},
types::{Epoch, InitialReplacementData},
verification_key::PagedVKSharesResponse,
};
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
@@ -28,31 +21,18 @@ pub struct InstantiateMsg {
pub multisig_addr: String,
pub time_configuration: Option<TimeConfiguration>,
pub mix_denom: String,
/// Specifies the number of elements in the derived keys
pub key_size: u32,
}
#[cw_serde]
pub enum ExecuteMsg {
// we could have just re-used AdvanceEpochState, but imo an explicit message is better
InitiateDkg {},
RegisterDealer {
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
identity_key: IdentityKey,
announce_address: String,
resharing: bool,
},
CommitDealingsMetadata {
dealing_index: DealingIndex,
chunks: Vec<DealingChunkInfo>,
resharing: bool,
},
CommitDealingsChunk {
chunk: PartialContractDealing,
CommitDealing {
dealing_bytes: ContractSafeBytes,
resharing: bool,
},
@@ -62,7 +42,8 @@ pub enum ExecuteMsg {
},
VerifyVerificationKeyShare {
owner: String,
// TODO: this should be using a String...
owner: Addr,
resharing: bool,
},
@@ -74,9 +55,6 @@ pub enum ExecuteMsg {
#[cw_serde]
#[cfg_attr(feature = "schema", derive(QueryResponses))]
pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(State))]
GetState {},
#[cfg_attr(feature = "schema", returns(Epoch))]
GetCurrentEpochState {},
@@ -101,53 +79,19 @@ pub enum QueryMsg {
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(DealingMetadataResponse))]
GetDealingsMetadata {
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
#[cfg_attr(feature = "schema", returns(PagedDealingsResponse))]
GetDealing {
idx: u64,
limit: Option<u32>,
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(DealerDealingsStatusResponse))]
GetDealerDealingsStatus { epoch_id: EpochId, dealer: String },
#[cfg_attr(feature = "schema", returns(DealingStatusResponse))]
GetDealingStatus {
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
},
#[cfg_attr(feature = "schema", returns(DealingChunkStatusResponse))]
GetDealingChunkStatus {
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
},
#[cfg_attr(feature = "schema", returns(DealingChunkResponse))]
GetDealingChunk {
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
},
#[cfg_attr(feature = "schema", returns(VkShareResponse))]
GetVerificationKey { epoch_id: EpochId, owner: String },
#[cfg_attr(feature = "schema", returns(PagedVKSharesResponse))]
GetVerificationKeys {
epoch_id: EpochId,
limit: Option<u32>,
start_after: Option<String>,
},
/// Gets the stored contract version information that's required by the CW2 spec interface for migrations.
#[serde(rename = "get_cw2_contract_version")]
#[cfg_attr(feature = "schema", returns(cw2::ContractVersion))]
GetCW2ContractVersion {},
}
#[cw_serde]
@@ -8,18 +8,14 @@ use std::str::FromStr;
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
pub use contracts_common::dealings::ContractSafeBytes;
pub use cosmwasm_std::{Addr, Coin, Timestamp};
pub use cw4::Cw4Contract;
pub type EncodedBTEPublicKeyWithProof = String;
pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str;
pub type NodeIndex = u64;
pub type EpochId = u64;
pub type DealingIndex = u32;
// we really don't need to hold more data than that (even u8 would have been enough),
// but explicitly make it different type than `DealingIndex` so type system would detect any
// accidental misuses
pub type ChunkIndex = u16;
pub type PartialContractDealingData = ContractSafeBytes;
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
#[cw_serde]
pub struct InitialReplacementData {
@@ -77,23 +73,13 @@ impl Default for TimeConfiguration {
}
}
#[cw_serde]
pub struct State {
pub mix_denom: String,
pub multisig_addr: Addr,
pub group_addr: Cw4Contract,
/// Specifies the number of elements in the derived keys
pub key_size: u32,
}
#[cw_serde]
#[derive(Copy, Default)]
pub struct Epoch {
pub state: EpochState,
pub epoch_id: EpochId,
pub time_configuration: TimeConfiguration,
pub finish_timestamp: Option<Timestamp>,
pub finish_timestamp: Timestamp,
}
impl Epoch {
@@ -104,40 +90,36 @@ impl Epoch {
current_timestamp: Timestamp,
) -> Self {
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)
time_configuration.public_key_submission_time_secs
}
EpochState::DealingExchange { .. } => time_configuration.dealing_exchange_time_secs,
EpochState::VerificationKeySubmission { .. } => {
Some(time_configuration.verification_key_submission_time_secs)
time_configuration.verification_key_submission_time_secs
}
EpochState::VerificationKeyValidation { .. } => {
Some(time_configuration.verification_key_validation_time_secs)
time_configuration.verification_key_validation_time_secs
}
EpochState::VerificationKeyFinalization { .. } => {
Some(time_configuration.verification_key_finalization_time_secs)
time_configuration.verification_key_finalization_time_secs
}
EpochState::InProgress => Some(time_configuration.in_progress_time_secs),
EpochState::InProgress => time_configuration.in_progress_time_secs,
};
Epoch {
state,
epoch_id,
time_configuration,
finish_timestamp: duration.map(|d| current_timestamp.plus_seconds(d)),
finish_timestamp: current_timestamp.plus_seconds(duration),
}
}
pub fn final_timestamp_secs(&self) -> Option<u64> {
let mut finish = self.finish_timestamp?.seconds();
pub fn final_timestamp_secs(&self) -> u64 {
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() {
curr_epoch_state = state;
let adding = match curr_epoch_state {
EpochState::WaitingInitialisation => return None,
EpochState::PublicKeySubmission { .. } => {
time_configuration.public_key_submission_time_secs
}
@@ -155,13 +137,12 @@ impl Epoch {
};
finish += adding;
}
Some(finish)
finish
}
}
// currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints),
// the epoch can be in the following states (in order):
// 0. WaitingInitialisation -> the contract has been instantiated, but awaits for the admin to kick off the process (group members might still be getting added)
// 1. PublicKeySubmission -> potential dealers are submitting their BTE and ed25519 public keys to participate in dealing exchange
// 2. DealingExchange -> the actual (off-chain) dealing exchange is happening
// 3. ComplaintSubmission -> receivers submitting evidence of other dealers sending malformed data
@@ -175,7 +156,6 @@ impl Epoch {
#[cw_serde]
#[derive(Copy)]
pub enum EpochState {
WaitingInitialisation,
PublicKeySubmission { resharing: bool },
DealingExchange { resharing: bool },
VerificationKeySubmission { resharing: bool },
@@ -186,28 +166,25 @@ pub enum EpochState {
impl Default for EpochState {
fn default() -> Self {
Self::WaitingInitialisation
Self::PublicKeySubmission { resharing: false }
}
}
impl Display for EpochState {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EpochState::WaitingInitialisation => write!(f, "Waiting for initialisation"),
EpochState::PublicKeySubmission { resharing } => {
write!(f, "PublicKeySubmission (resharing: {resharing})")
}
EpochState::DealingExchange { resharing } => {
write!(f, "DealingExchange (resharing: {resharing})")
write!(f, "PublicKeySubmission with resharing {resharing}")
}
EpochState::DealingExchange { resharing } => write!(f, "DealingExchange {resharing}"),
EpochState::VerificationKeySubmission { resharing } => {
write!(f, "VerificationKeySubmission (resharing: {resharing})")
write!(f, "VerificationKeySubmission with resharing {resharing}")
}
EpochState::VerificationKeyValidation { resharing } => {
write!(f, "VerificationKeyValidation (resharing: {resharing})")
write!(f, "VerificationKeyValidation with resharing {resharing}")
}
EpochState::VerificationKeyFinalization { resharing } => {
write!(f, "VerificationKeyFinalization (resharing: {resharing})")
write!(f, "VerificationKeyFinalization with resharing {resharing}")
}
EpochState::InProgress => write!(f, "InProgress"),
}
@@ -215,13 +192,8 @@ impl Display for EpochState {
}
impl EpochState {
pub fn first() -> Self {
EpochState::PublicKeySubmission { resharing: false }
}
pub fn next(self) -> Option<Self> {
match self {
EpochState::WaitingInitialisation => None,
EpochState::PublicKeySubmission { resharing } => {
Some(EpochState::DealingExchange { resharing })
}
@@ -252,8 +224,4 @@ impl EpochState {
pub fn is_final(&self) -> bool {
*self == EpochState::InProgress
}
pub fn is_in_progress(&self) -> bool {
matches!(self, EpochState::InProgress)
}
}
@@ -20,13 +20,6 @@ pub struct ContractVKShare {
pub verified: bool,
}
#[cw_serde]
pub struct VkShareResponse {
pub owner: Addr,
pub epoch_id: EpochId,
pub share: Option<ContractVKShare>,
}
#[cw_serde]
pub struct PagedVKSharesResponse {
pub shares: Vec<ContractVKShare>,
@@ -43,10 +36,7 @@ pub fn to_cosmos_msg(
multisig_addr: String,
expiration_time: Timestamp,
) -> StdResult<CosmosMsg> {
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare {
owner: owner.to_string(),
resharing,
};
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare { owner, resharing };
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: coconut_dkg_addr,
msg: to_binary(&verify_vk_share_req)?,
@@ -67,14 +57,7 @@ pub fn to_cosmos_msg(
Ok(msg)
}
// DKG SAFETY:
// each legit verification proposal will only contain a single execute msg,
// if they have more than one, we can safely ignore it
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<String> {
if msgs.len() != 1 {
return None;
}
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<Addr> {
if let Some(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: _,
msg,
@@ -1,17 +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 schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};
use std::ops::{Deref, DerefMut};
use std::ops::Deref;
// some sane upper-bound size on byte sizes
// currently set to 128 bytes
pub const MAX_DISPLAY_SIZE: usize = 128;
// helps to transfer bytes between contract boundary to decrease amount of data sent accross
// after it's put to `Binary`
// TODO: if we are to use this for different types, it might make sense to introduce something like
// CommitmentTypeId field on the below for distinguishing different ones. it would somehow become part of the trait
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, JsonSchema)]
pub struct ContractSafeBytes(pub Vec<u8>);
@@ -23,24 +23,6 @@ impl Deref for ContractSafeBytes {
}
}
impl DerefMut for ContractSafeBytes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<Vec<u8>> for ContractSafeBytes {
fn from(value: Vec<u8>) -> Self {
ContractSafeBytes(value)
}
}
impl<'a> From<&'a [u8]> for ContractSafeBytes {
fn from(value: &'a [u8]) -> Self {
value.to_vec().into()
}
}
impl Display for ContractSafeBytes {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if !self.0.is_empty() {
@@ -2,7 +2,6 @@
name = "nym-ephemera-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -14,4 +13,4 @@ cw-utils = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
[features]
schema = []
schema = []
@@ -2,7 +2,6 @@
name = "nym-group-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -2,7 +2,6 @@
name = "nym-multisig-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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