Compare commits

..

1 Commits

Author SHA1 Message Date
Raphaël Walther 979b745e63 Switch to new arc runner Ubuntu 20.04 2024-09-06 12:33:14 +02:00
239 changed files with 2344 additions and 5644 deletions
+1 -31
View File
@@ -5,35 +5,5 @@ on:
jobs:
build:
runs-on: arc-ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Build Environment
run: sudo apt-get update && sudo apt-get install -y make dpkg-dev
- name: Build Debian Packages
working-directory: ppa/packages
run: make
- name: Find .deb files
working-directory: ppa/packages
run: |
echo "file1=$(ls nym-repo-setup*.deb)" >> $GITHUB_ENV
echo "file2=$(ls nym-vpn*.deb)" >> $GITHUB_ENV
- name: Upload nym-repo-setup
uses: actions/upload-artifact@v4
with:
name: ${{ env.file1 }}
path: ppa/packages/nym-repo-setup*.deb
retention-days: 10
- name: Upload nym-vpn
uses: actions/upload-artifact@v4
with:
name: ${{ env.file2 }}
path: ppa/packages/nym-vpn*.deb
retention-days: 10
run: "echo hello"
@@ -13,7 +13,6 @@ on:
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- '.github/workflows/ci-binary-config-checker.yml'
pull_request:
paths:
- 'clients/**'
@@ -23,7 +22,6 @@ on:
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- '.github/workflows/ci-binary-config-checker.yml'
env:
NETWORK: mainnet
@@ -33,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [arc-ubuntu-20.04]
platform: [custom-linux]
runs-on: ${{ matrix.platform }}
steps:
-1
View File
@@ -5,7 +5,6 @@ on:
paths:
- "ts-packages/**"
- "sdk/typescript/**"
- ".github/workflows/ci-build-ts.yml"
jobs:
build:
@@ -31,14 +31,13 @@ on:
- "service-providers/**"
- "tools/**"
- "nymvisor/**"
- ".github/workflows/ci-build-upload-binaries.yml"
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [ arc-ubuntu-20.04 ]
platform: [ ubuntu-20.04 ]
runs-on: ${{ matrix.platform }}
env:
@@ -63,6 +62,10 @@ jobs:
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
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
+34 -19
View File
@@ -1,6 +1,23 @@
name: ci-build
on:
push:
paths:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/lib/socks5-listener/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
pull_request:
paths:
- 'clients/**'
@@ -9,19 +26,15 @@ on:
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/**'
- 'sdk/lib/**'
- 'sdk/lib/socks5-listener/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- 'nym-network-monitor/**'
- 'nym-api/**'
- 'nym-node/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'tools/**'
- 'wasm/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci-build.yml'
workflow_dispatch:
jobs:
@@ -37,7 +50,7 @@ jobs:
- 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 protobuf-compiler
continue-on-error: true
if: contains(matrix.os, 'ubuntu')
if: matrix.os == 'arc-ubuntu-20.04'
- name: Check out repository code
uses: actions/checkout@v4
@@ -60,6 +73,8 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
# Enable wireguard by default on linux only
args: --workspace --features wireguard
# while disabled by default, this build ensures nothing is broken within
# `axum` feature
@@ -70,36 +85,36 @@ jobs:
args: --features axum
- name: Build all examples
if: contains(matrix.os, 'ubuntu')
if: matrix.os == 'arc-ubuntu-20.04'
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples
args: --workspace --examples --features wireguard
- name: Run all tests
if: contains(matrix.os, 'ubuntu')
if: matrix.os == 'arc-ubuntu-20.04'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
args: --workspace --features wireguard
- name: Run expensive tests
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && contains(matrix.os, 'ubuntu')
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'arc-ubuntu-20.04'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace -- --ignored
args: --workspace --features wireguard -- --ignored
- name: Annotate with clippy checks
if: contains(matrix.os, 'ubuntu')
if: matrix.os == 'arc-ubuntu-20.04'
uses: actions-rs/clippy-check@v1
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
args: --workspace --features wireguard
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --features axum -- -D warnings
args: --workspace --all-targets --features wireguard,axum -- -D warnings
+1 -5
View File
@@ -2,14 +2,10 @@ name: ci-cargo-deny
on:
workflow_dispatch:
pull_request:
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci-cargo-deny.yml'
jobs:
cargo-deny:
runs-on: arc-ubuntu-22.04-dind
runs-on: ubuntu-22.04
strategy:
matrix:
checks:
+1 -2
View File
@@ -6,12 +6,11 @@ on:
paths:
- 'contracts/**'
- 'common/**'
- '.github/workflows/ci-contracts-schema.yml'
jobs:
check-schema:
name: Generate and check schema
runs-on: arc-ubuntu-20.04
runs-on: custom-linux
env:
CARGO_TERM_COLOR: always
steps:
@@ -6,7 +6,6 @@ on:
paths:
- 'common/**'
- 'contracts/**'
- '.github/workflows/ci-contracts-upload-binaries.yml'
env:
NETWORK: mainnet
@@ -16,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: arc-ubuntu-20.04
platform: [ ubuntu-20.04 ]
runs-on: ${{ matrix.platform }}
env:
+1 -2
View File
@@ -9,11 +9,10 @@ on:
paths:
- 'contracts/**'
- 'common/**'
- '.github/workflows/ci-contracts.yml'
jobs:
matrix_prep:
runs-on: arc-ubuntu-20.04
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
-1
View File
@@ -6,7 +6,6 @@ on:
branches-ignore: master
paths:
- 'documentation/docs/**'
- '.github/workflows/ci-docs.yml'
jobs:
build:
-1
View File
@@ -10,7 +10,6 @@ on:
- "nym-wallet/src/**"
- "nym-wallet/package.json"
- "explorer/**"
- ".github/workflows/ci-lint-typescript.yml"
jobs:
build:
@@ -5,7 +5,6 @@ on:
push:
paths:
- 'explorer/**'
- '.github/workflows/ci-nym-network-explorer.yml'
defaults:
run:
+7 -2
View File
@@ -1,17 +1,22 @@
name: ci-nym-wallet-rust
on:
push:
paths:
- 'nym-wallet/**'
- 'common/**'
- 'contracts/vesting/**'
- 'nym-api/nym-api-requests/**'
pull_request:
paths:
- 'nym-wallet/**'
- 'common/**'
- 'contracts/vesting/**'
- 'nym-api/nym-api-requests/**'
- '.github/workflows/ci-nym-wallet-rust.yml'
jobs:
build:
runs-on: arc-ubuntu-20.04
runs-on: [ self-hosted, custom-linux ]
env:
CARGO_TERM_COLOR: always
steps:
@@ -4,7 +4,6 @@ on:
pull_request:
paths:
- 'nym-wallet/**'
- '.github/workflows/ci-nym-wallet-storybook.yml'
jobs:
build:
@@ -5,7 +5,6 @@ on:
paths:
- "sdk/typescript/**"
- "wasm/**"
- '.github/workflows/ci-sdk-docs-typescript.yml'
jobs:
build:
+1 -2
View File
@@ -6,11 +6,10 @@ on:
- 'wasm/**'
- 'clients/client-core/**'
- 'common/**'
- '.github/workflows/ci-sdk-wasm.yml'
jobs:
wasm:
runs-on: arc-ubuntu-20.04
runs-on: [custom-linux]
env:
CARGO_TERM_COLOR: always
steps:
@@ -51,6 +51,10 @@ jobs:
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
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -1,4 +1,4 @@
name: publish-nym-wallet-win11
name: publish-nym-wallet-win10
on:
workflow_dispatch:
release:
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-windows-11]
platform: [windows10]
runs-on: ${{ matrix.platform }}
outputs:
@@ -62,9 +62,6 @@ jobs:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install Yarn
run: npm install -g yarn
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
-54
View File
@@ -4,60 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.10-caramello] (2024-09-10)
- Backport 4844 and 4845 ([#4857])
- Bugfix/client registration vol2 ([#4856])
- Remove wireguard feature flag and pass runtime enabled flag ([#4839])
- Eliminate cancel unsafe sig awaiting ([#4834])
- added explicit updateable admin to the mixnet contract ([#4822])
- using legacy signing payload in CLI and verifying both variants in contract ([#4821])
- adding ecash contract address ([#4819])
- Check profit margin of node before defaulting to hardcoded value ([#4802])
- Sync last_seen_bandwidth immediately ([#4774])
- Feature/additional ecash nym cli utils ([#4773])
- Better storage error logging ([#4772])
- bugfix: make sure DKG parses data out of events if logs are empty ([#4764])
- Fix clippy on rustc beta toolchain ([#4746])
- Fix clippy for beta toolchain ([#4742])
- Disable testnet-manager on non-unix ([#4741])
- Don't set NYM_VPN_API to default ([#4740])
- Update publish-nym-binaries.yml ([#4739])
- Update ci-build-upload-binaries.yml ([#4738])
- Add NYM_VPN_API to network config ([#4736])
- Re-export RecipientFormattingError in nym sdk ([#4735])
- Persist wireguard peers ([#4732])
- Fix tokio error in 1.39 ([#4730])
- Feature/vesting purge plus ranged cost params ([#4716])
- Fix (some) feature unification build failures ([#4681])
- Feature Compact Ecash : The One PR ([#4623])
[#4857]: https://github.com/nymtech/nym/pull/4857
[#4856]: https://github.com/nymtech/nym/pull/4856
[#4839]: https://github.com/nymtech/nym/pull/4839
[#4834]: https://github.com/nymtech/nym/pull/4834
[#4822]: https://github.com/nymtech/nym/pull/4822
[#4821]: https://github.com/nymtech/nym/pull/4821
[#4819]: https://github.com/nymtech/nym/pull/4819
[#4802]: https://github.com/nymtech/nym/pull/4802
[#4774]: https://github.com/nymtech/nym/pull/4774
[#4773]: https://github.com/nymtech/nym/pull/4773
[#4772]: https://github.com/nymtech/nym/pull/4772
[#4764]: https://github.com/nymtech/nym/pull/4764
[#4746]: https://github.com/nymtech/nym/pull/4746
[#4742]: https://github.com/nymtech/nym/pull/4742
[#4741]: https://github.com/nymtech/nym/pull/4741
[#4740]: https://github.com/nymtech/nym/pull/4740
[#4739]: https://github.com/nymtech/nym/pull/4739
[#4738]: https://github.com/nymtech/nym/pull/4738
[#4736]: https://github.com/nymtech/nym/pull/4736
[#4735]: https://github.com/nymtech/nym/pull/4735
[#4732]: https://github.com/nymtech/nym/pull/4732
[#4730]: https://github.com/nymtech/nym/pull/4730
[#4716]: https://github.com/nymtech/nym/pull/4716
[#4681]: https://github.com/nymtech/nym/pull/4681
[#4623]: https://github.com/nymtech/nym/pull/4623
## [2024.9-topdeck] (2024-07-26)
- chore: fix 1.80 lint issues ([#4731])
Generated
+354 -595
View File
File diff suppressed because it is too large Load Diff
+22 -23
View File
@@ -45,7 +45,6 @@ members = [
"common/credentials",
"common/credential-utils",
"common/credentials-interface",
"common/credential-verification",
"common/crypto",
"common/dkg",
"common/ecash-double-spending",
@@ -117,8 +116,6 @@ members = [
"nym-validator-rewarder",
"tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query",
@@ -129,21 +126,21 @@ members = [
"wasm/mix-fetch",
"wasm/node-tester",
"wasm/zknym-lib",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
]
default-members = [
"clients/native",
"clients/socks5",
"explorer-api",
"gateway",
"service-providers/network-requester",
"mixnode",
"nym-api",
"nym-node",
"nym-validator-rewarder",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
"nym-node",
]
exclude = [
@@ -169,9 +166,9 @@ readme = "README.md"
addr = "0.15.6"
aes = "0.8.1"
aes-gcm = "0.10.1"
anyhow = "1.0.87"
anyhow = "1.0.71"
argon2 = "0.5.0"
async-trait = "0.1.82"
async-trait = "0.1.81"
axum = "0.7.5"
axum-extra = "0.9.3"
base64 = "0.22.1"
@@ -194,7 +191,7 @@ chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.31"
cipher = "0.4.3"
clap = "4.5.17"
clap = "4.5.16"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.0"
@@ -203,7 +200,7 @@ console = "0.15.8"
console-subscriber = "0.1.1"
console_error_panic_hook = "0.1"
const-str = "0.5.6"
const_format = "0.2.33"
const_format = "0.2.32"
criterion = "0.4"
csv = "1.3.0"
ctr = "0.9.1"
@@ -212,7 +209,7 @@ curve25519-dalek = "4.1"
dashmap = "5.5.3"
defguard_wireguard_rs = "0.4.2"
digest = "0.10.7"
dirs = "5.0"
dirs = "4.0"
doc-comment = "0.3"
dotenvy = "0.15.6"
ecdsa = "0.16"
@@ -255,7 +252,7 @@ okapi = "0.7.0"
once_cell = "1.7.2"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.3"
parking_lot = "0.12.1"
pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.0"
@@ -263,7 +260,9 @@ pretty_env_logger = "0.4.0"
publicsuffix = "2.2.3"
quote = "1"
rand = "0.8.5"
rand-07 = "0.7.3"
rand_chacha = "0.3"
rand_chacha_02 = "0.2"
rand_core = "0.6.3"
rand_distr = "0.4"
rand_pcg = "0.3.1"
@@ -275,12 +274,12 @@ rocket = "0.5.0"
rocket_cors = "0.6.0"
rocket_okapi = "0.8.0"
safer-ffi = "0.1.12"
schemars = "0.8.21"
schemars = "0.8.1"
semver = "1.0.23"
serde = "1.0.210"
serde = "1.0.209"
serde_bytes = "0.11.15"
serde_derive = "1.0"
serde_json = "1.0.128"
serde_json = "1.0.127"
serde_repr = "0.1"
serde_with = "3.9.0"
serde_yaml = "0.9.25"
@@ -288,20 +287,20 @@ sha2 = "0.10.8"
si-scale = "0.2.3"
sphinx-packet = "0.1.1"
sqlx = "0.6.3"
strum = "0.26"
strum = "0.25"
subtle-encoding = "0.5"
syn = "1"
sysinfo = "0.30.12"
tap = "1.0.1"
tar = "0.4.41"
tar = "0.4.40"
tempfile = "3.5.0"
thiserror = "1.0.63"
time = "0.3.30"
tokio = "1.39"
tokio-stream = "0.1.16"
tokio-stream = "0.1.15"
tokio-test = "0.4.4"
tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.12"
tokio-util = "0.7.11"
toml = "0.8.14"
tower = "0.4.13"
tower-http = "0.5.2"
@@ -319,7 +318,7 @@ utoipauto = "0.1"
uuid = "*"
vergen = { version = "=8.3.1", default-features = false }
walkdir = "2"
wasm-bindgen-test = "0.3.43"
wasm-bindgen-test = "0.3.36"
x25519-dalek = "2.0.0"
zeroize = "1.6.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.40"
version = "1.1.39"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_coin_index_signatures::{
import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCoinIndexSignaturesArgs,
) -> Result<(), ClientError> {
import_coin_index_signatures::<CliNativeClient, _>(args).await?;
println!("successfully imported coin index signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{
import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportExpirationDateSignaturesArgs,
) -> Result<(), ClientError> {
import_expiration_date_signatures::<CliNativeClient, _>(args).await?;
println!("successfully imported expiration date signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_master_verification_key::{
import_master_verification_key, CommonClientImportMasterVerificationKeyArgs,
};
pub(crate) async fn execute(
args: CommonClientImportMasterVerificationKeyArgs,
) -> Result<(), ClientError> {
import_master_verification_key::<CliNativeClient, _>(args).await?;
println!("successfully imported master verification key!");
Ok(())
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs;
use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs;
use std::error::Error;
pub(crate) mod import_coin_index_signatures;
pub(crate) mod import_credential;
pub(crate) mod import_expiration_date_signatures;
pub(crate) mod import_master_verification_key;
pub(crate) mod show_ticketbooks;
#[derive(Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Ecash {
#[clap(subcommand)]
pub command: EcashCommands,
}
impl Ecash {
pub async fn execute(self) -> Result<(), Box<dyn Error + Send + Sync>> {
match self.command {
EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?,
EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?,
EcashCommands::ImportCoinIndexSignatures(args) => {
import_coin_index_signatures::execute(args).await?
}
EcashCommands::ImportExpirationDateSignatures(args) => {
import_expiration_date_signatures::execute(args).await?
}
EcashCommands::ImportMasterVerificationKey(args) => {
import_master_verification_key::execute(args).await?
}
}
Ok(())
}
}
#[derive(Subcommand)]
pub enum EcashCommands {
/// Display information associated with the imported ticketbooks,
ShowTicketBooks(show_ticketbooks::Args),
/// Import a pre-generated ticketbook
ImportTicketBook(CommonClientImportTicketBookArgs),
/// Import coin index signatures needed for ticketbooks
ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs),
/// Import expiration date signatures needed for ticketbooks
ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs),
/// Import master verification key needed for ticketbooks
ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs),
}
@@ -4,10 +4,10 @@
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportTicketBookArgs,
import_credential, CommonClientImportCredentialArgs,
};
pub(crate) async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), ClientError> {
pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await?;
println!("successfully imported credential!");
Ok(())
+10 -5
View File
@@ -6,13 +6,13 @@ use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::client::config::{BaseClientConfig, Config};
use crate::commands::ecash::Ecash;
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_config::OptionalSet;
@@ -22,10 +22,11 @@ use std::sync::OnceLock;
mod add_gateway;
pub(crate) mod build_info;
pub(crate) mod ecash;
pub(crate) mod import_credential;
pub(crate) mod init;
mod list_gateways;
pub(crate) mod run;
mod show_ticketbooks;
mod switch_gateway;
pub(crate) struct CliNativeClient;
@@ -72,8 +73,8 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Ecash-related functionalities
Ecash(Ecash),
/// Import a pre-generated credential
ImportCredential(CommonClientImportCredentialArgs),
/// List all registered with gateways
ListGateways(list_gateways::Args),
@@ -84,6 +85,9 @@ pub(crate) enum Commands {
/// Change the currently active gateway. Note that you must have already registered with the new gateway!
SwitchGateway(switch_gateway::Args),
/// Display information associated with the imported ticketbooks,
ShowTicketbooks(show_ticketbooks::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -112,10 +116,11 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::Ecash(ecash) => ecash.execute().await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.40"
version = "1.1.39"
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"
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_coin_index_signatures::{
import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCoinIndexSignaturesArgs,
) -> Result<(), Socks5ClientError> {
import_coin_index_signatures::<CliSocks5Client, _>(args).await?;
println!("successfully imported coin index signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{
import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportExpirationDateSignaturesArgs,
) -> Result<(), Socks5ClientError> {
import_expiration_date_signatures::<CliSocks5Client, _>(args).await?;
println!("successfully imported expiration date signatures!");
Ok(())
}
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_master_verification_key::{
import_master_verification_key, CommonClientImportMasterVerificationKeyArgs,
};
pub(crate) async fn execute(
args: CommonClientImportMasterVerificationKeyArgs,
) -> Result<(), Socks5ClientError> {
import_master_verification_key::<CliSocks5Client, _>(args).await?;
println!("successfully imported master verification key!");
Ok(())
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs;
use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs;
use std::error::Error;
pub(crate) mod import_coin_index_signatures;
pub(crate) mod import_credential;
pub(crate) mod import_expiration_date_signatures;
pub(crate) mod import_master_verification_key;
pub(crate) mod show_ticketbooks;
#[derive(Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Ecash {
#[clap(subcommand)]
pub command: EcashCommands,
}
impl Ecash {
pub async fn execute(self) -> Result<(), Box<dyn Error + Send + Sync>> {
match self.command {
EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?,
EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?,
EcashCommands::ImportCoinIndexSignatures(args) => {
import_coin_index_signatures::execute(args).await?
}
EcashCommands::ImportExpirationDateSignatures(args) => {
import_expiration_date_signatures::execute(args).await?
}
EcashCommands::ImportMasterVerificationKey(args) => {
import_master_verification_key::execute(args).await?
}
}
Ok(())
}
}
#[derive(Subcommand)]
pub enum EcashCommands {
/// Display information associated with the imported ticketbooks,
ShowTicketBooks(show_ticketbooks::Args),
/// Import a pre-generated ticketbook
ImportTicketBook(CommonClientImportTicketBookArgs),
/// Import coin index signatures needed for ticketbooks
ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs),
/// Import expiration date signatures needed for ticketbooks
ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs),
/// Import master verification key needed for ticketbooks
ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs),
}
@@ -4,10 +4,12 @@
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportTicketBookArgs,
import_credential, CommonClientImportCredentialArgs,
};
pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), Socks5ClientError> {
pub(crate) async fn execute(
args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await?;
println!("successfully imported credential!");
Ok(())
+10 -5
View File
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::ecash::Ecash;
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
@@ -14,6 +13,7 @@ use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
@@ -26,10 +26,11 @@ use std::sync::OnceLock;
mod add_gateway;
pub(crate) mod build_info;
pub mod ecash;
mod import_credential;
pub mod init;
mod list_gateways;
pub(crate) mod run;
mod show_ticketbooks;
mod switch_gateway;
pub(crate) struct CliSocks5Client;
@@ -76,8 +77,8 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Ecash-related functionalities
Ecash(Ecash),
/// Import a pre-generated credential
ImportCredential(CommonClientImportCredentialArgs),
/// List all registered with gateways
ListGateways(list_gateways::Args),
@@ -88,6 +89,9 @@ pub(crate) enum Commands {
/// Change the currently active gateway. Note that you must have already registered with the new gateway!
SwitchGateway(switch_gateway::Args),
/// Display information associated with the imported ticketbooks,
ShowTicketbooks(show_ticketbooks::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -119,10 +123,11 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::Ecash(ecash) => ecash.execute().await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
@@ -9,7 +9,7 @@ use nym_client_core::cli_helpers::client_show_ticketbooks::{
};
#[derive(clap::Args)]
pub struct Args {
pub(crate) struct Args {
#[command(flatten)]
common_args: CommonShowTicketbooksArgs,
@@ -23,7 +23,7 @@ impl AsRef<CommonShowTicketbooksArgs> for Args {
}
}
pub async fn execute(args: Args) -> Result<(), Socks5ClientError> {
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let output = args.output;
let res = show_ticketbooks::<CliSocks5Client, _>(args).await?;
@@ -2,9 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError;
use crate::utils::{
get_aggregate_verification_key, get_coin_index_signatures, get_expiration_date_signatures,
};
use crate::utils::{get_coin_index_signatures, get_expiration_date_signatures};
use log::info;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::IssuanceTicketBook;
@@ -57,7 +55,7 @@ where
))
}
pub async fn query_and_persist_required_global_data<S>(
pub async fn query_and_persist_required_global_signatures<S>(
storage: &S,
epoch_id: EpochId,
expiration_date: Date,
@@ -67,10 +65,6 @@ where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
{
log::info!("Getting master verification key");
// this will also persist the key in the storage if was not there already
get_aggregate_verification_key(storage, epoch_id, apis.clone()).await?;
log::info!("Getting expiration date signatures");
// this will also persist the signatures in the storage if they were not there already
get_expiration_date_signatures(storage, epoch_id, expiration_date, apis.clone()).await?;
+2 -4
View File
@@ -16,7 +16,7 @@ use nym_credential_storage::models::RetrievedTicketbook;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
use nym_credentials_interface::{
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, NymPayInfo, VerificationKeyAuth,
};
use nym_ecash_time::Date;
use nym_validator_client::nym_api::EpochId;
@@ -165,9 +165,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
.get_coin_index_signatures(epoch_id, &mut api_clients)
.await?;
let pay_info = retrieved_ticketbook
.ticketbook
.generate_pay_info(provider_pk);
let pay_info = NymPayInfo::generate(provider_pk);
let spend_request = retrieved_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
+6 -26
View File
@@ -4,10 +4,6 @@
use crate::error::BandwidthControllerError;
use log::warn;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials_interface::{
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
};
@@ -98,18 +94,13 @@ where
.await?
.key;
let full = EpochVerificationKey {
epoch_id,
key: master_vk,
};
// store the retrieved key
storage
.insert_master_verification_key(&full)
.insert_master_verification_key(epoch_id, &master_vk)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(full.key)
Ok(master_vk)
}
pub(crate) async fn get_coin_index_signatures<St>(
@@ -141,18 +132,13 @@ where
.await?
.signatures;
let aggregated = AggregatedCoinIndicesSignatures {
epoch_id,
signatures: index_sigs,
};
// store the retrieved key
storage
.insert_coin_index_signatures(&aggregated)
.insert_coin_index_signatures(epoch_id, &index_sigs)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(aggregated.signatures)
Ok(index_sigs)
}
pub(crate) async fn get_expiration_date_signatures<St>(
@@ -185,17 +171,11 @@ where
.await?
.signatures;
let aggregated = AggregatedExpirationDateSignatures {
epoch_id,
expiration_date,
signatures: expiration_sigs,
};
// store the retrieved key
storage
.insert_expiration_date_signatures(&aggregated)
.insert_expiration_date_signatures(epoch_id, expiration_date, &expiration_sigs)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(aggregated.signatures)
Ok(expiration_sigs)
}
+2 -2
View File
@@ -71,7 +71,7 @@ features = ["tokio"]
###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.16"
version = "0.1.11"
features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
@@ -102,7 +102,7 @@ workspace = true
features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.3.0"
version = "0.2.4"
features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
@@ -1,68 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("sig_data").required(true)),
))
]
pub struct CommonClientImportCoinIndexSignaturesArgs {
/// Id of client that is going to import the signatures
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the signatures.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[cfg_attr(feature = "cli", clap(long, group = "sig_data"))]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_coin_index_signatures<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportCoinIndexSignaturesArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.signatures_path.unwrap())?
}
};
nym_id::import_coin_index_signatures(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -11,14 +11,9 @@ fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("cred_data").required(true)),
group(clap::ArgGroup::new("type").required(true)),
))
]
#[cfg_attr(feature = "cli", clap(group(clap::ArgGroup::new("cred_data").required(true))))]
#[derive(Debug, Clone)]
pub struct CommonClientImportTicketBookArgs {
pub struct CommonClientImportCredentialArgs {
/// Id of client that is going to import the credential
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
@@ -31,15 +26,6 @@ pub struct CommonClientImportTicketBookArgs {
#[cfg_attr(feature = "cli", clap(long, group = "cred_data"))]
pub(crate) credential_path: Option<PathBuf>,
/// Specifies whether we're attempting to import a standalone ticketbook (i.e. serialised `IssuedTicketBook`)
#[cfg_attr(feature = "cli", clap(long, group = "type"))]
pub(crate) standalone: bool,
/// Specifies whether we're attempting to import full ticketboot
/// (i.e. one that **might** contain required global signatures; that is serialised `ImportableTicketBook`)
#[cfg_attr(feature = "cli", clap(long, group = "type"))]
pub(crate) full: bool,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
@@ -47,7 +33,7 @@ pub struct CommonClientImportTicketBookArgs {
pub async fn import_credential<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportTicketBookArgs>,
A: Into<CommonClientImportCredentialArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
@@ -68,19 +54,6 @@ where
}
};
if common_args.standalone {
nym_id::import_standalone_ticketbook(
credentials_store,
raw_credential,
common_args.version,
)
.await?;
} else {
// sanity check; clap should have ensured it
assert!(common_args.full);
nym_id::import_full_ticketbook(credentials_store, raw_credential, common_args.version)
.await?;
}
nym_id::import_credential(credentials_store, raw_credential, common_args.version).await?;
Ok(())
}
@@ -1,68 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("sig_data").required(true)),
))
]
pub struct CommonClientImportExpirationDateSignaturesArgs {
/// Id of client that is going to import the signatures
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the signatures.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[cfg_attr(feature = "cli", clap(long, group = "sig_data"))]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_expiration_date_signatures<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportExpirationDateSignaturesArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.signatures_path.unwrap())?
}
};
nym_id::import_expiration_date_signatures(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -1,68 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("key_data_group").required(true)),
))
]
pub struct CommonClientImportMasterVerificationKeyArgs {
/// Id of client that is going to import the key
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the key.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded key data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "key_data_group", value_parser = parse_encoded_key_data))]
pub(crate) key_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary key data
#[cfg_attr(feature = "cli", clap(long, group = "key_data_group"))]
pub(crate) key_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_master_verification_key<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportMasterVerificationKeyArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.key_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.key_path.unwrap())?
}
};
nym_id::import_master_verification_key(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -2,10 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client_add_gateway;
pub mod client_import_coin_index_signatures;
pub mod client_import_credential;
pub mod client_import_expiration_date_signatures;
pub mod client_import_master_verification_key;
pub mod client_init;
pub mod client_list_gateways;
pub mod client_run;
+1 -1
View File
@@ -43,7 +43,7 @@ workspace = true
features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.16"
version = "0.1.11"
features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
@@ -417,8 +417,6 @@ impl<C, St> GatewayClient<C, St> {
self.local_identity.as_ref(),
self.gateway_identity,
self.cfg.bandwidth.require_tickets,
#[cfg(not(target_arch = "wasm32"))]
self.task_client.clone(),
)
.await
.map_err(GatewayClientError::RegistrationFailure),
@@ -42,10 +42,6 @@ pub trait MixnetQueryClient {
// state/sys-params-related
async fn admin(&self) -> Result<cw_controllers::AdminResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::Admin {}).await
}
async fn get_mixnet_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {})
.await
@@ -584,7 +580,6 @@ mod tests {
msg: MixnetQueryMsg,
) -> u32 {
match msg {
MixnetQueryMsg::Admin {} => client.admin().ignore(),
MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after } => client
.get_all_family_members_paged(start_after, limit)
.ignore(),
@@ -31,15 +31,6 @@ pub trait MixnetSigningClient {
// state/sys-params-related
async fn update_admin(
&self,
admin: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UpdateAdmin { admin }, vec![])
.await
}
async fn update_rewarding_validator_address(
&self,
address: AccountId,
@@ -769,7 +760,6 @@ mod tests {
msg: MixnetExecuteMsg,
) {
match msg {
MixnetExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(),
MixnetExecuteMsg::AssignNodeLayer { mix_id, layer } => {
client.assign_node_layer(mix_id, layer, None).ignore()
}
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use base64::Engine;
use cosmrs::abci::TxMsgData;
@@ -11,6 +10,7 @@ use log::error;
use prost::bytes::Bytes;
use tendermint_rpc::endpoint::broadcast;
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
pub use cosmrs::abci::MsgResponse;
pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
@@ -21,8 +21,7 @@ pub struct Log {
/// Searches in logs for the first event of the given event type and in that event
/// for the first attribute with the given attribute key.
#[deprecated]
pub fn find_attribute_in_logs<'a>(
pub fn find_attribute<'a>(
logs: &'a [Log],
event_type: &str,
attribute_key: &str,
@@ -36,7 +35,6 @@ pub fn find_attribute_in_logs<'a>(
}
/// Search for the proposal id in the given log. It'll be in the LAST wasm event, with attribute key "proposal_id"
#[deprecated]
pub fn find_proposal_id(logs: &[Log]) -> Result<u64, NyxdError> {
let maybe_attributes = logs
.iter()
@@ -1,24 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::logs::Log;
use crate::nyxd::TxResponse;
use cosmrs::tendermint::abci;
pub use abci::Event;
// Searches in events for an event of the given event type which contains an
// attribute for with the given key.
pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str) -> Option<String> {
find_event_attribute(&tx.tx_result.events, event_type, attribute_key)
}
pub fn find_event_attribute(
events: &[Event],
event_type: &str,
attribute_key: &str,
) -> Option<String> {
let event = events.iter().find(|e| e.kind == event_type)?;
let event = tx.tx_result.events.iter().find(|e| e.kind == event_type)?;
let attribute = event.attributes.iter().find(|&attr| {
if let Ok(key_str) = attr.key_str() {
key_str == attribute_key
@@ -28,23 +16,3 @@ pub fn find_event_attribute(
})?;
Some(attribute.value_str().ok().map(|str| str.to_string())).flatten()
}
pub fn find_attribute_value_in_logs_or_events(
logs: &[Log],
events: &[Event],
event_type: &str,
attribute_key: &str,
) -> Option<String> {
// if logs are empty, i.e. we're using post 0.50 code, parse the events instead
if !logs.is_empty() {
#[allow(deprecated)]
return crate::nyxd::cosmwasm_client::logs::find_attribute_in_logs(
logs,
event_type,
attribute_key,
)
.map(|attr| attr.value.clone());
}
find_event_attribute(events, event_type, attribute_key)
}
+2 -4
View File
@@ -10,7 +10,6 @@ anyhow = { workspace = true }
base64 = { workspace = true }
bip39 = { workspace = true }
bs58 = { workspace = true }
colored = { workspace = true }
comfy-table = { workspace = true }
cfg-if = { workspace = true }
clap = { workspace = true, features = ["derive"] }
@@ -26,10 +25,9 @@ rand = { workspace = true, features = ["std"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
tempfile = { workspace = true }
time = { workspace = true, features = ["parsing", "formatting"] }
tokio = { workspace = true, features = ["sync"] }
toml = { workspace = true }
tokio = { workspace = true, features = ["sync"]}
toml = "0.5.6"
url = { workspace = true }
tap = { workspace = true }
zeroize = { workspace = true }
@@ -0,0 +1,64 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
@@ -0,0 +1,56 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_utils::utils;
use nym_credentials_interface::TicketType;
use nym_crypto::asymmetric::identity;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
/// Specify which type of ticketbook should be issued
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
let Ok(private_id_key) = loaded.try_get_private_id_key() else {
bail!("the loaded config does not have a public id key information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let persistent_storage = initialise_persistent_storage(credentials_store).await;
let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?;
utils::issue_credential(
&client,
&persistent_storage,
&private_id_key.to_bytes(),
args.ticketbook_type,
)
.await?;
Ok(())
}
@@ -3,10 +3,6 @@
use clap::{Args, Subcommand};
pub mod generate_ticket;
pub mod import_coin_index_signatures;
pub mod import_expiration_date_signatures;
pub mod import_master_verification_key;
pub mod import_ticket_book;
pub mod issue_ticket_book;
pub mod recover_ticket_book;
@@ -23,8 +19,4 @@ pub enum EcashCommands {
IssueTicketBook(issue_ticket_book::Args),
RecoverTicketBook(recover_ticket_book::Args),
ImportTicketBook(import_ticket_book::Args),
GenerateTicket(generate_ticket::Args),
ImportCoinIndexSignatures(import_coin_index_signatures::Args),
ImportExpirationDateSignatures(import_expiration_date_signatures::Args),
ImportMasterVerificationKey(import_master_verification_key::Args),
}
@@ -1,178 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::{anyhow, bail};
use clap::Parser;
use colored::Colorize;
use comfy_table::Table;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
/// Specify the index of the ticket to retrieve from the ticketbook.
/// By default, the current unspent value is used.
#[clap(long, group = "output")]
pub(crate) ticket_index: Option<u64>,
/// Specify whether we should display payments for ALL available tickets
#[clap(long, group = "output")]
pub(crate) full: bool,
/// Base58-encoded identity of the provider (must be 32 bytes long)
#[clap(long)]
pub(crate) provider: String,
/// Config file of the client that is supposed to use the credential.
#[clap(long, group = "source")]
pub(crate) client_config: Option<PathBuf>,
/// Path to the dedicated credential storage database
#[clap(long, group = "source")]
pub(crate) credential_storage: Option<PathBuf>,
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let credentials_store = if let Some(explicit) = args.credential_storage {
explicit
} else {
// SAFETY: at least one of them MUST HAVE been specified
let cfg = args.client_config.unwrap();
let loaded = CommonConfigsWrapper::try_load(cfg)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
credentials_store
};
let decoded_provider = bs58::decode(&args.provider).into_vec()?;
if decoded_provider.len() != 32 {
bail!("the provided provider information is malformed")
}
let provider_arr: [u8; 32] = decoded_provider.try_into().unwrap();
let persistent_storage = initialise_persistent_storage(&credentials_store).await;
let Some(mut next_ticketbook) = persistent_storage
.get_next_unspent_usable_ticketbook(0)
.await?
else {
bail!(
"there are no valid ticketbooks in the storage at {}",
credentials_store.display()
)
};
let epoch_id = next_ticketbook.ticketbook.epoch_id();
let expiration_date = next_ticketbook.ticketbook.expiration_date();
let verification_key = persistent_storage
.get_master_verification_key(epoch_id)
.await?
.ok_or_else(|| {
anyhow!("ticketbook got incorrectly imported - the master verification key is missing")
})?;
let expiration_signatures = persistent_storage
.get_expiration_date_signatures(expiration_date)
.await?
.ok_or_else(|| {
anyhow!(
"ticketbook got incorrectly imported - the expiration date signatures are missing"
)
})?;
let coin_indices_signatures = persistent_storage
.get_coin_index_signatures(epoch_id)
.await?
.ok_or_else(|| {
anyhow!("ticketbook got incorrectly imported - the coin index signatures are missing")
})?;
let ticketbook_data = next_ticketbook.ticketbook.pack();
let next_ticket = args
.ticket_index
.unwrap_or(next_ticketbook.ticketbook.spent_tickets());
let pay_info = next_ticketbook.ticketbook.generate_pay_info(provider_arr);
println!("{}", "TICKETBOOK DATA:".bold());
println!("{}", bs58::encode(&ticketbook_data.data).into_string());
println!();
// display it only for a single ticket
if !args.full {
println!("attempting to generate payment for ticket {next_ticket}...");
println!();
next_ticketbook.ticketbook.update_spent_tickets(next_ticket);
let req = next_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
pay_info.into(),
&coin_indices_signatures,
&expiration_signatures,
1,
)?;
let payment = req.payment;
println!("{}", format!("PAYMENT FOR TICKET {next_ticket}: ").bold());
println!("{}", bs58::encode(&payment.to_bytes()).into_string());
return Ok(());
}
println!(
"generating payment information for {} tickets. this might take a while!...",
next_ticketbook.ticketbook.params_total_tickets()
);
// otherwise generate all the payments
let last_spent = next_ticketbook.ticketbook.spent_tickets();
let mut table = Table::new();
table.set_header(vec!["index", "binary data", "spend status"]);
for i in 0..next_ticketbook.ticketbook.params_total_tickets() {
let status = if i < last_spent {
"SPENT".red()
} else {
"NOT SPENT".green()
};
next_ticketbook.ticketbook.update_spent_tickets(i);
let req = next_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
pay_info.into(),
&coin_indices_signatures,
&expiration_signatures,
1,
)?;
let payment = req.payment;
let payment_bytes = payment.to_bytes();
let len = payment_bytes.len();
let display_size = 100;
let remaining = len - display_size;
table.add_row(vec![
i.to_string(),
format!(
"{}{remaining}bytes remaining",
bs58::encode(&payment_bytes[..display_size]).into_string()
),
status.to_string(),
]);
}
println!("{}", "AVAILABLE TICKETS".bold());
println!("{table}");
Ok(())
}
@@ -1,76 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential::import_expiration_date_signatures;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("sig_data").required(true)),
)]
pub struct Args {
/// Config file of the client that is supposed to use the signatures.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data)]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[clap(long, group = "sig_data")]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
impl Args {
fn signatures_data(self) -> anyhow::Result<Vec<u8>> {
let data = match self.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(self.signatures_path.unwrap())?
}
};
Ok(data)
}
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(&args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let version = args.version;
let raw_signatures = args.signatures_data()?;
import_expiration_date_signatures(credentials_store, raw_signatures, version).await?;
Ok(())
}
@@ -1,76 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential::import_expiration_date_signatures;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("sig_data").required(true)),
)]
pub struct Args {
/// Config file of the client that is supposed to use the signatures.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data)]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[clap(long, group = "sig_data")]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
impl Args {
fn signatures_data(self) -> anyhow::Result<Vec<u8>> {
let data = match self.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(self.signatures_path.unwrap())?
}
};
Ok(data)
}
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(&args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let version = args.version;
let raw_signatures = args.signatures_data()?;
import_expiration_date_signatures(credentials_store, raw_signatures, version).await?;
Ok(())
}
@@ -1,76 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential::import_master_verification_key;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("key_data_group").required(true)),
)]
pub struct Args {
/// Config file of the client that is supposed to use the key.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded key data (as base58)
#[clap(long, group = "key_data_group", value_parser = parse_encoded_key_data)]
pub(crate) key_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary key data
#[clap(long, group = "key_data_group")]
pub(crate) key_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
impl Args {
fn key_data(self) -> anyhow::Result<Vec<u8>> {
let data = match self.key_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(self.key_path.unwrap())?
}
};
Ok(data)
}
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(&args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let version = args.version;
let raw_key = args.key_data()?;
import_master_verification_key(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -1,103 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential::import_full_ticketbook;
use nym_id::import_standalone_ticketbook;
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct CredentialDataWrapper(Vec<u8>);
impl FromStr for CredentialDataWrapper {
type Err = bs58::decode::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
bs58::decode(s).into_vec().map(CredentialDataWrapper)
}
}
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("cred_data").required(true)),
group(ArgGroup::new("type").required(true)),
)]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data")]
pub(crate) credential_data: Option<CredentialDataWrapper>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
/// Specifies whether we're attempting to import a standalone ticketbook (i.e. serialised `IssuedTicketBook`)
#[clap(long, group = "type")]
pub(crate) standalone: bool,
/// Specifies whether we're attempting to import full ticketboot
/// (i.e. one that **might** contain required global signatures; that is serialised `ImportableTicketBook`)
#[clap(long, group = "type")]
pub(crate) full: bool,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
impl Args {
fn credential_data(self) -> anyhow::Result<Vec<u8>> {
let data = match self.credential_data {
Some(data) => data.0,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(self.credential_path.unwrap())?
}
};
Ok(data)
}
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(&args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let version = args.version;
let standalone = args.standalone;
let full = args.full;
let raw_credential = args.credential_data()?;
if standalone {
import_standalone_ticketbook(credentials_store, raw_credential, version).await?;
} else {
// sanity check; clap should have ensured it
assert!(full);
import_full_ticketbook(credentials_store, raw_credential, version).await?;
}
Ok(())
}
@@ -1,170 +0,0 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::CommonConfigsWrapper;
use anyhow::{anyhow, bail};
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_storage::storage::Storage;
use nym_credential_utils::utils;
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
use nym_credentials::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, EpochVerificationKey,
};
use nym_credentials_interface::TicketType;
use nym_crypto::asymmetric::identity;
use std::fs;
use std::path::PathBuf;
use tempfile::NamedTempFile;
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("output").required(true)),
)]
pub struct Args {
/// Specify which type of ticketbook should be issued
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Config file of the client that is supposed to use the credential.
#[clap(long, group = "output")]
pub(crate) client_config: Option<PathBuf>,
/// Output file for the ticketbook
#[clap(long, group = "output", requires = "bs58_encoded_client_secret")]
pub(crate) output_file: Option<PathBuf>,
/// Specifies whether the output file should use binary or bs58 encoded data
#[clap(long, requires = "output_file")]
pub(crate) bs58_output: bool,
/// Specifies whether the file output should contain expiration date signatures
#[clap(long, requires = "output_file")]
pub(crate) include_expiration_date_signatures: bool,
/// Specifies whether the file output should contain coin index signatures
#[clap(long, requires = "output_file")]
pub(crate) include_coin_index_signatures: bool,
/// Specifies whether the file output should contain master verification key
#[clap(long, requires = "output_file")]
pub(crate) include_master_verification_key: bool,
/// Secret value that's used for deriving underlying ecash keypair
#[clap(long)]
pub(crate) bs58_encoded_client_secret: Option<String>,
}
async fn issue_client_ticketbook(
config_path: PathBuf,
ticketbook_type: TicketType,
client: SigningClient,
) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(config_path)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
let Ok(private_id_key) = loaded.try_get_private_id_key() else {
bail!("the loaded config does not have a public id key information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let persistent_storage = initialise_persistent_storage(credentials_store).await;
let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?;
utils::issue_credential(
&client,
&persistent_storage,
&private_id_key.to_bytes(),
ticketbook_type,
)
.await?;
Ok(())
}
async fn issue_to_file(args: Args, client: SigningClient) -> anyhow::Result<()> {
// those MUST HAVE been specified; clap ensures it
let output_file = args.output_file.unwrap();
let secret = bs58::decode(&args.bs58_encoded_client_secret.unwrap()).into_vec()?;
let temp_credential_store_file = NamedTempFile::new()?;
let credential_store_path = temp_credential_store_file.into_temp_path();
let credentials_store = initialise_persistent_storage(credential_store_path).await;
utils::issue_credential(&client, &credentials_store, &secret, args.ticketbook_type).await?;
let ticketbook = credentials_store
.get_next_unspent_usable_ticketbook(0)
.await?
.ok_or(anyhow!("we just issued a ticketbook, it must be present!"))?
.ticketbook;
let expiration_date = ticketbook.expiration_date();
let epoch_id = ticketbook.epoch_id();
let mut exported = ticketbook.begin_export();
if args.include_expiration_date_signatures {
let signatures = credentials_store
.get_expiration_date_signatures(expiration_date)
.await?
.ok_or(anyhow!("missing expiration date signatures!"))?;
exported = exported.with_expiration_date_signatures(&AggregatedExpirationDateSignatures {
epoch_id,
expiration_date,
signatures,
});
}
if args.include_coin_index_signatures {
let signatures = credentials_store
.get_coin_index_signatures(epoch_id)
.await?
.ok_or(anyhow!("missing coin index signatures!"))?;
exported = exported.with_coin_index_signatures(&AggregatedCoinIndicesSignatures {
epoch_id,
signatures,
});
}
if args.include_master_verification_key {
let key = credentials_store
.get_master_verification_key(epoch_id)
.await?
.ok_or(anyhow!("missing master verification key!"))?;
exported = exported.with_master_verification_key(&EpochVerificationKey { epoch_id, key });
}
let data = exported.pack().data;
if args.bs58_output {
fs::write(output_file, bs58::encode(&data).into_string())?;
} else {
fs::write(output_file, &data)?;
}
Ok(())
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
if let Some(client_config) = args.client_config {
return issue_client_ticketbook(client_config, args.ticketbook_type, client).await;
}
issue_to_file(args, client).await
}
+1 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod coconut;
pub mod context;
pub mod ecash;
pub mod utils;
pub mod validator;
@@ -6,7 +6,7 @@ use crate::utils::{account_id_to_cw_addr, DataWrapper};
use clap::Parser;
use cosmwasm_std::Coin;
use nym_bin_common::output_format::OutputFormat;
use nym_mixnet_contract_common::construct_legacy_gateway_bonding_sign_payload;
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
@@ -71,7 +71,7 @@ pub async fn create_payload(args: Args, client: SigningClient) {
let address = account_id_to_cw_addr(&client.address());
let payload = construct_legacy_gateway_bonding_sign_payload(nonce, address, coin, gateway);
let payload = construct_gateway_bonding_sign_payload(nonce, address, coin, gateway);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use base64::Engine;
use clap::Parser;
#[derive(Debug, Parser)]
@@ -10,9 +11,7 @@ pub struct Args {
}
pub fn decode_mixnode_key(args: Args) {
use base64::{engine::general_purpose::STANDARD, Engine as _};
let b64_decoded = STANDARD
let b64_decoded = base64::prelude::BASE64_STANDARD
.decode(args.key)
.expect("failed to decode base64 string");
let b58_encoded = bs58::encode(&b64_decoded).into_string();
@@ -7,9 +7,7 @@ use clap::Parser;
use cosmwasm_std::{Coin, Uint128};
use nym_bin_common::output_format::OutputFormat;
use nym_contracts_common::Percent;
use nym_mixnet_contract_common::{
construct_legacy_mixnode_bonding_sign_payload, MixNodeCostParams,
};
use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNodeCostParams};
use nym_network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
@@ -100,7 +98,7 @@ pub async fn create_payload(args: Args, client: SigningClient) {
let address = account_id_to_cw_addr(&client.address());
let payload =
construct_legacy_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
construct_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -6,14 +6,14 @@ use clap::Parser;
use cosmwasm_std::Uint128;
use log::info;
use nym_mixnet_contract_common::{MixNodeCostParams, Percent};
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
use nym_validator_client::nyxd::CosmWasmCoin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(
long,
help = "input your profit margin as follows; (so it would be 20, rather than 0.2)"
help = "input your profit margin as follows; (so it would be 10, rather than 0.1)"
)]
pub profit_margin_percent: Option<u8>,
@@ -27,48 +27,11 @@ pub struct Args {
pub async fn update_cost_params(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
fn convert_to_percent(value: u64) -> Percent {
Percent::from_percentage_value(value).expect("Invalid value")
}
let default_profit_margin: Percent = convert_to_percent(20);
let mixownership_response = match client.get_owned_mixnode(&client.address()).await {
Ok(response) => response,
Err(_) => {
eprintln!("Failed to obtain owned mixnode");
return;
}
};
let mix_id = match mixownership_response.mixnode_details {
Some(details) => details.bond_information.mix_id,
None => {
eprintln!("Failed to obtain mixnode details");
return;
}
};
let rewarding_response = match client.get_mixnode_rewarding_details(mix_id).await {
Ok(details) => details,
Err(_) => {
eprintln!("Failed to obtain rewarding details");
return;
}
};
let profit_margin_percent = rewarding_response
.rewarding_details
.map(|rd| rd.cost_params.profit_margin_percent)
.unwrap_or(default_profit_margin);
let profit_margin_value = args
.profit_margin_percent
.map(|pm| convert_to_percent(pm as u64))
.unwrap_or(profit_margin_percent);
let cost_params = MixNodeCostParams {
profit_margin_percent: profit_margin_value,
profit_margin_percent: Percent::from_percentage_value(
args.profit_margin_percent.unwrap_or(10) as u64,
)
.unwrap(),
interval_operating_cost: CosmWasmCoin {
denom: denom.into(),
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
@@ -248,31 +248,3 @@ impl<T> ContractMessageContent<T> {
}
}
}
impl<T> From<ContractMessageContent<T>> for LegacyContractMessageContent<T> {
fn from(value: ContractMessageContent<T>) -> Self {
LegacyContractMessageContent {
sender: value.sender,
proxy: None,
funds: value.funds,
data: value.data,
}
}
}
#[derive(Serialize)]
pub struct LegacyContractMessageContent<T> {
pub sender: Addr,
pub proxy: Option<Addr>,
pub funds: Vec<Coin>,
pub data: T,
}
impl<T> SigningPurpose for LegacyContractMessageContent<T>
where
T: SigningPurpose,
{
fn message_type() -> MessageType {
T::message_type()
}
}
@@ -11,4 +11,4 @@ cosmwasm-schema = { workspace = true }
cw4 = { workspace = true }
cw-controllers = { workspace = true }
schemars = { workspace = true }
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
serde = { version = "1.0.209", default-features = false, features = ["derive"] }
@@ -12,7 +12,6 @@ repository = { workspace = true }
bs58 = "0.5.1"
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-controllers = { workspace = true }
cw2 = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_repr = { workspace = true }
@@ -5,7 +5,6 @@ use crate::{EpochEventId, EpochState, IdentityKey, MixId, OperatingCostRange, Pr
use contracts_common::signing::verifier::ApiVerifierError;
use contracts_common::Percent;
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
use cw_controllers::AdminError;
use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
@@ -13,9 +12,6 @@ pub enum MixnetContractError {
#[error("could not perform contract migration: {comment}")]
FailedMigration { comment: String },
#[error(transparent)]
Admin(#[from] AdminError),
#[error("{source}")]
StdErr {
#[from]
@@ -110,11 +110,6 @@ impl InitialRewardingParams {
#[cw_serde]
pub enum ExecuteMsg {
/// Change the admin
UpdateAdmin {
admin: String,
},
AssignNodeLayer {
mix_id: MixId,
layer: Layer,
@@ -297,7 +292,6 @@ pub enum ExecuteMsg {
impl ExecuteMsg {
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::UpdateAdmin { admin } => format!("updating contract admin to {admin}"),
ExecuteMsg::AssignNodeLayer { mix_id, layer } => {
format!("assigning mix {mix_id} for layer {layer:?}")
}
@@ -414,9 +408,6 @@ impl ExecuteMsg {
#[cw_serde]
#[cfg_attr(feature = "schema", derive(QueryResponses))]
pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(cw_controllers::AdminResponse))]
Admin {},
// families
/// Gets the list of families registered in this contract.
#[cfg_attr(feature = "schema", returns(PagedFamiliesResponse))]
@@ -4,18 +4,13 @@
use crate::families::FamilyHead;
use crate::{Gateway, IdentityKey, MixNode, MixNodeCostParams};
use contracts_common::signing::{
ContractMessageContent, LegacyContractMessageContent, MessageType, Nonce, SignableMessage,
SigningPurpose,
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
use cosmwasm_std::{Addr, Coin};
use serde::Serialize;
pub type SignableMixNodeBondingMsg = SignableMessage<ContractMessageContent<MixnodeBondingPayload>>;
pub type SignableGatewayBondingMsg = SignableMessage<ContractMessageContent<GatewayBondingPayload>>;
pub type SignableLegacyMixNodeBondingMsg =
SignableMessage<LegacyContractMessageContent<MixnodeBondingPayload>>;
pub type SignableLegacyGatewayBondingMsg =
SignableMessage<LegacyContractMessageContent<GatewayBondingPayload>>;
pub type SignableFamilyJoinPermitMsg = SignableMessage<FamilyJoinPermit>;
#[derive(Serialize)]
@@ -52,20 +47,6 @@ pub fn construct_mixnode_bonding_sign_payload(
SignableMessage::new(nonce, content)
}
pub fn construct_legacy_mixnode_bonding_sign_payload(
nonce: Nonce,
sender: Addr,
pledge: Coin,
mix_node: MixNode,
cost_params: MixNodeCostParams,
) -> SignableLegacyMixNodeBondingMsg {
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
let content: LegacyContractMessageContent<_> =
ContractMessageContent::new(sender, vec![pledge], payload).into();
SignableMessage::new(nonce, content)
}
#[derive(Serialize)]
pub struct GatewayBondingPayload {
gateway: Gateway,
@@ -95,19 +76,6 @@ pub fn construct_gateway_bonding_sign_payload(
SignableMessage::new(nonce, content)
}
pub fn construct_legacy_gateway_bonding_sign_payload(
nonce: Nonce,
sender: Addr,
pledge: Coin,
gateway: Gateway,
) -> SignableLegacyGatewayBondingMsg {
let payload = GatewayBondingPayload::new(gateway);
let content: LegacyContractMessageContent<_> =
ContractMessageContent::new(sender, vec![pledge], payload).into();
SignableMessage::new(nonce, content)
}
#[derive(Serialize)]
pub struct FamilyJoinPermit {
// the granter of this permit
@@ -187,11 +187,7 @@ impl Index<Layer> for LayerDistribution {
#[cw_serde]
pub struct ContractState {
/// Address of the contract owner.
#[deprecated(
note = "use explicit ADMIN instead. this field will be removed in future release"
)]
#[serde(default)]
pub owner: Option<Addr>,
pub owner: Addr,
/// Address of "rewarding validator" (nym-api) that's allowed to send any rewarding-related transactions.
pub rewarding_validator_address: Addr,
@@ -14,5 +14,5 @@ cw-storage-plus = { workspace = true }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
schemars = { workspace = true }
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
serde = { version = "1.0.209", default-features = false, features = ["derive"] }
thiserror = { workspace = true }
@@ -1,13 +0,0 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE master_verification_key
ADD COLUMN serialization_revision INTEGER NOT NULL default 1;
ALTER TABLE coin_indices_signatures
ADD COLUMN serialization_revision INTEGER NOT NULL default 1;
ALTER TABLE expiration_date_signatures
ADD COLUMN serialization_revision INTEGER NOT NULL default 1;
@@ -5,10 +5,6 @@ use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, Retr
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use nym_compact_ecash::VerificationKeyAuth;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
use nym_credentials::{IssuanceTicketBook, IssuedTicketBook};
use nym_ecash_time::Date;
@@ -196,10 +192,14 @@ impl MemoryEcachTicketbookManager {
guard.master_vk.get(&epoch_id).cloned()
}
pub(crate) async fn insert_master_verification_key(&self, key: &EpochVerificationKey) {
pub(crate) async fn insert_master_verification_key(
&self,
epoch_id: u64,
key: &VerificationKeyAuth,
) {
let mut guard = self.inner.write().await;
guard.master_vk.insert(key.epoch_id, key.key.clone());
guard.master_vk.insert(epoch_id, key.clone());
}
pub(crate) async fn get_coin_index_signatures(
@@ -213,13 +213,12 @@ impl MemoryEcachTicketbookManager {
pub(crate) async fn insert_coin_index_signatures(
&self,
sigs: &AggregatedCoinIndicesSignatures,
epoch_id: u64,
sigs: &[AnnotatedCoinIndexSignature],
) {
let mut guard = self.inner.write().await;
guard
.coin_indices_sigs
.insert(sigs.epoch_id, sigs.signatures.clone());
guard.coin_indices_sigs.insert(epoch_id, sigs.to_vec());
}
pub(crate) async fn get_expiration_date_signatures(
@@ -233,12 +232,14 @@ impl MemoryEcachTicketbookManager {
pub(crate) async fn insert_expiration_date_signatures(
&self,
sigs: &AggregatedExpirationDateSignatures,
_epoch_id: u64,
expiration_date: Date,
sigs: &[AnnotatedExpirationDateSignature],
) {
let mut guard = self.inner.write().await;
guard
.expiration_date_sigs
.insert(sigs.expiration_date, sigs.signatures.clone());
.insert(expiration_date, sigs.to_vec());
}
}
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::models::{
BasicTicketbookInformation, RawCoinIndexSignatures, RawExpirationDateSignatures,
RawVerificationKey, StoredIssuedTicketbook, StoredPendingTicketbook,
BasicTicketbookInformation, RawExpirationDateSignatures, StoredIssuedTicketbook,
StoredPendingTicketbook,
};
use nym_ecash_time::Date;
use sqlx::{Executor, Sqlite, Transaction};
@@ -151,30 +151,25 @@ impl SqliteEcashTicketbookManager {
pub(crate) async fn get_master_verification_key(
&self,
epoch_id: i64,
) -> Result<Option<RawVerificationKey>, sqlx::Error> {
sqlx::query_as!(
RawVerificationKey,
r#"
SELECT epoch_id as "epoch_id: u32", serialised_key, serialization_revision as "serialization_revision: u8"
FROM master_verification_key WHERE epoch_id = ?
"#,
) -> Result<Option<Vec<u8>>, sqlx::Error> {
sqlx::query!(
"SELECT serialised_key FROM master_verification_key WHERE epoch_id = ?",
epoch_id
)
.fetch_optional(&self.connection_pool)
.await
.map(|maybe_record| maybe_record.map(|r| r.serialised_key))
}
pub(crate) async fn insert_master_verification_key(
&self,
serialisation_revision: u8,
epoch_id: i64,
data: &[u8],
) -> Result<(), sqlx::Error> {
sqlx::query!(
"INSERT INTO master_verification_key(epoch_id, serialised_key, serialization_revision) VALUES (?, ?, ?)",
"INSERT INTO master_verification_key(epoch_id, serialised_key) VALUES (?, ?)",
epoch_id,
data,
serialisation_revision
data
)
.execute(&self.connection_pool)
.await?;
@@ -184,30 +179,25 @@ impl SqliteEcashTicketbookManager {
pub(crate) async fn get_coin_index_signatures(
&self,
epoch_id: i64,
) -> Result<Option<RawCoinIndexSignatures>, sqlx::Error> {
sqlx::query_as!(
RawCoinIndexSignatures,
r#"
SELECT epoch_id as "epoch_id: u32", serialised_signatures, serialization_revision as "serialization_revision: u8"
FROM coin_indices_signatures WHERE epoch_id = ?
"#,
) -> Result<Option<Vec<u8>>, sqlx::Error> {
sqlx::query!(
"SELECT serialised_signatures FROM coin_indices_signatures WHERE epoch_id = ?",
epoch_id
)
.fetch_optional(&self.connection_pool)
.await
.map(|maybe_record| maybe_record.map(|r| r.serialised_signatures))
}
pub(crate) async fn insert_coin_index_signatures(
&self,
serialisation_revision: u8,
epoch_id: i64,
data: &[u8],
) -> Result<(), sqlx::Error> {
sqlx::query!(
"INSERT INTO coin_indices_signatures(epoch_id, serialised_signatures, serialization_revision) VALUES (?, ?, ?)",
"INSERT INTO coin_indices_signatures(epoch_id, serialised_signatures) VALUES (?, ?)",
epoch_id,
data,
serialisation_revision
data
)
.execute(&self.connection_pool)
.await?;
@@ -221,7 +211,7 @@ impl SqliteEcashTicketbookManager {
sqlx::query_as!(
RawExpirationDateSignatures,
r#"
SELECT epoch_id as "epoch_id: u32", serialised_signatures, serialization_revision as "serialization_revision: u8"
SELECT epoch_id as "epoch_id: u32", serialised_signatures
FROM expiration_date_signatures
WHERE expiration_date = ?
"#,
@@ -233,20 +223,15 @@ impl SqliteEcashTicketbookManager {
pub(crate) async fn insert_expiration_date_signatures(
&self,
serialisation_revision: u8,
epoch_id: i64,
expiration_date: Date,
data: &[u8],
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures, serialization_revision)
VALUES (?, ?, ?, ?)
"#,
"INSERT INTO expiration_date_signatures(expiration_date, epoch_id, serialised_signatures) VALUES (?, ?, ?)",
expiration_date,
epoch_id,
data,
serialisation_revision
data
)
.execute(&self.connection_pool)
.await?;
@@ -9,10 +9,6 @@ use async_trait::async_trait;
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use nym_compact_ecash::VerificationKeyAuth;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials::{IssuanceTicketBook, IssuedTicketBook};
use nym_ecash_time::Date;
use std::fmt::{self, Debug, Formatter};
@@ -123,10 +119,11 @@ impl Storage for EphemeralStorage {
async fn insert_master_verification_key(
&self,
key: &EpochVerificationKey,
epoch_id: u64,
key: &VerificationKeyAuth,
) -> Result<(), Self::StorageError> {
self.storage_manager
.insert_master_verification_key(key)
.insert_master_verification_key(epoch_id, key)
.await;
Ok(())
}
@@ -143,10 +140,11 @@ impl Storage for EphemeralStorage {
async fn insert_coin_index_signatures(
&self,
signatures: &AggregatedCoinIndicesSignatures,
epoch_id: u64,
data: &[AnnotatedCoinIndexSignature],
) -> Result<(), Self::StorageError> {
self.storage_manager
.insert_coin_index_signatures(signatures)
.insert_coin_index_signatures(epoch_id, data)
.await;
Ok(())
}
@@ -163,10 +161,12 @@ impl Storage for EphemeralStorage {
async fn insert_expiration_date_signatures(
&self,
signatures: &AggregatedExpirationDateSignatures,
epoch_id: u64,
expiration_date: Date,
data: &[AnnotatedExpirationDateSignature],
) -> Result<(), Self::StorageError> {
self.storage_manager
.insert_expiration_date_signatures(signatures)
.insert_expiration_date_signatures(epoch_id, expiration_date, data)
.await;
Ok(())
}
-15
View File
@@ -62,19 +62,4 @@ pub struct StoredPendingTicketbook {
pub struct RawExpirationDateSignatures {
pub epoch_id: u32,
pub serialised_signatures: Vec<u8>,
pub serialization_revision: u8,
}
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
pub struct RawCoinIndexSignatures {
pub epoch_id: u32,
pub serialised_signatures: Vec<u8>,
pub serialization_revision: u8,
}
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
pub struct RawVerificationKey {
pub epoch_id: u32,
pub serialised_key: Vec<u8>,
pub serialization_revision: u8,
}
@@ -0,0 +1,54 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::StorageError;
use bincode::Options;
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct StorageBorrowedSerdeWrapper<'a, T>(&'a T);
#[derive(Serialize, Deserialize)]
struct StorageSerdeWrapper<T>(T);
pub(crate) fn serialise_coin_index_signatures(sigs: &[AnnotatedCoinIndexSignature]) -> Vec<u8> {
storage_serialiser()
.serialize(&StorageBorrowedSerdeWrapper(&sigs))
.unwrap()
}
pub(crate) fn deserialise_coin_index_signatures(
raw: &[u8],
) -> Result<Vec<AnnotatedCoinIndexSignature>, StorageError> {
let de: StorageSerdeWrapper<_> = storage_serialiser().deserialize(raw).map_err(|_| {
StorageError::database_inconsistency("malformed stored coin index signatures")
})?;
Ok(de.0)
}
pub(crate) fn serialise_expiration_date_signatures(
sigs: &[AnnotatedExpirationDateSignature],
) -> Vec<u8> {
storage_serialiser()
.serialize(&StorageBorrowedSerdeWrapper(&sigs))
.unwrap()
}
pub(crate) fn deserialise_expiration_date_signatures(
raw: &[u8],
) -> Result<Vec<AnnotatedExpirationDateSignature>, StorageError> {
let de: StorageSerdeWrapper<_> = storage_serialiser().deserialize(raw).map_err(|_| {
StorageError::database_inconsistency("malformed expiration date signatures")
})?;
Ok(de.0)
}
// storage serialiser used for non-critical data, such as global expiration signatures or master verification keys,
// i.e. data that could always be queried for again if malformed
fn storage_serialiser() -> impl bincode::Options {
bincode::DefaultOptions::new()
.with_big_endian()
.with_varint_encoding()
}
@@ -1,44 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::StorageError;
use bincode::Options;
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use nym_compact_ecash::VerificationKeyAuth;
use serde::Deserialize;
#[derive(Deserialize)]
struct StorageSerdeWrapper<T>(T);
pub(crate) fn deserialise_v1_expiration_date_signatures(
raw: &[u8],
) -> Result<Vec<AnnotatedExpirationDateSignature>, StorageError> {
let de: StorageSerdeWrapper<_> = v1_signatures_serialiser().deserialize(raw).map_err(|_| {
StorageError::database_inconsistency("malformed expiration date signatures")
})?;
Ok(de.0)
}
pub(crate) fn deserialise_v1_coin_index_signatures(
raw: &[u8],
) -> Result<Vec<AnnotatedCoinIndexSignature>, StorageError> {
let de: StorageSerdeWrapper<_> = v1_signatures_serialiser().deserialize(raw).map_err(|_| {
StorageError::database_inconsistency("malformed stored coin index signatures")
})?;
Ok(de.0)
}
pub(crate) fn deserialise_v1_master_verification_key(
raw: &[u8],
) -> Result<VerificationKeyAuth, StorageError> {
VerificationKeyAuth::from_bytes(raw).map_err(|_| {
StorageError::database_inconsistency("malformed stored master verification key")
})
}
fn v1_signatures_serialiser() -> impl bincode::Options {
bincode::DefaultOptions::new()
.with_big_endian()
.with_varint_encoding()
}
@@ -1,16 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod legacy_helpers;
use crate::backends::sqlite::{
get_next_unspent_ticketbook, increase_used_ticketbook_tickets, SqliteEcashTicketbookManager,
};
use crate::error::StorageError;
use crate::models::{BasicTicketbookInformation, RetrievedPendingTicketbook, RetrievedTicketbook};
use crate::persistent_storage::legacy_helpers::{
deserialise_v1_coin_index_signatures, deserialise_v1_expiration_date_signatures,
deserialise_v1_master_verification_key,
use crate::persistent_storage::helpers::{
deserialise_coin_index_signatures, deserialise_expiration_date_signatures,
serialise_coin_index_signatures, serialise_expiration_date_signatures,
};
use crate::storage::Storage;
use async_trait::async_trait;
@@ -18,10 +16,6 @@ use log::{debug, error};
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use nym_compact_ecash::VerificationKeyAuth;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
use nym_credentials::{IssuanceTicketBook, IssuedTicketBook};
use nym_ecash_time::{ecash_today, Date, EcashTime};
@@ -29,6 +23,8 @@ use sqlx::ConnectOptions;
use std::path::Path;
use zeroize::Zeroizing;
mod helpers;
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub struct PersistentStorage {
@@ -233,24 +229,21 @@ impl Storage for PersistentStorage {
return Ok(None);
};
match raw.serialization_revision {
1 => deserialise_v1_master_verification_key(&raw.serialised_key).map(Some),
other => {
let deserialised = EpochVerificationKey::try_unpack(&raw.serialised_key, other)
.map_err(|err| StorageError::database_inconsistency(err.to_string()))?;
Ok(Some(deserialised.key))
}
}
let master_vk = VerificationKeyAuth::from_bytes(&raw).map_err(|_| {
StorageError::database_inconsistency("malformed stored master verification key")
})?;
Ok(Some(master_vk))
}
async fn insert_master_verification_key(
&self,
key: &EpochVerificationKey,
epoch_id: u64,
key: &VerificationKeyAuth,
) -> Result<(), Self::StorageError> {
let packed = key.pack();
Ok(self
.storage_manager
.insert_master_verification_key(packed.revision, key.epoch_id as i64, &packed.data)
.insert_master_verification_key(epoch_id as i64, &key.to_bytes())
.await?)
}
@@ -266,24 +259,16 @@ impl Storage for PersistentStorage {
return Ok(None);
};
match raw.serialization_revision {
1 => deserialise_v1_coin_index_signatures(&raw.serialised_signatures).map(Some),
other => {
let deserialised =
AggregatedCoinIndicesSignatures::try_unpack(&raw.serialised_signatures, other)
.map_err(|err| StorageError::database_inconsistency(err.to_string()))?;
Ok(Some(deserialised.signatures))
}
}
Ok(Some(deserialise_coin_index_signatures(&raw)?))
}
async fn insert_coin_index_signatures(
&self,
signatures: &AggregatedCoinIndicesSignatures,
epoch_id: u64,
sigs: &[AnnotatedCoinIndexSignature],
) -> Result<(), Self::StorageError> {
let packed = signatures.pack();
self.storage_manager
.insert_coin_index_signatures(packed.revision, signatures.epoch_id as i64, &packed.data)
.insert_coin_index_signatures(epoch_id as i64, &serialise_coin_index_signatures(sigs))
.await?;
Ok(())
}
@@ -300,30 +285,22 @@ impl Storage for PersistentStorage {
return Ok(None);
};
match raw.serialization_revision {
1 => deserialise_v1_expiration_date_signatures(&raw.serialised_signatures).map(Some),
other => {
let deserialised = AggregatedExpirationDateSignatures::try_unpack(
&raw.serialised_signatures,
other,
)
.map_err(|err| StorageError::database_inconsistency(err.to_string()))?;
Ok(Some(deserialised.signatures))
}
}
Ok(Some(deserialise_expiration_date_signatures(
&raw.serialised_signatures,
)?))
}
async fn insert_expiration_date_signatures(
&self,
signatures: &AggregatedExpirationDateSignatures,
epoch_id: u64,
expiration_date: Date,
sigs: &[AnnotatedExpirationDateSignature],
) -> Result<(), Self::StorageError> {
let packed = signatures.pack();
self.storage_manager
.insert_expiration_date_signatures(
packed.revision,
signatures.epoch_id as i64,
signatures.expiration_date,
&packed.data,
epoch_id as i64,
expiration_date,
&serialise_expiration_date_signatures(sigs),
)
.await?;
Ok(())
+7 -7
View File
@@ -6,10 +6,6 @@ use async_trait::async_trait;
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
use nym_compact_ecash::VerificationKeyAuth;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials::{IssuanceTicketBook, IssuedTicketBook};
use nym_ecash_time::Date;
use std::error::Error;
@@ -68,7 +64,8 @@ pub trait Storage: Send + Sync {
async fn insert_master_verification_key(
&self,
key: &EpochVerificationKey,
epoch_id: u64,
key: &VerificationKeyAuth,
) -> Result<(), Self::StorageError>;
async fn get_coin_index_signatures(
@@ -78,7 +75,8 @@ pub trait Storage: Send + Sync {
async fn insert_coin_index_signatures(
&self,
signatures: &AggregatedCoinIndicesSignatures,
epoch_id: u64,
data: &[AnnotatedCoinIndexSignature],
) -> Result<(), Self::StorageError>;
async fn get_expiration_date_signatures(
@@ -88,6 +86,8 @@ pub trait Storage: Send + Sync {
async fn insert_expiration_date_signatures(
&self,
signatures: &AggregatedExpirationDateSignatures,
epoch_id: u64,
expiration_date: Date,
data: &[AnnotatedExpirationDateSignature],
) -> Result<(), Self::StorageError>;
}
+11 -5
View File
@@ -3,7 +3,9 @@
use crate::errors::{Error, Result};
use log::*;
use nym_bandwidth_controller::acquire::{get_ticket_book, query_and_persist_required_global_data};
use nym_bandwidth_controller::acquire::{
get_ticket_book, query_and_persist_required_global_signatures,
};
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_config::DEFAULT_DATA_DIR;
use nym_credential_storage::persistent_storage::PersistentStorage;
@@ -43,10 +45,14 @@ where
let apis = all_ecash_api_clients(client, epoch_id).await?;
let ticketbook_expiration = ecash_default_expiration_date();
// make sure we have all required coin indices and expiration date signatures alongside the master verification key
// before attempting the deposit
query_and_persist_required_global_data(storage, epoch_id, ticketbook_expiration, apis.clone())
.await?;
// make sure we have all required coin indices and expiration date signatures before attempting the deposit
query_and_persist_required_global_signatures(
storage,
epoch_id,
ticketbook_expiration,
apis.clone(),
)
.await?;
let issuance_data = nym_bandwidth_controller::acquire::make_deposit(
client,
-33
View File
@@ -1,33 +0,0 @@
[package]
name = "nym-credential-verification"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
bs58 = { workspace = true }
cosmwasm-std = { workspace = true }
cw-utils = { workspace = true }
futures = { workspace = true }
rand = { workspace = true }
si-scale = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
time = { workspace = true }
tracing = { workspace = true }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-double-spending = { path = "../ecash-double-spending" }
nym-gateway-requests = { path = "../gateway-requests" }
nym-gateway-storage = { path = "../gateway-storage" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
@@ -1,148 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials::ecash::utils::ecash_today;
use nym_credentials_interface::Bandwidth;
use nym_gateway_requests::ServerResponse;
use nym_gateway_storage::Storage;
use si_scale::helpers::bibytes2;
use time::OffsetDateTime;
use tracing::*;
use crate::error::*;
use crate::BandwidthFlushingBehaviourConfig;
use crate::ClientBandwidth;
const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
#[derive(Clone)]
pub struct BandwidthStorageManager<S> {
pub(crate) storage: S,
pub(crate) client_bandwidth: ClientBandwidth,
pub(crate) client_id: i64,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
pub(crate) only_coconut_credentials: bool,
}
impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
pub fn new(
storage: S,
client_bandwidth: ClientBandwidth,
client_id: i64,
bandwidth_cfg: BandwidthFlushingBehaviourConfig,
only_coconut_credentials: bool,
) -> Self {
BandwidthStorageManager {
storage,
client_bandwidth,
client_id,
bandwidth_cfg,
only_coconut_credentials,
}
}
async fn sync_expiration(&mut self) -> Result<()> {
self.storage
.set_expiration(self.client_id, self.client_bandwidth.bandwidth.expiration)
.await?;
Ok(())
}
pub async fn handle_claim_testnet_bandwidth(&mut self) -> Result<ServerResponse> {
debug!("handling testnet bandwidth request");
if self.only_coconut_credentials {
return Err(Error::OnlyCoconutCredentials);
}
self.increase_bandwidth(FREE_TESTNET_BANDWIDTH_VALUE, ecash_today())
.await?;
let available_total = self.client_bandwidth.bandwidth.bytes;
Ok(ServerResponse::Bandwidth { available_total })
}
#[instrument(skip_all)]
pub async fn try_use_bandwidth(&mut self, required_bandwidth: i64) -> Result<i64> {
if self.client_bandwidth.bandwidth.expired() {
self.expire_bandwidth().await?;
}
let available_bandwidth = self.client_bandwidth.bandwidth.bytes;
if available_bandwidth < required_bandwidth {
return Err(Error::OutOfBandwidth {
required: required_bandwidth,
available: available_bandwidth,
});
}
let available_bi2 = bibytes2(available_bandwidth as f64);
let required_bi2 = bibytes2(required_bandwidth as f64);
debug!(available = available_bi2, required = required_bi2);
self.consume_bandwidth(required_bandwidth).await?;
Ok(available_bandwidth)
}
async fn expire_bandwidth(&mut self) -> Result<()> {
self.storage.reset_bandwidth(self.client_id).await?;
self.client_bandwidth.bandwidth = Default::default();
self.client_bandwidth.update_sync_data();
Ok(())
}
/// Decreases the amount of available bandwidth of the connected client by the specified value.
///
/// # Arguments
///
/// * `amount`: amount to decrease the available bandwidth by.
async fn consume_bandwidth(&mut self, amount: i64) -> Result<()> {
self.client_bandwidth.bandwidth.bytes -= amount;
self.client_bandwidth.bytes_delta_since_sync -= amount;
// since we're going to be operating on a fair use policy anyway, even if we crash and let extra few packets
// through, that's completely fine
if self.client_bandwidth.should_sync(self.bandwidth_cfg) {
self.sync_bandwidth().await?;
}
Ok(())
}
#[instrument(level = "trace", skip_all)]
async fn sync_bandwidth(&mut self) -> Result<()> {
trace!("syncing client bandwidth with the underlying storage");
let updated = self
.storage
.increase_bandwidth(self.client_id, self.client_bandwidth.bytes_delta_since_sync)
.await?;
trace!(updated);
self.client_bandwidth.bandwidth.bytes = updated;
self.client_bandwidth.update_sync_data();
Ok(())
}
/// Increases the amount of available bandwidth of the connected client by the specified value.
///
/// # Arguments
///
/// * `amount`: amount to increase the available bandwidth by.
/// * `expiration` : the expiration date of that bandwidth
pub async fn increase_bandwidth(
&mut self,
bandwidth: Bandwidth,
expiration: OffsetDateTime,
) -> Result<()> {
self.client_bandwidth.bandwidth.bytes += bandwidth.value() as i64;
self.client_bandwidth.bytes_delta_since_sync += bandwidth.value() as i64;
self.client_bandwidth.bandwidth.expiration = expiration;
// any increases to bandwidth should get flushed immediately
// (we don't want to accidentally miss somebody claiming a gigabyte voucher)
self.sync_expiration().await?;
self.sync_bandwidth().await
}
}
@@ -1,57 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::time::Duration;
use nym_credentials_interface::AvailableBandwidth;
use time::OffsetDateTime;
#[derive(Debug, Clone, Copy)]
pub struct BandwidthFlushingBehaviourConfig {
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage.
pub client_bandwidth_max_flushing_rate: Duration,
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage.
pub client_bandwidth_max_delta_flushing_amount: i64,
}
#[derive(Debug, Clone, Copy)]
pub struct ClientBandwidth {
pub(crate) bandwidth: AvailableBandwidth,
pub(crate) last_flushed: OffsetDateTime,
/// the number of bytes the client had during the last sync.
/// it is used to determine whether the current value should be synced with the storage
/// by checking the delta with the known amount
pub(crate) bytes_at_last_sync: i64,
pub(crate) bytes_delta_since_sync: i64,
}
impl ClientBandwidth {
pub fn new(bandwidth: AvailableBandwidth) -> ClientBandwidth {
ClientBandwidth {
bandwidth,
last_flushed: OffsetDateTime::now_utc(),
bytes_at_last_sync: bandwidth.bytes,
bytes_delta_since_sync: 0,
}
}
pub(crate) fn should_sync(&self, cfg: BandwidthFlushingBehaviourConfig) -> bool {
if self.bytes_delta_since_sync.abs() >= cfg.client_bandwidth_max_delta_flushing_amount {
return true;
}
if self.last_flushed + cfg.client_bandwidth_max_flushing_rate < OffsetDateTime::now_utc() {
return true;
}
false
}
pub(crate) fn update_sync_data(&mut self) {
self.last_flushed = OffsetDateTime::now_utc();
self.bytes_at_last_sync = self.bandwidth.bytes;
self.bytes_delta_since_sync = 0;
}
}
@@ -1,57 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
use time::Date;
use crate::ecash::error::EcashTicketError;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("the provided bandwidth credential has already been spent before at this gateway")]
BandwidthCredentialAlreadySpent,
#[error(transparent)]
EcashFailure(EcashTicketError),
#[error(
"the provided credential has an invalid spending date. got {got} but expected {expected}"
)]
InvalidCredentialSpendingDate { got: Date, expected: Date },
#[error("the current multisig contract is not using 'AbsolutePercentage' threshold!")]
InvalidMultisigThreshold,
#[error(
"the received payment contained more than a single ticket. that's currently not supported"
)]
MultipleTickets,
#[error("Nyxd Error - {0}")]
NyxdError(#[from] nym_validator_client::nyxd::error::NyxdError),
#[error("This gateway is only accepting coconut credentials for bandwidth")]
OnlyCoconutCredentials,
#[error("insufficient bandwidth available to process the request. required: {required}B, available: {available}B")]
OutOfBandwidth { required: i64, available: i64 },
#[error("Internal gateway storage error")]
StorageError(#[from] nym_gateway_storage::error::StorageError),
#[error("{0}")]
UnknownTicketType(#[from] nym_credentials_interface::UnknownTicketType),
}
impl From<EcashTicketError> for Error {
fn from(err: EcashTicketError) -> Self {
// don't expose storage issue details to the user
if let EcashTicketError::InternalStorageFailure { source } = err {
Error::StorageError(source)
} else {
Error::EcashFailure(err)
}
}
}
-156
View File
@@ -1,156 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bandwidth_storage_manager::BandwidthStorageManager;
use std::sync::Arc;
use time::{Date, OffsetDateTime};
use tracing::*;
use nym_credentials::ecash::utils::{ecash_today, EcashTime};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
use nym_gateway_requests::models::CredentialSpendingRequest;
use nym_gateway_storage::Storage;
pub use client_bandwidth::*;
use ecash::EcashManager;
pub use error::*;
pub mod bandwidth_storage_manager;
mod client_bandwidth;
pub mod ecash;
pub mod error;
pub struct CredentialVerifier<S> {
credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager<S>>,
bandwidth_storage_manager: BandwidthStorageManager<S>,
}
impl<S: Storage + Clone + 'static> CredentialVerifier<S> {
pub fn new(
credential: CredentialSpendingRequest,
ecash_verifier: Arc<EcashManager<S>>,
bandwidth_storage_manager: BandwidthStorageManager<S>,
) -> Self {
CredentialVerifier {
credential,
ecash_verifier,
bandwidth_storage_manager,
}
}
fn check_credential_spending_date(&self, today: Date) -> Result<()> {
let proposed = self.credential.data.spend_date;
trace!("checking ticket spending date...");
if today != proposed {
trace!("invalid credential spending date. received {proposed}");
return Err(Error::InvalidCredentialSpendingDate {
got: proposed,
expected: today,
});
}
Ok(())
}
async fn check_bloomfilter(&self, serial_number: &Vec<u8>) -> Result<()> {
trace!("checking the bloomfilter...");
let spent = self.ecash_verifier.check_double_spend(serial_number).await;
if spent {
trace!("the credential has already been spent before at some gateway before (bloomfilter failure)");
return Err(Error::BandwidthCredentialAlreadySpent);
}
Ok(())
}
async fn check_local_db_for_double_spending(&self, serial_number: &[u8]) -> Result<()> {
trace!("checking local db for double spending...");
let spent = self
.bandwidth_storage_manager
.storage
.contains_ticket(serial_number)
.await?;
if spent {
trace!("the credential has already been spent before at this gateway");
return Err(Error::BandwidthCredentialAlreadySpent);
}
Ok(())
}
async fn cryptographically_verify_ticket(&self) -> Result<()> {
trace!("attempting to perform ticket verification...");
let aggregated_verification_key = self
.ecash_verifier
.verification_key(self.credential.data.epoch_id)
.await?;
self.ecash_verifier
.check_payment(&self.credential.data, &aggregated_verification_key)
.await?;
Ok(())
}
async fn store_received_ticket(&self, received_at: OffsetDateTime) -> Result<i64> {
trace!("storing received ticket");
let ticket_id = self
.bandwidth_storage_manager
.storage
.insert_received_ticket(
self.bandwidth_storage_manager.client_id,
received_at,
self.credential.encoded_serial_number(),
self.credential.to_bytes(),
)
.await?;
Ok(ticket_id)
}
fn async_verify_ticket(&self, ticket_id: i64) {
let client_ticket = ClientTicket::new(self.credential.data.clone(), ticket_id);
self.ecash_verifier.async_verify(client_ticket);
}
pub async fn verify(&mut self) -> Result<i64> {
let received_at = OffsetDateTime::now_utc();
let spend_date = ecash_today();
// check if the credential hasn't been spent before
let serial_number = self.credential.data.encoded_serial_number();
let credential_type = TicketType::try_from_encoded(self.credential.data.payment.t_type)?;
if self.credential.data.payment.spend_value != 1 {
return Err(Error::MultipleTickets);
}
self.check_credential_spending_date(spend_date.ecash_date())?;
self.check_bloomfilter(&serial_number).await?;
self.check_local_db_for_double_spending(&serial_number)
.await?;
// TODO: do we HAVE TO do it?
self.cryptographically_verify_ticket().await?;
let ticket_id = self.store_received_ticket(received_at).await?;
self.async_verify_ticket(ticket_id);
// TODO: double storing?
// self.store_spent_credential(serial_number_bs58).await?;
let bandwidth = Bandwidth::ticket_amount(credential_type.into());
self.bandwidth_storage_manager
.increase_bandwidth(bandwidth, spend_date)
.await?;
Ok(self
.bandwidth_storage_manager
.client_bandwidth
.bandwidth
.bytes)
}
}
+1 -22
View File
@@ -28,7 +28,7 @@ pub use nym_compact_ecash::{
withdrawal_request, Base58, BlindedSignature, Bytable, EncodedDate, EncodedTicketType,
PartialWallet, PayInfo, PublicKeyUser, SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
};
pub use nym_ecash_time::{ecash_today, EcashTime};
use nym_ecash_time::{ecash_today, EcashTime};
#[derive(Debug, Clone)]
pub struct CredentialSigningData {
@@ -319,24 +319,3 @@ impl Default for AvailableBandwidth {
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Bandwidth {
value: u64,
}
impl Bandwidth {
pub const fn new_unchecked(value: u64) -> Bandwidth {
Bandwidth { value }
}
pub fn ticket_amount(typ: TicketTypeRepr) -> Self {
Bandwidth {
value: typ.bandwidth_value(),
}
}
pub fn value(&self) -> u64 {
self.value
}
}
-1
View File
@@ -25,7 +25,6 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-network-defaults = { path = "../network-defaults" }
nym-serde-helpers = { path = "../serde-helpers", features = ["date"] }
[dev-dependencies]
rand = { workspace = true }
@@ -1,148 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use crate::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use crate::ecash::bandwidth::{
issued::IssuedTicketBook,
serialiser::{VersionSerialised, VersionedSerialise},
};
use crate::Error;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
pub struct DecodedImportableTicketBook {
pub ticketbook: IssuedTicketBook,
pub expiration_date_signatures: Option<AggregatedExpirationDateSignatures>,
pub coin_index_signatures: Option<AggregatedCoinIndicesSignatures>,
pub master_verification_key: Option<EpochVerificationKey>,
}
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct ImportableTicketBook {
pub serialised_ticketbook: VersionSerialised<IssuedTicketBook>,
#[zeroize(skip)]
pub serialised_expiration_date_signatures:
Option<VersionSerialised<AggregatedExpirationDateSignatures>>,
#[zeroize(skip)]
pub serialised_coin_index_signatures:
Option<VersionSerialised<AggregatedCoinIndicesSignatures>>,
#[zeroize(skip)]
pub serialised_master_verification_key: Option<VersionSerialised<EpochVerificationKey>>,
}
impl From<IssuedTicketBook> for ImportableTicketBook {
fn from(ticketbook: IssuedTicketBook) -> Self {
ImportableTicketBook {
serialised_ticketbook: ticketbook.pack(),
serialised_expiration_date_signatures: None,
serialised_coin_index_signatures: None,
serialised_master_verification_key: None,
}
}
}
impl ImportableTicketBook {
pub fn with_expiration_date_signatures(
mut self,
signatures: &AggregatedExpirationDateSignatures,
) -> Self {
self.serialised_expiration_date_signatures = Some(signatures.pack());
self
}
pub fn with_coin_index_signatures(
mut self,
signatures: &AggregatedCoinIndicesSignatures,
) -> Self {
self.serialised_coin_index_signatures = Some(signatures.pack());
self
}
pub fn with_master_verification_key(mut self, key: &EpochVerificationKey) -> Self {
self.serialised_master_verification_key = Some(key.pack());
self
}
pub fn with_maybe_expiration_date_signatures(
self,
signatures: &Option<AggregatedExpirationDateSignatures>,
) -> Self {
if let Some(sigs) = signatures {
self.with_expiration_date_signatures(sigs)
} else {
self
}
}
pub fn with_maybe_coin_index_signatures(
self,
signatures: &Option<AggregatedCoinIndicesSignatures>,
) -> Self {
if let Some(sigs) = signatures {
self.with_coin_index_signatures(sigs)
} else {
self
}
}
pub fn with_maybe_master_verification_key(self, key: &Option<EpochVerificationKey>) -> Self {
if let Some(sigs) = key {
self.with_master_verification_key(sigs)
} else {
self
}
}
pub fn finalize_export(self) -> VersionSerialised<Self> {
self.pack()
}
pub fn try_unpack_full(&self) -> Result<DecodedImportableTicketBook, Error> {
Ok(DecodedImportableTicketBook {
ticketbook: self.serialised_ticketbook.try_unpack()?,
expiration_date_signatures: self
.serialised_expiration_date_signatures
.as_ref()
.map(|sigs| sigs.try_unpack())
.transpose()?,
coin_index_signatures: self
.serialised_coin_index_signatures
.as_ref()
.map(|sigs| sigs.try_unpack())
.transpose()?,
master_verification_key: self
.serialised_master_verification_key
.as_ref()
.map(|key| key.try_unpack())
.transpose()?,
})
}
}
impl VersionedSerialise for ImportableTicketBook {
const CURRENT_SERIALISATION_REVISION: u8 = 1;
fn try_unpack(b: &[u8], revision: impl Into<Option<u8>>) -> Result<Self, Error>
where
Self: DeserializeOwned,
{
let revision = revision
.into()
.unwrap_or(<Self as VersionedSerialise>::CURRENT_SERIALISATION_REVISION);
match revision {
1 => Self::try_unpack_current(b),
_ => Err(Error::UnknownSerializationRevision { revision }),
}
}
}
@@ -177,7 +177,7 @@ impl IssuanceTicketBook {
}
// ideally this would have been generic over credential type, but we really don't need secp256k1 keys for bandwidth vouchers
pub async fn obtain_partial_ticketbook_credential(
pub async fn obtain_partial_bandwidth_voucher_credential(
&self,
client: &nym_validator_client::client::NymApiClient,
signer_index: u64,
@@ -191,6 +191,13 @@ impl IssuanceTicketBook {
self.unblind_signature(validator_vk, &signing_data, blinded_signature, signer_index)
}
// pub fn unchecked_aggregate_signature_shares(
// &self,
// shares: &[SignatureShare],
// ) -> Result<Signature, Error> {
// aggregate_signature_shares(shares).map_err(Error::SignatureAggregationError)
// }
pub fn aggregate_signature_shares(
&self,
verification_key: &VerificationKeyAuth,
@@ -1,13 +1,12 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ecash::bandwidth::importable::ImportableTicketBook;
use crate::ecash::bandwidth::serialiser::VersionedSerialise;
use crate::ecash::bandwidth::CredentialSpendingData;
use crate::ecash::utils::ecash_today;
use crate::error::Error;
use nym_credentials_interface::{
CoinIndexSignature, ExpirationDateSignature, NymPayInfo, PayInfo, SecretKeyUser, TicketType,
CoinIndexSignature, ExpirationDateSignature, PayInfo, SecretKeyUser, TicketType,
VerificationKeyAuth, Wallet, WalletSignatures,
};
use nym_ecash_time::EcashTime;
@@ -115,10 +114,6 @@ impl IssuedTicketBook {
&self.signatures_wallet
}
pub fn generate_pay_info(&self, provider_pk: [u8; 32]) -> NymPayInfo {
NymPayInfo::generate(provider_pk)
}
pub fn prepare_for_spending<BI, BE>(
&mut self,
verification_key: &VerificationKeyAuth,
@@ -158,10 +153,6 @@ impl IssuedTicketBook {
epoch_id: self.epoch_id,
})
}
pub fn begin_export(self) -> ImportableTicketBook {
self.into()
}
}
impl VersionedSerialise for IssuedTicketBook {
@@ -5,7 +5,6 @@ pub use issuance::IssuanceTicketBook;
pub use issued::IssuedTicketBook;
pub use nym_credentials_interface::{CredentialSigningData, CredentialSpendingData};
pub mod importable;
pub mod issuance;
pub mod issued;
pub mod serialiser;
@@ -5,33 +5,17 @@ use crate::ecash::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use crate::Error;
use bincode::Options;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use std::marker::PhantomData;
use zeroize::Zeroize;
pub mod keys;
pub mod signatures;
#[derive(Zeroize, Serialize, Deserialize)]
pub struct VersionSerialised<T: ?Sized> {
pub data: Vec<u8>,
pub revision: u8,
// still wondering if there's any point in having the phantom in here
#[zeroize(skip)]
#[serde(skip)]
_phantom: PhantomData<T>,
}
impl<T> VersionSerialised<T> {
pub fn try_unpack(&self) -> Result<T, Error>
where
T: VersionedSerialise + DeserializeOwned,
{
T::try_unpack(&self.data, self.revision)
}
}
pub trait VersionedSerialise {
const CURRENT_SERIALISATION_REVISION: u8;
@@ -1,35 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ecash::bandwidth::serialiser::VersionedSerialise;
use crate::Error;
use nym_credentials_interface::VerificationKeyAuth;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EpochVerificationKey {
pub epoch_id: u64,
pub key: VerificationKeyAuth,
}
impl VersionedSerialise for EpochVerificationKey {
// we start with revision 2 as the initial, 1, only contained the inner `key` field data
const CURRENT_SERIALISATION_REVISION: u8 = 2;
fn try_unpack(b: &[u8], revision: impl Into<Option<u8>>) -> Result<Self, Error>
where
Self: DeserializeOwned,
{
let revision = revision
.into()
.unwrap_or(<Self as VersionedSerialise>::CURRENT_SERIALISATION_REVISION);
match revision {
1 => Err(Error::UnsupportedSerializationRevision { revision }),
2 => Self::try_unpack_current(b),
_ => Err(Error::UnknownSerializationRevision { revision }),
}
}
}

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