Merge branch 'release/2024.10-caramello' into feature/2024.10-caramello-merge
This commit is contained in:
@@ -63,10 +63,6 @@ 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:
|
||||
|
||||
@@ -75,8 +75,6 @@ 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
|
||||
@@ -91,21 +89,21 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples --features wireguard
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Run all tests
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --features wireguard
|
||||
args: --workspace
|
||||
|
||||
- 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')
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --features wireguard -- --ignored
|
||||
args: --workspace -- --ignored
|
||||
|
||||
- name: Annotate with clippy checks
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
@@ -113,10 +111,10 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace --features wireguard
|
||||
args: --workspace
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets --features wireguard,axum -- -D warnings
|
||||
args: --workspace --all-targets --features axum -- -D warnings
|
||||
|
||||
@@ -51,10 +51,6 @@ 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:
|
||||
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
name: publish-nym-wallet-win10
|
||||
name: publish-nym-wallet-win11
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [windows10]
|
||||
platform: [custom-windows-11]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
@@ -62,6 +62,9 @@ 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
|
||||
@@ -4,6 +4,60 @@ 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
+482
-229
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.39"
|
||||
version = "1.1.40"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.39"
|
||||
version = "1.1.40"
|
||||
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"
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use crate::utils::{get_coin_index_signatures, get_expiration_date_signatures};
|
||||
use crate::utils::{
|
||||
get_aggregate_verification_key, get_coin_index_signatures, get_expiration_date_signatures,
|
||||
};
|
||||
use log::info;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::IssuanceTicketBook;
|
||||
@@ -55,7 +57,7 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn query_and_persist_required_global_signatures<S>(
|
||||
pub async fn query_and_persist_required_global_data<S>(
|
||||
storage: &S,
|
||||
epoch_id: EpochId,
|
||||
expiration_date: Date,
|
||||
@@ -65,6 +67,10 @@ 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?;
|
||||
|
||||
@@ -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, NymPayInfo, VerificationKeyAuth,
|
||||
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
|
||||
};
|
||||
use nym_ecash_time::Date;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
@@ -165,7 +165,9 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
.get_coin_index_signatures(epoch_id, &mut api_clients)
|
||||
.await?;
|
||||
|
||||
let pay_info = NymPayInfo::generate(provider_pk);
|
||||
let pay_info = retrieved_ticketbook
|
||||
.ticketbook
|
||||
.generate_pay_info(provider_pk);
|
||||
|
||||
let spend_request = retrieved_ticketbook.ticketbook.prepare_for_spending(
|
||||
&verification_key,
|
||||
|
||||
@@ -417,6 +417,8 @@ 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,6 +42,10 @@ 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
|
||||
@@ -580,6 +584,7 @@ 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,6 +31,15 @@ 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,
|
||||
@@ -760,6 +769,7 @@ 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,6 +1,7 @@
|
||||
// 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;
|
||||
@@ -10,7 +11,6 @@ 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,7 +21,8 @@ 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.
|
||||
pub fn find_attribute<'a>(
|
||||
#[deprecated]
|
||||
pub fn find_attribute_in_logs<'a>(
|
||||
logs: &'a [Log],
|
||||
event_type: &str,
|
||||
attribute_key: &str,
|
||||
@@ -35,6 +36,7 @@ pub fn find_attribute<'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,12 +1,24 @@
|
||||
// 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> {
|
||||
let event = tx.tx_result.events.iter().find(|e| e.kind == event_type)?;
|
||||
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 attribute = event.attributes.iter().find(|&attr| {
|
||||
if let Ok(key_str) = attr.key_str() {
|
||||
key_str == attribute_key
|
||||
@@ -16,3 +28,23 @@ pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str)
|
||||
})?;
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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"] }
|
||||
@@ -28,7 +29,7 @@ thiserror = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
time = { workspace = true, features = ["parsing", "formatting"] }
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
toml = "0.5.6"
|
||||
toml = { workspace = true }
|
||||
url = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// 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(())
|
||||
}
|
||||
@@ -10,9 +10,17 @@ 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;
|
||||
|
||||
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
|
||||
bs58::decode(raw).into_vec()
|
||||
#[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)]
|
||||
@@ -26,8 +34,8 @@ pub struct Args {
|
||||
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>>,
|
||||
#[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")]
|
||||
@@ -50,7 +58,7 @@ pub struct Args {
|
||||
impl Args {
|
||||
fn credential_data(self) -> anyhow::Result<Vec<u8>> {
|
||||
let data = match self.credential_data {
|
||||
Some(data) => data,
|
||||
Some(data) => data.0,
|
||||
None => {
|
||||
// SAFETY: one of those arguments must have been set
|
||||
#[allow(clippy::unwrap_used)]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
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;
|
||||
@@ -22,6 +23,7 @@ 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),
|
||||
|
||||
+2
-2
@@ -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_gateway_bonding_sign_payload;
|
||||
use nym_mixnet_contract_common::construct_legacy_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_gateway_bonding_sign_payload(nonce, address, coin, gateway);
|
||||
let payload = construct_legacy_gateway_bonding_sign_payload(nonce, address, coin, gateway);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use base64::Engine;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -11,7 +10,9 @@ pub struct Args {
|
||||
}
|
||||
|
||||
pub fn decode_mixnode_key(args: Args) {
|
||||
let b64_decoded = base64::prelude::BASE64_STANDARD
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
|
||||
let b64_decoded = STANDARD
|
||||
.decode(args.key)
|
||||
.expect("failed to decode base64 string");
|
||||
let b58_encoded = bs58::encode(&b64_decoded).into_string();
|
||||
|
||||
+4
-2
@@ -7,7 +7,9 @@ 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_mixnode_bonding_sign_payload, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{
|
||||
construct_legacy_mixnode_bonding_sign_payload, MixNodeCostParams,
|
||||
};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
@@ -98,7 +100,7 @@ pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
let address = account_id_to_cw_addr(&client.address());
|
||||
|
||||
let payload =
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
|
||||
construct_legacy_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
+43
-6
@@ -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::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, 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 10, rather than 0.1)"
|
||||
help = "input your profit margin as follows; (so it would be 20, rather than 0.2)"
|
||||
)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
|
||||
@@ -27,11 +27,48 @@ 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: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
)
|
||||
.unwrap(),
|
||||
profit_margin_percent: profit_margin_value,
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
|
||||
@@ -248,3 +248,31 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ 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,6 +5,7 @@ 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)]
|
||||
@@ -12,6 +13,9 @@ pub enum MixnetContractError {
|
||||
#[error("could not perform contract migration: {comment}")]
|
||||
FailedMigration { comment: String },
|
||||
|
||||
#[error(transparent)]
|
||||
Admin(#[from] AdminError),
|
||||
|
||||
#[error("{source}")]
|
||||
StdErr {
|
||||
#[from]
|
||||
|
||||
@@ -110,6 +110,11 @@ impl InitialRewardingParams {
|
||||
|
||||
#[cw_serde]
|
||||
pub enum ExecuteMsg {
|
||||
/// Change the admin
|
||||
UpdateAdmin {
|
||||
admin: String,
|
||||
},
|
||||
|
||||
AssignNodeLayer {
|
||||
mix_id: MixId,
|
||||
layer: Layer,
|
||||
@@ -292,6 +297,7 @@ 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:?}")
|
||||
}
|
||||
@@ -408,6 +414,9 @@ 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,13 +4,18 @@
|
||||
use crate::families::FamilyHead;
|
||||
use crate::{Gateway, IdentityKey, MixNode, MixNodeCostParams};
|
||||
use contracts_common::signing::{
|
||||
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
|
||||
ContractMessageContent, LegacyContractMessageContent, 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)]
|
||||
@@ -47,6 +52,20 @@ 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,
|
||||
@@ -76,6 +95,19 @@ 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,7 +187,11 @@ impl Index<Layer> for LayerDistribution {
|
||||
#[cw_serde]
|
||||
pub struct ContractState {
|
||||
/// Address of the contract owner.
|
||||
pub owner: Addr,
|
||||
#[deprecated(
|
||||
note = "use explicit ADMIN instead. this field will be removed in future release"
|
||||
)]
|
||||
#[serde(default)]
|
||||
pub owner: Option<Addr>,
|
||||
|
||||
/// Address of "rewarding validator" (nym-api) that's allowed to send any rewarding-related transactions.
|
||||
pub rewarding_validator_address: Addr,
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::acquire::{
|
||||
get_ticket_book, query_and_persist_required_global_signatures,
|
||||
};
|
||||
use nym_bandwidth_controller::acquire::{get_ticket_book, query_and_persist_required_global_data};
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_config::DEFAULT_DATA_DIR;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
@@ -45,14 +43,10 @@ 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 before attempting the deposit
|
||||
query_and_persist_required_global_signatures(
|
||||
storage,
|
||||
epoch_id,
|
||||
ticketbook_expiration,
|
||||
apis.clone(),
|
||||
)
|
||||
.await?;
|
||||
// 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?;
|
||||
|
||||
let issuance_data = nym_bandwidth_controller::acquire::make_deposit(
|
||||
client,
|
||||
|
||||
@@ -122,7 +122,7 @@ pub struct CredentialHandlerConfig {
|
||||
pub minimum_redemption_tickets: usize,
|
||||
|
||||
/// Specifies the maximum time between two subsequent tickets redemptions.
|
||||
/// That's required as nym-apis will purge all ticket information for tickets older than 30 days.
|
||||
/// That's required as nym-apis will purge all ticket information for tickets older than maximum validity.
|
||||
pub maximum_time_between_redemption: Duration,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::ecash::bandwidth::CredentialSpendingData;
|
||||
use crate::ecash::utils::ecash_today;
|
||||
use crate::error::Error;
|
||||
use nym_credentials_interface::{
|
||||
CoinIndexSignature, ExpirationDateSignature, PayInfo, SecretKeyUser, TicketType,
|
||||
CoinIndexSignature, ExpirationDateSignature, NymPayInfo, PayInfo, SecretKeyUser, TicketType,
|
||||
VerificationKeyAuth, Wallet, WalletSignatures,
|
||||
};
|
||||
use nym_ecash_time::EcashTime;
|
||||
@@ -115,6 +115,10 @@ 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,
|
||||
|
||||
@@ -80,6 +80,15 @@ impl KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivateKey> for KeyPair {
|
||||
fn from(private_key: PrivateKey) -> Self {
|
||||
KeyPair {
|
||||
public_key: (&private_key).into(),
|
||||
private_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = PrivateKey;
|
||||
type PublicPemKey = PublicKey;
|
||||
|
||||
@@ -90,6 +90,15 @@ impl KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivateKey> for KeyPair {
|
||||
fn from(private_key: PrivateKey) -> Self {
|
||||
KeyPair {
|
||||
public_key: (&private_key).into(),
|
||||
private_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = PrivateKey;
|
||||
type PublicPemKey = PublicKey;
|
||||
|
||||
@@ -24,6 +24,7 @@ zeroize = { workspace = true }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
|
||||
@@ -25,6 +25,7 @@ impl<'a> ClientHandshake<'a> {
|
||||
identity: &'a nym_crypto::asymmetric::identity::KeyPair,
|
||||
gateway_pubkey: identity::PublicKey,
|
||||
expects_credential_usage: bool,
|
||||
#[cfg(not(target_arch = "wasm32"))] shutdown: nym_task::TaskClient,
|
||||
) -> Self
|
||||
where
|
||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
|
||||
@@ -35,6 +36,8 @@ impl<'a> ClientHandshake<'a> {
|
||||
identity,
|
||||
Some(gateway_pubkey),
|
||||
expects_credential_usage,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown,
|
||||
);
|
||||
|
||||
ClientHandshake {
|
||||
|
||||
@@ -25,6 +25,8 @@ pub enum HandshakeError {
|
||||
MalformedRequest,
|
||||
#[error("sent request was malformed")]
|
||||
HandshakeFailure,
|
||||
#[error("received shutdown")]
|
||||
ReceivedShutdown,
|
||||
|
||||
#[error("timed out waiting for a handshake message")]
|
||||
Timeout,
|
||||
|
||||
@@ -8,6 +8,7 @@ use futures::future::BoxFuture;
|
||||
use futures::task::{Context, Poll};
|
||||
use futures::{Future, Sink, Stream};
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::pin::Pin;
|
||||
use tungstenite::Message as WsMessage;
|
||||
@@ -22,11 +23,12 @@ impl<'a> GatewayHandshake<'a> {
|
||||
ws_stream: &'a mut S,
|
||||
identity: &'a nym_crypto::asymmetric::identity::KeyPair,
|
||||
received_init_payload: Vec<u8>,
|
||||
shutdown: TaskClient,
|
||||
) -> Self
|
||||
where
|
||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
|
||||
{
|
||||
let mut state = State::new(rng, ws_stream, identity, None, true);
|
||||
let mut state = State::new(rng, ws_stream, identity, None, true, shutdown);
|
||||
GatewayHandshake {
|
||||
handshake_future: Box::pin(async move {
|
||||
// If any step along the way failed (that are non-network related),
|
||||
|
||||
@@ -8,6 +8,8 @@ use self::gateway::GatewayHandshake;
|
||||
pub use self::shared_key::{SharedKeySize, SharedKeys};
|
||||
use futures::{Sink, Stream};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use tungstenite::{Error as WsError, Message as WsMessage};
|
||||
|
||||
@@ -31,6 +33,7 @@ pub async fn client_handshake<'a, S>(
|
||||
identity: &'a identity::KeyPair,
|
||||
gateway_pubkey: identity::PublicKey,
|
||||
expects_credential_usage: bool,
|
||||
#[cfg(not(target_arch = "wasm32"))] shutdown: TaskClient,
|
||||
) -> Result<SharedKeys, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
|
||||
@@ -41,6 +44,8 @@ where
|
||||
identity,
|
||||
gateway_pubkey,
|
||||
expects_credential_usage,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -51,11 +56,12 @@ pub async fn gateway_handshake<'a, S>(
|
||||
ws_stream: &'a mut S,
|
||||
identity: &'a identity::KeyPair,
|
||||
received_init_payload: Vec<u8>,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<SharedKeys, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
|
||||
{
|
||||
GatewayHandshake::new(rng, ws_stream, identity, received_init_payload).await
|
||||
GatewayHandshake::new(rng, ws_stream, identity, received_init_payload, shutdown).await
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -13,6 +13,8 @@ use nym_crypto::{
|
||||
symmetric::stream_cipher,
|
||||
};
|
||||
use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewaySharedKeyHkdfAlgorithm};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use tracing::log::*;
|
||||
|
||||
@@ -48,6 +50,10 @@ pub(crate) struct State<'a, S> {
|
||||
// this field is really out of place here, however, we need to propagate this information somehow
|
||||
// in order to establish correct protocol for backwards compatibility reasons
|
||||
expects_credential_usage: bool,
|
||||
|
||||
// channel to receive shutdown signal
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown: TaskClient,
|
||||
}
|
||||
|
||||
impl<'a, S> State<'a, S> {
|
||||
@@ -57,6 +63,7 @@ impl<'a, S> State<'a, S> {
|
||||
identity: &'a identity::KeyPair,
|
||||
remote_pubkey: Option<identity::PublicKey>,
|
||||
expects_credential_usage: bool,
|
||||
#[cfg(not(target_arch = "wasm32"))] shutdown: TaskClient,
|
||||
) -> Self {
|
||||
let ephemeral_keypair = encryption::KeyPair::new(rng);
|
||||
State {
|
||||
@@ -66,6 +73,8 @@ impl<'a, S> State<'a, S> {
|
||||
remote_pubkey,
|
||||
derived_shared_keys: None,
|
||||
expects_credential_usage,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,46 +208,76 @@ impl<'a, S> State<'a, S> {
|
||||
self.remote_pubkey = Some(remote_pubkey)
|
||||
}
|
||||
|
||||
fn on_wg_msg(msg: Option<WsItem>) -> Result<Option<Vec<u8>>, HandshakeError> {
|
||||
let Some(msg) = msg else {
|
||||
return Err(HandshakeError::ClosedStream);
|
||||
};
|
||||
|
||||
let Ok(msg) = msg else {
|
||||
return Err(HandshakeError::NetworkError);
|
||||
};
|
||||
match msg {
|
||||
WsMessage::Text(ref ws_msg) => {
|
||||
match types::RegistrationHandshake::from_str(ws_msg) {
|
||||
Ok(reg_handshake_msg) => {
|
||||
match reg_handshake_msg {
|
||||
// hehe, that's a bit disgusting that the type system requires we explicitly ignore the
|
||||
// protocol_version field that we actually never attach at this point
|
||||
// yet another reason for the overdue refactor
|
||||
types::RegistrationHandshake::HandshakePayload { data, .. } => {
|
||||
Ok(Some(data))
|
||||
}
|
||||
types::RegistrationHandshake::HandshakeError { message } => {
|
||||
Err(HandshakeError::RemoteError(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Received a non-handshake message during the registration handshake! It's getting dropped. The received content was: '{msg}'");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Received non-text message during registration handshake");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn _receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Unpin,
|
||||
{
|
||||
loop {
|
||||
let Some(msg) = self.ws_stream.next().await else {
|
||||
return Err(HandshakeError::ClosedStream);
|
||||
};
|
||||
|
||||
let Ok(msg) = msg else {
|
||||
return Err(HandshakeError::NetworkError);
|
||||
};
|
||||
|
||||
match msg {
|
||||
WsMessage::Text(ref ws_msg) => {
|
||||
match types::RegistrationHandshake::from_str(ws_msg) {
|
||||
Ok(reg_handshake_msg) => {
|
||||
return match reg_handshake_msg {
|
||||
// hehe, that's a bit disgusting that the type system requires we explicitly ignore the
|
||||
// protocol_version field that we actually never attach at this point
|
||||
// yet another reason for the overdue refactor
|
||||
types::RegistrationHandshake::HandshakePayload { data, .. } => {
|
||||
Ok(data)
|
||||
}
|
||||
types::RegistrationHandshake::HandshakeError { message } => {
|
||||
Err(HandshakeError::RemoteError(message))
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Received a non-handshake message during the registration handshake! It's getting dropped. The received content was: '{msg}'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.shutdown.recv() => return Err(HandshakeError::ReceivedShutdown),
|
||||
msg = self.ws_stream.next() => {
|
||||
let Some(ret) = Self::on_wg_msg(msg)? else {
|
||||
continue;
|
||||
};
|
||||
return Ok(ret);
|
||||
}
|
||||
_ => error!("Received non-text message during registration handshake"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn _receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Unpin,
|
||||
{
|
||||
loop {
|
||||
let msg = self.ws_stream.next().await;
|
||||
let Some(ret) = Self::on_wg_msg(msg)? else {
|
||||
continue;
|
||||
};
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Unpin,
|
||||
|
||||
@@ -10,8 +10,8 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bincode = { workspace = true, optional = true }
|
||||
defguard_wireguard_rs = { workspace = true, optional = true }
|
||||
bincode = { workspace = true }
|
||||
defguard_wireguard_rs = { workspace = true }
|
||||
log = { workspace = true }
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
@@ -36,6 +36,3 @@ sqlx = { workspace = true, features = [
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
|
||||
[features]
|
||||
wireguard = ["defguard_wireguard_rs", "bincode"]
|
||||
|
||||
@@ -25,7 +25,6 @@ mod inboxes;
|
||||
pub mod models;
|
||||
mod shared_keys;
|
||||
mod tickets;
|
||||
#[cfg(feature = "wireguard")]
|
||||
mod wireguard_peers;
|
||||
|
||||
#[async_trait]
|
||||
@@ -217,7 +216,6 @@ pub trait Storage: Send + Sync {
|
||||
///
|
||||
/// * `peer`: wireguard peer data to be stored
|
||||
/// * `suspended`: if peer exists, but it's currently suspended
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn insert_wireguard_peer(
|
||||
&self,
|
||||
peer: &defguard_wireguard_rs::host::Peer,
|
||||
@@ -229,14 +227,12 @@ pub trait Storage: Send + Sync {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `peer_public_key`: wireguard public key of the peer to be retrieved.
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn get_wireguard_peer(
|
||||
&self,
|
||||
peer_public_key: &str,
|
||||
) -> Result<Option<WireguardPeer>, StorageError>;
|
||||
|
||||
/// Retrieves all wireguard peers.
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, StorageError>;
|
||||
|
||||
/// Remove a wireguard peer from the storage.
|
||||
@@ -244,7 +240,6 @@ pub trait Storage: Send + Sync {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `peer_public_key`: wireguard public key of the peer to be removed.
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn remove_wireguard_peer(&self, peer_public_key: &str) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
@@ -255,7 +250,6 @@ pub struct PersistentStorage {
|
||||
inbox_manager: InboxManager,
|
||||
bandwidth_manager: BandwidthManager,
|
||||
ticket_manager: TicketStorageManager,
|
||||
#[cfg(feature = "wireguard")]
|
||||
wireguard_peer_manager: wireguard_peers::WgPeerManager,
|
||||
}
|
||||
|
||||
@@ -300,7 +294,6 @@ impl PersistentStorage {
|
||||
|
||||
// the cloning here are cheap as connection pool is stored behind an Arc
|
||||
Ok(PersistentStorage {
|
||||
#[cfg(feature = "wireguard")]
|
||||
wireguard_peer_manager: wireguard_peers::WgPeerManager::new(connection_pool.clone()),
|
||||
shared_key_manager: SharedKeysManager::new(connection_pool.clone()),
|
||||
inbox_manager: InboxManager::new(connection_pool.clone(), message_retrieval_limit),
|
||||
@@ -620,7 +613,6 @@ impl Storage for PersistentStorage {
|
||||
Ok(self.ticket_manager.get_epoch_signers(epoch_id).await?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn insert_wireguard_peer(
|
||||
&self,
|
||||
peer: &defguard_wireguard_rs::host::Peer,
|
||||
@@ -632,7 +624,6 @@ impl Storage for PersistentStorage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn get_wireguard_peer(
|
||||
&self,
|
||||
peer_public_key: &str,
|
||||
@@ -644,13 +635,11 @@ impl Storage for PersistentStorage {
|
||||
Ok(peer)
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn get_all_wireguard_peers(&self) -> Result<Vec<WireguardPeer>, StorageError> {
|
||||
let ret = self.wireguard_peer_manager.retrieve_all_peers().await?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn remove_wireguard_peer(&self, peer_public_key: &str) -> Result<(), StorageError> {
|
||||
self.wireguard_peer_manager
|
||||
.remove_peer(peer_public_key)
|
||||
|
||||
@@ -72,7 +72,6 @@ impl TryFrom<UnverifiedTicketData> for ClientTicket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
pub struct WireguardPeer {
|
||||
pub public_key: String,
|
||||
@@ -87,7 +86,6 @@ pub struct WireguardPeer {
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
impl From<defguard_wireguard_rs::host::Peer> for WireguardPeer {
|
||||
fn from(value: defguard_wireguard_rs::host::Peer) -> Self {
|
||||
WireguardPeer {
|
||||
@@ -120,7 +118,6 @@ impl From<defguard_wireguard_rs::host::Peer> for WireguardPeer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
impl TryFrom<WireguardPeer> for defguard_wireguard_rs::host::Peer {
|
||||
type Error = crate::error::StorageError;
|
||||
|
||||
|
||||
@@ -40,9 +40,17 @@ impl SharedKeysManager {
|
||||
client_address_bs58: String,
|
||||
derived_aes128_ctr_blake3_hmac_keys_bs58: String,
|
||||
) -> Result<i64, sqlx::Error> {
|
||||
sqlx::query!("INSERT OR REPLACE INTO shared_keys(client_address_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58) VALUES (?, ?)",
|
||||
// https://stackoverflow.com/a/20310838
|
||||
// we don't want to be using `INSERT OR REPLACE INTO` due to the foreign key on `available_bandwidth` if the entry already exists
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT OR IGNORE INTO shared_keys(client_address_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58) VALUES (?, ?);
|
||||
UPDATE shared_keys SET derived_aes128_ctr_blake3_hmac_keys_bs58 = ? WHERE client_address_bs58 = ?
|
||||
"#,
|
||||
client_address_bs58,
|
||||
derived_aes128_ctr_blake3_hmac_keys_bs58,
|
||||
derived_aes128_ctr_blake3_hmac_keys_bs58,
|
||||
client_address_bs58,
|
||||
).execute(&self.connection_pool).await?;
|
||||
|
||||
self.client_id(&client_address_bs58).await
|
||||
|
||||
@@ -26,8 +26,17 @@ impl WgPeerManager {
|
||||
/// * `peer`: peer information needed by wireguard interface.
|
||||
pub(crate) async fn insert_peer(&self, peer: &WireguardPeer) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT OR REPLACE INTO wireguard_peer(public_key, preshared_key, protocol_version, endpoint, last_handshake, tx_bytes, rx_bytes, persistent_keepalive_interval, allowed_ips, suspended) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
peer.public_key, peer.preshared_key, peer.protocol_version, peer.endpoint, peer.last_handshake, peer.tx_bytes, peer.rx_bytes, peer.persistent_keepalive_interval, peer.allowed_ips, peer.suspended
|
||||
r#"
|
||||
INSERT OR IGNORE INTO wireguard_peer(public_key, preshared_key, protocol_version, endpoint, last_handshake, tx_bytes, rx_bytes, persistent_keepalive_interval, allowed_ips, suspended)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
|
||||
UPDATE wireguard_peer
|
||||
SET preshared_key = ?, protocol_version = ?, endpoint = ?, last_handshake = ?, tx_bytes = ?, rx_bytes = ?, persistent_keepalive_interval = ?, allowed_ips = ?, suspended = ?
|
||||
WHERE public_key = ?
|
||||
"#,
|
||||
peer.public_key, peer.preshared_key, peer.protocol_version, peer.endpoint, peer.last_handshake, peer.tx_bytes, peer.rx_bytes, peer.persistent_keepalive_interval, peer.allowed_ips, peer.suspended,
|
||||
|
||||
peer.preshared_key, peer.protocol_version, peer.endpoint, peer.last_handshake, peer.tx_bytes, peer.rx_bytes, peer.persistent_keepalive_interval, peer.allowed_ips, peer.suspended,peer.public_key,
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
@@ -17,8 +17,8 @@ pub const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
|
||||
pub const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
|
||||
pub const ECASH_CONTRACT_ADDRESS: &str = "";
|
||||
pub const ECASH_CONTRACT_ADDRESS: &str =
|
||||
"n1r7s6aksyc6pqardx88k3rkgfagwvj4z4zum9mmz2sfk3zm2mha0sd4dnun";
|
||||
pub const GROUP_CONTRACT_ADDRESS: &str =
|
||||
"n1e2zq4886zzewpvpucmlw8v9p7zv692f6yck4zjzxh699dkcmlrfqk2knsr";
|
||||
pub const MULTISIG_CONTRACT_ADDRESS: &str =
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use futures::{future::pending, FutureExt, SinkExt, StreamExt};
|
||||
use log::{log, Level};
|
||||
use std::future::Future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{error::Error, time::Duration};
|
||||
use tokio::sync::{
|
||||
@@ -368,17 +367,6 @@ impl TaskClient {
|
||||
self.named(name)
|
||||
}
|
||||
|
||||
pub async fn run_future<Fut, T>(&mut self, fut: Fut) -> Option<T>
|
||||
where
|
||||
Fut: Future<Output = T>,
|
||||
{
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.recv() => None,
|
||||
res = fut => Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a dummy that will never report that we should shutdown.
|
||||
pub fn dummy() -> TaskClient {
|
||||
let (_notify_tx, notify_rx) = watch::channel(());
|
||||
|
||||
@@ -23,7 +23,7 @@ x25519-dalek = { workspace = true }
|
||||
ip_network = { workspace = true }
|
||||
log.workspace = true
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
nym-gateway-storage = { path = "../gateway-storage", features = ["wireguard"] }
|
||||
nym-gateway-storage = { path = "../gateway-storage" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-task = { path = "../task" }
|
||||
nym-wireguard-types = { path = "../wireguard-types" }
|
||||
|
||||
@@ -61,14 +61,19 @@ impl<St: Storage> PeerController<St> {
|
||||
let timeout_check_interval = tokio_stream::wrappers::IntervalStream::new(
|
||||
tokio::time::interval(DEFAULT_PEER_TIMEOUT_CHECK),
|
||||
);
|
||||
let active_peers = peers
|
||||
let active_peers: HashMap<Key, Peer> = peers
|
||||
.into_iter()
|
||||
.map(|peer| (peer.public_key.clone(), peer))
|
||||
.collect();
|
||||
let suspended_peers = suspended_peers
|
||||
let suspended_peers: HashMap<Key, Peer> = suspended_peers
|
||||
.into_iter()
|
||||
.map(|peer| (peer.public_key.clone(), peer))
|
||||
.collect();
|
||||
let last_seen_bandwidth = active_peers
|
||||
.iter()
|
||||
.map(|(k, p)| (k.clone(), p.rx_bytes + p.tx_bytes))
|
||||
.chain(suspended_peers.keys().map(|k| (k.clone(), 0)))
|
||||
.collect();
|
||||
|
||||
PeerController {
|
||||
storage,
|
||||
@@ -78,7 +83,7 @@ impl<St: Storage> PeerController<St> {
|
||||
timeout_check_interval,
|
||||
active_peers,
|
||||
suspended_peers,
|
||||
last_seen_bandwidth: HashMap::new(),
|
||||
last_seen_bandwidth,
|
||||
timeout_count: 0,
|
||||
}
|
||||
}
|
||||
@@ -199,6 +204,7 @@ impl<St: Storage> PeerController<St> {
|
||||
log::error!("Could not configure peer: {:?}", e);
|
||||
false
|
||||
} else {
|
||||
self.last_seen_bandwidth.insert(peer.public_key.clone(), peer.rx_bytes + peer.tx_bytes);
|
||||
self.active_peers.insert(peer.public_key.clone(), peer);
|
||||
true
|
||||
};
|
||||
|
||||
Generated
+2
@@ -1276,6 +1276,7 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"nym-contracts-common",
|
||||
@@ -1295,6 +1296,7 @@ dependencies = [
|
||||
"bs58 0.5.1",
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw2",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
|
||||
@@ -34,6 +34,7 @@ cosmwasm-schema = { workspace = true, optional = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cosmwasm-derive = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
|
||||
|
||||
@@ -177,6 +177,28 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Change the admin",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"update_admin"
|
||||
],
|
||||
"properties": {
|
||||
"update_admin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -1692,6 +1714,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the list of families registered in this contract.",
|
||||
"type": "object",
|
||||
@@ -2937,6 +2972,21 @@
|
||||
},
|
||||
"sudo": null,
|
||||
"responses": {
|
||||
"admin": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AdminResponse",
|
||||
"description": "Returned from Admin.query_admin()",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"get_all_delegations": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedAllDelegationsResponse",
|
||||
@@ -8140,7 +8190,6 @@
|
||||
"description": "The current state of the mixnet contract.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"owner",
|
||||
"params",
|
||||
"rewarding_denom",
|
||||
"rewarding_validator_address",
|
||||
@@ -8149,9 +8198,14 @@
|
||||
"properties": {
|
||||
"owner": {
|
||||
"description": "Address of the contract owner.",
|
||||
"allOf": [
|
||||
"default": null,
|
||||
"deprecated": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Change the admin",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"update_admin"
|
||||
],
|
||||
"properties": {
|
||||
"update_admin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the list of families registered in this contract.",
|
||||
"type": "object",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AdminResponse",
|
||||
"description": "Returned from Admin.query_admin()",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
"description": "The current state of the mixnet contract.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"owner",
|
||||
"params",
|
||||
"rewarding_denom",
|
||||
"rewarding_validator_address",
|
||||
@@ -13,9 +12,14 @@
|
||||
"properties": {
|
||||
"owner": {
|
||||
"description": "Address of the contract owner.",
|
||||
"allOf": [
|
||||
"default": null,
|
||||
"deprecated": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -57,6 +57,7 @@ pub const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
|
||||
pub const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
|
||||
pub const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
|
||||
|
||||
pub const ADMIN_STORAGE_KEY: &str = "admin";
|
||||
pub const CONTRACT_STATE_KEY: &str = "state";
|
||||
|
||||
pub const LAYER_DISTRIBUTION_KEY: &str = "layers";
|
||||
|
||||
@@ -28,8 +28,10 @@ fn default_initial_state(
|
||||
profit_margin: ProfitMarginRange,
|
||||
interval_operating_cost: OperatingCostRange,
|
||||
) -> ContractState {
|
||||
// we have to temporarily preserve this functionalities until it can be removed
|
||||
#[allow(deprecated)]
|
||||
ContractState {
|
||||
owner,
|
||||
owner: Some(owner),
|
||||
rewarding_validator_address,
|
||||
vesting_contract_address,
|
||||
rewarding_denom: rewarding_denom.clone(),
|
||||
@@ -56,7 +58,7 @@ fn default_initial_state(
|
||||
/// `msg` is the contract initialization message, sort of like a constructor call.
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
deps: DepsMut<'_>,
|
||||
mut deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
@@ -72,7 +74,7 @@ pub fn instantiate(
|
||||
let rewarding_validator_address = deps.api.addr_validate(&msg.rewarding_validator_address)?;
|
||||
let vesting_contract_address = deps.api.addr_validate(&msg.vesting_contract_address)?;
|
||||
let state = default_initial_state(
|
||||
info.sender,
|
||||
info.sender.clone(),
|
||||
rewarding_validator_address.clone(),
|
||||
msg.rewarding_denom,
|
||||
vesting_contract_address,
|
||||
@@ -90,7 +92,7 @@ pub fn instantiate(
|
||||
starting_interval,
|
||||
rewarding_validator_address,
|
||||
)?;
|
||||
mixnet_params_storage::initialise_storage(deps.storage, state)?;
|
||||
mixnet_params_storage::initialise_storage(deps.branch(), state, info.sender)?;
|
||||
mixnode_storage::initialise_storage(deps.storage)?;
|
||||
rewards_storage::initialise_storage(deps.storage, reward_params)?;
|
||||
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
@@ -108,6 +110,11 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::UpdateAdmin { admin } => {
|
||||
crate::mixnet_contract_settings::transactions::try_update_contract_admin(
|
||||
deps, info, admin,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::AssignNodeLayer { mix_id, layer } => {
|
||||
crate::mixnodes::transactions::assign_mixnode_layer(deps, info, mix_id, layer)
|
||||
}
|
||||
@@ -335,6 +342,9 @@ pub fn query(
|
||||
QueryMsg::GetState {} => {
|
||||
to_binary(&crate::mixnet_contract_settings::queries::query_contract_state(deps)?)
|
||||
}
|
||||
QueryMsg::Admin {} => to_binary(&crate::mixnet_contract_settings::queries::query_admin(
|
||||
deps,
|
||||
)?),
|
||||
QueryMsg::GetRewardingParams {} => {
|
||||
to_binary(&crate::rewards::queries::query_rewarding_params(deps)?)
|
||||
}
|
||||
@@ -528,13 +538,15 @@ pub fn query(
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(
|
||||
deps: DepsMut<'_>,
|
||||
mut deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
msg: MigrateMsg,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
set_build_information!(deps.storage)?;
|
||||
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
crate::queued_migrations::explicit_contract_admin(deps.branch())?;
|
||||
|
||||
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
|
||||
// and vesting contract requiring the mixnet contract address), if we ever want to deploy any new fresh
|
||||
// environment, one of the contracts will HAVE TO go through a migration
|
||||
@@ -592,8 +604,9 @@ mod tests {
|
||||
let res = instantiate(deps.as_mut(), env, sender, init_msg);
|
||||
assert!(res.is_ok());
|
||||
|
||||
#[allow(deprecated)]
|
||||
let expected_state = ContractState {
|
||||
owner: Addr::unchecked("sender"),
|
||||
owner: Some(Addr::unchecked("sender")),
|
||||
rewarding_validator_address: Addr::unchecked("foomp123"),
|
||||
vesting_contract_address: Addr::unchecked("bar456"),
|
||||
rewarding_denom: "uatom".into(),
|
||||
|
||||
@@ -5,7 +5,9 @@ use crate::signing::storage as signing_storage;
|
||||
use crate::support::helpers::decode_ed25519_identity_key;
|
||||
use cosmwasm_std::{Addr, Coin, Deps};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::{construct_gateway_bonding_sign_payload, Gateway};
|
||||
use mixnet_contract_common::{
|
||||
construct_gateway_bonding_sign_payload, construct_legacy_gateway_bonding_sign_payload, Gateway,
|
||||
};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_contracts_common::signing::Verifier;
|
||||
|
||||
@@ -19,13 +21,31 @@ pub(crate) fn verify_gateway_bonding_signature(
|
||||
// recover the public key
|
||||
let public_key = decode_ed25519_identity_key(&gateway.identity_key)?;
|
||||
|
||||
// reconstruct the payload
|
||||
// reconstruct the payload, first try the current format, then attempt legacy
|
||||
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
|
||||
let msg = construct_gateway_bonding_sign_payload(nonce, sender, pledge, gateway);
|
||||
let msg = construct_gateway_bonding_sign_payload(
|
||||
nonce,
|
||||
sender.clone(),
|
||||
pledge.clone(),
|
||||
gateway.clone(),
|
||||
);
|
||||
|
||||
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||
if deps
|
||||
.api
|
||||
.verify_message(msg, signature.clone(), &public_key)?
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixnetContractError::InvalidEd25519Signature)
|
||||
// attempt to use legacy
|
||||
let msg_legacy =
|
||||
construct_legacy_gateway_bonding_sign_payload(nonce, sender, pledge, gateway);
|
||||
if deps
|
||||
.api
|
||||
.verify_message(msg_legacy, signature, &public_key)?
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixnetContractError::InvalidEd25519Signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ use super::storage;
|
||||
use crate::interval::helpers::change_interval_config;
|
||||
use crate::interval::pending_events::ContractExecutableEvent;
|
||||
use crate::interval::storage::push_new_interval_event;
|
||||
use crate::mixnet_contract_settings::storage::ADMIN;
|
||||
use crate::mixnodes::transactions::update_mixnode_layer;
|
||||
use crate::rewards;
|
||||
use crate::rewards::storage as rewards_storage;
|
||||
use crate::support::helpers::{
|
||||
ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_is_owner,
|
||||
ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_authorized,
|
||||
};
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Order, Response, Storage};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
@@ -325,7 +326,7 @@ pub(crate) fn try_update_interval_config(
|
||||
epoch_duration_secs: u64,
|
||||
force_immediately: bool,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_is_owner(info.sender, deps.storage)?;
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
if epochs_in_interval == 0 {
|
||||
return Err(MixnetContractError::EpochsInIntervalZero);
|
||||
@@ -1766,6 +1767,7 @@ mod tests {
|
||||
use super::*;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::Decimal;
|
||||
use cw_controllers::AdminError::NotAdmin;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
@@ -1831,11 +1833,11 @@ mod tests {
|
||||
1000,
|
||||
false,
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let res =
|
||||
try_update_interval_config(test.deps_mut(), env.clone(), random, 100, 1000, false);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let res = try_update_interval_config(test.deps_mut(), env, owner, 100, 1000, false);
|
||||
assert!(res.is_ok())
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use crate::mixnet_contract_settings::storage::ADMIN;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use cw_controllers::AdminResponse;
|
||||
use mixnet_contract_common::{ContractBuildInformation, ContractState, ContractStateParams};
|
||||
use nym_contracts_common::get_build_information;
|
||||
|
||||
pub(crate) fn query_admin(deps: Deps<'_>) -> StdResult<AdminResponse> {
|
||||
ADMIN.query_admin(deps)
|
||||
}
|
||||
|
||||
pub(crate) fn query_contract_state(deps: Deps<'_>) -> StdResult<ContractState> {
|
||||
storage::CONTRACT_STATE.load(deps.storage)
|
||||
}
|
||||
@@ -36,8 +42,9 @@ pub(crate) mod tests {
|
||||
fn query_for_contract_settings_works() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
#[allow(deprecated)]
|
||||
let dummy_state = ContractState {
|
||||
owner: Addr::unchecked("someowner"),
|
||||
owner: Some(Addr::unchecked("foomp")),
|
||||
rewarding_validator_address: Addr::unchecked("monitor"),
|
||||
vesting_contract_address: Addr::unchecked("foomp"),
|
||||
rewarding_denom: "unym".to_string(),
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::CONTRACT_STATE_KEY;
|
||||
use cosmwasm_std::{Addr, Storage};
|
||||
use crate::constants::{ADMIN_STORAGE_KEY, CONTRACT_STATE_KEY};
|
||||
use cosmwasm_std::{Addr, DepsMut, Storage};
|
||||
use cosmwasm_std::{Coin, StdResult};
|
||||
use cw_controllers::Admin;
|
||||
use cw_storage_plus::Item;
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::{ContractState, OperatingCostRange, ProfitMarginRange};
|
||||
|
||||
pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new(CONTRACT_STATE_KEY);
|
||||
pub(crate) const ADMIN: Admin = Admin::new(ADMIN_STORAGE_KEY);
|
||||
|
||||
pub fn rewarding_validator_address(storage: &dyn Storage) -> Result<Addr, MixnetContractError> {
|
||||
Ok(CONTRACT_STATE
|
||||
@@ -66,8 +68,10 @@ pub(crate) fn vesting_contract_address(storage: &dyn Storage) -> Result<Addr, Mi
|
||||
}
|
||||
|
||||
pub(crate) fn initialise_storage(
|
||||
storage: &mut dyn Storage,
|
||||
deps: DepsMut<'_>,
|
||||
initial_state: ContractState,
|
||||
initial_admin: Addr,
|
||||
) -> StdResult<()> {
|
||||
CONTRACT_STATE.save(storage, &initial_state)
|
||||
CONTRACT_STATE.save(deps.storage, &initial_state)?;
|
||||
ADMIN.set(deps, Some(initial_admin))
|
||||
}
|
||||
|
||||
@@ -2,15 +2,36 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use cosmwasm_std::DepsMut;
|
||||
use crate::mixnet_contract_settings::storage::ADMIN;
|
||||
use cosmwasm_std::MessageInfo;
|
||||
use cosmwasm_std::Response;
|
||||
use cosmwasm_std::{DepsMut, StdResult};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::events::{
|
||||
new_rewarding_validator_address_update_event, new_settings_update_event,
|
||||
};
|
||||
use mixnet_contract_common::ContractStateParams;
|
||||
|
||||
pub fn try_update_contract_admin(
|
||||
mut deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
new_admin: String,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let new_admin = deps.api.addr_validate(&new_admin)?;
|
||||
|
||||
let res = ADMIN.execute_update_admin(deps.branch(), info, Some(new_admin.clone()))?;
|
||||
|
||||
// SAFETY: we don't need to perform any authentication checks on the sender as it was performed
|
||||
// during 'execute_update_admin' call
|
||||
#[allow(deprecated)]
|
||||
storage::CONTRACT_STATE.update(deps.storage, |mut state| -> StdResult<_> {
|
||||
state.owner = Some(new_admin);
|
||||
Ok(state)
|
||||
})?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn try_update_rewarding_validator_address(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
@@ -18,9 +39,7 @@ pub fn try_update_rewarding_validator_address(
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let mut state = storage::CONTRACT_STATE.load(deps.storage)?;
|
||||
|
||||
if info.sender != state.owner {
|
||||
return Err(MixnetContractError::Unauthorized);
|
||||
}
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
let new_address = deps.api.addr_validate(&address)?;
|
||||
let old_address = state.rewarding_validator_address;
|
||||
@@ -43,10 +62,7 @@ pub(crate) fn try_update_contract_settings(
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let mut state = storage::CONTRACT_STATE.load(deps.storage)?;
|
||||
|
||||
// check if this is executed by the owner, if not reject the transaction
|
||||
if info.sender != state.owner {
|
||||
return Err(MixnetContractError::Unauthorized);
|
||||
}
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
let response = Response::new().add_event(new_settings_update_event(&state.params, ¶ms));
|
||||
|
||||
@@ -65,6 +81,7 @@ pub mod tests {
|
||||
use crate::support::tests::test_helpers;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::{Addr, Coin, Uint128};
|
||||
use cw_controllers::AdminError::NotAdmin;
|
||||
|
||||
#[test]
|
||||
fn update_contract_rewarding_validtor_address() {
|
||||
@@ -76,7 +93,7 @@ pub mod tests {
|
||||
info,
|
||||
"not-the-creator".to_string(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let info = mock_info("creator", &[]);
|
||||
let res = try_update_rewarding_validator_address(
|
||||
@@ -136,7 +153,7 @@ pub mod tests {
|
||||
// cannot be updated from non-owner account
|
||||
let info = mock_info("not-the-creator", &[]);
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
// but works fine from the creator account
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
@@ -5,7 +5,10 @@ use crate::signing::storage as signing_storage;
|
||||
use crate::support::helpers::decode_ed25519_identity_key;
|
||||
use cosmwasm_std::{Addr, Coin, Deps};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode, MixNodeCostParams};
|
||||
use mixnet_contract_common::{
|
||||
construct_legacy_mixnode_bonding_sign_payload, construct_mixnode_bonding_sign_payload, MixNode,
|
||||
MixNodeCostParams,
|
||||
};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_contracts_common::signing::Verifier;
|
||||
|
||||
@@ -20,13 +23,37 @@ pub(crate) fn verify_mixnode_bonding_signature(
|
||||
// recover the public key
|
||||
let public_key = decode_ed25519_identity_key(&mixnode.identity_key)?;
|
||||
|
||||
// reconstruct the payload
|
||||
// reconstruct the payload, first try the current format, then attempt legacy
|
||||
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
|
||||
let msg = construct_mixnode_bonding_sign_payload(nonce, sender, pledge, mixnode, cost_params);
|
||||
let msg = construct_mixnode_bonding_sign_payload(
|
||||
nonce,
|
||||
sender.clone(),
|
||||
pledge.clone(),
|
||||
mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
);
|
||||
|
||||
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||
if deps
|
||||
.api
|
||||
.verify_message(msg, signature.clone(), &public_key)?
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixnetContractError::InvalidEd25519Signature)
|
||||
// attempt to use legacy
|
||||
let msg_legacy = construct_legacy_mixnode_bonding_sign_payload(
|
||||
nonce,
|
||||
sender,
|
||||
pledge,
|
||||
mixnode,
|
||||
cost_params,
|
||||
);
|
||||
if deps
|
||||
.api
|
||||
.verify_message(msg_legacy, signature, &public_key)?
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixnetContractError::InvalidEd25519Signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,20 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use cosmwasm_std::DepsMut;
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
|
||||
pub(crate) fn explicit_contract_admin(deps: DepsMut) -> Result<(), MixnetContractError> {
|
||||
// we need to read the deprecated field to migrate it over
|
||||
#[allow(deprecated)]
|
||||
// SAFETY: this value should ALWAYS exist on the first execution of this migration;
|
||||
// as a matter of fact, it should ALWAYS continue existing until another migration
|
||||
#[allow(clippy::expect_used)]
|
||||
let existing_admin = mixnet_params_storage::CONTRACT_STATE
|
||||
.load(deps.storage)?
|
||||
.owner
|
||||
.expect("the contract state is corrupt - there's no admin set");
|
||||
mixnet_params_storage::ADMIN.set(deps, Some(existing_admin))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ use crate::delegations::storage as delegations_storage;
|
||||
use crate::interval::storage as interval_storage;
|
||||
use crate::interval::storage::{push_new_epoch_event, push_new_interval_event};
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use crate::mixnet_contract_settings::storage::ADMIN;
|
||||
use crate::mixnodes::helpers::get_mixnode_details_by_owner;
|
||||
use crate::mixnodes::storage as mixnodes_storage;
|
||||
use crate::rewards::helpers;
|
||||
use crate::rewards::helpers::update_and_save_last_rewarded;
|
||||
use crate::support::helpers::{
|
||||
ensure_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_owner,
|
||||
AttachSendTokens,
|
||||
ensure_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state, AttachSendTokens,
|
||||
};
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
@@ -227,7 +227,7 @@ pub(crate) fn try_update_active_set_size(
|
||||
active_set_size: u32,
|
||||
force_immediately: bool,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_is_owner(info.sender, deps.storage)?;
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
let mut rewarding_params = storage::REWARDING_PARAMS.load(deps.storage)?;
|
||||
if active_set_size == 0 {
|
||||
@@ -273,7 +273,7 @@ pub(crate) fn try_update_rewarding_params(
|
||||
updated_params: IntervalRewardingParamsUpdate,
|
||||
force_immediately: bool,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_is_owner(info.sender, deps.storage)?;
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
if !updated_params.contains_updates() {
|
||||
return Err(MixnetContractError::EmptyParamsChangeMsg);
|
||||
@@ -1796,6 +1796,7 @@ pub mod tests {
|
||||
|
||||
#[cfg(test)]
|
||||
mod updating_active_set {
|
||||
use cw_controllers::AdminError::NotAdmin;
|
||||
use mixnet_contract_common::EpochStatus;
|
||||
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
@@ -1858,10 +1859,10 @@ pub mod tests {
|
||||
42,
|
||||
false,
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let res = try_update_active_set_size(test.deps_mut(), env.clone(), random, 42, false);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let res = try_update_active_set_size(test.deps_mut(), env, owner, 42, false);
|
||||
assert!(res.is_ok())
|
||||
@@ -2002,6 +2003,7 @@ pub mod tests {
|
||||
#[cfg(test)]
|
||||
mod updating_rewarding_params {
|
||||
use cosmwasm_std::Decimal;
|
||||
use cw_controllers::AdminError::NotAdmin;
|
||||
|
||||
use mixnet_contract_common::EpochStatus;
|
||||
|
||||
@@ -2085,11 +2087,11 @@ pub mod tests {
|
||||
update,
|
||||
false,
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let res =
|
||||
try_update_rewarding_params(test.deps_mut(), env.clone(), random, update, false);
|
||||
assert_eq!(res, Err(MixnetContractError::Unauthorized));
|
||||
assert_eq!(res, Err(MixnetContractError::Admin(NotAdmin {})));
|
||||
|
||||
let res = try_update_rewarding_params(test.deps_mut(), env, owner, update, false);
|
||||
assert!(res.is_ok())
|
||||
|
||||
@@ -194,20 +194,6 @@ pub(crate) fn ensure_can_advance_epoch(
|
||||
Ok(epoch_status)
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_is_owner(
|
||||
sender: Addr,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
if sender
|
||||
!= crate::mixnet_contract_settings::storage::CONTRACT_STATE
|
||||
.load(storage)?
|
||||
.owner
|
||||
{
|
||||
return Err(MixnetContractError::Unauthorized);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_bonded(bond: &MixNodeBond) -> Result<(), MixnetContractError> {
|
||||
if bond.is_unbonding {
|
||||
return Err(MixnetContractError::MixnodeIsUnbonding {
|
||||
|
||||
@@ -105,10 +105,11 @@ pub mod test_helpers {
|
||||
let deps = init_contract();
|
||||
let rewarding_validator_address =
|
||||
rewarding_validator_address(deps.as_ref().storage).unwrap();
|
||||
let owner = mixnet_params_storage::CONTRACT_STATE
|
||||
.load(deps.as_ref().storage)
|
||||
let owner = mixnet_params_storage::ADMIN
|
||||
.query_admin(deps.as_ref())
|
||||
.unwrap()
|
||||
.owner;
|
||||
.admin
|
||||
.unwrap();
|
||||
|
||||
TestSetup {
|
||||
deps,
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@ CONFIGURED=true
|
||||
|
||||
RUST_LOG=info
|
||||
RUST_BACKTRACE=1
|
||||
NETWORK_NAME=sandbox
|
||||
NETWORK_NAME=canary
|
||||
BECH32_PREFIX=n
|
||||
MIX_DENOM=unym
|
||||
MIX_DENOM_DISPLAY=nym
|
||||
@@ -12,7 +12,8 @@ DENOMS_EXPONENT=6
|
||||
|
||||
REWARDING_VALIDATOR_ADDRESS=n1duuyj2th2y0z4u4f4wtljpdz9s3pxtu0xx6zdz
|
||||
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
|
||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1mf6ptkssddfmxvhdx0ech0k03ktp6kf9yk59renau2gvht3nq2gqt5tdrk
|
||||
VESTING_CONTRACT_ADDRESS=n1780emnrt7v9uqx5txhpxc0z8zfawq0czjmtd4q2maz83cckwjlfsx45u6c
|
||||
ECASH_CONTRACT_ADDRESS=n14jf8r2kxlymgjkp3xj8xuyud7h974648qdn9tsgsj0pascmntt9s3lvlwm
|
||||
GROUP_CONTRACT_ADDRESS=n1qg5ega6dykkxc307y25pecuufrjkxkaggkkxh7nad0vhyhtuhw3sa07c47
|
||||
MULTISIG_CONTRACT_ADDRESS=n1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqx5a364
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n1aakfpghcanxtc45gpqlx8j3rq0zcpyf49qmhm9mdjrfx036h4z5sy2vfh9
|
||||
|
||||
@@ -15,6 +15,7 @@ DENOMS_EXPONENT=6
|
||||
MIXNET_CONTRACT_ADDRESS=n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr
|
||||
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
|
||||
GROUP_CONTRACT_ADDRESS=n1e2zq4886zzewpvpucmlw8v9p7zv692f6yck4zjzxh699dkcmlrfqk2knsr
|
||||
ECASH_CONTRACT_ADDRESS=n1r7s6aksyc6pqardx88k3rkgfagwvj4z4zum9mmz2sfk3zm2mha0sd4dnun
|
||||
MULTISIG_CONTRACT_ADDRESS=n1txayqfz5g9qww3rlflpg025xd26m9payz96u54x4fe3s2ktz39xqk67gzx
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n19604yflqggs9mk2z26mqygq43q2kr3n932egxx630svywd5mpxjsztfpvx
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@ DENOMS_EXPONENT=6
|
||||
REWARDING_VALIDATOR_ADDRESS=n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa
|
||||
MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
|
||||
VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz
|
||||
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq746x3uffylqkjar4l
|
||||
GROUP_CONTRACT_ADDRESS=n1pd7kfgvr5tpcv0xnlv46c4jsq9jg2r799xxrcwqdm4l2jhq2pjwqrmz5ju
|
||||
MULTISIG_CONTRACT_ADDRESS=n14ph4e660eyqz0j36zlkaey4zgzexm5twkmjlqaequxr2cjm9eprqsmad6k
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a
|
||||
ECASH_CONTRACT_ADDRESS=n1v3vydvs2ued84yv3khqwtgldmgwn0elljsdh08dr5s2j9x4rc5fs9jlwz9
|
||||
GROUP_CONTRACT_ADDRESS=n1ewmwz97xm0h8rdk8sw7h9mwn866qkx9hl9zlmagqfkhuzvwk5hhq844ue9
|
||||
MULTISIG_CONTRACT_ADDRESS=n1tz0setr8vkh9udp8xyxgpqc89ns27k4d0jx2h942hr0ax63yjhmqz6xct8
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n1v3n2ly2dp3a9ng3ff6rh26yfkn0pc5hed7w2shc5u9ca5c865utqj5elvh
|
||||
|
||||
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.38"
|
||||
version = "1.1.39"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
+2
-7
@@ -88,10 +88,10 @@ nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
|
||||
nym-wireguard = { path = "../common/wireguard", optional = true }
|
||||
nym-wireguard = { path = "../common/wireguard" }
|
||||
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
|
||||
|
||||
defguard_wireguard_rs = { workspace = true, optional = true }
|
||||
defguard_wireguard_rs = { workspace = true }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
@@ -104,11 +104,6 @@ sqlx = { workspace = true, features = [
|
||||
] }
|
||||
|
||||
[features]
|
||||
wireguard = [
|
||||
"nym-wireguard",
|
||||
"defguard_wireguard_rs",
|
||||
"nym-gateway-storage/wireguard",
|
||||
]
|
||||
bin-deps = ["clap", 'nym-bin-common/output_format']
|
||||
|
||||
[package.metadata.deb]
|
||||
|
||||
@@ -10,7 +10,7 @@ use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
||||
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
||||
};
|
||||
use nym_network_defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT};
|
||||
use nym_network_defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT, TICKETBOOK_VALIDITY_DAYS};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
@@ -501,7 +501,7 @@ pub struct ZkNymTicketHandlerDebug {
|
||||
pub minimum_redemption_tickets: usize,
|
||||
|
||||
/// Specifies the maximum time between two subsequent tickets redemptions.
|
||||
/// That's required as nym-apis will purge all ticket information for tickets older than 30 days.
|
||||
/// That's required as nym-apis will purge all ticket information for tickets older than maximum validity.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_time_between_redemption: Duration,
|
||||
}
|
||||
@@ -511,7 +511,28 @@ impl ZkNymTicketHandlerDebug {
|
||||
pub const DEFAULT_PENDING_POLLER: Duration = Duration::from_secs(300);
|
||||
pub const DEFAULT_MINIMUM_API_QUORUM: f32 = 0.8;
|
||||
pub const DEFAULT_MINIMUM_REDEMPTION_TICKETS: usize = 100;
|
||||
pub const DEFAULT_MAXIMUM_TIME_BETWEEN_REDEMPTION: Duration = Duration::from_secs(86400 * 25);
|
||||
|
||||
// use min(4/5 of max validity, validity - 1), but making sure it's no greater than 1 day
|
||||
// ASSUMPTION: our validity period is AT LEAST 2 days
|
||||
//
|
||||
// this could have been a constant, but it's more readable as a function
|
||||
pub const fn default_maximum_time_between_redemption() -> Duration {
|
||||
let desired_secs = TICKETBOOK_VALIDITY_DAYS * (86400 * 4) / 5;
|
||||
let desired_secs_alt = (TICKETBOOK_VALIDITY_DAYS - 1) * 86400;
|
||||
|
||||
// can't use `min` in const context
|
||||
let target_secs = if desired_secs < desired_secs_alt {
|
||||
desired_secs
|
||||
} else {
|
||||
desired_secs_alt
|
||||
};
|
||||
|
||||
assert!(
|
||||
target_secs > 86400,
|
||||
"the maximum time between redemption can't be lower than 1 day!"
|
||||
);
|
||||
Duration::from_secs(target_secs as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ZkNymTicketHandlerDebug {
|
||||
@@ -521,7 +542,7 @@ impl Default for ZkNymTicketHandlerDebug {
|
||||
pending_poller: Self::DEFAULT_PENDING_POLLER,
|
||||
minimum_api_quorum: Self::DEFAULT_MINIMUM_API_QUORUM,
|
||||
minimum_redemption_tickets: Self::DEFAULT_MINIMUM_REDEMPTION_TICKETS,
|
||||
maximum_time_between_redemption: Self::DEFAULT_MAXIMUM_TIME_BETWEEN_REDEMPTION,
|
||||
maximum_time_between_redemption: Self::default_maximum_time_between_redemption(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,11 +189,12 @@ pub enum GatewayError {
|
||||
source: ipnetwork::IpNetworkError,
|
||||
},
|
||||
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
#[error("the current multisig contract is not using 'AbsolutePercentage' threshold!")]
|
||||
InvalidMultisigThreshold,
|
||||
|
||||
#[error("failed to remove wireguard interface: {0}")]
|
||||
WireguardInterfaceError(#[from] defguard_wireguard_rs::error::WireguardInterfaceError),
|
||||
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
#[error("internal wireguard error {0}")]
|
||||
InternalWireguardError(String),
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use nym_gateway_requests::{
|
||||
};
|
||||
use nym_mixnet_client::forwarder::MixForwardingSender;
|
||||
use nym_sphinx::DestinationAddressBytes;
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
@@ -111,6 +112,9 @@ pub(crate) enum InitialAuthenticationError {
|
||||
|
||||
#[error("failed to upgrade the client handler: {source}")]
|
||||
HandlerUpgradeFailure { source: RequestHandlingError },
|
||||
|
||||
#[error("received shutdown")]
|
||||
ReceivedShutdown,
|
||||
}
|
||||
|
||||
impl InitialAuthenticationError {
|
||||
@@ -127,6 +131,7 @@ pub(crate) struct FreshHandler<R, S, St> {
|
||||
pub(crate) outbound_mix_sender: MixForwardingSender,
|
||||
pub(crate) socket_connection: SocketStream<S>,
|
||||
pub(crate) peer_address: SocketAddr,
|
||||
pub(crate) shutdown: TaskClient,
|
||||
|
||||
// currently unused (but populated)
|
||||
pub(crate) negotiated_protocol: Option<u8>,
|
||||
@@ -149,6 +154,7 @@ where
|
||||
active_clients_store: ActiveClientsStore,
|
||||
shared_state: CommonHandlerState<St>,
|
||||
peer_address: SocketAddr,
|
||||
shutdown: TaskClient,
|
||||
) -> Self {
|
||||
FreshHandler {
|
||||
rng,
|
||||
@@ -158,6 +164,7 @@ where
|
||||
peer_address,
|
||||
negotiated_protocol: None,
|
||||
shared_state,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +208,7 @@ where
|
||||
ws_stream,
|
||||
self.shared_state.local_identity.as_ref(),
|
||||
init_msg,
|
||||
self.shutdown.clone(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -521,6 +529,11 @@ where
|
||||
/// * `client_address`: address of the client wishing to authenticate.
|
||||
/// * `encrypted_address`: ciphertext of the address of the client wishing to authenticate.
|
||||
/// * `iv`: fresh IV received with the request.
|
||||
#[instrument(skip_all
|
||||
fields(
|
||||
address = %address,
|
||||
)
|
||||
)]
|
||||
async fn handle_authenticate(
|
||||
&mut self,
|
||||
client_protocol_version: Option<u8>,
|
||||
@@ -531,6 +544,8 @@ where
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
debug!("handling client registration");
|
||||
|
||||
let negotiated_protocol = self.negotiate_client_protocol(client_protocol_version)?;
|
||||
// populate the negotiated protocol for future uses
|
||||
self.negotiated_protocol = Some(negotiated_protocol);
|
||||
@@ -650,6 +665,8 @@ where
|
||||
let remote_identity = Self::extract_remote_identity_from_register_init(&init_data)?;
|
||||
let remote_address = remote_identity.derive_destination_address();
|
||||
|
||||
debug!(remote_client = %remote_identity);
|
||||
|
||||
if self.active_clients_store.is_active(remote_address) {
|
||||
return Err(InitialAuthenticationError::DuplicateConnection);
|
||||
}
|
||||
@@ -657,6 +674,8 @@ where
|
||||
let shared_keys = self.perform_registration_handshake(init_data).await?;
|
||||
let client_id = self.register_client(remote_address, &shared_keys).await?;
|
||||
|
||||
debug!(client_id = %client_id, "managed to finalize client registration");
|
||||
|
||||
let client_details = ClientDetails::new(client_id, remote_address, shared_keys);
|
||||
|
||||
Ok(InitialAuthResult::new(
|
||||
@@ -716,111 +735,125 @@ where
|
||||
{
|
||||
trace!("Started waiting for authenticate/register request...");
|
||||
|
||||
while let Some(msg) = self.read_websocket_message().await {
|
||||
let msg = match msg {
|
||||
Ok(msg) => msg,
|
||||
Err(source) => {
|
||||
debug!("failed to obtain message from websocket stream! stopping connection handler: {source}");
|
||||
return Err(InitialAuthenticationError::FailedToReadMessage { source });
|
||||
}
|
||||
};
|
||||
let mut shutdown = self.shutdown.clone();
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = shutdown.recv() => {
|
||||
trace!("received shutdown signal while performing initial authentication");
|
||||
return Err(InitialAuthenticationError::ReceivedShutdown);
|
||||
},
|
||||
msg = self.read_websocket_message() => {
|
||||
let Some(msg) = msg else {
|
||||
break;
|
||||
};
|
||||
|
||||
if msg.is_close() {
|
||||
return Err(InitialAuthenticationError::CloseMessage);
|
||||
}
|
||||
let msg = match msg {
|
||||
Ok(msg) => msg,
|
||||
Err(source) => {
|
||||
debug!("failed to obtain message from websocket stream! stopping connection handler: {source}");
|
||||
return Err(InitialAuthenticationError::FailedToReadMessage { source });
|
||||
}
|
||||
};
|
||||
|
||||
// ONLY handle 'Authenticate' or 'Register' requests, ignore everything else
|
||||
match msg {
|
||||
// we have explicitly checked for close message
|
||||
Message::Close(_) => unreachable!(),
|
||||
Message::Text(text_msg) => {
|
||||
let (mix_sender, mix_receiver) = mpsc::unbounded();
|
||||
return match self.handle_initial_authentication_request(text_msg).await {
|
||||
Err(err) => {
|
||||
debug!("authentication failure: {err}");
|
||||
if msg.is_close() {
|
||||
return Err(InitialAuthenticationError::CloseMessage);
|
||||
}
|
||||
|
||||
// try to send error to the client
|
||||
if let Err(source) =
|
||||
self.send_websocket_message(err.to_error_message()).await
|
||||
// ONLY handle 'Authenticate' or 'Register' requests, ignore everything else
|
||||
match msg {
|
||||
// we have explicitly checked for close message
|
||||
Message::Close(_) => unreachable!(),
|
||||
Message::Text(text_msg) => {
|
||||
let (mix_sender, mix_receiver) = mpsc::unbounded();
|
||||
return match self.handle_initial_authentication_request(text_msg).await {
|
||||
Err(err) => {
|
||||
debug!("authentication failure: {err}");
|
||||
|
||||
// try to send error to the client
|
||||
if let Err(source) =
|
||||
self.send_websocket_message(err.to_error_message()).await
|
||||
{
|
||||
debug!("Failed to send authentication error response: {source}");
|
||||
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
|
||||
source,
|
||||
});
|
||||
}
|
||||
// return the underlying error
|
||||
Err(err)
|
||||
}
|
||||
Ok(auth_result) => {
|
||||
// try to send auth response back to the client
|
||||
if let Err(source) = self
|
||||
.send_websocket_message(auth_result.server_response.into())
|
||||
.await
|
||||
{
|
||||
debug!("Failed to send authentication response: {source}");
|
||||
return Err(InitialAuthenticationError::ResponseSendFailure {
|
||||
source,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(client_details) = auth_result.client_details {
|
||||
// Channel for handlers to ask other handlers if they are still active.
|
||||
let (is_active_request_sender, is_active_request_receiver) =
|
||||
mpsc::unbounded();
|
||||
self.active_clients_store.insert_remote(
|
||||
client_details.address,
|
||||
mix_sender,
|
||||
is_active_request_sender,
|
||||
);
|
||||
AuthenticatedHandler::upgrade(
|
||||
self,
|
||||
client_details,
|
||||
mix_receiver,
|
||||
is_active_request_receiver,
|
||||
)
|
||||
.await
|
||||
.map_err(|source| {
|
||||
InitialAuthenticationError::HandlerUpgradeFailure { source }
|
||||
})
|
||||
} else {
|
||||
// honestly, it's been so long I don't remember under what conditions its possible (if at all)
|
||||
// to have empty client details
|
||||
Err(InitialAuthenticationError::EmptyClientDetails)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Message::Binary(_) => {
|
||||
// perhaps logging level should be reduced here, let's leave it for now and see what happens
|
||||
// if client is working correctly, this should have never happened
|
||||
debug!("possibly received a sphinx packet without prior authentication. Request is going to be ignored");
|
||||
if let Err(source) = self
|
||||
.send_websocket_message(
|
||||
ServerResponse::new_error(
|
||||
"binary request without prior authentication",
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
debug!("Failed to send authentication error response: {source}");
|
||||
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
|
||||
source,
|
||||
});
|
||||
}
|
||||
// return the underlying error
|
||||
Err(err)
|
||||
return Err(InitialAuthenticationError::BinaryRequestWithoutAuthentication);
|
||||
}
|
||||
Ok(auth_result) => {
|
||||
// try to send auth response back to the client
|
||||
if let Err(source) = self
|
||||
.send_websocket_message(auth_result.server_response.into())
|
||||
.await
|
||||
{
|
||||
debug!("Failed to send authentication response: {source}");
|
||||
return Err(InitialAuthenticationError::ResponseSendFailure {
|
||||
source,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(client_details) = auth_result.client_details {
|
||||
// Channel for handlers to ask other handlers if they are still active.
|
||||
let (is_active_request_sender, is_active_request_receiver) =
|
||||
mpsc::unbounded();
|
||||
self.active_clients_store.insert_remote(
|
||||
client_details.address,
|
||||
mix_sender,
|
||||
is_active_request_sender,
|
||||
);
|
||||
AuthenticatedHandler::upgrade(
|
||||
self,
|
||||
client_details,
|
||||
mix_receiver,
|
||||
is_active_request_receiver,
|
||||
)
|
||||
.await
|
||||
.map_err(|source| {
|
||||
InitialAuthenticationError::HandlerUpgradeFailure { source }
|
||||
})
|
||||
} else {
|
||||
// honestly, it's been so long I don't remember under what conditions its possible (if at all)
|
||||
// to have empty client details
|
||||
Err(InitialAuthenticationError::EmptyClientDetails)
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
Message::Binary(_) => {
|
||||
// perhaps logging level should be reduced here, let's leave it for now and see what happens
|
||||
// if client is working correctly, this should have never happened
|
||||
debug!("possibly received a sphinx packet without prior authentication. Request is going to be ignored");
|
||||
if let Err(source) = self
|
||||
.send_websocket_message(
|
||||
ServerResponse::new_error(
|
||||
"binary request without prior authentication",
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
|
||||
source,
|
||||
});
|
||||
}
|
||||
return Err(InitialAuthenticationError::BinaryRequestWithoutAuthentication);
|
||||
}
|
||||
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Err(InitialAuthenticationError::ClosedConnection)
|
||||
}
|
||||
|
||||
pub(crate) async fn start_handling(self, shutdown: nym_task::TaskClient)
|
||||
pub(crate) async fn start_handling(self)
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin + Send,
|
||||
{
|
||||
super::handle_connection(self, shutdown).await
|
||||
super::handle_connection(self).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::config::Config;
|
||||
use fresh::InitialAuthenticationError;
|
||||
use nym_credential_verification::BandwidthFlushingBehaviourConfig;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::ServerResponse;
|
||||
use nym_gateway_storage::Storage;
|
||||
use nym_sphinx::DestinationAddressBytes;
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio_tungstenite::WebSocketStream;
|
||||
use tracing::{instrument, trace, warn};
|
||||
@@ -20,6 +21,8 @@ pub(crate) use self::fresh::FreshHandler;
|
||||
pub(crate) mod authenticated;
|
||||
mod fresh;
|
||||
|
||||
const WEBSOCKET_HANDSHAKE_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
|
||||
// TODO: note for my future self to consider the following idea:
|
||||
// split the socket connection into sink and stream
|
||||
// stream will be for reading explicit requests
|
||||
@@ -83,47 +86,47 @@ impl InitialAuthResult {
|
||||
|
||||
// imo there's no point in including the peer address in anything higher than debug
|
||||
#[instrument(level = "debug", skip_all, fields(peer = %handle.peer_address))]
|
||||
pub(crate) async fn handle_connection<R, S, St>(
|
||||
mut handle: FreshHandler<R, S, St>,
|
||||
mut shutdown: TaskClient,
|
||||
) where
|
||||
pub(crate) async fn handle_connection<R, S, St>(mut handle: FreshHandler<R, S, St>)
|
||||
where
|
||||
R: Rng + CryptoRng,
|
||||
S: AsyncRead + AsyncWrite + Unpin + Send,
|
||||
St: Storage + Clone + 'static,
|
||||
{
|
||||
// If the connection handler abruptly stops, we shouldn't signal global shutdown
|
||||
shutdown.disarm();
|
||||
handle.shutdown.disarm();
|
||||
|
||||
match shutdown
|
||||
.run_future(handle.perform_websocket_handshake())
|
||||
.await
|
||||
match tokio::time::timeout(
|
||||
WEBSOCKET_HANDSHAKE_TIMEOUT,
|
||||
handle.perform_websocket_handshake(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
None => {
|
||||
trace!("received shutdown signal while performing websocket handshake");
|
||||
Err(timeout_err) => {
|
||||
warn!("websocket handshake timedout: {timeout_err}");
|
||||
return;
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
Ok(Err(err)) => {
|
||||
warn!("Failed to complete WebSocket handshake: {err}. Stopping the handler");
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
trace!("Managed to perform websocket handshake!");
|
||||
|
||||
match shutdown
|
||||
.run_future(handle.perform_initial_authentication())
|
||||
.await
|
||||
{
|
||||
None => {
|
||||
trace!("received shutdown signal while performing initial authentication");
|
||||
return;
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
let shutdown = handle.shutdown.clone();
|
||||
match handle.perform_initial_authentication().await {
|
||||
// For storage error, we want to print the extended storage error, but without
|
||||
// including it in the error that's returned to the clients
|
||||
Err(InitialAuthenticationError::StorageError(err)) => {
|
||||
warn!("authentication has failed: {err}");
|
||||
return;
|
||||
}
|
||||
Some(Ok(auth_handle)) => auth_handle.listen_for_requests(shutdown).await,
|
||||
Err(err) => {
|
||||
warn!("authentication has failed: {err}");
|
||||
return;
|
||||
}
|
||||
Ok(auth_handle) => auth_handle.listen_for_requests(shutdown).await,
|
||||
}
|
||||
|
||||
trace!("The handler is done!");
|
||||
|
||||
@@ -54,6 +54,7 @@ where
|
||||
connection = tcp_listener.accept() => {
|
||||
match connection {
|
||||
Ok((socket, remote_addr)) => {
|
||||
let shutdown = shutdown.clone().named(format!("ClientConnectionHandler_{remote_addr}"));
|
||||
trace!("received a socket connection from {remote_addr}");
|
||||
// TODO: I think we *REALLY* need a mechanism for having a maximum number of connected
|
||||
// clients or spawned tokio tasks -> perhaps a worker system?
|
||||
@@ -64,9 +65,9 @@ where
|
||||
active_clients_store.clone(),
|
||||
self.shared_state.clone(),
|
||||
remote_addr,
|
||||
shutdown,
|
||||
);
|
||||
let shutdown = shutdown.clone().named(format!("ClientConnectionHandler_{remote_addr}"));
|
||||
tokio::spawn(async move { handle.start_handling(shutdown).await });
|
||||
tokio::spawn(handle.start_handling());
|
||||
}
|
||||
Err(err) => warn!("failed to get client: {err}"),
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ struct StartedNetworkRequester {
|
||||
// TODO: should this struct live here?
|
||||
#[allow(unused)]
|
||||
struct StartedAuthenticator {
|
||||
#[cfg(feature = "wireguard")]
|
||||
wg_api: Arc<nym_wireguard::WgApiWrapper>,
|
||||
|
||||
/// Handle to interact with the local authenticator
|
||||
@@ -146,7 +145,6 @@ pub struct Gateway<St = PersistentStorage> {
|
||||
|
||||
storage: St,
|
||||
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
wireguard_data: Option<nym_wireguard::WireguardData>,
|
||||
|
||||
run_http_server: bool,
|
||||
@@ -169,7 +167,6 @@ impl<St> Gateway<St> {
|
||||
network_requester_opts,
|
||||
ip_packet_router_opts,
|
||||
authenticator_opts: None,
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
wireguard_data: None,
|
||||
run_http_server: true,
|
||||
task_client: None,
|
||||
@@ -193,7 +190,6 @@ impl<St> Gateway<St> {
|
||||
identity_keypair,
|
||||
sphinx_keypair,
|
||||
storage,
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
wireguard_data: None,
|
||||
run_http_server: true,
|
||||
task_client: None,
|
||||
@@ -208,7 +204,6 @@ impl<St> Gateway<St> {
|
||||
self.task_client = Some(task_client)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
pub fn set_wireguard_data(&mut self, wireguard_data: nym_wireguard::WireguardData) {
|
||||
self.wireguard_data = Some(wireguard_data)
|
||||
}
|
||||
@@ -246,7 +241,7 @@ impl<St> Gateway<St> {
|
||||
mixnet_handling::Listener::new(listening_address, shutdown).start(connection_handler);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
#[cfg(target_os = "linux")]
|
||||
async fn start_authenticator(
|
||||
&mut self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
@@ -342,7 +337,7 @@ impl<St> Gateway<St> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wireguard", not(target_os = "linux")))]
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
async fn start_authenticator(
|
||||
&self,
|
||||
_forwarding_channel: MixForwardingSender,
|
||||
@@ -673,14 +668,15 @@ impl<St> Gateway<St> {
|
||||
info!("embedded ip packet router is disabled");
|
||||
};
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
let _wg_api = {
|
||||
let _wg_api = if self.wireguard_data.is_some() {
|
||||
let embedded_auth = self
|
||||
.start_authenticator(mix_forwarding_channel, shutdown.fork("authenticator"))
|
||||
.await
|
||||
.map_err(|source| GatewayError::AuthenticatorStartError { source })?;
|
||||
active_clients_store.insert_embedded(embedded_auth.handle);
|
||||
Some(embedded_auth.wg_api)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if self.run_http_server {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-api"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -16,8 +16,9 @@ use nym_coconut_dkg_common::types::{
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_dkg::Threshold;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::{find_attribute, NODE_INDEX};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::NODE_INDEX;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use nym_validator_client::nyxd::helpers::find_attribute_value_in_logs_or_events;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
|
||||
pub(crate) struct DkgClient {
|
||||
@@ -168,15 +169,15 @@ impl DkgClient {
|
||||
.inner
|
||||
.register_dealer(bte_key, identity_key, announce_address, resharing)
|
||||
.await?;
|
||||
let node_index = find_attribute(&res.logs, "wasm", NODE_INDEX)
|
||||
.ok_or(EcashError::NodeIndexRecoveryError {
|
||||
reason: String::from("node index not found"),
|
||||
})?
|
||||
.value
|
||||
.parse::<NodeIndex>()
|
||||
.map_err(|_| EcashError::NodeIndexRecoveryError {
|
||||
reason: String::from("node index could not be parsed"),
|
||||
})?;
|
||||
let node_index =
|
||||
find_attribute_value_in_logs_or_events(&res.logs, &res.events, "wasm", NODE_INDEX)
|
||||
.ok_or(EcashError::NodeIndexRecoveryError {
|
||||
reason: String::from("node index not found"),
|
||||
})?
|
||||
.parse::<NodeIndex>()
|
||||
.map_err(|_| EcashError::NodeIndexRecoveryError {
|
||||
reason: String::from("node index could not be parsed"),
|
||||
})?;
|
||||
|
||||
Ok(node_index)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ use nym_dkg::{
|
||||
bte::{self, decrypt_share},
|
||||
combine_shares, try_recover_verification_keys, Dealing,
|
||||
};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::{find_attribute, Log};
|
||||
use nym_validator_client::nyxd::Hash;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::Log;
|
||||
use nym_validator_client::nyxd::helpers::find_attribute_value_in_logs_or_events;
|
||||
use nym_validator_client::nyxd::{Event, Hash};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ops::Deref;
|
||||
@@ -453,25 +454,25 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
key: &VerificationKeyAuth,
|
||||
resharing: bool,
|
||||
) -> Result<u64, KeyDerivationError> {
|
||||
fn extract_proposal_id_from_logs(
|
||||
fn extract_proposal_id(
|
||||
logs: &[Log],
|
||||
events: &[Event],
|
||||
tx_hash: Hash,
|
||||
) -> Result<u64, KeyDerivationError> {
|
||||
let event_type = "wasm";
|
||||
let attribute_key = DKG_PROPOSAL_ID;
|
||||
let proposal_attribute = find_attribute(logs, event_type, attribute_key).ok_or(
|
||||
KeyDerivationError::MissingProposalIdAttribute {
|
||||
tx_hash,
|
||||
event_type: event_type.to_string(),
|
||||
attribute_key: attribute_key.to_string(),
|
||||
},
|
||||
)?;
|
||||
let proposal_value =
|
||||
find_attribute_value_in_logs_or_events(logs, events, event_type, attribute_key)
|
||||
.ok_or(KeyDerivationError::MissingProposalIdAttribute {
|
||||
tx_hash,
|
||||
event_type: event_type.to_string(),
|
||||
attribute_key: attribute_key.to_string(),
|
||||
})?;
|
||||
|
||||
proposal_attribute
|
||||
.value
|
||||
proposal_value
|
||||
.parse()
|
||||
.map_err(|_| KeyDerivationError::UnparsableProposalId {
|
||||
raw: proposal_attribute.value.clone(),
|
||||
raw: proposal_value.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -481,7 +482,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
.submit_verification_key_share(key.to_bs58(), resharing)
|
||||
.await?;
|
||||
let hash = res.transaction_hash;
|
||||
let proposal_id = extract_proposal_id_from_logs(&res.logs, hash)?;
|
||||
let proposal_id = extract_proposal_id(&res.logs, &res.events, hash)?;
|
||||
debug!("Submitted own verification key share, proposal id {proposal_id} is attached to it. tx hash: {hash}");
|
||||
|
||||
Ok(proposal_id)
|
||||
|
||||
@@ -56,7 +56,7 @@ pub(crate) async fn prepare_partial_bloomfilter_builder(
|
||||
.try_load_partial_bloomfilter_bitmap(date, params_id)
|
||||
.await?
|
||||
else {
|
||||
log::warn!("missing double spending bloomfilter bitmap for {date} (if this API hasn't been running for at least 30 days since 'ecash'-based zk-nyms were introduced this is expected)");
|
||||
log::warn!("missing double spending bloomfilter bitmap for {date} (if this API hasn't been running for at least {days} day(s) since 'ecash'-based zk-nyms were introduced this is expected)");
|
||||
continue;
|
||||
};
|
||||
if !filter_builder.add_bytes(&bitmap) {
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.1.6"
|
||||
version = "1.1.7"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -17,7 +17,7 @@ license = "GPL-3.0"
|
||||
anyhow.workspace = true
|
||||
bip39 = { workspace = true, features = ["zeroize"] }
|
||||
bs58.workspace = true
|
||||
celes = { workspace = true } # country codes
|
||||
celes = { workspace = true } # country codes
|
||||
colored = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "env"] }
|
||||
humantime-serde = { workspace = true }
|
||||
@@ -39,7 +39,10 @@ semver = { workspace = true }
|
||||
cupid = { workspace = true }
|
||||
sysinfo = { workspace = true }
|
||||
|
||||
nym-bin-common = { path = "../common/bin-common", features = ["basic_tracing", "output_format"] }
|
||||
nym-bin-common = { path = "../common/bin-common", features = [
|
||||
"basic_tracing",
|
||||
"output_format",
|
||||
] }
|
||||
nym-client-core-config-types = { path = "../common/client-core/config-types" }
|
||||
nym-config = { path = "../common/config" }
|
||||
nym-crypto = { path = "../common/crypto", features = ["asymmetric", "rand"] }
|
||||
@@ -62,6 +65,3 @@ nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
[build-dependencies]
|
||||
# temporary bonding information v1 (to grab and parse nym-mixnode and nym-gateway package versions)
|
||||
cargo_metadata = { workspace = true }
|
||||
|
||||
[features]
|
||||
wireguard = ["nym-gateway/wireguard"]
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::config::helpers::ephemeral_gateway_config;
|
||||
use crate::config::persistence::EntryGatewayPaths;
|
||||
use crate::config::Config;
|
||||
use crate::error::EntryGatewayError;
|
||||
use nym_config::defaults::DEFAULT_CLIENT_LISTENING_PORT;
|
||||
use nym_config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, TICKETBOOK_VALIDITY_DAYS};
|
||||
use nym_config::helpers::inaddr_any;
|
||||
use nym_config::serde_helpers::de_maybe_port;
|
||||
use nym_gateway::node::LocalAuthenticatorOpts;
|
||||
@@ -90,7 +90,7 @@ pub struct ZkNymTicketHandlerDebug {
|
||||
pub minimum_redemption_tickets: usize,
|
||||
|
||||
/// Specifies the maximum time between two subsequent tickets redemptions.
|
||||
/// That's required as nym-apis will purge all ticket information for tickets older than 30 days.
|
||||
/// That's required as nym-apis will purge all ticket information for tickets older than maximum validity.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_time_between_redemption: Duration,
|
||||
}
|
||||
@@ -100,7 +100,28 @@ impl ZkNymTicketHandlerDebug {
|
||||
pub const DEFAULT_PENDING_POLLER: Duration = Duration::from_secs(300);
|
||||
pub const DEFAULT_MINIMUM_API_QUORUM: f32 = 0.8;
|
||||
pub const DEFAULT_MINIMUM_REDEMPTION_TICKETS: usize = 100;
|
||||
pub const DEFAULT_MAXIMUM_TIME_BETWEEN_REDEMPTION: Duration = Duration::from_secs(86400 * 25);
|
||||
|
||||
// use min(4/5 of max validity, validity - 1), but making sure it's no greater than 1 day
|
||||
// ASSUMPTION: our validity period is AT LEAST 2 days
|
||||
//
|
||||
// this could have been a constant, but it's more readable as a function
|
||||
pub const fn default_maximum_time_between_redemption() -> Duration {
|
||||
let desired_secs = TICKETBOOK_VALIDITY_DAYS * (86400 * 4) / 5;
|
||||
let desired_secs_alt = (TICKETBOOK_VALIDITY_DAYS - 1) * 86400;
|
||||
|
||||
// can't use `min` in const context
|
||||
let target_secs = if desired_secs < desired_secs_alt {
|
||||
desired_secs
|
||||
} else {
|
||||
desired_secs_alt
|
||||
};
|
||||
|
||||
assert!(
|
||||
target_secs > 86400,
|
||||
"the maximum time between redemption can't be lower than 1 day!"
|
||||
);
|
||||
Duration::from_secs(target_secs as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ZkNymTicketHandlerDebug {
|
||||
@@ -110,7 +131,7 @@ impl Default for ZkNymTicketHandlerDebug {
|
||||
pending_poller: Self::DEFAULT_PENDING_POLLER,
|
||||
minimum_api_quorum: Self::DEFAULT_MINIMUM_API_QUORUM,
|
||||
minimum_redemption_tickets: Self::DEFAULT_MINIMUM_REDEMPTION_TICKETS,
|
||||
maximum_time_between_redemption: Self::DEFAULT_MAXIMUM_TIME_BETWEEN_REDEMPTION,
|
||||
maximum_time_between_redemption: Self::default_maximum_time_between_redemption(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,8 +580,9 @@ impl NymNode {
|
||||
);
|
||||
entry_gateway.disable_http_server();
|
||||
entry_gateway.set_task_client(task_client);
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
entry_gateway.set_wireguard_data(self.wireguard.into());
|
||||
if self.config.wireguard.enabled {
|
||||
entry_gateway.set_wireguard_data(self.wireguard.into());
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = entry_gateway.run().await {
|
||||
@@ -608,8 +609,9 @@ impl NymNode {
|
||||
);
|
||||
exit_gateway.disable_http_server();
|
||||
exit_gateway.set_task_client(task_client);
|
||||
#[cfg(all(feature = "wireguard", target_os = "linux"))]
|
||||
exit_gateway.set_wireguard_data(self.wireguard.into());
|
||||
if self.config.wireguard.enabled {
|
||||
exit_gateway.set_wireguard_data(self.wireguard.into());
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = exit_gateway.run().await {
|
||||
|
||||
Generated
+10
-9
@@ -3282,6 +3282,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"nym-contracts-common",
|
||||
@@ -3521,7 +3522,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.13"
|
||||
version = "1.2.14"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
@@ -4664,9 +4665,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.16"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
|
||||
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"indexmap 1.9.3",
|
||||
@@ -4677,14 +4678,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.16"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
|
||||
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.55",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4834,13 +4835,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.26.0"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
|
||||
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.55",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -15,14 +15,14 @@ pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n1hm4y6fzgxgu688jgf7ek66px6xkrtmn3gyk8fax3eawhp68c2d5qujz296";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1jlzdxnyces4hrhqz68dqk28mrw5jgwtcfq0c2funcwrmw0dx9l9s8nnnvj";
|
||||
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"n1w798gp0zqv3s9hjl3jlnwxtwhykga6rn93p46q2crsdqhaj3y4gs68f74j";
|
||||
pub(crate) const ECASH_CONTRACT_ADDRESS: &str =
|
||||
"n13xspq62y9gq6nueqmywxcdv2yep4p6nzv98w2889k25v3nhdy2dq2rkrk7";
|
||||
pub(crate) const GROUP_CONTRACT_ADDRESS: &str =
|
||||
"n1sthrn5ep8ls5vzz8f9gp89khhmedahhdqd244dh9uqzk3hx2pzrsvf7zgk";
|
||||
"n13l7rwuwktklrwskc7m6lv70zws07en85uma28j7dxwsz9y5hvvhspl7a2t";
|
||||
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str =
|
||||
"n1sr06m8yqg0wzqqyqvzvp5t07dj4nevx9u8qc7j4qa72qu8e3ct8qledthy";
|
||||
"n138c9pyf7f3hyx0j3t6vmsz7ultnw2wj0lu6hzndep9z5grgq9haqlc25k0";
|
||||
pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str =
|
||||
"n1udfs22xpxle475m2nz7u47jfa3vngncdegmczwwdx00cmetypa3s7uyuqn";
|
||||
"n1pk8jgr6y4c5k93gz7qf3xc0hvygmp7csk88c2tf8l39tkq6834wq2a6dtr";
|
||||
|
||||
// -- Constructor functions --
|
||||
|
||||
@@ -48,7 +48,7 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails {
|
||||
contracts: NymContracts {
|
||||
mixnet_contract_address: parse_optional_str(MIXNET_CONTRACT_ADDRESS),
|
||||
vesting_contract_address: parse_optional_str(VESTING_CONTRACT_ADDRESS),
|
||||
ecash_contract_address: parse_optional_str(COCONUT_BANDWIDTH_CONTRACT_ADDRESS),
|
||||
ecash_contract_address: parse_optional_str(ECASH_CONTRACT_ADDRESS),
|
||||
group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS),
|
||||
multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS),
|
||||
coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS),
|
||||
|
||||
@@ -15,14 +15,14 @@ pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz";
|
||||
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq746x3uffylqkjar4l";
|
||||
pub(crate) const ECASH_CONTRACT_ADDRESS: &str =
|
||||
"n1v3vydvs2ued84yv3khqwtgldmgwn0elljsdh08dr5s2j9x4rc5fs9jlwz9";
|
||||
pub(crate) const GROUP_CONTRACT_ADDRESS: &str =
|
||||
"n1pd7kfgvr5tpcv0xnlv46c4jsq9jg2r799xxrcwqdm4l2jhq2pjwqrmz5ju";
|
||||
"n1ewmwz97xm0h8rdk8sw7h9mwn866qkx9hl9zlmagqfkhuzvwk5hhq844ue9";
|
||||
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str =
|
||||
"n14ph4e660eyqz0j36zlkaey4zgzexm5twkmjlqaequxr2cjm9eprqsmad6k";
|
||||
"n1tz0setr8vkh9udp8xyxgpqc89ns27k4d0jx2h942hr0ax63yjhmqz6xct8";
|
||||
pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str =
|
||||
"n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a";
|
||||
"n1v3n2ly2dp3a9ng3ff6rh26yfkn0pc5hed7w2shc5u9ca5c865utqj5elvh";
|
||||
|
||||
// -- Constructor functions --
|
||||
|
||||
@@ -48,7 +48,7 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails {
|
||||
contracts: NymContracts {
|
||||
mixnet_contract_address: parse_optional_str(MIXNET_CONTRACT_ADDRESS),
|
||||
vesting_contract_address: parse_optional_str(VESTING_CONTRACT_ADDRESS),
|
||||
ecash_contract_address: parse_optional_str(COCONUT_BANDWIDTH_CONTRACT_ADDRESS),
|
||||
ecash_contract_address: parse_optional_str(ECASH_CONTRACT_ADDRESS),
|
||||
group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS),
|
||||
multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS),
|
||||
coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/nym-wallet-app",
|
||||
"version": "1.2.13",
|
||||
"version": "1.2.14",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.13"
|
||||
version = "1.2.14"
|
||||
description = "Nym Native Wallet"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
|
||||
@@ -9,8 +9,8 @@ use nym_contracts_common::signing::{
|
||||
};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::{
|
||||
construct_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode,
|
||||
MixNodeCostParams, SignableGatewayBondingMsg, SignableMixNodeBondingMsg,
|
||||
construct_legacy_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode,
|
||||
MixNodeCostParams, SignableGatewayBondingMsg, SignableLegacyMixNodeBondingMsg,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
@@ -42,14 +42,14 @@ pub(crate) async fn create_mixnode_bonding_sign_payload<P: AddressAndNonceProvid
|
||||
cost_params: MixNodeCostParams,
|
||||
pledge: Coin,
|
||||
vesting: bool,
|
||||
) -> Result<SignableMixNodeBondingMsg, BackendError> {
|
||||
) -> Result<SignableLegacyMixNodeBondingMsg, BackendError> {
|
||||
if vesting {
|
||||
return Err(BackendError::UnsupportedVestingOperation);
|
||||
}
|
||||
let sender = client.cw_address();
|
||||
let nonce = client.get_signing_nonce().await?;
|
||||
|
||||
Ok(construct_mixnode_bonding_sign_payload(
|
||||
Ok(construct_legacy_mixnode_bonding_sign_payload(
|
||||
nonce,
|
||||
sender,
|
||||
pledge.into(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-wallet",
|
||||
"version": "1.2.13"
|
||||
"version": "1.2.14"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -7,6 +7,7 @@ use nym_credential_utils::utils::issue_credential;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
use std::ops::Deref;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
/// Represents a client that can be used to acquire bandwidth. You typically create one when you
|
||||
@@ -17,7 +18,7 @@ use zeroize::Zeroizing;
|
||||
pub struct BandwidthAcquireClient<'a, St: Storage> {
|
||||
client: DirectSigningHttpRpcNyxdClient,
|
||||
storage: &'a St,
|
||||
client_id: Zeroizing<String>,
|
||||
client_id: Zeroizing<Vec<u8>>,
|
||||
ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
@@ -30,7 +31,7 @@ where
|
||||
network_details: NymNetworkDetails,
|
||||
mnemonic: String,
|
||||
storage: &'a St,
|
||||
client_id: String,
|
||||
client_id: Vec<u8>,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<Self> {
|
||||
let nyxd_url = network_details.endpoints[0].nyxd_url.as_str();
|
||||
@@ -53,7 +54,7 @@ where
|
||||
issue_credential(
|
||||
&self.client,
|
||||
self.storage,
|
||||
self.client_id.as_bytes(),
|
||||
self.client_id.deref(),
|
||||
self.ticketbook_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -38,6 +38,7 @@ use rand::rngs::OsRng;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use url::Url;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
// The number of surbs to include in a message by default
|
||||
const DEFAULT_NUMBER_OF_SURBS: u32 = 10;
|
||||
@@ -532,17 +533,20 @@ where
|
||||
if !self.config.enabled_credentials_mode {
|
||||
return Err(Error::DisabledCredentialsMode);
|
||||
}
|
||||
let client_id = self
|
||||
.storage
|
||||
.key_store()
|
||||
.load_keys()
|
||||
.await
|
||||
.map_err(|e| Error::KeyStorageError {
|
||||
source: Box::new(e),
|
||||
})?
|
||||
.identity_keypair()
|
||||
.private_key()
|
||||
.to_base58_string();
|
||||
let client_id_array = Zeroizing::new(
|
||||
self.storage
|
||||
.key_store()
|
||||
.load_keys()
|
||||
.await
|
||||
.map_err(|e| Error::KeyStorageError {
|
||||
source: Box::new(e),
|
||||
})?
|
||||
.identity_keypair()
|
||||
.private_key()
|
||||
.to_bytes(),
|
||||
);
|
||||
let client_id = client_id_array.to_vec();
|
||||
|
||||
BandwidthAcquireClient::new(
|
||||
self.config.network_details.clone(),
|
||||
mnemonic,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.40"
|
||||
version = "1.1.41"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version = "1.70"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.40"
|
||||
version = "1.1.41"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
@@ -4,10 +4,10 @@ use nym_network_defaults::NymNetworkDetails;
|
||||
|
||||
pub(crate) async fn execute(
|
||||
global_args: ClientArgs,
|
||||
coconut: nym_cli_commands::ecash::Ecash,
|
||||
ecash: nym_cli_commands::ecash::Ecash,
|
||||
network_details: &NymNetworkDetails,
|
||||
) -> anyhow::Result<()> {
|
||||
match coconut.command {
|
||||
match ecash.command {
|
||||
EcashCommands::IssueTicketBook(args) => {
|
||||
nym_cli_commands::ecash::issue_ticket_book::execute(
|
||||
args,
|
||||
@@ -25,6 +25,9 @@ pub(crate) async fn execute(
|
||||
EcashCommands::ImportTicketBook(args) => {
|
||||
nym_cli_commands::ecash::import_ticket_book::execute(args).await?
|
||||
}
|
||||
EcashCommands::GenerateTicket(args) => {
|
||||
nym_cli_commands::ecash::generate_ticket::execute(args).await?
|
||||
}
|
||||
EcashCommands::ImportCoinIndexSignatures(args) => {
|
||||
nym_cli_commands::ecash::import_coin_index_signatures::execute(args).await?
|
||||
}
|
||||
@@ -7,8 +7,8 @@ use nym_bin_common::logging::setup_logging;
|
||||
use nym_cli_commands::context::{get_network_details, ClientArgs};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
|
||||
mod coconut;
|
||||
mod completion;
|
||||
mod ecash;
|
||||
mod validator;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -104,7 +104,7 @@ async fn execute(cli: Cli) -> anyhow::Result<()> {
|
||||
Commands::Signature(signature) => {
|
||||
validator::signature::execute(signature, &network_details, mnemonic).await?
|
||||
}
|
||||
Commands::Ecash(coconut) => coconut::execute(args, coconut, &network_details).await?,
|
||||
Commands::Ecash(coconut) => ecash::execute(args, coconut, &network_details).await?,
|
||||
Commands::Block(block) => validator::block::execute(block, &network_details).await?,
|
||||
Commands::Cosmwasm(cosmwasm) => {
|
||||
validator::cosmwasm::execute(args, cosmwasm, &network_details).await?
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user