Merge pull request #5428 from nymtech/release/2025.2-hu
Merge release/2025.2-hu to develop
This commit is contained in:
@@ -4,6 +4,86 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2025.2-hu] (2025-02-04)
|
||||
|
||||
- Feature/remove double spending bloomfilter ([#5417])
|
||||
- HU - Downgrade harmless log message from info to debug ([#5405])
|
||||
- lower default ticket verification quorum to 0.7 ([#5404])
|
||||
- Downgrade harmless log message from info to debug ([#5403])
|
||||
- Redirect from mixnode page to nodes page ([#5397])
|
||||
- chore :update version of chain watcher and validator rewarder ([#5394])
|
||||
- bugfix: correctly handle ingore epoch roles flag ([#5390])
|
||||
- bugfix: terminate mixnet socket listener on shutdown ([#5389])
|
||||
- feat: make client ignore dual mode nodes by default ([#5388])
|
||||
- Handle ecash network errors differently ([#5378])
|
||||
- Remove empty ephemeral keys ([#5376])
|
||||
- fixed sql migration for adding default message timestamp ([#5374])
|
||||
- Bind to [::] on nym-node for both IP versions ([#5361])
|
||||
- exposed NymApiClient method for obtaining node performance history ([#5360])
|
||||
- Client gateway selection ([#5358])
|
||||
- chore: refresh wasm sdk ([#5353])
|
||||
- chore: update indexed_db_futures ([#5347])
|
||||
- build(deps): bump mikefarah/yq from 4.44.6 to 4.45.1 ([#5342])
|
||||
- updated cosmrs and tendermint-rpc to their most recent versions ([#5339])
|
||||
- build(deps): bump ts-rs from 10.0.0 to 10.1.0 ([#5338])
|
||||
- build(deps): bump tempfile from 3.14.0 to 3.15.0 ([#5337])
|
||||
- build(deps): bump the patch-updates group with 8 updates ([#5336])
|
||||
- feature: introduce /load endpoint for self-reported quantised NymNode load ([#5326])
|
||||
- feature: `CancellationToken`-based shutdowns ([#5325])
|
||||
- Use expect in geodata test to give error message on failure ([#5314])
|
||||
- feature: periodically remove stale gateway messages ([#5312])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 35 updates ([#5310])
|
||||
- Add dependabot assignes for the root cargo ecosystem ([#5297])
|
||||
- Move tun constants to network defaults ([#5286])
|
||||
- Include IPINFO_API_TOKEN in nightly CI ([#5285])
|
||||
- Nyx Chain Watcher ([#5274])
|
||||
- bugfix: remove unnecessary arguments for nym-api swagger endpoints ([#5272])
|
||||
- feature: nym topology revamp ([#5271])
|
||||
- Add windows to CI builds ([#5269])
|
||||
- http-api-client: deduplicate code ([#5267])
|
||||
- build(deps): bump http from 1.1.0 to 1.2.0 ([#5228])
|
||||
- NS API: add mixnet scraper ([#5200])
|
||||
- build(deps): bump criterion from 0.4.0 to 0.5.1 ([#4911])
|
||||
|
||||
[#5417]: https://github.com/nymtech/nym/pull/5417
|
||||
[#5405]: https://github.com/nymtech/nym/pull/5405
|
||||
[#5404]: https://github.com/nymtech/nym/pull/5404
|
||||
[#5403]: https://github.com/nymtech/nym/pull/5403
|
||||
[#5397]: https://github.com/nymtech/nym/pull/5397
|
||||
[#5394]: https://github.com/nymtech/nym/pull/5394
|
||||
[#5390]: https://github.com/nymtech/nym/pull/5390
|
||||
[#5389]: https://github.com/nymtech/nym/pull/5389
|
||||
[#5388]: https://github.com/nymtech/nym/pull/5388
|
||||
[#5378]: https://github.com/nymtech/nym/pull/5378
|
||||
[#5376]: https://github.com/nymtech/nym/pull/5376
|
||||
[#5374]: https://github.com/nymtech/nym/pull/5374
|
||||
[#5361]: https://github.com/nymtech/nym/pull/5361
|
||||
[#5360]: https://github.com/nymtech/nym/pull/5360
|
||||
[#5358]: https://github.com/nymtech/nym/pull/5358
|
||||
[#5353]: https://github.com/nymtech/nym/pull/5353
|
||||
[#5347]: https://github.com/nymtech/nym/pull/5347
|
||||
[#5342]: https://github.com/nymtech/nym/pull/5342
|
||||
[#5339]: https://github.com/nymtech/nym/pull/5339
|
||||
[#5338]: https://github.com/nymtech/nym/pull/5338
|
||||
[#5337]: https://github.com/nymtech/nym/pull/5337
|
||||
[#5336]: https://github.com/nymtech/nym/pull/5336
|
||||
[#5326]: https://github.com/nymtech/nym/pull/5326
|
||||
[#5325]: https://github.com/nymtech/nym/pull/5325
|
||||
[#5314]: https://github.com/nymtech/nym/pull/5314
|
||||
[#5312]: https://github.com/nymtech/nym/pull/5312
|
||||
[#5310]: https://github.com/nymtech/nym/pull/5310
|
||||
[#5297]: https://github.com/nymtech/nym/pull/5297
|
||||
[#5286]: https://github.com/nymtech/nym/pull/5286
|
||||
[#5285]: https://github.com/nymtech/nym/pull/5285
|
||||
[#5274]: https://github.com/nymtech/nym/pull/5274
|
||||
[#5272]: https://github.com/nymtech/nym/pull/5272
|
||||
[#5271]: https://github.com/nymtech/nym/pull/5271
|
||||
[#5269]: https://github.com/nymtech/nym/pull/5269
|
||||
[#5267]: https://github.com/nymtech/nym/pull/5267
|
||||
[#5228]: https://github.com/nymtech/nym/pull/5228
|
||||
[#5200]: https://github.com/nymtech/nym/pull/5200
|
||||
[#4911]: https://github.com/nymtech/nym/pull/4911
|
||||
|
||||
## [2025.1-reeses] (2025-01-15)
|
||||
|
||||
- Feture/legacy alert ([#5346])
|
||||
|
||||
Generated
+13
-48
@@ -721,12 +721,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.11.0"
|
||||
@@ -822,17 +816,6 @@ dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bloomfilter"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0bdbcf2078e0ba8a74e1fe0cf36f54054a04485759b61dfd60b174658e9607"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"getrandom",
|
||||
"siphasher 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.8.0"
|
||||
@@ -2425,7 +2408,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
@@ -4738,7 +4721,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.48"
|
||||
version = "1.1.49"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4747,7 +4730,6 @@ dependencies = [
|
||||
"axum-test",
|
||||
"bincode",
|
||||
"bip39",
|
||||
"bloomfilter",
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"clap",
|
||||
@@ -4778,7 +4760,6 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-dkg",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-double-spending",
|
||||
"nym-ecash-time",
|
||||
"nym-gateway-client",
|
||||
"nym-http-api-common",
|
||||
@@ -4988,7 +4969,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -5071,7 +5052,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -5491,7 +5472,6 @@ dependencies = [
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-double-spending",
|
||||
"nym-gateway-requests",
|
||||
"nym-gateway-storage",
|
||||
"nym-task",
|
||||
@@ -5608,15 +5588,6 @@ dependencies = [
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ecash-double-spending"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bloomfilter",
|
||||
"nym-network-defaults",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ecash-time"
|
||||
version = "0.1.0"
|
||||
@@ -6140,7 +6111,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.47"
|
||||
version = "1.1.48"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6191,7 +6162,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -6565,7 +6536,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -7008,7 +6979,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-validator-rewarder"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bip39",
|
||||
@@ -7170,7 +7141,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7200,7 +7171,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nyx-chain-watcher"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -7667,7 +7638,7 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
|
||||
dependencies = [
|
||||
"siphasher 0.3.11",
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7676,7 +7647,7 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||
dependencies = [
|
||||
"siphasher 0.3.11",
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9290,12 +9261,6 @@ version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@@ -11003,7 +10968,7 @@ checksum = "71dc8573a7b1ac4b71643d6da34888273ebfc03440c525121f1b3634ad3417a2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"siphasher 0.3.11",
|
||||
"siphasher",
|
||||
"uniffi_checksum_derive",
|
||||
]
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ members = [
|
||||
"common/credentials-interface",
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/ecash-double-spending",
|
||||
"common/ecash-time",
|
||||
"common/execute",
|
||||
"common/exit-policy",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
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.46"
|
||||
version = "1.1.47"
|
||||
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"
|
||||
|
||||
@@ -517,7 +517,7 @@ impl Default for Acknowledgements {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
#[serde(default)]
|
||||
pub struct Topology {
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
@@ -558,6 +558,10 @@ pub struct Topology {
|
||||
/// Specifies whether this client should ignore the current epoch role of the target egress node
|
||||
/// when constructing the final hop packets.
|
||||
pub ignore_egress_epoch_role: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
pub ignore_ingress_epoch_role: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@@ -595,7 +599,9 @@ impl Default for Topology {
|
||||
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
|
||||
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
|
||||
use_extended_topology: false,
|
||||
ignore_egress_epoch_role: false,
|
||||
|
||||
ignore_egress_epoch_role: true,
|
||||
ignore_ingress_epoch_role: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ where
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -175,6 +175,7 @@ where
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// TODO: combine those more closely. Perhaps into a single underlying store.
|
||||
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
|
||||
use crate::client::replies::reply_storage;
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
@@ -63,7 +65,7 @@ pub trait MixnetClientStorage {
|
||||
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone)]
|
||||
pub struct Ephemeral {
|
||||
key_store: InMemEphemeralKeys,
|
||||
reply_store: reply_storage::Empty,
|
||||
@@ -71,9 +73,14 @@ pub struct Ephemeral {
|
||||
gateway_details_store: InMemGatewaysDetails,
|
||||
}
|
||||
|
||||
impl Ephemeral {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
impl Default for Ephemeral {
|
||||
fn default() -> Self {
|
||||
Ephemeral {
|
||||
key_store: InMemEphemeralKeys::new(&mut OsRng),
|
||||
reply_store: Default::default(),
|
||||
credential_store: Default::default(),
|
||||
gateway_details_store: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::client::key_manager::ClientKeys;
|
||||
use async_trait::async_trait;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -195,9 +196,20 @@ impl KeyStore for OnDiskKeys {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone)]
|
||||
pub struct InMemEphemeralKeys {
|
||||
keys: Arc<Mutex<Option<ClientKeys>>>,
|
||||
keys: Arc<Mutex<ClientKeys>>,
|
||||
}
|
||||
|
||||
impl InMemEphemeralKeys {
|
||||
pub fn new<R>(rng: &mut R) -> Self
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
InMemEphemeralKeys {
|
||||
keys: Arc::new(Mutex::new(ClientKeys::generate_new(rng))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -210,11 +222,11 @@ impl KeyStore for InMemEphemeralKeys {
|
||||
type StorageError = EphemeralKeysError;
|
||||
|
||||
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
|
||||
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
|
||||
Ok(self.keys.lock().await.clone())
|
||||
}
|
||||
|
||||
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
|
||||
*self.keys.lock().await = Some(keys.clone());
|
||||
*self.keys.lock().await = keys.clone();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
@@ -113,7 +114,7 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
// (we don't want instability)
|
||||
let valid_gateways = gateways
|
||||
.iter()
|
||||
.filter(|g| !g.supported_roles.mixnode)
|
||||
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
|
||||
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -11,8 +11,7 @@ use crate::{
|
||||
use nym_api_requests::ecash::models::{
|
||||
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
||||
BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse,
|
||||
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, SpentCredentialsResponse,
|
||||
VerifyEcashTicketBody,
|
||||
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, VerifyEcashTicketBody,
|
||||
};
|
||||
use nym_api_requests::ecash::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
|
||||
@@ -647,13 +646,6 @@ impl NymApiClient {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
pub async fn spent_credentials_filter(
|
||||
&self,
|
||||
) -> Result<SpentCredentialsResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.double_spending_filter_v1().await?)
|
||||
}
|
||||
|
||||
pub async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
expiration_date: Option<Date>,
|
||||
|
||||
@@ -65,6 +65,12 @@ pub enum EcashApiError {
|
||||
#[from]
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("nym api error")]
|
||||
NymApi {
|
||||
#[from]
|
||||
source: crate::ValidatorClientError,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<ContractVKShare> for EcashApiClient {
|
||||
|
||||
@@ -850,20 +850,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn double_spending_filter_v1(&self) -> Result<SpentCredentialsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::ECASH_ROUTES,
|
||||
routes::DOUBLE_SPENDING_FILTER_V1,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
|
||||
@@ -13,8 +13,6 @@ pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
pub const DOUBLE_SPENDING_FILTER_V1: &str = "double-spending-filter-v1";
|
||||
|
||||
pub const ECASH_ROUTES: &str = "ecash";
|
||||
|
||||
pub use ecash::*;
|
||||
|
||||
@@ -26,7 +26,6 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-double-spending = { path = "../ecash-double-spending" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-gateway-storage = { path = "../gateway-storage" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
@@ -13,6 +13,7 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
|
||||
use nym_credentials_interface::Bandwidth;
|
||||
use nym_credentials_interface::{ClientTicket, TicketType};
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
|
||||
@@ -352,7 +353,9 @@ impl CredentialHandler {
|
||||
}
|
||||
Err(err) => {
|
||||
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
|
||||
Ok(false)
|
||||
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
|
||||
source: err,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "nym-ecash-double-spending"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bit-vec = { workspace = true }
|
||||
bloomfilter = { workspace = true }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bit_vec::BitVec;
|
||||
use bloomfilter::Bloom;
|
||||
use nym_network_defaults::{BloomfilterParameters, ECASH_DS_BLOOMFILTER_PARAMS};
|
||||
|
||||
pub struct DoubleSpendingFilter {
|
||||
params: BloomfilterParameters,
|
||||
inner: Bloom<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Default for DoubleSpendingFilter {
|
||||
fn default() -> Self {
|
||||
DoubleSpendingFilter::new_empty_ecash()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bloom_from_params<T>(params: &BloomfilterParameters, bitvec: BitVec) -> Bloom<Vec<T>> {
|
||||
assert_eq!(params.bitmap_size, bitvec.len() as u64);
|
||||
|
||||
Bloom::from_bit_vec(
|
||||
bitvec,
|
||||
params.bitmap_size,
|
||||
params.num_hashes,
|
||||
params.sip_keys,
|
||||
)
|
||||
}
|
||||
|
||||
impl DoubleSpendingFilter {
|
||||
pub fn new_empty(params: BloomfilterParameters) -> Self {
|
||||
let bitvec = BitVec::from_elem(params.bitmap_size as usize, false);
|
||||
DoubleSpendingFilter {
|
||||
inner: bloom_from_params(¶ms, bitvec),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn params(&self) -> BloomfilterParameters {
|
||||
self.params
|
||||
}
|
||||
|
||||
pub fn rebuild(&self) -> DoubleSpendingFilterBuilder {
|
||||
DoubleSpendingFilterBuilder::new(self.params)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
|
||||
pub fn new_empty_ecash() -> Self {
|
||||
DoubleSpendingFilter::new_empty(ECASH_DS_BLOOMFILTER_PARAMS)
|
||||
}
|
||||
|
||||
pub fn builder(params: BloomfilterParameters) -> DoubleSpendingFilterBuilder {
|
||||
DoubleSpendingFilterBuilder::new(params)
|
||||
}
|
||||
|
||||
pub fn from_bytes(params: BloomfilterParameters, bitmap: &[u8]) -> Self {
|
||||
DoubleSpendingFilter {
|
||||
inner: bloom_from_params(¶ms, BitVec::from_bytes(bitmap)),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_bitvec(&mut self, new: BitVec) {
|
||||
self.inner = bloom_from_params(&self.params, new)
|
||||
}
|
||||
|
||||
pub fn dump_bitmap(&self) -> Vec<u8> {
|
||||
self.inner.bitmap()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, b: &Vec<u8>) {
|
||||
self.inner.set(b);
|
||||
}
|
||||
|
||||
pub fn check(&self, b: &Vec<u8>) -> bool {
|
||||
self.inner.check(b)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DoubleSpendingFilterBuilder {
|
||||
params: BloomfilterParameters,
|
||||
bit_vec_builder: Option<BitVecBuilder>,
|
||||
}
|
||||
|
||||
impl DoubleSpendingFilterBuilder {
|
||||
pub fn new(params: BloomfilterParameters) -> Self {
|
||||
DoubleSpendingFilterBuilder {
|
||||
params,
|
||||
bit_vec_builder: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
|
||||
match &mut self.bit_vec_builder {
|
||||
None => {
|
||||
self.bit_vec_builder = Some(BitVecBuilder::new(b));
|
||||
true
|
||||
}
|
||||
Some(builder) => builder.add_bytes(b),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> DoubleSpendingFilter {
|
||||
match self.bit_vec_builder {
|
||||
None => DoubleSpendingFilter::new_empty(self.params),
|
||||
Some(builder) => DoubleSpendingFilter {
|
||||
inner: bloom_from_params(&self.params, builder.finish()),
|
||||
params: self.params,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitVecBuilder(BitVec);
|
||||
|
||||
impl BitVecBuilder {
|
||||
pub fn new(initial_bitmap: &[u8]) -> Self {
|
||||
BitVecBuilder(BitVec::from_bytes(initial_bitmap))
|
||||
}
|
||||
|
||||
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
|
||||
let add = BitVec::from_bytes(b);
|
||||
if self.0.len() != add.len() {
|
||||
return false;
|
||||
}
|
||||
self.0.or(&add);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn finish(self) -> BitVec {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,22 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
|
||||
ALTER TABLE message_store
|
||||
ADD COLUMN timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
RENAME TO message_store_old;
|
||||
|
||||
-- add new column with message timestamp.
|
||||
-- note: we can't simply alter existing table to add it since the default value is non-constant
|
||||
CREATE TABLE message_store
|
||||
(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
client_address_bs58 TEXT NOT NULL,
|
||||
content BLOB NOT NULL,
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO message_store(id, client_address_bs58, content)
|
||||
SELECT id, client_address_bs58, content
|
||||
FROM message_store_old;
|
||||
DROP TABLE message_store_old;
|
||||
|
||||
|
||||
@@ -395,6 +395,10 @@ pub struct TopologyWasm {
|
||||
/// Specifies whether this client should ignore the current epoch role of the target egress node
|
||||
/// when constructing the final hop packets.
|
||||
pub ignore_egress_epoch_role: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
pub ignore_ingress_epoch_role: bool,
|
||||
}
|
||||
|
||||
impl Default for TopologyWasm {
|
||||
@@ -419,6 +423,7 @@ impl From<TopologyWasm> for ConfigTopology {
|
||||
minimum_gateway_performance: topology.minimum_gateway_performance,
|
||||
use_extended_topology: topology.use_extended_topology,
|
||||
ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
|
||||
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,6 +441,7 @@ impl From<ConfigTopology> for TopologyWasm {
|
||||
minimum_gateway_performance: topology.minimum_gateway_performance,
|
||||
use_extended_topology: topology.use_extended_topology,
|
||||
ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
|
||||
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +281,11 @@ pub struct TopologyWasmOverride {
|
||||
/// when constructing the final hop packets.
|
||||
#[tsify(optional)]
|
||||
pub ignore_egress_epoch_role: Option<bool>,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
#[tsify(optional)]
|
||||
pub ignore_ingress_epoch_role: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<TopologyWasmOverride> for TopologyWasm {
|
||||
@@ -310,6 +315,9 @@ impl From<TopologyWasmOverride> for TopologyWasm {
|
||||
ignore_egress_epoch_role: value
|
||||
.ignore_egress_epoch_role
|
||||
.unwrap_or(def.ignore_egress_epoch_role),
|
||||
ignore_ingress_epoch_role: value
|
||||
.ignore_ingress_epoch_role
|
||||
.unwrap_or(def.ignore_ingress_epoch_role),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,9 +132,17 @@ pub async fn setup_gateway_from_api(
|
||||
chosen_gateway: Option<IdentityKey>,
|
||||
nym_apis: &[Url],
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<InitialisationResult, WasmCoreError> {
|
||||
let mut rng = thread_rng();
|
||||
let gateways = gateways_for_init(&mut rng, nym_apis, None, minimum_performance).await?;
|
||||
let gateways = gateways_for_init(
|
||||
&mut rng,
|
||||
nym_apis,
|
||||
None,
|
||||
minimum_performance,
|
||||
ignore_epoch_roles,
|
||||
)
|
||||
.await?;
|
||||
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).await
|
||||
}
|
||||
|
||||
@@ -142,9 +150,17 @@ pub async fn current_gateways_wasm(
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||
let mut rng = thread_rng();
|
||||
gateways_for_init(&mut rng, nym_apis, user_agent, minimum_performance).await
|
||||
gateways_for_init(
|
||||
&mut rng,
|
||||
nym_apis,
|
||||
user_agent,
|
||||
minimum_performance,
|
||||
ignore_epoch_roles,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn setup_from_topology(
|
||||
@@ -163,6 +179,7 @@ pub async fn generate_new_client_keys(store: &ClientStorage) -> Result<(), WasmC
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn add_gateway(
|
||||
preferred_gateway: Option<IdentityKey>,
|
||||
latency_based_selection: Option<bool>,
|
||||
@@ -170,6 +187,7 @@ pub async fn add_gateway(
|
||||
nym_apis: &[Url],
|
||||
user_agent: UserAgent,
|
||||
min_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
storage: &ClientStorage,
|
||||
) -> Result<(), WasmCoreError> {
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
@@ -203,8 +221,13 @@ pub async fn add_gateway(
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let available_gateways =
|
||||
current_gateways_wasm(nym_apis, Some(user_agent), min_performance).await?;
|
||||
let available_gateways = current_gateways_wasm(
|
||||
nym_apis,
|
||||
Some(user_agent),
|
||||
min_performance,
|
||||
ignore_epoch_roles,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// since we're registering with a brand new gateway,
|
||||
// make sure the list of available gateways doesn't overlap the list of known gateways
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
+1
-3
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-api"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.48"
|
||||
version = "1.1.49"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
rust-version.workspace = true
|
||||
@@ -16,7 +16,6 @@ async-trait = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
bincode.workspace = true
|
||||
bloomfilter = { workspace = true }
|
||||
cfg-if = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "derive", "env"] }
|
||||
console-subscriber = { workspace = true, optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
|
||||
@@ -84,7 +83,6 @@ tracing = { workspace = true }
|
||||
#ephemera = { path = "../ephemera" }
|
||||
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
|
||||
nym-ecash-contract-common = { path = "../common/cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-double-spending = { path = "../common/ecash-double-spending" }
|
||||
nym-ecash-time = { path = "../common/ecash-time", features = ["expiration"] }
|
||||
nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-compact-ecash = { path = "../common/nym_offline_compact_ecash" }
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
DROP TABLE partial_bloomfilter;
|
||||
DROP TABLE bloomfilter_parameters;
|
||||
@@ -6,6 +6,7 @@ use crate::ecash::state::EcashState;
|
||||
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||
use crate::support::http::state::AppState;
|
||||
use axum::extract::State;
|
||||
use axum::http::StatusCode;
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||
use nym_api_requests::ecash::models::{
|
||||
@@ -89,12 +90,6 @@ async fn verify_ticket(
|
||||
});
|
||||
}
|
||||
|
||||
// check the bloomfilter for obvious double-spending so that we wouldn't need to waste time on crypto verification
|
||||
// TODO: when blacklisting is implemented, this should get removed
|
||||
if state.check_bloomfilter(sn).await {
|
||||
return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket);
|
||||
}
|
||||
|
||||
// actual double spend detection with storage
|
||||
if let Some(previous_payment) = state.get_ticket_data_by_serial_number(sn).await? {
|
||||
match nym_compact_ecash::identify::identify(
|
||||
@@ -127,27 +122,17 @@ async fn verify_ticket(
|
||||
return reject_ticket(EcashTicketVerificationRejection::InvalidTicket);
|
||||
}
|
||||
|
||||
// finally get EXCLUSIVE lock on the bloomfilter, check if for the final time and insert the SN
|
||||
let was_present = state
|
||||
.update_bloomfilter(sn, spend_date, today_ecash)
|
||||
// store credential and check whether it wasn't already there (due to a parallel request)
|
||||
let was_inserted = state
|
||||
.store_verified_ticket(credential_data, gateway_cosmos_addr)
|
||||
.await?;
|
||||
if was_present {
|
||||
if !was_inserted {
|
||||
return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket);
|
||||
}
|
||||
|
||||
//store credential
|
||||
state
|
||||
.store_verified_ticket(credential_data, gateway_cosmos_addr)
|
||||
.await?;
|
||||
|
||||
Ok(Json(EcashTicketVerificationResponse { verified: Ok(()) }))
|
||||
}
|
||||
|
||||
// // for particular SN returns what gateway has submitted it and whether it has been verified correctly
|
||||
// async fn credential_status() -> ! {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Ecash",
|
||||
post,
|
||||
@@ -240,5 +225,8 @@ async fn batch_redeem_tickets(
|
||||
)]
|
||||
#[deprecated]
|
||||
async fn double_spending_filter_v1() -> AxumResult<Json<SpentCredentialsResponse>> {
|
||||
AxumResult::Err(AxumErrorResponse::internal_msg("permanently restricted"))
|
||||
AxumResult::Err(AxumErrorResponse::new(
|
||||
"permanently restricted",
|
||||
StatusCode::GONE,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::ecash::storage::EcashStorageExt;
|
||||
use crate::node_status_api::models::NymApiStorageError;
|
||||
use crate::support::config::Config;
|
||||
use crate::support::storage::NymApiStorage;
|
||||
use nym_ecash_time::ecash_today_date;
|
||||
use nym_task::TaskClient;
|
||||
use std::time::Duration;
|
||||
use time::Date;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, error, trace};
|
||||
|
||||
/// Task responsible for clearing out the database from stale ecash data,
|
||||
/// such as verified tickets or issued partial ticketbooks.
|
||||
pub struct EcashBackgroundStateCleaner {
|
||||
run_interval: Duration,
|
||||
issued_ticketbooks_retention_period_days: u32,
|
||||
verified_tickets_retention_period_days: u32,
|
||||
|
||||
storage: NymApiStorage,
|
||||
task_client: TaskClient,
|
||||
}
|
||||
|
||||
impl EcashBackgroundStateCleaner {
|
||||
pub fn new(global_config: &Config, storage: NymApiStorage, task_client: TaskClient) -> Self {
|
||||
EcashBackgroundStateCleaner {
|
||||
run_interval: global_config.ecash_signer.debug.stale_data_cleaner_interval,
|
||||
issued_ticketbooks_retention_period_days: global_config
|
||||
.ecash_signer
|
||||
.debug
|
||||
.issued_ticketbooks_retention_period_days,
|
||||
verified_tickets_retention_period_days: global_config
|
||||
.ecash_signer
|
||||
.debug
|
||||
.verified_tickets_retention_period_days,
|
||||
storage,
|
||||
task_client,
|
||||
}
|
||||
}
|
||||
|
||||
fn ticketbook_retention_cutoff(&self) -> Date {
|
||||
ecash_today_date()
|
||||
- time::Duration::days(self.issued_ticketbooks_retention_period_days as i64)
|
||||
}
|
||||
|
||||
fn verified_tickets_retention_cutoff(&self) -> Date {
|
||||
ecash_today_date()
|
||||
- time::Duration::days(self.verified_tickets_retention_period_days as i64)
|
||||
}
|
||||
|
||||
async fn clean_stale_data(&self) -> Result<(), NymApiStorageError> {
|
||||
// 1. remove old verified tickets
|
||||
self.storage
|
||||
.remove_expired_verified_tickets(self.verified_tickets_retention_cutoff())
|
||||
.await?;
|
||||
|
||||
// 2. remove old issued partial ticketbooks
|
||||
self.storage
|
||||
.remove_old_issued_ticketbooks(self.ticketbook_retention_cutoff())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run(&mut self) {
|
||||
let mut ticker = tokio::time::interval(self.run_interval);
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = self.task_client.recv() => {
|
||||
trace!("EcashBackgroundStateCleaner: Received shutdown");
|
||||
break;
|
||||
}
|
||||
_ = ticker.tick() => {
|
||||
if let Err(err) = self.clean_stale_data().await {
|
||||
error!("failed to clear out stale data: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("EcashBackgroundStateCleaner: exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move { self.run().await })
|
||||
}
|
||||
}
|
||||
@@ -2,106 +2,14 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::ecash::error::EcashError;
|
||||
use crate::ecash::state::local::TicketDoubleSpendingFilter;
|
||||
use crate::ecash::storage::EcashStorageExt;
|
||||
use crate::support::storage::NymApiStorage;
|
||||
use futures::{stream, StreamExt};
|
||||
use nym_compact_ecash::constants;
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_dkg::Threshold;
|
||||
use nym_ecash_double_spending::{DoubleSpendingFilter, DoubleSpendingFilterBuilder};
|
||||
use nym_ecash_time::{cred_exp_date, ecash_today};
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use std::future::Future;
|
||||
use time::ext::NumericalDuration;
|
||||
use time::Date;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
// attempt to completely rebuild the bloomfilter data for given day
|
||||
async fn try_rebuild_today_bloomfilter(
|
||||
today: Date,
|
||||
params: BloomfilterParameters,
|
||||
storage: &NymApiStorage,
|
||||
) -> Result<DoubleSpendingFilter, EcashError> {
|
||||
info!("rebuilding bloomfilter for {today}");
|
||||
|
||||
let tickets = storage.get_all_spent_tickets_on(today).await?;
|
||||
debug!(
|
||||
"there are {} tickets to insert into the filter",
|
||||
tickets.len()
|
||||
);
|
||||
|
||||
let mut filter = DoubleSpendingFilter::new_empty(params);
|
||||
for ticket in tickets {
|
||||
filter.set(&ticket.serial_number)
|
||||
}
|
||||
Ok(filter)
|
||||
}
|
||||
|
||||
pub(crate) async fn prepare_partial_bloomfilter_builder(
|
||||
storage: &NymApiStorage,
|
||||
params: BloomfilterParameters,
|
||||
params_id: i64,
|
||||
start: Date,
|
||||
days: i64,
|
||||
) -> Result<DoubleSpendingFilterBuilder, EcashError> {
|
||||
info!(
|
||||
"attempting to rebuild partial bloomfilter starting at {start} which includes {days} days"
|
||||
);
|
||||
|
||||
let mut filter_builder = DoubleSpendingFilter::builder(params);
|
||||
for i in 0..days {
|
||||
let date = start - i.days();
|
||||
let Some(bitmap) = storage
|
||||
.try_load_partial_bloomfilter_bitmap(date, params_id)
|
||||
.await?
|
||||
else {
|
||||
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) {
|
||||
error!(
|
||||
"failed to add bitmap from {date} to the global bloomfilter. it may be malformed!"
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(filter_builder)
|
||||
}
|
||||
|
||||
pub(super) async fn try_rebuild_bloomfilter(
|
||||
storage: &NymApiStorage,
|
||||
) -> Result<TicketDoubleSpendingFilter, EcashError> {
|
||||
info!("attempting to rebuild the double spending bloomfilter...");
|
||||
let today = ecash_today().date();
|
||||
|
||||
let (params_id, params) = storage.get_double_spending_filter_params().await?;
|
||||
info!("will use the following parameters: {params:?}");
|
||||
|
||||
// we're never going to have persisted data for 'today'. we need to rebuild it from scratch
|
||||
let today_filter = try_rebuild_today_bloomfilter(today, params, storage).await?;
|
||||
|
||||
info!("attempting to rebuild the global filter");
|
||||
let mut global_filter = prepare_partial_bloomfilter_builder(
|
||||
storage,
|
||||
params,
|
||||
params_id,
|
||||
today.previous_day().unwrap(),
|
||||
constants::CRED_VALIDITY_PERIOD_DAYS as i64 - 1,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !global_filter.add_bytes(&today_filter.dump_bitmap()) {
|
||||
error!("failed to add bitmap from {today} to the global bloomfilter. it may be malformed!");
|
||||
}
|
||||
|
||||
Ok(TicketDoubleSpendingFilter::new(
|
||||
today,
|
||||
params_id,
|
||||
global_filter.build(),
|
||||
today_filter,
|
||||
))
|
||||
}
|
||||
use tracing::warn;
|
||||
|
||||
pub(crate) fn ensure_sane_expiration_date(expiration_date: Date) -> Result<(), EcashError> {
|
||||
let today = ecash_today();
|
||||
|
||||
@@ -9,9 +9,7 @@ use crate::ecash::helpers::{
|
||||
use crate::ecash::keys::KeyPair;
|
||||
use crate::ecash::storage::models::IssuedHash;
|
||||
use nym_api_requests::ecash::models::{CommitedDeposit, DepositId};
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_double_spending::DoubleSpendingFilter;
|
||||
use nym_ticketbooks_merkle::{
|
||||
IssuedTicketbook, IssuedTicketbooksFullMerkleProof, IssuedTicketbooksMerkleTree, MerkleLeaf,
|
||||
};
|
||||
@@ -21,69 +19,6 @@ use time::Date;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::error;
|
||||
|
||||
pub(crate) struct TicketDoubleSpendingFilter {
|
||||
built_on: Date,
|
||||
params_id: i64,
|
||||
|
||||
today_filter: DoubleSpendingFilter,
|
||||
global_filter: DoubleSpendingFilter,
|
||||
}
|
||||
|
||||
impl TicketDoubleSpendingFilter {
|
||||
pub(crate) fn new(
|
||||
built_on: Date,
|
||||
params_id: i64,
|
||||
global_filter: DoubleSpendingFilter,
|
||||
today_filter: DoubleSpendingFilter,
|
||||
) -> TicketDoubleSpendingFilter {
|
||||
TicketDoubleSpendingFilter {
|
||||
built_on,
|
||||
params_id,
|
||||
today_filter,
|
||||
global_filter,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn built_on(&self) -> Date {
|
||||
self.built_on
|
||||
}
|
||||
|
||||
pub(crate) fn params(&self) -> BloomfilterParameters {
|
||||
self.today_filter.params()
|
||||
}
|
||||
|
||||
pub(crate) fn params_id(&self) -> i64 {
|
||||
self.params_id
|
||||
}
|
||||
|
||||
pub(crate) fn check(&self, sn: &Vec<u8>) -> bool {
|
||||
self.global_filter.check(sn)
|
||||
}
|
||||
|
||||
/// Returns boolean to indicate if the entry was already present
|
||||
pub(crate) fn insert_both(&mut self, sn: &Vec<u8>) -> bool {
|
||||
self.today_filter.set(sn);
|
||||
self.insert_global_only(sn)
|
||||
}
|
||||
|
||||
/// Returns boolean to indicate if the entry was already present
|
||||
pub(crate) fn insert_global_only(&mut self, sn: &Vec<u8>) -> bool {
|
||||
let existed = self.global_filter.check(sn);
|
||||
self.global_filter.set(sn);
|
||||
existed
|
||||
}
|
||||
|
||||
pub(crate) fn export_today_bitmap(&self) -> Vec<u8> {
|
||||
self.today_filter.dump_bitmap()
|
||||
}
|
||||
|
||||
pub(crate) fn advance_day(&mut self, date: Date, new_global: DoubleSpendingFilter) {
|
||||
self.built_on = date;
|
||||
self.global_filter = new_global;
|
||||
self.today_filter.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct DailyMerkleTree {
|
||||
pub(crate) merkle_tree: IssuedTicketbooksMerkleTree,
|
||||
@@ -207,9 +142,6 @@ pub(crate) struct LocalEcashState {
|
||||
pub(crate) partial_expiration_date_signatures:
|
||||
CachedImmutableItems<Date, IssuedExpirationDateSignatures>,
|
||||
|
||||
// the actual, up to date, bloomfilter
|
||||
pub(crate) double_spending_filter: Arc<RwLock<TicketDoubleSpendingFilter>>,
|
||||
|
||||
// merkle trees for ticketbooks issued for particular expiration dates
|
||||
pub(crate) issued_merkle_trees: Arc<RwLock<HashMap<Date, DailyMerkleTree>>>,
|
||||
}
|
||||
@@ -218,7 +150,6 @@ impl LocalEcashState {
|
||||
pub(crate) fn new(
|
||||
ecash_keypair: KeyPair,
|
||||
identity_keypair: identity::KeyPair,
|
||||
double_spending_filter: TicketDoubleSpendingFilter,
|
||||
explicitly_disabled: bool,
|
||||
) -> Self {
|
||||
LocalEcashState {
|
||||
@@ -228,7 +159,6 @@ impl LocalEcashState {
|
||||
active_signer: Default::default(),
|
||||
partial_coin_index_signatures: Default::default(),
|
||||
partial_expiration_date_signatures: Default::default(),
|
||||
double_spending_filter: Arc::new(RwLock::new(double_spending_filter)),
|
||||
issued_merkle_trees: Arc::new(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
+51
-172
@@ -8,11 +8,9 @@ use crate::ecash::error::{EcashError, RedemptionError, Result};
|
||||
use crate::ecash::helpers::{IssuedCoinIndicesSignatures, IssuedExpirationDateSignatures};
|
||||
use crate::ecash::keys::KeyPair;
|
||||
use crate::ecash::state::auxiliary::AuxiliaryEcashState;
|
||||
use crate::ecash::state::cleaner::EcashBackgroundStateCleaner;
|
||||
use crate::ecash::state::global::GlobalEcachState;
|
||||
use crate::ecash::state::helpers::{
|
||||
ensure_sane_expiration_date, prepare_partial_bloomfilter_builder, query_all_threshold_apis,
|
||||
try_rebuild_bloomfilter,
|
||||
};
|
||||
use crate::ecash::state::helpers::{ensure_sane_expiration_date, query_all_threshold_apis};
|
||||
use crate::ecash::state::local::{DailyMerkleTree, LocalEcashState};
|
||||
use crate::ecash::storage::models::{SerialNumberWrapper, TicketProvider};
|
||||
use crate::ecash::storage::EcashStorageExt;
|
||||
@@ -33,31 +31,30 @@ use nym_compact_ecash::scheme::expiration_date_signatures::{
|
||||
aggregate_annotated_expiration_signatures, ExpirationDateSignatureShare,
|
||||
};
|
||||
use nym_compact_ecash::{
|
||||
constants, scheme::expiration_date_signatures::sign_expiration_date, BlindedSignature, Bytable,
|
||||
scheme::expiration_date_signatures::sign_expiration_date, BlindedSignature, Bytable,
|
||||
SecretKeyAuth, VerificationKeyAuth,
|
||||
};
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_credentials::ecash::utils::EcashTime;
|
||||
use nym_credentials::{aggregate_verification_keys, CredentialSpendingData};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_contract_common::deposit::{Deposit, DepositId};
|
||||
use nym_ecash_contract_common::msg::ExecuteMsg;
|
||||
use nym_ecash_contract_common::redeem_credential::BATCH_REDEMPTION_PROPOSAL_TITLE;
|
||||
use nym_ecash_double_spending::DoubleSpendingFilter;
|
||||
use nym_ecash_time::{cred_exp_date, ecash_today_date};
|
||||
use nym_ecash_time::{ecash_default_expiration_date, ecash_today_date};
|
||||
use nym_task::TaskClient;
|
||||
use nym_ticketbooks_merkle::{IssuedTicketbook, IssuedTicketbooksFullMerkleProof, MerkleLeaf};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use rand::{thread_rng, RngCore};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use time::ext::NumericalDuration;
|
||||
use time::{Date, OffsetDateTime};
|
||||
use tokio::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
pub(crate) mod auxiliary;
|
||||
pub(crate) mod bloom;
|
||||
mod cleaner;
|
||||
pub(crate) mod global;
|
||||
mod helpers;
|
||||
pub(crate) mod local;
|
||||
@@ -84,10 +81,24 @@ impl EcashStateConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) enum BackgroundCleanerState {
|
||||
WaitingStartup(EcashBackgroundStateCleaner),
|
||||
Running {
|
||||
_handle: JoinHandle<()>,
|
||||
},
|
||||
|
||||
// an ephemeral state so that we could swap between the other two
|
||||
#[default]
|
||||
Invalid,
|
||||
}
|
||||
|
||||
pub struct EcashState {
|
||||
// additional global config parameters
|
||||
pub(crate) config: EcashStateConfig,
|
||||
|
||||
pub(crate) background_cleaner_state: BackgroundCleanerState,
|
||||
|
||||
// state global to the system, like aggregated keys, addresses, etc.
|
||||
pub(crate) global: GlobalEcachState,
|
||||
|
||||
@@ -99,7 +110,8 @@ pub struct EcashState {
|
||||
}
|
||||
|
||||
impl EcashState {
|
||||
pub(crate) async fn new<C, D>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new<C, D>(
|
||||
global_config: &Config,
|
||||
contract_address: AccountId,
|
||||
client: C,
|
||||
@@ -107,24 +119,38 @@ impl EcashState {
|
||||
key_pair: KeyPair,
|
||||
comm_channel: D,
|
||||
storage: NymApiStorage,
|
||||
) -> Result<Self>
|
||||
task_client: TaskClient,
|
||||
) -> Self
|
||||
where
|
||||
C: LocalClient + Send + Sync + 'static,
|
||||
D: APICommunicationChannel + Send + Sync + 'static,
|
||||
{
|
||||
let double_spending_filter = try_rebuild_bloomfilter(&storage).await?;
|
||||
|
||||
Ok(Self {
|
||||
Self {
|
||||
config: EcashStateConfig::new(global_config),
|
||||
background_cleaner_state: BackgroundCleanerState::WaitingStartup(
|
||||
EcashBackgroundStateCleaner::new(global_config, storage.clone(), task_client),
|
||||
),
|
||||
global: GlobalEcachState::new(contract_address),
|
||||
local: LocalEcashState::new(
|
||||
key_pair,
|
||||
identity_keypair,
|
||||
double_spending_filter,
|
||||
!global_config.ecash_signer.enabled,
|
||||
),
|
||||
aux: AuxiliaryEcashState::new(client, comm_channel, storage),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn spawn_background_cleaner(&mut self) {
|
||||
match std::mem::take(&mut self.background_cleaner_state) {
|
||||
BackgroundCleanerState::WaitingStartup(cleaner) => {
|
||||
self.background_cleaner_state = BackgroundCleanerState::Running {
|
||||
_handle: cleaner.start(),
|
||||
}
|
||||
}
|
||||
// whilst we normally don't want to panic, this one would only occur at startup,
|
||||
// if some logical invariants got broken (which have to be fixed in code anyway)
|
||||
_ => panic!("attempted to spawn background cleaner more than once"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that this nym-api is one of ecash signers for the current epoch
|
||||
@@ -766,12 +792,6 @@ impl EcashState {
|
||||
// remove the in-memory merkle tree
|
||||
map.remove(&date);
|
||||
}
|
||||
|
||||
// remove data from the storage
|
||||
self.aux
|
||||
.storage
|
||||
.remove_old_issued_ticketbooks(cutoff)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -807,6 +827,12 @@ impl EcashState {
|
||||
return Err(EcashError::ExpirationDateTooEarly);
|
||||
}
|
||||
|
||||
if challenge.expiration_date > ecash_default_expiration_date() {
|
||||
// we wouldn't have issued any credentials for that expiration date so no point
|
||||
// in attempting to construct an ultimately empty response
|
||||
return Err(EcashError::ExpirationDateTooLate);
|
||||
}
|
||||
|
||||
let merkle_proof = self
|
||||
.get_merkle_proof(challenge.expiration_date, &challenge.deposits)
|
||||
.await?;
|
||||
@@ -863,18 +889,17 @@ impl EcashState {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a boolean to indicate whether the ticket has actually been inserted
|
||||
pub async fn store_verified_ticket(
|
||||
&self,
|
||||
ticket_data: &CredentialSpendingData,
|
||||
gateway_addr: &AccountId,
|
||||
) -> Result<()> {
|
||||
) -> Result<bool> {
|
||||
self.aux
|
||||
.storage
|
||||
.store_verified_ticket(ticket_data, gateway_addr)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
|
||||
// TODO UNIMPLEMENTED: we should probably also be removing old tickets here
|
||||
}
|
||||
|
||||
pub async fn get_ticket_provider(
|
||||
@@ -921,150 +946,4 @@ impl EcashState {
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn check_bloomfilter(&self, serial_number: &Vec<u8>) -> bool {
|
||||
self.local
|
||||
.double_spending_filter
|
||||
.read()
|
||||
.await
|
||||
.check(serial_number)
|
||||
}
|
||||
|
||||
async fn update_archived_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
params: BloomfilterParameters,
|
||||
sn: &Vec<u8>,
|
||||
) -> Result<(), EcashError> {
|
||||
let mut filter = match self
|
||||
.aux
|
||||
.storage
|
||||
.try_load_partial_bloomfilter_bitmap(date, params_id)
|
||||
.await?
|
||||
{
|
||||
Some(bitmap) => DoubleSpendingFilter::from_bytes(params, &bitmap),
|
||||
None => {
|
||||
warn!("no existing partial bloomfilter for {date}");
|
||||
DoubleSpendingFilter::new_empty(params)
|
||||
}
|
||||
};
|
||||
filter.set(sn);
|
||||
let updated_bitmap = filter.dump_bitmap();
|
||||
self.aux
|
||||
.storage
|
||||
.update_archived_partial_bloomfilter(date, &updated_bitmap)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempt to insert the provided serial number into the bloomfilter.
|
||||
/// Furthermore, attempt to rotate the filter if we have advanced into a next day.
|
||||
pub async fn update_bloomfilter(
|
||||
&self,
|
||||
serial_number: &Vec<u8>,
|
||||
spending_date: Date,
|
||||
today: Date,
|
||||
) -> Result<bool, EcashError> {
|
||||
let mut guard = self.local.double_spending_filter.write().await;
|
||||
|
||||
let filter_date = guard.built_on();
|
||||
let yesterday = today.previous_day().unwrap();
|
||||
|
||||
let params_id = guard.params_id();
|
||||
let params = guard.params();
|
||||
|
||||
// if the filter is up-to-date, we just insert the entry and call it a day
|
||||
if filter_date == today {
|
||||
if spending_date == today {
|
||||
return Ok(guard.insert_both(serial_number));
|
||||
}
|
||||
// sanity check because this should NEVER happen,
|
||||
// but when it inevitably does, we don't want to crash
|
||||
if spending_date != yesterday {
|
||||
error!("attempted to insert a ticket with spending date of {spending_date} while it's {today} today!!");
|
||||
}
|
||||
|
||||
// this shouldn't be happening too often, so it's fine to interact with the storage
|
||||
warn!("updating archived partial bloomfilter for {spending_date}. those logs have to be closely controlled to make sure they're not too frequent");
|
||||
self.update_archived_partial_bloomfilter(
|
||||
spending_date,
|
||||
params_id,
|
||||
params,
|
||||
serial_number,
|
||||
)
|
||||
.await?;
|
||||
|
||||
return Ok(guard.insert_global_only(serial_number));
|
||||
}
|
||||
|
||||
info!("we need to advance our bloomfilter");
|
||||
let previous_bitmap = guard.export_today_bitmap();
|
||||
|
||||
// archive the BF for today's date
|
||||
self.aux
|
||||
.storage
|
||||
.insert_partial_bloomfilter(filter_date, params_id, &previous_bitmap)
|
||||
.await?;
|
||||
|
||||
let new_global_filter = if filter_date == yesterday {
|
||||
// normal case when we update filter daily
|
||||
let two_days_ago = yesterday.previous_day().unwrap();
|
||||
let mut filter_builder = prepare_partial_bloomfilter_builder(
|
||||
&self.aux.storage,
|
||||
params,
|
||||
params_id,
|
||||
two_days_ago,
|
||||
constants::CRED_VALIDITY_PERIOD_DAYS as i64 - 2,
|
||||
)
|
||||
.await?;
|
||||
// add the bitmap from 'old today', i.e. yesterday
|
||||
// (we have it on hand so no point in retrieving it from storage)
|
||||
filter_builder.add_bytes(&previous_bitmap);
|
||||
filter_builder.build()
|
||||
} else {
|
||||
// initial deployment case when we don't even get tickets daily
|
||||
prepare_partial_bloomfilter_builder(
|
||||
&self.aux.storage,
|
||||
params,
|
||||
params_id,
|
||||
yesterday,
|
||||
constants::CRED_VALIDITY_PERIOD_DAYS as i64 - 1,
|
||||
)
|
||||
.await?
|
||||
.build()
|
||||
};
|
||||
|
||||
guard.advance_day(today, new_global_filter);
|
||||
|
||||
// drop guard so other tasks could read the filter already whilst we clean-up the storage
|
||||
let res = if spending_date == today {
|
||||
Ok(guard.insert_both(serial_number))
|
||||
} else {
|
||||
Ok(guard.insert_global_only(serial_number))
|
||||
};
|
||||
drop(guard);
|
||||
|
||||
let cutoff = cred_exp_date().ecash_date();
|
||||
|
||||
// sanity check:
|
||||
assert_eq!(
|
||||
cutoff,
|
||||
today + (constants::CRED_VALIDITY_PERIOD_DAYS as i64 - 1).days()
|
||||
);
|
||||
|
||||
// remove the data we no longer need to hold, i.e. partial bloomfilters beyond max credential validity
|
||||
// and the ticket data for those
|
||||
self.aux
|
||||
.storage
|
||||
.remove_old_partial_bloomfilters(cutoff)
|
||||
.await?;
|
||||
self.aux
|
||||
.storage
|
||||
.remove_expired_verified_tickets(cutoff)
|
||||
.await?;
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::ecash::storage::models::{
|
||||
IssuedHash, RawExpirationDateSignatures, RawIssuedTicketbook, SerialNumberWrapper,
|
||||
StoredBloomfilterParams, TicketProvider, VerifiedTicket,
|
||||
TicketProvider, VerifiedTicket,
|
||||
};
|
||||
use crate::support::storage::manager::StorageManager;
|
||||
use async_trait::async_trait;
|
||||
@@ -65,6 +65,7 @@ pub trait EcashStorageManagerExt {
|
||||
gateway_address: &str,
|
||||
) -> Result<Option<TicketProvider>, sqlx::Error>;
|
||||
|
||||
/// Returns a boolean to indicate whether the ticket has actually been inserted
|
||||
async fn insert_verified_ticket(
|
||||
&self,
|
||||
provider_id: i64,
|
||||
@@ -72,7 +73,7 @@ pub trait EcashStorageManagerExt {
|
||||
verified_at: OffsetDateTime,
|
||||
ticket_data: Vec<u8>,
|
||||
serial_number: Vec<u8>,
|
||||
) -> Result<(), sqlx::Error>;
|
||||
) -> Result<bool, sqlx::Error>;
|
||||
|
||||
async fn get_ticket(&self, serial_number: &[u8])
|
||||
-> Result<Option<VerifiedTicket>, sqlx::Error>;
|
||||
@@ -145,41 +146,6 @@ pub trait EcashStorageManagerExt {
|
||||
data: &[u8],
|
||||
) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn insert_double_spending_filter_params(
|
||||
&self,
|
||||
num_hashes: u32,
|
||||
bitmap_size: u32,
|
||||
sip0_key0: &[u8],
|
||||
sip0_key1: &[u8],
|
||||
sip1_key0: &[u8],
|
||||
sip1_key1: &[u8],
|
||||
) -> Result<i64, sqlx::Error>;
|
||||
|
||||
async fn get_latest_double_spending_filter_params(
|
||||
&self,
|
||||
) -> Result<Option<StoredBloomfilterParams>, sqlx::Error>;
|
||||
|
||||
async fn update_archived_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
new_bitmap: &[u8],
|
||||
) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn try_load_partial_bloomfilter_bitmap(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
) -> Result<Option<Vec<u8>>, sqlx::Error>;
|
||||
|
||||
async fn insert_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
bitmap: &[u8],
|
||||
) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn remove_old_partial_bloomfilters(&self, cutoff: Date) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn remove_expired_verified_tickets(&self, cutoff: Date) -> Result<(), sqlx::Error>;
|
||||
}
|
||||
|
||||
@@ -330,6 +296,8 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns a boolean to indicate whether the ticket has actually been inserted
|
||||
async fn insert_verified_ticket(
|
||||
&self,
|
||||
provider_id: i64,
|
||||
@@ -337,10 +305,10 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
verified_at: OffsetDateTime,
|
||||
ticket_data: Vec<u8>,
|
||||
serial_number: Vec<u8>,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
) -> Result<bool, sqlx::Error> {
|
||||
let affected = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO verified_tickets(ticket_data, serial_number, spending_date, verified_at, gateway_id)
|
||||
INSERT OR IGNORE INTO verified_tickets(ticket_data, serial_number, spending_date, verified_at, gateway_id)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"#,
|
||||
ticket_data,
|
||||
@@ -350,9 +318,9 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
provider_id
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
.await?.rows_affected();
|
||||
|
||||
Ok(())
|
||||
Ok(affected == 1)
|
||||
}
|
||||
|
||||
async fn get_ticket(
|
||||
@@ -573,95 +541,9 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn insert_double_spending_filter_params(
|
||||
&self,
|
||||
num_hashes: u32,
|
||||
bitmap_size: u32,
|
||||
sip0_key0: &[u8],
|
||||
sip0_key1: &[u8],
|
||||
sip1_key0: &[u8],
|
||||
sip1_key1: &[u8],
|
||||
) -> Result<i64, sqlx::Error> {
|
||||
let row_id = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO bloomfilter_parameters(num_hashes, bitmap_size,sip0_key0, sip0_key1, sip1_key0, sip1_key1)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
num_hashes,
|
||||
bitmap_size,
|
||||
sip0_key0,
|
||||
sip0_key1,
|
||||
sip1_key0,
|
||||
sip1_key1
|
||||
).execute(&self.connection_pool).await?.last_insert_rowid();
|
||||
Ok(row_id)
|
||||
}
|
||||
|
||||
async fn get_latest_double_spending_filter_params(
|
||||
&self,
|
||||
) -> Result<Option<StoredBloomfilterParams>, sqlx::Error> {
|
||||
sqlx::query_as("SELECT * FROM bloomfilter_parameters ORDER BY id DESC LIMIT 1")
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_archived_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
new_bitmap: &[u8],
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"UPDATE partial_bloomfilter SET bitmap = ? WHERE date = ?",
|
||||
new_bitmap,
|
||||
date
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_load_partial_bloomfilter_bitmap(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
) -> Result<Option<Vec<u8>>, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"SELECT bitmap FROM partial_bloomfilter WHERE date = ? AND parameters = ?",
|
||||
date,
|
||||
params_id
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
.map(|maybe_record| maybe_record.map(|r| r.bitmap))
|
||||
}
|
||||
|
||||
async fn insert_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
bitmap: &[u8],
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO partial_bloomfilter(date, parameters, bitmap) VALUES (?, ?, ?)",
|
||||
date,
|
||||
params_id,
|
||||
bitmap
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_old_partial_bloomfilters(&self, cutoff: Date) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("DELETE FROM partial_bloomfilter WHERE date > ?", cutoff)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_expired_verified_tickets(&self, cutoff: Date) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"DELETE FROM verified_tickets WHERE spending_date > ?",
|
||||
"DELETE FROM verified_tickets WHERE spending_date < ?",
|
||||
cutoff
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
|
||||
@@ -14,7 +14,6 @@ use async_trait::async_trait;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
|
||||
use nym_compact_ecash::{BlindedSignature, VerificationKeyAuth};
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_credentials::CredentialSpendingData;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
@@ -22,7 +21,7 @@ use nym_ticketbooks_merkle::{IssuedTicketbook, MerkleLeaf};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use std::collections::HashSet;
|
||||
use time::{Date, OffsetDateTime};
|
||||
use tracing::{info, warn};
|
||||
use tracing::warn;
|
||||
|
||||
mod helpers;
|
||||
pub(crate) mod manager;
|
||||
@@ -30,32 +29,6 @@ pub(crate) mod models;
|
||||
|
||||
#[async_trait]
|
||||
pub trait EcashStorageExt {
|
||||
async fn get_double_spending_filter_params(
|
||||
&self,
|
||||
) -> Result<(i64, BloomfilterParameters), NymApiStorageError>;
|
||||
|
||||
async fn update_archived_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
new_bitmap: &[u8],
|
||||
) -> Result<(), NymApiStorageError>;
|
||||
|
||||
async fn try_load_partial_bloomfilter_bitmap(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
) -> Result<Option<Vec<u8>>, NymApiStorageError>;
|
||||
|
||||
async fn insert_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
bitmap: &[u8],
|
||||
) -> Result<(), NymApiStorageError>;
|
||||
|
||||
async fn remove_old_partial_bloomfilters(&self, cutoff: Date)
|
||||
-> Result<(), NymApiStorageError>;
|
||||
|
||||
async fn remove_expired_verified_tickets(&self, cutoff: Date)
|
||||
-> Result<(), NymApiStorageError>;
|
||||
|
||||
@@ -96,11 +69,12 @@ pub trait EcashStorageExt {
|
||||
serial_number: &[u8],
|
||||
) -> Result<Option<CredentialSpendingData>, NymApiStorageError>;
|
||||
|
||||
/// Returns a boolean to indicate whether the ticket has actually been inserted
|
||||
async fn store_verified_ticket(
|
||||
&self,
|
||||
ticket_data: &CredentialSpendingData,
|
||||
gateway_addr: &AccountId,
|
||||
) -> Result<(), NymApiStorageError>;
|
||||
) -> Result<bool, NymApiStorageError>;
|
||||
|
||||
async fn get_ticket_provider(
|
||||
&self,
|
||||
@@ -118,6 +92,7 @@ pub trait EcashStorageExt {
|
||||
last_batch_verification: OffsetDateTime,
|
||||
) -> Result<(), NymApiStorageError>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_all_spent_tickets_on(
|
||||
&self,
|
||||
date: Date,
|
||||
@@ -181,75 +156,6 @@ pub trait EcashStorageExt {
|
||||
|
||||
#[async_trait]
|
||||
impl EcashStorageExt for NymApiStorage {
|
||||
async fn get_double_spending_filter_params(
|
||||
&self,
|
||||
) -> Result<(i64, BloomfilterParameters), NymApiStorageError> {
|
||||
match self
|
||||
.manager
|
||||
.get_latest_double_spending_filter_params()
|
||||
.await?
|
||||
{
|
||||
Some(raw) => Ok((raw.id, (&raw).try_into()?)),
|
||||
None => {
|
||||
let default = BloomfilterParameters::default_ecash();
|
||||
info!("using default bloomfilter parameters: {default:?}");
|
||||
let id = self
|
||||
.manager
|
||||
.insert_double_spending_filter_params(
|
||||
default.num_hashes,
|
||||
default.bitmap_size as u32,
|
||||
&default.sip_keys[0].0.to_be_bytes(),
|
||||
&default.sip_keys[0].1.to_be_bytes(),
|
||||
&default.sip_keys[1].0.to_be_bytes(),
|
||||
&default.sip_keys[1].1.to_be_bytes(),
|
||||
)
|
||||
.await?;
|
||||
Ok((id, default))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_archived_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
new_bitmap: &[u8],
|
||||
) -> Result<(), NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.update_archived_partial_bloomfilter(date, new_bitmap)
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn try_load_partial_bloomfilter_bitmap(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
) -> Result<Option<Vec<u8>>, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.try_load_partial_bloomfilter_bitmap(date, params_id)
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn insert_partial_bloomfilter(
|
||||
&self,
|
||||
date: Date,
|
||||
params_id: i64,
|
||||
bitmap: &[u8],
|
||||
) -> Result<(), NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.insert_partial_bloomfilter(date, params_id, bitmap)
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn remove_old_partial_bloomfilters(
|
||||
&self,
|
||||
cutoff: Date,
|
||||
) -> Result<(), NymApiStorageError> {
|
||||
Ok(self.manager.remove_old_partial_bloomfilters(cutoff).await?)
|
||||
}
|
||||
|
||||
async fn remove_expired_verified_tickets(
|
||||
&self,
|
||||
cutoff: Date,
|
||||
@@ -354,11 +260,12 @@ impl EcashStorageExt for NymApiStorage {
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Returns a boolean to indicate whether the ticket has actually been inserted
|
||||
async fn store_verified_ticket(
|
||||
&self,
|
||||
ticket_data: &CredentialSpendingData,
|
||||
gateway_addr: &AccountId,
|
||||
) -> Result<(), NymApiStorageError> {
|
||||
) -> Result<bool, NymApiStorageError> {
|
||||
let provider_id = self
|
||||
.get_or_create_ticket_provider_with_id(gateway_addr.as_ref())
|
||||
.await?;
|
||||
@@ -411,6 +318,7 @@ impl EcashStorageExt for NymApiStorage {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_all_spent_tickets_on(
|
||||
&self,
|
||||
date: Date,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node_status_api::models::NymApiStorageError;
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
use nym_ticketbooks_merkle::IssuedTicketbook;
|
||||
@@ -89,50 +88,3 @@ pub struct RawExpirationDateSignatures {
|
||||
pub epoch_id: u32,
|
||||
pub serialised_signatures: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
pub(crate) struct StoredBloomfilterParams {
|
||||
pub(crate) id: i64,
|
||||
pub(crate) num_hashes: u32,
|
||||
pub(crate) bitmap_size: u32,
|
||||
|
||||
pub(crate) sip0_key0: Vec<u8>,
|
||||
pub(crate) sip0_key1: Vec<u8>,
|
||||
|
||||
pub(crate) sip1_key0: Vec<u8>,
|
||||
pub(crate) sip1_key1: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a StoredBloomfilterParams> for BloomfilterParameters {
|
||||
type Error = NymApiStorageError;
|
||||
fn try_from(value: &'a StoredBloomfilterParams) -> Result<Self, Self::Error> {
|
||||
let Ok(sip0_key0) = <[u8; 8]>::try_from(value.sip0_key0.as_ref()) else {
|
||||
return Err(NymApiStorageError::database_inconsistency(
|
||||
"malformed sip0 key0",
|
||||
));
|
||||
};
|
||||
let Ok(sip0_key1) = <[u8; 8]>::try_from(value.sip0_key1.as_ref()) else {
|
||||
return Err(NymApiStorageError::database_inconsistency(
|
||||
"malformed sip0 key1",
|
||||
));
|
||||
};
|
||||
let Ok(sip1_key0) = <[u8; 8]>::try_from(value.sip1_key0.as_ref()) else {
|
||||
return Err(NymApiStorageError::database_inconsistency(
|
||||
"malformed sip1 key0",
|
||||
));
|
||||
};
|
||||
let Ok(sip1_key1) = <[u8; 8]>::try_from(value.sip1_key1.as_ref()) else {
|
||||
return Err(NymApiStorageError::database_inconsistency(
|
||||
"malformed sip1 key1",
|
||||
));
|
||||
};
|
||||
Ok(BloomfilterParameters {
|
||||
num_hashes: value.num_hashes,
|
||||
bitmap_size: value.bitmap_size as u64,
|
||||
sip_keys: [
|
||||
(u64::from_be_bytes(sip0_key0), u64::from_be_bytes(sip0_key1)),
|
||||
(u64::from_be_bytes(sip1_key0), u64::from_be_bytes(sip1_key1)),
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::{NodeIndex, Threshold};
|
||||
use nym_ecash_contract_common::blacklist::{BlacklistedAccountResponse, Blacklisting};
|
||||
use nym_ecash_contract_common::deposit::{Deposit, DepositId, DepositResponse};
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::nym_api::routes::{
|
||||
API_VERSION, ECASH_BLIND_SIGN, ECASH_ISSUED_TICKETBOOKS_CHALLENGE,
|
||||
ECASH_ISSUED_TICKETBOOKS_FOR, ECASH_ROUTES,
|
||||
@@ -1333,9 +1334,8 @@ impl TestFixture {
|
||||
staged_key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
TaskClient::dummy(),
|
||||
);
|
||||
|
||||
TestFixture {
|
||||
axum: TestServer::new(
|
||||
@@ -1459,6 +1459,7 @@ mod credential_tests {
|
||||
use super::*;
|
||||
use crate::ecash::storage::EcashStorageExt;
|
||||
use axum::http::StatusCode;
|
||||
use nym_task::TaskClient;
|
||||
use nym_ticketbooks_merkle::MerkleLeaf;
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1547,9 +1548,8 @@ mod credential_tests {
|
||||
staged_key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
TaskClient::dummy(),
|
||||
);
|
||||
|
||||
let deposit_id = 42;
|
||||
assert!(state.already_issued(deposit_id).await.unwrap().is_none());
|
||||
|
||||
@@ -329,6 +329,13 @@ pub(crate) struct AxumErrorResponse {
|
||||
}
|
||||
|
||||
impl AxumErrorResponse {
|
||||
pub(crate) fn new<S: Into<String>>(message: S, status: StatusCode) -> Self {
|
||||
AxumErrorResponse {
|
||||
message: RequestError::new(message),
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn internal_msg(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
|
||||
@@ -106,6 +106,8 @@ pub(crate) struct Args {
|
||||
}
|
||||
|
||||
async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHandles> {
|
||||
let task_manager = TaskManager::new(TASK_MANAGER_TIMEOUT_S);
|
||||
|
||||
let nyxd_client = nyxd::Client::new(config);
|
||||
let connected_nyxd = config.get_nyxd_url();
|
||||
let nym_network_details = NymNetworkDetails::new_from_env();
|
||||
@@ -148,7 +150,7 @@ async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHan
|
||||
let comm_channel = QueryCommunicationChannel::new(nyxd_client.clone());
|
||||
|
||||
let encoded_identity = identity_keypair.public_key().to_base58_string();
|
||||
let ecash_state = EcashState::new(
|
||||
let mut ecash_state = EcashState::new(
|
||||
config,
|
||||
ecash_contract,
|
||||
nyxd_client.clone(),
|
||||
@@ -156,8 +158,8 @@ async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHan
|
||||
ecash_keypair_wrapper.clone(),
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
)
|
||||
.await?;
|
||||
task_manager.subscribe_named("ecash-state-data-cleaner"),
|
||||
);
|
||||
|
||||
// if ecash signer is enabled, there are additional constraints on the nym-api,
|
||||
// such as having sufficient token balance
|
||||
@@ -187,6 +189,7 @@ async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHan
|
||||
None
|
||||
};
|
||||
|
||||
ecash_state.spawn_background_cleaner();
|
||||
let router = router.with_state(AppState {
|
||||
forced_refresh: ForcedRefresh::new(
|
||||
config.topology_cacher.debug.node_describe_allow_illegal_ips,
|
||||
@@ -202,8 +205,6 @@ async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHan
|
||||
ecash_state: Arc::new(ecash_state),
|
||||
});
|
||||
|
||||
let task_manager = TaskManager::new(TASK_MANAGER_TIMEOUT_S);
|
||||
|
||||
// start note describe cache refresher
|
||||
// we should be doing the below, but can't due to our current startup structure
|
||||
// let refresher = node_describe_cache::new_refresher(&config.topology_cacher);
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::support::config::persistence::{
|
||||
use crate::support::config::r#override::OverrideConfig;
|
||||
use crate::support::config::template::CONFIG_TEMPLATE;
|
||||
use anyhow::bail;
|
||||
use nym_compact_ecash::constants;
|
||||
use nym_config::defaults::mainnet::read_parsed_var_if_not_default;
|
||||
use nym_config::defaults::var_names::{CONFIGURED, NYXD};
|
||||
use nym_config::serde_helpers::de_maybe_stringified;
|
||||
@@ -32,8 +33,6 @@ mod upgrade_helpers;
|
||||
|
||||
pub const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657";
|
||||
|
||||
pub const DEFAULT_DKG_CONTRACT_POLLING_RATE: Duration = Duration::from_secs(30);
|
||||
|
||||
const DEFAULT_GATEWAY_SENDING_RATE: usize = 200;
|
||||
const DEFAULT_MAX_CONCURRENT_GATEWAY_CLIENTS: usize = 50;
|
||||
const DEFAULT_PACKET_DELIVERY_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
@@ -57,9 +56,6 @@ const DEFAULT_CIRCULATING_SUPPLY_CACHE_INTERVAL: Duration = Duration::from_secs(
|
||||
pub(crate) const DEFAULT_NODE_DESCRIBE_CACHE_INTERVAL: Duration = Duration::from_secs(4500);
|
||||
pub(crate) const DEFAULT_NODE_DESCRIBE_BATCH_SIZE: usize = 50;
|
||||
|
||||
// keep them for 2 extra days beyond the specified expiration date
|
||||
pub(crate) const DEFAULT_MAX_ISSUED_TICKETBOOKS_RETENTION_DAYS: u32 = 2;
|
||||
|
||||
const DEFAULT_MONITOR_THRESHOLD: u8 = 60;
|
||||
const DEFAULT_MIN_MIXNODE_RELIABILITY: u8 = 50;
|
||||
const DEFAULT_MIN_GATEWAY_RELIABILITY: u8 = 20;
|
||||
@@ -560,15 +556,40 @@ pub struct EcashSignerDebug {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub dkg_contract_polling_rate: Duration,
|
||||
|
||||
/// Specifies interval at which the stale ecash data is removed from the storage.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub stale_data_cleaner_interval: Duration,
|
||||
|
||||
/// Specifies how long should the issued ticketbooks be kept (beyond the specified expiration date)
|
||||
pub issued_ticketbooks_retention_period_days: u32,
|
||||
|
||||
/// Specifies how long should the full ticket data of verified gateway tickets be kept (beyond the spending date)
|
||||
pub verified_tickets_retention_period_days: u32,
|
||||
}
|
||||
|
||||
impl EcashSignerDebug {
|
||||
pub const DEFAULT_DKG_CONTRACT_POLLING_RATE: Duration = Duration::from_secs(30);
|
||||
|
||||
// it still operates at "day" cutoffs
|
||||
pub const DEFAULT_STALE_DATA_CLEANER_INTERVAL: Duration = Duration::from_secs(2 * 60 * 60);
|
||||
|
||||
// keep them for 2 extra days beyond the specified expiration date
|
||||
pub(crate) const DEFAULT_MAX_ISSUED_TICKETBOOKS_RETENTION_DAYS: u32 = 2;
|
||||
|
||||
// keep the tickets for maximum theoretical validity (+1 day)
|
||||
pub(crate) const DEFAULT_VERIFIED_TICKETS_RETENTION_PERIOD_DAYS: u32 =
|
||||
constants::CRED_VALIDITY_PERIOD_DAYS + 1;
|
||||
}
|
||||
|
||||
impl Default for EcashSignerDebug {
|
||||
fn default() -> Self {
|
||||
EcashSignerDebug {
|
||||
dkg_contract_polling_rate: DEFAULT_DKG_CONTRACT_POLLING_RATE,
|
||||
issued_ticketbooks_retention_period_days: DEFAULT_MAX_ISSUED_TICKETBOOKS_RETENTION_DAYS,
|
||||
dkg_contract_polling_rate: Self::DEFAULT_DKG_CONTRACT_POLLING_RATE,
|
||||
stale_data_cleaner_interval: Self::DEFAULT_STALE_DATA_CLEANER_INTERVAL,
|
||||
issued_ticketbooks_retention_period_days:
|
||||
Self::DEFAULT_MAX_ISSUED_TICKETBOOKS_RETENTION_DAYS,
|
||||
verified_tickets_retention_period_days:
|
||||
Self::DEFAULT_VERIFIED_TICKETS_RETENTION_PERIOD_DAYS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -97,7 +97,7 @@ pub struct ZkNymTicketHandlerDebug {
|
||||
impl ZkNymTicketHandlerDebug {
|
||||
pub const DEFAULT_REVOCATION_BANDWIDTH_PENALTY: f32 = 10.0;
|
||||
pub const DEFAULT_PENDING_POLLER: Duration = Duration::from_secs(300);
|
||||
pub const DEFAULT_MINIMUM_API_QUORUM: f32 = 0.8;
|
||||
pub const DEFAULT_MINIMUM_API_QUORUM: f32 = 0.7;
|
||||
pub const DEFAULT_MINIMUM_REDEMPTION_TICKETS: usize = 100;
|
||||
|
||||
// use min(4/5 of max validity, validity - 1), but making sure it's no greater than 1 day
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::node::mixnet::SharedData;
|
||||
use nym_task::ShutdownToken;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{error, info, trace};
|
||||
use tracing::{debug, error, info, trace};
|
||||
|
||||
pub(crate) struct Listener {
|
||||
bind_address: SocketAddr,
|
||||
@@ -39,12 +39,14 @@ impl Listener {
|
||||
biased;
|
||||
_ = self.shutdown.cancelled() => {
|
||||
trace!("mixnet listener: received shutdown");
|
||||
break
|
||||
}
|
||||
connection = tcp_listener.accept() => {
|
||||
self.shared_data.try_handle_connection(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("mixnet socket listener: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self) -> JoinHandle<()> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-validator-rewarder"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
Generated
+1
-1
@@ -4223,7 +4223,7 @@ dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nyx-chain-watcher"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -27,15 +27,15 @@ nym-node-requests = { path = "../nym-node/nym-node-requests", features = [
|
||||
"openapi",
|
||||
] }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nyxd-scraper = {path = "../common/nyxd-scraper"}
|
||||
reqwest = {workspace= true, features = ["rustls-tls"]}
|
||||
nyxd-scraper = { path = "../common/nyxd-scraper" }
|
||||
reqwest = { workspace = true, features = ["rustls-tls"] }
|
||||
rocket = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "time"] }
|
||||
thiserror = { workspace = true }
|
||||
time = {version = "0.3.37"}
|
||||
time = { version = "0.3.37" }
|
||||
tokio = { workspace = true, features = ["process", "rt-multi-thread"] }
|
||||
tokio-util = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
@@ -489,7 +489,7 @@ where
|
||||
user_chosen_gateway: &str,
|
||||
) -> Result<bool> {
|
||||
let storage = self.storage.gateway_details_store();
|
||||
// Stricly speaking, `set_active_gateway` does this check internally as well, but since the
|
||||
// Strictly speaking, `set_active_gateway` does this check internally as well, but since the
|
||||
// error is boxed away and we're using a generic storage, it's not so easy to match on it.
|
||||
// This function is at least less likely to fail on something unrelated to the existence of
|
||||
// the gateway in the set of registered gateways
|
||||
@@ -512,15 +512,14 @@ where
|
||||
|
||||
let user_agent = self.user_agent.clone();
|
||||
|
||||
let topology_cfg = &self.config.debug_config.topology;
|
||||
let mut rng = OsRng;
|
||||
let available_gateways = gateways_for_init(
|
||||
&mut rng,
|
||||
&nym_api_endpoints,
|
||||
user_agent,
|
||||
self.config
|
||||
.debug_config
|
||||
.topology
|
||||
.minimum_gateway_performance,
|
||||
topology_cfg.minimum_gateway_performance,
|
||||
topology_cfg.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.47"
|
||||
version = "1.1.48"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version = "1.70"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nymvisor"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -217,6 +217,7 @@ impl NymClientBuilder {
|
||||
&self.config.base.client.nym_api_urls,
|
||||
bin_info!().into(),
|
||||
self.config.base.debug.topology.minimum_gateway_performance,
|
||||
self.config.base.debug.topology.ignore_ingress_epoch_role,
|
||||
&client_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -156,6 +156,7 @@ impl MixFetchClientBuilder {
|
||||
&self.config.base.client.nym_api_urls,
|
||||
bin_info!().into(),
|
||||
self.config.base.debug.topology.minimum_gateway_performance,
|
||||
self.config.base.debug.topology.ignore_ingress_epoch_role,
|
||||
&client_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Reference in New Issue
Block a user