Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2d77aedd3 | |||
| 58dcf2171c | |||
| a6ad6c7d49 | |||
| cbc977c491 | |||
| f40c05a34c | |||
| 776443131e | |||
| b5eab7f07f | |||
| eeeb4b3246 | |||
| e3e4dc6db9 | |||
| 461b7bcfb7 | |||
| bbf0d06583 | |||
| 6393d6093f | |||
| c0ea599913 | |||
| 16d09a35ba | |||
| e6c5eddbe5 | |||
| 515aedac60 | |||
| dca2b2c763 | |||
| c92f09543e | |||
| b45eb16783 | |||
| f8523dc7d1 | |||
| 996ce6a233 | |||
| 1dcb0a0456 | |||
| 55b99e4ce1 | |||
| bd94dd3055 | |||
| 0d19bb4ddc | |||
| dcd70155cd | |||
| a469aeff05 | |||
| 107199bd9c | |||
| 53524447c4 | |||
| bc832c97d8 | |||
| 4989d47ea2 | |||
| 3cb69780a6 | |||
| 68b61bfa84 | |||
| 06fca9bd1f | |||
| 4c10cebf1b | |||
| aea962b546 | |||
| ab2a1c3fe5 | |||
| 8d0c040015 | |||
| 42efff83da | |||
| 86fe955592 | |||
| c3ce0d0b5c | |||
| 0e2cfa5be0 | |||
| 17a5872c6d | |||
| 969155bf91 | |||
| f4fafbfea5 | |||
| 5e97b1f79a | |||
| b52bf951a6 | |||
| 98805a11e4 | |||
| 0a2f28b0ec | |||
| db1ad4dcab | |||
| 78ca539018 | |||
| 04cafc72dc | |||
| 63812994a1 | |||
| 19dee11539 | |||
| 6f3a6b7855 | |||
| 7c84ad4384 | |||
| bc647fc8e2 | |||
| 429ff6045d | |||
| 0de1deced4 | |||
| fc2eedfc66 | |||
| 7ddd819ff3 | |||
| 83b416d12d | |||
| b9c775c3ae | |||
| b613cf87c8 | |||
| 6f669866e9 | |||
| 4e61fefec8 | |||
| b4514ecd83 | |||
| 4f6902525e | |||
| 881139e36f | |||
| 32e2557456 | |||
| 8b44820e51 | |||
| 5e6417f837 | |||
| 3d5ac0b883 | |||
| dfb2a2f380 | |||
| d1de751850 | |||
| ecee6ca863 | |||
| 31ea3f92e2 | |||
| f19c934fae | |||
| 10d6f20de7 | |||
| 96b33bfbe4 | |||
| 444c787d0a | |||
| 61fcd4ac69 | |||
| b76802e6eb | |||
| 7d351029a4 | |||
| 4ee445c119 | |||
| 61ddeea495 | |||
| 7b802033b3 | |||
| b484f47369 | |||
| 66979df10c | |||
| 82f161fb91 | |||
| 9d0fd681d4 | |||
| c2ab47a102 | |||
| 8704c21621 | |||
| 03ffb25bf9 | |||
| 70db1ad062 | |||
| 952ed9b642 | |||
| f57fe79686 | |||
| 9179f1c351 | |||
| c4f7a1e09d | |||
| 701012a968 | |||
| 9767f72b8f | |||
| de0fb7459d | |||
| e2ead6dbe1 | |||
| 7b10d92ca4 | |||
| 2c6e5eb673 | |||
| 02fde4e530 | |||
| cc25fc1f32 | |||
| c971e486b5 | |||
| 96a9eb6f6a | |||
| 9eeb61ea0a | |||
| 08042c61ad | |||
| 36c74f30e5 | |||
| 4956d13bdc | |||
| d9f6c0723e | |||
| 52f5656190 | |||
| 21cd90f238 | |||
| 72e243042e | |||
| c2ad4e5bb4 | |||
| 5f7f5ef92d | |||
| 008afe7a85 |
@@ -8,11 +8,6 @@ on:
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
enable_wireguard:
|
||||
description: "Add --features wireguard"
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
enable_deb:
|
||||
description: "True to enable cargo-deb installation and .deb package building"
|
||||
required: false
|
||||
@@ -70,9 +65,6 @@ jobs:
|
||||
- name: Set CARGO_FEATURES
|
||||
run: |
|
||||
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
|
||||
if: >
|
||||
github.event_name == 'schedule' ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true)
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
||||
@@ -51,6 +51,10 @@ jobs:
|
||||
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
|
||||
|
||||
- name: Set CARGO_FEATURES
|
||||
run: |
|
||||
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -60,8 +64,8 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
|
||||
args: --workspace --release ${{ env.CARGO_FEATURES }}
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
+70
-1
@@ -4,6 +4,76 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.10-caramello] (2024-08-19)
|
||||
|
||||
- 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])
|
||||
|
||||
[#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])
|
||||
- Handle clients with different versions in IPR ([#4723])
|
||||
- Add 1GB/day/user bandwidth cap ([#4717])
|
||||
- Feature/merge back ([#4710])
|
||||
- removed mixnode/gateway config migration code and disabled cli without explicit flag ([#4706])
|
||||
|
||||
[#4731]: https://github.com/nymtech/nym/pull/4731
|
||||
[#4723]: https://github.com/nymtech/nym/pull/4723
|
||||
[#4717]: https://github.com/nymtech/nym/pull/4717
|
||||
[#4710]: https://github.com/nymtech/nym/pull/4710
|
||||
[#4706]: https://github.com/nymtech/nym/pull/4706
|
||||
|
||||
## [2024.8-wispa] (2024-07-10)
|
||||
|
||||
- add event parsing to support cosmos_sdk > 0.50 ([#4697])
|
||||
- Fix NR config compatibility ([#4690])
|
||||
- Remove UserAgent constructor since it's weakly typed ([#4689])
|
||||
- [bugfix]: Node_api_check CLI looked over roles on blacklisted nodes ([#4687])
|
||||
- Add mixnodes to self describing api cache ([#4684])
|
||||
- Move and whole bump of crates to workspace and upgrade some ([#4680])
|
||||
- Remove code that refers to removed nym-network-statistics ([#4679])
|
||||
- Remove nym-network-statistics ([#4678])
|
||||
- Create UserAgent that can be passed from the binary to the nym api client ([#4677])
|
||||
- Add authenticator ([#4667])
|
||||
|
||||
[#4697]: https://github.com/nymtech/nym/pull/4697
|
||||
[#4690]: https://github.com/nymtech/nym/pull/4690
|
||||
[#4689]: https://github.com/nymtech/nym/pull/4689
|
||||
[#4687]: https://github.com/nymtech/nym/pull/4687
|
||||
[#4684]: https://github.com/nymtech/nym/pull/4684
|
||||
[#4680]: https://github.com/nymtech/nym/pull/4680
|
||||
[#4679]: https://github.com/nymtech/nym/pull/4679
|
||||
[#4678]: https://github.com/nymtech/nym/pull/4678
|
||||
[#4677]: https://github.com/nymtech/nym/pull/4677
|
||||
[#4667]: https://github.com/nymtech/nym/pull/4667
|
||||
|
||||
## [2024.7-doubledecker] (2024-07-04)
|
||||
|
||||
- Add an early return in `parse_raw_str_logs` for empty raw log strings. ([#4686])
|
||||
@@ -513,7 +583,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#3187]: https://github.com/nymtech/nym/issues/3187
|
||||
[#3203]: https://github.com/nymtech/nym/pull/3203
|
||||
[#3199]: https://github.com/nymtech/nym/pull/3199
|
||||
>>>>>>> master
|
||||
|
||||
## [v1.1.13] (2023-03-15)
|
||||
|
||||
|
||||
Generated
+143
-241
File diff suppressed because it is too large
Load Diff
+9
-8
@@ -52,6 +52,8 @@ members = [
|
||||
"common/ecash-time",
|
||||
"common/execute",
|
||||
"common/exit-policy",
|
||||
"common/gateway-requests",
|
||||
"common/gateway-storage",
|
||||
"common/http-api-client",
|
||||
"common/http-api-common",
|
||||
"common/inclusion-probability",
|
||||
@@ -96,7 +98,6 @@ members = [
|
||||
"explorer-api/explorer-api-requests",
|
||||
"explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"gateway/gateway-requests",
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/lib/socks5-listener",
|
||||
@@ -139,7 +140,7 @@ default-members = [
|
||||
"tools/nymvisor",
|
||||
"explorer-api",
|
||||
"nym-validator-rewarder",
|
||||
"nym-node"
|
||||
"nym-node",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
@@ -290,11 +291,11 @@ tar = "0.4.40"
|
||||
tempfile = "3.5.0"
|
||||
thiserror = "1.0.48"
|
||||
time = "0.3.30"
|
||||
tokio = "1.33.0"
|
||||
tokio-stream = "0.1.14"
|
||||
tokio-test = "0.4.2"
|
||||
tokio = "1.39"
|
||||
tokio-stream = "0.1.15"
|
||||
tokio-test = "0.4.4"
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = "0.7.10"
|
||||
tokio-util = "0.7.11"
|
||||
toml = "0.8.14"
|
||||
tower = "0.4.13"
|
||||
tower-http = "0.5.2"
|
||||
@@ -346,8 +347,8 @@ bip32 = { version = "0.5.1", default-features = false }
|
||||
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
|
||||
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
|
||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
|
||||
tendermint = "0.37.0" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
||||
tendermint = "0.37.0" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
||||
prost = { version = "0.12", default-features = false }
|
||||
|
||||
# wasm-related dependencies
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.37"
|
||||
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"
|
||||
@@ -26,30 +26,46 @@ clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
dirs = { workspace = true }
|
||||
log = { workspace = true } # self explanatory
|
||||
rand = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
serde = { workspace = true, features = [
|
||||
"derive",
|
||||
] } # for config serialization/deserialization
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio = { workspace = true, features = [
|
||||
"rt-multi-thread",
|
||||
"net",
|
||||
"signal",
|
||||
] } # async runtime
|
||||
tokio-tungstenite = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
## internal
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format", "clap"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = [
|
||||
"output_format",
|
||||
"clap",
|
||||
] }
|
||||
nym-client-core = { path = "../../common/client-core", features = [
|
||||
"fs-credentials-storage",
|
||||
"fs-surb-storage",
|
||||
"fs-gateways-storage",
|
||||
"cli",
|
||||
] }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||
nym-gateway-requests = { path = "../../common/gateway-requests" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-pemstore = { path = "../../common/pemstore" }
|
||||
nym-task = { path = "../../common/task" }
|
||||
nym-topology = { path = "../../common/topology" }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
"http-client",
|
||||
] }
|
||||
nym-client-websocket-requests = { path = "websocket-requests" }
|
||||
nym-id = { path = "../../common/nym-id" }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.37"
|
||||
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"
|
||||
@@ -11,7 +11,9 @@ license.workspace = true
|
||||
bs58 = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
log = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
serde = { workspace = true, features = [
|
||||
"derive",
|
||||
] } # for config serialization/deserialization
|
||||
serde_json = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -22,13 +24,21 @@ url = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = [
|
||||
"output_format",
|
||||
"clap",
|
||||
] }
|
||||
nym-client-core = { path = "../../common/client-core", features = [
|
||||
"fs-credentials-storage",
|
||||
"fs-surb-storage",
|
||||
"fs-gateways-storage",
|
||||
"cli",
|
||||
] }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||
nym-gateway-requests = { path = "../../common/gateway-requests" }
|
||||
nym-id = { path = "../../common/nym-id" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
|
||||
@@ -36,7 +46,9 @@ nym-pemstore = { path = "../../common/pemstore" }
|
||||
nym-socks5-client-core = { path = "../../common/socks5-client-core" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-topology = { path = "../../common/topology" }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
"http-client",
|
||||
] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
// 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;
|
||||
use nym_credentials::ecash::utils::obtain_aggregate_wallet;
|
||||
use nym_credentials::IssuedTicketBook;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_time::{ecash_default_expiration_date, Date};
|
||||
use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
@@ -22,6 +25,7 @@ pub async fn make_deposit<C>(
|
||||
client: &C,
|
||||
client_id: &[u8],
|
||||
expiration: Option<Date>,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<IssuanceTicketBook, BandwidthControllerError>
|
||||
where
|
||||
C: EcashSigningClient + EcashQueryClient + Sync,
|
||||
@@ -48,11 +52,12 @@ where
|
||||
deposit_id,
|
||||
client_id,
|
||||
signing_key,
|
||||
ticketbook_type,
|
||||
expiration,
|
||||
))
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -62,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,
|
||||
|
||||
@@ -38,7 +38,7 @@ nym-country-group = { path = "../country-group" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-metrics = { path = "../nym-metrics" }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
@@ -46,9 +46,12 @@ nym-pemstore = { path = "../pemstore" }
|
||||
nym-topology = { path = "../topology", features = ["serializable"] }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-task = { path = "../task" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"] }
|
||||
nym-client-core-config-types = { path = "./config-types", features = [
|
||||
"disk-persistence",
|
||||
] }
|
||||
nym-client-core-surb-storage = { path = "./surb-storage" }
|
||||
nym-client-core-gateways-storage = { path = "./gateways-storage" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
@@ -115,6 +118,7 @@ tempfile = { workspace = true }
|
||||
[features]
|
||||
default = []
|
||||
cli = ["clap", "comfy-table"]
|
||||
fs-credentials-storage = ["nym-credential-storage/persistent-storage"]
|
||||
fs-surb-storage = ["nym-client-core-surb-storage/fs-surb-storage"]
|
||||
fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"]
|
||||
wasm = ["nym-gateway-client/wasm"]
|
||||
|
||||
@@ -18,7 +18,7 @@ url.workspace = true
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["asymmetric"] }
|
||||
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
@@ -27,7 +27,12 @@ optional = true
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
|
||||
[features]
|
||||
fs-gateways-storage = ["sqlx"]
|
||||
fs-gateways-storage = ["sqlx"]
|
||||
|
||||
@@ -5,14 +5,15 @@ use crate::cli_helpers::{CliClient, CliClientConfig};
|
||||
use crate::error::ClientCoreError;
|
||||
use nym_credential_storage::models::BasicTicketbookInformation;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_time::ecash_today;
|
||||
use nym_network_defaults::TicketbookType::MixnetEntry;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AvailableTicketbook {
|
||||
pub id: i64,
|
||||
pub typ: TicketType,
|
||||
pub expiration: Date,
|
||||
pub issued_tickets: u32,
|
||||
pub claimed_tickets: u32,
|
||||
@@ -45,6 +46,7 @@ impl AvailableTicketbook {
|
||||
|
||||
vec![
|
||||
comfy_table::Cell::new(self.id.to_string()),
|
||||
comfy_table::Cell::new(self.typ),
|
||||
expiration,
|
||||
comfy_table::Cell::new(format!("{issued} ({si_issued})")),
|
||||
comfy_table::Cell::new(format!("{claimed} ({si_claimed})")),
|
||||
@@ -55,17 +57,22 @@ impl AvailableTicketbook {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BasicTicketbookInformation> for AvailableTicketbook {
|
||||
fn from(value: BasicTicketbookInformation) -> Self {
|
||||
AvailableTicketbook {
|
||||
impl TryFrom<BasicTicketbookInformation> for AvailableTicketbook {
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn try_from(value: BasicTicketbookInformation) -> Result<Self, Self::Error> {
|
||||
let typ = value
|
||||
.ticketbook_type
|
||||
.parse()
|
||||
.map_err(|_| ClientCoreError::UnknownTicketType)?;
|
||||
Ok(AvailableTicketbook {
|
||||
id: value.id,
|
||||
typ,
|
||||
expiration: value.expiration_date,
|
||||
issued_tickets: value.total_tickets,
|
||||
claimed_tickets: value.used_tickets,
|
||||
|
||||
// TODO: this will change when 'type' field is introduced; for now doesn't matter what we put there
|
||||
ticket_size: MixnetEntry.bandwidth_value(),
|
||||
}
|
||||
ticket_size: typ.to_repr().bandwidth_value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +86,7 @@ impl std::fmt::Display for AvailableTicketbooks {
|
||||
let mut table = comfy_table::Table::new();
|
||||
table.set_header(vec![
|
||||
"id",
|
||||
"type",
|
||||
"expiration",
|
||||
"issued tickets (bandwidth)",
|
||||
"claimed tickets (bandwidth)",
|
||||
@@ -124,6 +132,9 @@ where
|
||||
})?;
|
||||
|
||||
Ok(AvailableTicketbooks(
|
||||
ticketbooks.into_iter().map(Into::into).collect(),
|
||||
ticketbooks
|
||||
.into_iter()
|
||||
.map(TryInto::<AvailableTicketbook>::try_into)
|
||||
.collect::<Result<_, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
config::{self, disk_persistence::CommonClientPaths},
|
||||
error::ClientCoreError,
|
||||
};
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-credentials-storage"))]
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
|
||||
|
||||
pub use nym_client_core_gateways_storage as gateways_storage;
|
||||
|
||||
@@ -68,6 +68,9 @@ pub enum ClientCoreError {
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("the provided ticket type is invalid")]
|
||||
UnknownTicketType,
|
||||
|
||||
#[error("the gateway id is invalid - {0}")]
|
||||
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ use std::future::Future;
|
||||
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "cli",
|
||||
feature = "fs-surb-storage",
|
||||
feature = "fs-credentials-storage",
|
||||
feature = "fs-gateways-storage"
|
||||
))]
|
||||
pub mod cli_helpers;
|
||||
|
||||
@@ -24,7 +24,7 @@ nym-bandwidth-controller = { path = "../../bandwidth-controller" }
|
||||
nym-credentials = { path = "../../credentials" }
|
||||
nym-credential-storage = { path = "../../credential-storage" }
|
||||
nym-crypto = { path = "../../crypto" }
|
||||
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
nym-pemstore = { path = "../../pemstore" }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use nym_network_defaults::TicketbookType::MixnetEntry;
|
||||
use nym_network_defaults::TicketTypeRepr::V1MixnetEntry;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -103,7 +103,7 @@ impl BandwidthTickets {
|
||||
|
||||
// 20% of entry ticket value
|
||||
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
|
||||
(MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
(V1MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
|
||||
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
pub use nym_api_requests::{
|
||||
ecash::{
|
||||
models::{
|
||||
EpochCredentialsResponse, IssuedCredential, IssuedCredentialBody,
|
||||
IssuedCredentialResponse, IssuedCredentialsResponse, SpentCredentialsResponse,
|
||||
EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse,
|
||||
IssuedTicketbook, IssuedTicketbookBody, SpentCredentialsResponse,
|
||||
},
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody,
|
||||
PartialCoinIndicesSignatureResponse, PartialExpirationDateSignatureResponse,
|
||||
|
||||
@@ -683,6 +683,24 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn migrate_vested_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::MigrateVestedMixNode {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn migrate_vested_delegation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::MigrateVestedDelegation { mix_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
async fn testing_resolve_all_pending_events(
|
||||
&self,
|
||||
@@ -928,6 +946,12 @@ mod tests {
|
||||
MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client
|
||||
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::MigrateVestedMixNode { .. } => {
|
||||
client.migrate_vested_mixnode(None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::MigrateVestedDelegation { mix_id } => {
|
||||
client.migrate_vested_delegation(mix_id, None).ignore()
|
||||
}
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
|
||||
@@ -437,6 +437,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
|
||||
use nym_vesting_contract_common::ExecuteMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
@@ -560,6 +561,9 @@ mod tests {
|
||||
VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client
|
||||
.update_locked_pledge_cap(address.parse().unwrap(), cap, None)
|
||||
.ignore(),
|
||||
// those will never be manually called by clients
|
||||
ExecuteMsg::TrackMigratedMixnode { .. } => "explicitly_ignored".ignore(),
|
||||
ExecuteMsg::TrackMigratedDelegation { .. } => "explicitly_ignored".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 cosmrs::abci::TxMsgData;
|
||||
use cosmrs::cosmwasm::MsgExecuteContractResponse;
|
||||
@@ -9,7 +10,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,6 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TEMPORARY WORKAROUND:
|
||||
// those features are expected as the below should only get activated whenever
|
||||
// the corresponding features in tendermint-rpc are enabled transitively
|
||||
#![allow(unexpected_cfgs)]
|
||||
|
||||
use crate::nyxd::cosmwasm_client::client_traits::SigningCosmWasmClient;
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{Config, GasPrice, Hash, Height};
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TEMPORARY WORKAROUND:
|
||||
// those features are expected as the below should only get activated whenever
|
||||
// the corresponding features in tendermint-rpc are enabled transitively
|
||||
#![allow(unexpected_cfgs)]
|
||||
|
||||
use crate::nyxd::contract_traits::{NymContractsProvider, TypedNymContracts};
|
||||
use crate::nyxd::cosmwasm_client::types::{
|
||||
ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TEMPORARY WORKAROUND:
|
||||
// those features are expected as the below should only get activated whenever
|
||||
// the corresponding features in tendermint-rpc are enabled transitively
|
||||
#![allow(unexpected_cfgs)]
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::tendermint::{self, abci, block::Height, evidence::Evidence, Genesis, Hash};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
@@ -7,9 +7,10 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
base64 = "0.13.0"
|
||||
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"] }
|
||||
@@ -21,13 +22,13 @@ humantime-serde = { workspace = true }
|
||||
inquire = { workspace = true }
|
||||
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
log = { workspace = true }
|
||||
rand = {version = "0.6", features = ["std"] }
|
||||
rand = { workspace = true, features = ["std"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
time = { workspace = true, features = ["parsing", "formatting"] }
|
||||
tokio = { workspace = true, features = ["sync"]}
|
||||
toml = "0.5.6"
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
toml = { workspace = true }
|
||||
url = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::CommonConfigsWrapper;
|
||||
use anyhow::bail;
|
||||
use clap::Parser;
|
||||
use nym_credential_storage::initialise_persistent_storage;
|
||||
use nym_credential_utils::utils;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Config file of the client that is supposed to use the credential.
|
||||
#[clap(long)]
|
||||
pub(crate) client_config: PathBuf,
|
||||
}
|
||||
|
||||
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
|
||||
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
|
||||
|
||||
if let Ok(id) = loaded.try_get_id() {
|
||||
println!("loaded config file for client '{id}'");
|
||||
}
|
||||
|
||||
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
|
||||
bail!("the loaded config does not have a credentials store information")
|
||||
};
|
||||
|
||||
let Ok(private_id_key) = loaded.try_get_private_id_key() else {
|
||||
bail!("the loaded config does not have a public id key information")
|
||||
};
|
||||
|
||||
println!(
|
||||
"using credentials store at '{}'",
|
||||
credentials_store.display()
|
||||
);
|
||||
|
||||
let persistent_storage = initialise_persistent_storage(credentials_store).await;
|
||||
let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?;
|
||||
utils::issue_credential(&client, &persistent_storage, &private_id_key.to_bytes()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
+13
-5
@@ -9,9 +9,17 @@ use nym_credential_storage::initialise_persistent_storage;
|
||||
use nym_id::import_credential;
|
||||
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)]
|
||||
@@ -22,8 +30,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")]
|
||||
@@ -52,7 +60,7 @@ pub async fn execute(args: Args) -> anyhow::Result<()> {
|
||||
let credentials_store = initialise_persistent_storage(credentials_store).await;
|
||||
|
||||
let raw_credential = match args.credential_data {
|
||||
Some(data) => data,
|
||||
Some(data) => data.0,
|
||||
None => {
|
||||
// SAFETY: one of those arguments must have been set
|
||||
fs::read(args.credential_path.unwrap())?
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::CommonConfigsWrapper;
|
||||
use anyhow::bail;
|
||||
use clap::Parser;
|
||||
use nym_credential_storage::initialise_persistent_storage;
|
||||
use nym_credential_utils::utils;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use std::fs::create_dir_all;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Specify which type of ticketbook should be issued
|
||||
#[clap(long, default_value_t = TicketType::default())]
|
||||
pub(crate) ticketbook_type: TicketType,
|
||||
|
||||
/// Config file of the client that is supposed to use the credential.
|
||||
#[clap(long, group = "output")]
|
||||
pub(crate) client_config: Option<PathBuf>,
|
||||
|
||||
/// Path to the dedicated credential storage database
|
||||
#[clap(long, group = "output")]
|
||||
pub(crate) credential_storage: Option<PathBuf>,
|
||||
}
|
||||
|
||||
async fn issue_client_ticketbook(
|
||||
cfg: PathBuf,
|
||||
typ: TicketType,
|
||||
client: SigningClient,
|
||||
) -> anyhow::Result<()> {
|
||||
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")
|
||||
};
|
||||
|
||||
let Ok(private_id_key) = loaded.try_get_private_id_key() else {
|
||||
bail!("the loaded config does not have a public id key information")
|
||||
};
|
||||
|
||||
println!(
|
||||
"using credentials store at '{}'",
|
||||
credentials_store.display()
|
||||
);
|
||||
|
||||
let persistent_storage = initialise_persistent_storage(credentials_store).await;
|
||||
let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?;
|
||||
utils::issue_credential(
|
||||
&client,
|
||||
&persistent_storage,
|
||||
&private_id_key.to_bytes(),
|
||||
typ,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn issue_standalone_ticketbook(
|
||||
credentials_store: PathBuf,
|
||||
typ: TicketType,
|
||||
client: SigningClient,
|
||||
) -> anyhow::Result<()> {
|
||||
println!("attempting to issue a standalone ticketbook");
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut random_seed = [0u8; 32];
|
||||
rng.fill_bytes(&mut random_seed);
|
||||
|
||||
if let Some(parent) = credentials_store.parent() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let persistent_storage = initialise_persistent_storage(credentials_store).await;
|
||||
utils::issue_credential(&client, &persistent_storage, &random_seed, typ).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
|
||||
match (args.client_config, args.credential_storage) {
|
||||
(Some(cfg), None) => issue_client_ticketbook(cfg, args.ticketbook_type, client).await,
|
||||
(None, Some(storage)) => {
|
||||
issue_standalone_ticketbook(storage, args.ticketbook_type, client).await
|
||||
}
|
||||
_ => unreachable!("clap should have made this branch impossible to reach!"),
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod generate_ticket;
|
||||
pub mod import_ticket_book;
|
||||
pub mod issue_ticket_book;
|
||||
pub mod recover_ticket_book;
|
||||
@@ -19,4 +20,5 @@ pub enum EcashCommands {
|
||||
IssueTicketBook(issue_ticket_book::Args),
|
||||
RecoverTicketBook(recover_ticket_book::Args),
|
||||
ImportTicketBook(import_ticket_book::Args),
|
||||
GenerateTicket(generate_ticket::Args),
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod coconut;
|
||||
pub mod context;
|
||||
pub mod ecash;
|
||||
pub mod utils;
|
||||
pub mod validator;
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
|
||||
use cosmwasm_std::Decimal;
|
||||
use nym_mixnet_contract_common::{InitialRewardingParams, InstantiateMsg, Percent};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use log::{debug, info};
|
||||
use nym_mixnet_contract_common::{
|
||||
InitialRewardingParams, InstantiateMsg, OperatingCostRange, Percent, ProfitMarginRange,
|
||||
};
|
||||
use nym_network_defaults::mainnet::MIX_DENOM;
|
||||
use nym_network_defaults::TOTAL_SUPPLY;
|
||||
use nym_validator_client::nyxd::{AccountId, Coin};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn default_maximum_operating_cost() -> Coin {
|
||||
Coin::new(TOTAL_SUPPLY, MIX_DENOM.base)
|
||||
}
|
||||
|
||||
pub fn default_minimum_operating_cost() -> Coin {
|
||||
Coin::new(0, MIX_DENOM.base)
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
@@ -50,6 +61,18 @@ pub struct Args {
|
||||
|
||||
#[clap(long, default_value_t = 240)]
|
||||
pub active_set_size: u32,
|
||||
|
||||
#[clap(long, default_value_t = Percent::zero())]
|
||||
pub minimum_profit_margin_percent: Percent,
|
||||
|
||||
#[clap(long, default_value_t = Percent::hundred())]
|
||||
pub maximum_profit_margin_percent: Percent,
|
||||
|
||||
#[clap(long, default_value_t = default_minimum_operating_cost())]
|
||||
pub minimum_interval_operating_cost: Coin,
|
||||
|
||||
#[clap(long, default_value_t = default_maximum_operating_cost())]
|
||||
pub maximum_interval_operating_cost: Coin,
|
||||
}
|
||||
|
||||
pub async fn generate(args: Args) {
|
||||
@@ -97,6 +120,10 @@ pub async fn generate(args: Args) {
|
||||
.expect("Rewarding (mix) denom has to be set")
|
||||
});
|
||||
|
||||
if args.minimum_interval_operating_cost.denom != args.maximum_interval_operating_cost.denom {
|
||||
panic!("different denoms for operating cost bounds")
|
||||
}
|
||||
|
||||
let instantiate_msg = InstantiateMsg {
|
||||
rewarding_validator_address: rewarding_validator_address.to_string(),
|
||||
vesting_contract_address: vesting_contract_address.to_string(),
|
||||
@@ -104,6 +131,14 @@ pub async fn generate(args: Args) {
|
||||
epochs_in_interval: args.epochs_in_interval,
|
||||
epoch_duration: Duration::from_secs(args.epoch_duration),
|
||||
initial_rewarding_params,
|
||||
profit_margin: ProfitMarginRange {
|
||||
minimum: args.minimum_profit_margin_percent,
|
||||
maximum: args.maximum_profit_margin_percent,
|
||||
},
|
||||
interval_operating_cost: OperatingCostRange {
|
||||
minimum: args.minimum_interval_operating_cost.amount.into(),
|
||||
maximum: args.maximum_interval_operating_cost.amount.into(),
|
||||
},
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn migrate_vested_delegation(args: Args, client: SigningClient) {
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.mixnode_details
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let res = client
|
||||
.migrate_vested_delegation(mix_id, None)
|
||||
.await
|
||||
.expect("failed to migrate delegation!");
|
||||
|
||||
info!("migration result: {:?}", res)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ pub mod rewards;
|
||||
|
||||
pub mod delegate_to_mixnode;
|
||||
pub mod delegate_to_multiple_mixnodes;
|
||||
pub mod migrate_vested_delegation;
|
||||
pub mod query_for_delegations;
|
||||
pub mod undelegate_from_mixnode;
|
||||
pub mod vesting_delegate_to_mixnode;
|
||||
@@ -35,4 +36,6 @@ pub enum MixnetDelegatorsCommands {
|
||||
DelegateVesting(vesting_delegate_to_mixnode::Args),
|
||||
/// Undelegate from a mixnode (when originally using locked tokens)
|
||||
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
|
||||
/// Migrate the delegation to use liquid tokens
|
||||
MigrateVestedDelegation(migrate_vested_delegation::Args),
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
|
||||
mix_id,
|
||||
amount,
|
||||
proxy,
|
||||
..
|
||||
} => {
|
||||
if owner.as_str() == client.nyxd.address().as_ref() {
|
||||
table.add_row(vec![
|
||||
@@ -111,6 +112,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
|
||||
owner,
|
||||
mix_id,
|
||||
proxy,
|
||||
..
|
||||
} => {
|
||||
if owner.as_str() == client.nyxd.address().as_ref() {
|
||||
table.add_row(vec![
|
||||
|
||||
+2
-13
@@ -8,7 +8,7 @@ use cosmwasm_std::Coin;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
|
||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -39,10 +39,6 @@ pub struct Args {
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the gateway is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
@@ -74,15 +70,8 @@ pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(&client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(
|
||||
client.vesting_contract_address().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload = construct_gateway_bonding_sign_payload(nonce, address, proxy, coin, gateway);
|
||||
let payload = construct_gateway_bonding_sign_payload(nonce, address, coin, gateway);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -5,33 +5,21 @@ use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Label that is going to be used for creating the family
|
||||
#[arg(long)]
|
||||
pub family_label: String,
|
||||
|
||||
/// Indicates whether the family is going to get created via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn create_family(args: Args, client: SigningClient) {
|
||||
info!("Create family");
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family with vesting account")
|
||||
} else {
|
||||
client
|
||||
.create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family")
|
||||
};
|
||||
let res = client
|
||||
.create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family");
|
||||
|
||||
info!("Family creation result: {:?}", res);
|
||||
}
|
||||
|
||||
+3
-16
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::QueryClient;
|
||||
use crate::utils::{account_id_to_cw_addr, DataWrapper};
|
||||
use crate::utils::DataWrapper;
|
||||
use clap::Parser;
|
||||
use cosmrs::AccountId;
|
||||
use log::info;
|
||||
@@ -10,7 +10,7 @@ use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::construct_family_join_permit;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -18,10 +18,6 @@ pub struct Args {
|
||||
#[arg(long)]
|
||||
pub address: AccountId,
|
||||
|
||||
/// Indicates whether the member joining the family is going to use the vesting account for joining.
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
// might as well validate the value when parsing the arguments
|
||||
/// Identity of the member for whom we're issuing the permit
|
||||
#[arg(long)]
|
||||
@@ -68,18 +64,9 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli
|
||||
}
|
||||
};
|
||||
|
||||
// let address = account_id_to_cw_addr(&args.address);
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(
|
||||
client.vesting_contract_address().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let head = FamilyHead::new(mixnode.bond_information.identity());
|
||||
|
||||
let payload = construct_family_join_permit(nonce, head, proxy, args.member.to_base58_string());
|
||||
let payload = construct_family_join_permit(nonce, head, args.member.to_base58_string());
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -16,10 +15,6 @@ pub struct Args {
|
||||
#[arg(long)]
|
||||
pub family_head: identity::PublicKey,
|
||||
|
||||
/// Indicates whether the member joining the family is going to do so via the vesting contract
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
/// Permission, as provided by the family head, for joining the family
|
||||
#[arg(long)]
|
||||
pub join_permit: MessageSignature,
|
||||
@@ -30,17 +25,10 @@ pub async fn join_family(args: Args, client: SigningClient) {
|
||||
|
||||
let family_head = FamilyHead::new(args.family_head.to_base58_string());
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family with vesting account")
|
||||
} else {
|
||||
client
|
||||
.join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family")
|
||||
};
|
||||
let res = client
|
||||
.join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family");
|
||||
|
||||
info!("Family join result: {:?}", res);
|
||||
}
|
||||
|
||||
@@ -7,17 +7,12 @@ use log::info;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// The head of the family that we intend to leave
|
||||
#[arg(long)]
|
||||
pub family_head: identity::PublicKey,
|
||||
|
||||
/// Indicates whether we joined the family via the vesting contract
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn leave_family(args: Args, client: SigningClient) {
|
||||
@@ -25,17 +20,10 @@ pub async fn leave_family(args: Args, client: SigningClient) {
|
||||
|
||||
let family_head = FamilyHead::new(args.family_head.to_base58_string());
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family with vesting account")
|
||||
} else {
|
||||
client
|
||||
.leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family")
|
||||
};
|
||||
let res = client
|
||||
.leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family");
|
||||
|
||||
info!("Family leave result: {:?}", res);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,11 @@ pub struct Args {
|
||||
}
|
||||
|
||||
pub fn decode_mixnode_key(args: Args) {
|
||||
let b64_decoded = base64::decode(args.key).expect("failed to decode base64 string");
|
||||
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();
|
||||
|
||||
println!("{b58_encoded}")
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
pub async fn migrate_vested_mixnode(_args: Args, client: SigningClient) {
|
||||
let res = client
|
||||
.migrate_vested_mixnode(None)
|
||||
.await
|
||||
.expect("failed to migrate mixnode!");
|
||||
|
||||
info!("migration result: {:?}", res)
|
||||
}
|
||||
+2
-13
@@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -52,10 +52,6 @@ pub struct Args {
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the mixnode is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
@@ -100,16 +96,9 @@ pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(&client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(
|
||||
client.vesting_contract_address().unwrap(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload =
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod bond_mixnode;
|
||||
pub mod decrease_pledge;
|
||||
pub mod families;
|
||||
pub mod keys;
|
||||
pub mod migrate_vested_mixnode;
|
||||
pub mod mixnode_bonding_sign_payload;
|
||||
pub mod pledge_more;
|
||||
pub mod rewards;
|
||||
@@ -52,4 +53,6 @@ pub enum MixnetOperatorsMixnodeCommands {
|
||||
DecreasePledge(decrease_pledge::Args),
|
||||
/// Decrease pledge with locked tokens
|
||||
DecreasePledgeVesting(vesting_decrease_pledge::Args),
|
||||
/// Migrate the mixnode to use liquid tokens
|
||||
MigrateVestedNode(migrate_vested_mixnode::Args),
|
||||
}
|
||||
|
||||
@@ -218,7 +218,6 @@ where
|
||||
#[derive(Serialize)]
|
||||
pub struct ContractMessageContent<T> {
|
||||
pub sender: Addr,
|
||||
pub proxy: Option<Addr>,
|
||||
pub funds: Vec<Coin>,
|
||||
pub data: T,
|
||||
}
|
||||
@@ -233,25 +232,17 @@ where
|
||||
}
|
||||
|
||||
impl<T> ContractMessageContent<T> {
|
||||
pub fn new(sender: Addr, proxy: Option<Addr>, funds: Vec<Coin>, data: T) -> Self {
|
||||
pub fn new(sender: Addr, funds: Vec<Coin>, data: T) -> Self {
|
||||
ContractMessageContent {
|
||||
sender,
|
||||
proxy,
|
||||
funds,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_info(info: MessageInfo, signer: Addr, data: T) -> Self {
|
||||
let proxy = if info.sender == signer {
|
||||
None
|
||||
} else {
|
||||
Some(info.sender)
|
||||
};
|
||||
|
||||
ContractMessageContent {
|
||||
sender: signer,
|
||||
proxy,
|
||||
funds: info.funds,
|
||||
data,
|
||||
}
|
||||
|
||||
@@ -7,6 +7,5 @@ use cosmwasm_std::Coin;
|
||||
#[cw_serde]
|
||||
pub struct PoolCounters {
|
||||
pub total_deposited: Coin,
|
||||
pub total_redeemed_gateways: Coin,
|
||||
pub total_redeemed_holding: Coin,
|
||||
pub total_redeemed: Coin,
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ impl Delegation {
|
||||
cumulative_reward_ratio: Decimal,
|
||||
amount: Coin,
|
||||
height: u64,
|
||||
proxy: Option<Addr>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
amount.amount <= TOKEN_SUPPLY,
|
||||
@@ -78,7 +77,7 @@ impl Delegation {
|
||||
cumulative_reward_ratio,
|
||||
amount,
|
||||
height,
|
||||
proxy,
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{EpochEventId, EpochState, IdentityKey, MixId};
|
||||
use crate::{EpochEventId, EpochState, IdentityKey, MixId, OperatingCostRange, ProfitMarginRange};
|
||||
use contracts_common::signing::verifier::ApiVerifierError;
|
||||
use contracts_common::Percent;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -76,21 +77,11 @@ pub enum MixnetContractError {
|
||||
#[error("Received multiple coin types during staking")]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("Proxy address mismatch, expected {existing}, got {incoming}")]
|
||||
ProxyMismatch { existing: String, incoming: String },
|
||||
|
||||
#[error("Proxy address ({received}) is not set to the vesting contract ({vesting_contract})")]
|
||||
ProxyIsNotVestingContract {
|
||||
received: Addr,
|
||||
vesting_contract: Addr,
|
||||
},
|
||||
#[error(
|
||||
"Sender of this message ({received}) is not the vesting contract ({vesting_contract})"
|
||||
)]
|
||||
SenderIsNotVestingContract {
|
||||
received: Addr,
|
||||
vesting_contract: Addr,
|
||||
},
|
||||
|
||||
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
|
||||
MalformedEd25519IdentityKey(String),
|
||||
@@ -239,6 +230,30 @@ pub enum MixnetContractError {
|
||||
#[from]
|
||||
source: ApiVerifierError,
|
||||
},
|
||||
|
||||
#[error("this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again")]
|
||||
DisabledVestingOperation,
|
||||
|
||||
#[error(
|
||||
"this mixnode has not been bonded with the vesting tokens or has already been migrated"
|
||||
)]
|
||||
NotAVestingMixnode,
|
||||
|
||||
#[error("this delegation has not been performed with the vesting tokens or has already been migrated")]
|
||||
NotAVestingDelegation,
|
||||
|
||||
#[error("the provided profit margin ({provided}) is outside the allowed range: {range}")]
|
||||
ProfitMarginOutsideRange {
|
||||
provided: Percent,
|
||||
range: ProfitMarginRange,
|
||||
},
|
||||
|
||||
#[error("the provided interval operating cost ({provided}{denom}) is outside the allowed range: {range}")]
|
||||
OperatingCostOutsideRange {
|
||||
denom: String,
|
||||
provided: Uint128,
|
||||
range: OperatingCostRange,
|
||||
},
|
||||
}
|
||||
|
||||
impl MixnetContractError {
|
||||
|
||||
@@ -103,7 +103,6 @@ impl Display for MixnetEventType {
|
||||
// attributes that are used in multiple places
|
||||
pub const OWNER_KEY: &str = "owner";
|
||||
pub const AMOUNT_KEY: &str = "amount";
|
||||
pub const PROXY_KEY: &str = "proxy";
|
||||
|
||||
// event-specific attributes
|
||||
|
||||
@@ -163,7 +162,6 @@ pub const NEW_EPOCHS_IN_INTERVAL: &str = "new_epochs_in_interval";
|
||||
pub fn new_delegation_event(
|
||||
created_at: BlockHeight,
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
mix_id: MixId,
|
||||
unit_reward: Decimal,
|
||||
@@ -171,58 +169,34 @@ pub fn new_delegation_event(
|
||||
Event::new(MixnetEventType::Delegation)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
.add_attribute(UNIT_REWARD_KEY, unit_reward.to_string())
|
||||
}
|
||||
|
||||
pub fn new_delegation_on_unbonded_node_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
pub fn new_delegation_on_unbonded_node_event(delegator: &Addr, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::Delegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_delegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
pub fn new_pending_delegation_event(delegator: &Addr, amount: &Coin, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::PendingDelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_withdraw_operator_reward_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: Coin,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
pub fn new_withdraw_operator_reward_event(owner: &Addr, amount: Coin, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::WithdrawOperatorReward)
|
||||
.add_attribute(OWNER_KEY, owner.as_str())
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_withdraw_delegator_reward_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: Coin,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
pub fn new_withdraw_delegator_reward_event(delegator: &Addr, amount: Coin, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::WithdrawDelegatorReward)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
@@ -278,59 +252,43 @@ pub fn new_pending_rewarding_params_update_event(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_undelegation_event(
|
||||
created_at: BlockHeight,
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
pub fn new_undelegation_event(created_at: BlockHeight, delegator: &Addr, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::Undelegation)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_undelegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
pub fn new_pending_undelegation_event(delegator: &Addr, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::PendingUndelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_bonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::GatewayBonding)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_unbonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::GatewayUnbonding)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_bonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: MixId,
|
||||
@@ -341,7 +299,6 @@ pub fn new_mixnode_bonding_event(
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
@@ -380,7 +337,6 @@ pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Ev
|
||||
|
||||
pub fn new_pending_mixnode_unbonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: MixId,
|
||||
) -> Event {
|
||||
@@ -388,43 +344,33 @@ pub fn new_pending_mixnode_unbonding_event(
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_config_update_event(
|
||||
mix_id: MixId,
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
update: &MixNodeConfigUpdate,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeConfigUpdate)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(UPDATED_MIXNODE_CONFIG_KEY, update.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_gateway_config_update_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
update: &GatewayConfigUpdate,
|
||||
) -> Event {
|
||||
pub fn new_gateway_config_update_event(owner: &Addr, update: &GatewayConfigUpdate) -> Event {
|
||||
Event::new(MixnetEventType::GatewayConfigUpdate)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(UPDATED_GATEWAY_CONFIG_KEY, update.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_pending_cost_params_update_event(
|
||||
mix_id: MixId,
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
new_costs: &MixNodeCostParams,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingMixnodeCostParamsUpdate)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::{IdentityKey, IdentityKeyRef};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{Display, Formatter};
|
||||
@@ -84,10 +83,10 @@ impl FamilyHead {
|
||||
}
|
||||
|
||||
impl Family {
|
||||
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: String) -> Self {
|
||||
pub fn new(head: FamilyHead, label: String) -> Self {
|
||||
Family {
|
||||
head,
|
||||
proxy: proxy.map(|p| p.to_string()),
|
||||
proxy: None,
|
||||
label,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,19 +55,13 @@ pub struct GatewayBond {
|
||||
}
|
||||
|
||||
impl GatewayBond {
|
||||
pub fn new(
|
||||
pledge_amount: Coin,
|
||||
owner: Addr,
|
||||
block_height: u64,
|
||||
gateway: Gateway,
|
||||
proxy: Option<Addr>,
|
||||
) -> Self {
|
||||
pub fn new(pledge_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
|
||||
GatewayBond {
|
||||
pledge_amount,
|
||||
owner,
|
||||
block_height,
|
||||
gateway,
|
||||
proxy,
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ use crate::helpers::IntoBaseDecimal;
|
||||
use crate::reward_params::{NodeRewardParams, RewardingParams};
|
||||
use crate::rewarding::helpers::truncate_reward;
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{Delegation, EpochEventId, EpochId, IdentityKey, MixId, Percent, SphinxKey};
|
||||
use crate::{
|
||||
Delegation, EpochEventId, EpochId, IdentityKey, MixId, OperatingCostRange, Percent,
|
||||
ProfitMarginRange, SphinxKey,
|
||||
};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
@@ -152,6 +155,16 @@ impl MixNodeRewarding {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn normalise_profit_margin(&mut self, allowed_range: ProfitMarginRange) {
|
||||
self.cost_params.profit_margin_percent =
|
||||
allowed_range.normalise(self.cost_params.profit_margin_percent)
|
||||
}
|
||||
|
||||
pub fn normalise_operating_cost(&mut self, allowed_range: OperatingCostRange) {
|
||||
self.cost_params.interval_operating_cost.amount =
|
||||
allowed_range.normalise(self.cost_params.interval_operating_cost.amount)
|
||||
}
|
||||
|
||||
/// Determines whether this node is still bonded. This is performed via a simple check,
|
||||
/// if there are no tokens left associated with the operator, it means they have unbonded
|
||||
/// and those params only exist for the purposes of calculating rewards for delegators that
|
||||
@@ -518,7 +531,6 @@ impl MixNodeBond {
|
||||
original_pledge: Coin,
|
||||
layer: Layer,
|
||||
mix_node: MixNode,
|
||||
proxy: Option<Addr>,
|
||||
bonding_height: u64,
|
||||
) -> Self {
|
||||
MixNodeBond {
|
||||
@@ -527,7 +539,7 @@ impl MixNodeBond {
|
||||
original_pledge,
|
||||
layer,
|
||||
mix_node,
|
||||
proxy,
|
||||
proxy: None,
|
||||
bonding_height,
|
||||
is_unbonding: false,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::delegation::{self, OwnerProxySubKey};
|
||||
@@ -12,6 +12,7 @@ use crate::reward_params::{
|
||||
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
||||
};
|
||||
use crate::types::{ContractStateParams, LayerAssignment, MixId};
|
||||
use crate::{OperatingCostRange, ProfitMarginRange};
|
||||
use contracts_common::{signing::MessageSignature, IdentityKey, Percent};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
@@ -57,6 +58,12 @@ pub struct InstantiateMsg {
|
||||
pub epochs_in_interval: u32,
|
||||
pub epoch_duration: Duration,
|
||||
pub initial_rewarding_params: InitialRewardingParams,
|
||||
|
||||
#[serde(default)]
|
||||
pub profit_margin: ProfitMarginRange,
|
||||
|
||||
#[serde(default)]
|
||||
pub interval_operating_cost: OperatingCostRange,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
@@ -269,6 +276,12 @@ pub enum ExecuteMsg {
|
||||
owner: String,
|
||||
},
|
||||
|
||||
// vesting migration:
|
||||
MigrateVestedMixNode {},
|
||||
MigrateVestedDelegation {
|
||||
mix_id: MixId,
|
||||
},
|
||||
|
||||
// testing-only
|
||||
#[cfg(feature = "contract-testing")]
|
||||
TestingResolveAllPendingEvents {
|
||||
@@ -381,6 +394,9 @@ impl ExecuteMsg {
|
||||
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, .. } => {
|
||||
format!("withdrawing delegator reward from mixnode {mix_id} on behalf")
|
||||
}
|
||||
ExecuteMsg::MigrateVestedMixNode { .. } => "migrate vested mixnode".into(),
|
||||
ExecuteMsg::MigrateVestedDelegation { .. } => "migrate vested delegation".to_string(),
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
ExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
"resolving all pending events".into()
|
||||
|
||||
@@ -38,6 +38,7 @@ pub enum PendingEpochEventKind {
|
||||
/// Request to create a delegation towards particular mixnode.
|
||||
/// Note that if such delegation already exists, it will get updated with the provided token amount.
|
||||
#[serde(alias = "Delegate")]
|
||||
#[non_exhaustive]
|
||||
Delegate {
|
||||
/// The address of the owner of the delegation.
|
||||
owner: Addr,
|
||||
@@ -55,6 +56,7 @@ pub enum PendingEpochEventKind {
|
||||
|
||||
/// Request to remove delegation from particular mixnode.
|
||||
#[serde(alias = "Undelegate")]
|
||||
#[non_exhaustive]
|
||||
Undelegate {
|
||||
/// The address of the owner of the delegation.
|
||||
owner: Addr,
|
||||
@@ -109,6 +111,23 @@ impl PendingEpochEventKind {
|
||||
kind: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_delegate(owner: Addr, mix_id: MixId, amount: Coin) -> Self {
|
||||
PendingEpochEventKind::Delegate {
|
||||
owner,
|
||||
mix_id,
|
||||
amount,
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_undelegate(owner: Addr, mix_id: MixId) -> Self {
|
||||
PendingEpochEventKind::Undelegate {
|
||||
owner,
|
||||
mix_id,
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
|
||||
|
||||
-1
@@ -47,7 +47,6 @@ impl SimulatedNode {
|
||||
self.rewarding_details.total_unit_reward,
|
||||
delegation,
|
||||
42,
|
||||
None,
|
||||
);
|
||||
|
||||
self.delegations.insert(delegator, delegation);
|
||||
|
||||
@@ -37,13 +37,12 @@ impl SigningPurpose for MixnodeBondingPayload {
|
||||
pub fn construct_mixnode_bonding_sign_payload(
|
||||
nonce: Nonce,
|
||||
sender: Addr,
|
||||
proxy: Option<Addr>,
|
||||
pledge: Coin,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
) -> SignableMixNodeBondingMsg {
|
||||
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
|
||||
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
|
||||
let content = ContractMessageContent::new(sender, vec![pledge], payload);
|
||||
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
@@ -68,12 +67,11 @@ impl SigningPurpose for GatewayBondingPayload {
|
||||
pub fn construct_gateway_bonding_sign_payload(
|
||||
nonce: Nonce,
|
||||
sender: Addr,
|
||||
proxy: Option<Addr>,
|
||||
pledge: Coin,
|
||||
gateway: Gateway,
|
||||
) -> SignableGatewayBondingMsg {
|
||||
let payload = GatewayBondingPayload::new(gateway);
|
||||
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
|
||||
let content = ContractMessageContent::new(sender, vec![pledge], payload);
|
||||
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
@@ -82,17 +80,14 @@ pub fn construct_gateway_bonding_sign_payload(
|
||||
pub struct FamilyJoinPermit {
|
||||
// the granter of this permit
|
||||
family_head: FamilyHead,
|
||||
// whether the **member** will want to join via the proxy (i.e. vesting contract)
|
||||
proxy: Option<Addr>,
|
||||
// the actual member we want to permit to join
|
||||
member_node: IdentityKey,
|
||||
}
|
||||
|
||||
impl FamilyJoinPermit {
|
||||
pub fn new(family_head: FamilyHead, proxy: Option<Addr>, member_node: IdentityKey) -> Self {
|
||||
pub fn new(family_head: FamilyHead, member_node: IdentityKey) -> Self {
|
||||
Self {
|
||||
family_head,
|
||||
proxy,
|
||||
member_node,
|
||||
}
|
||||
}
|
||||
@@ -107,10 +102,9 @@ impl SigningPurpose for FamilyJoinPermit {
|
||||
pub fn construct_family_join_permit(
|
||||
nonce: Nonce,
|
||||
family_head: FamilyHead,
|
||||
proxy: Option<Addr>,
|
||||
member_node: IdentityKey,
|
||||
) -> SignableFamilyJoinPermitMsg {
|
||||
let payload = FamilyJoinPermit::new(family_head, proxy, member_node);
|
||||
let payload = FamilyJoinPermit::new(family_head, member_node);
|
||||
|
||||
// note: we're NOT wrapping it in `ContractMessageContent` because the family head is not going to be the one
|
||||
// sending the message to the contract
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::Layer;
|
||||
use contracts_common::Percent;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
use cosmwasm_std::Coin;
|
||||
use cosmwasm_std::{Addr, Uint128};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Index;
|
||||
|
||||
// type aliases for better reasoning about available data
|
||||
@@ -15,6 +17,65 @@ pub type SphinxKeyRef<'a> = &'a str;
|
||||
pub type MixId = u32;
|
||||
pub type BlockHeight = u64;
|
||||
|
||||
#[cw_serde]
|
||||
pub struct RangedValue<T> {
|
||||
pub minimum: T,
|
||||
pub maximum: T,
|
||||
}
|
||||
|
||||
impl<T> Copy for RangedValue<T> where T: Copy {}
|
||||
|
||||
pub type ProfitMarginRange = RangedValue<Percent>;
|
||||
pub type OperatingCostRange = RangedValue<Uint128>;
|
||||
|
||||
impl<T> Display for RangedValue<T>
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} - {}", self.minimum, self.maximum)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProfitMarginRange {
|
||||
fn default() -> Self {
|
||||
ProfitMarginRange {
|
||||
minimum: Percent::zero(),
|
||||
maximum: Percent::hundred(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OperatingCostRange {
|
||||
fn default() -> Self {
|
||||
OperatingCostRange {
|
||||
minimum: Uint128::zero(),
|
||||
|
||||
// 1 billion (native tokens, i.e. 1 billion * 1'000'000 base tokens) - the total supply
|
||||
maximum: Uint128::new(1_000_000_000_000_000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RangedValue<T>
|
||||
where
|
||||
T: Copy + PartialOrd + PartialEq,
|
||||
{
|
||||
pub fn normalise(&self, value: T) -> T {
|
||||
if value < self.minimum {
|
||||
self.minimum
|
||||
} else if value > self.maximum {
|
||||
self.maximum
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn within_range(&self, value: T) -> bool {
|
||||
value >= self.minimum && value <= self.maximum
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies layer assignment for the given mixnode.
|
||||
#[cw_serde]
|
||||
pub struct LayerAssignment {
|
||||
@@ -154,4 +215,14 @@ pub struct ContractStateParams {
|
||||
|
||||
/// Minimum amount a gateway must pledge to get into the system.
|
||||
pub minimum_gateway_pledge: Coin,
|
||||
|
||||
/// Defines the allowed profit margin range of operators.
|
||||
/// default: 0% - 100%
|
||||
#[serde(default)]
|
||||
pub profit_margin: ProfitMarginRange,
|
||||
|
||||
/// Defines the allowed interval operating cost range of operators.
|
||||
/// default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)
|
||||
#[serde(default)]
|
||||
pub interval_operating_cost: OperatingCostRange,
|
||||
}
|
||||
|
||||
@@ -167,3 +167,11 @@ pub fn new_track_undelegation_event() -> Event {
|
||||
pub fn new_track_reward_event() -> Event {
|
||||
Event::new(TRACK_REWARD_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_migrate_mixnode_event() -> Event {
|
||||
Event::new("track_migrate_vesting_mixnode")
|
||||
}
|
||||
|
||||
pub fn new_track_migrate_delegation_event() -> Event {
|
||||
Event::new("track_migrate_vesting_delegation")
|
||||
}
|
||||
|
||||
@@ -136,6 +136,14 @@ pub enum ExecuteMsg {
|
||||
address: String,
|
||||
cap: PledgeCap,
|
||||
},
|
||||
TrackMigratedMixnode {
|
||||
owner: String,
|
||||
},
|
||||
// no need to track migrated gateways as there are no vesting gateways on mainnet
|
||||
TrackMigratedDelegation {
|
||||
owner: String,
|
||||
mix_id: MixId,
|
||||
},
|
||||
}
|
||||
|
||||
impl ExecuteMsg {
|
||||
@@ -171,6 +179,10 @@ impl ExecuteMsg {
|
||||
ExecuteMsg::TransferOwnership { .. } => "VestingExecuteMsg::TransferOwnership",
|
||||
ExecuteMsg::UpdateStakingAddress { .. } => "VestingExecuteMsg::UpdateStakingAddress",
|
||||
ExecuteMsg::UpdateLockedPledgeCap { .. } => "VestingExecuteMsg::UpdateLockedPledgeCap",
|
||||
ExecuteMsg::TrackMigratedMixnode { .. } => "VestingExecuteMsg::TrackMigratedMixnode",
|
||||
ExecuteMsg::TrackMigratedDelegation { .. } => {
|
||||
"VestingExecuteMsg::TrackMigratedDelegation"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ CREATE TABLE ecash_ticketbook
|
||||
-- introduce a way for us to introduce breaking changes in serialization of data
|
||||
serialization_revision INTEGER NOT NULL,
|
||||
|
||||
-- the type of the associated ticketbook
|
||||
ticketbook_type TEXT NOT NULL,
|
||||
|
||||
-- the actual crypto data of the ticketbook (wallet, keys, etc.)
|
||||
ticketbook_data BLOB NOT NULL UNIQUE,
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ impl MemoryEcachTicketbookManager {
|
||||
.map(|t| BasicTicketbookInformation {
|
||||
id: t.ticketbook_id,
|
||||
expiration_date: t.ticketbook.expiration_date(),
|
||||
ticketbook_type: t.ticketbook.ticketbook_type().to_string(),
|
||||
epoch_id: t.ticketbook.epoch_id() as u32,
|
||||
total_tickets: t.ticketbook.spent_tickets() as u32,
|
||||
used_tickets: t.ticketbook.params_total_tickets() as u32,
|
||||
|
||||
@@ -61,11 +61,13 @@ impl SqliteEcashTicketbookManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn insert_new_ticketbook(
|
||||
&self,
|
||||
serialisation_revision: u8,
|
||||
data: &[u8],
|
||||
expiration_date: Date,
|
||||
typ: &str,
|
||||
epoch_id: u32,
|
||||
total_tickets: u32,
|
||||
used_tickets: u32,
|
||||
@@ -73,12 +75,13 @@ impl SqliteEcashTicketbookManager {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO ecash_ticketbook
|
||||
(serialization_revision, ticketbook_data, expiration_date, epoch_id, total_tickets, used_tickets)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
(serialization_revision, ticketbook_data, expiration_date, ticketbook_type, epoch_id, total_tickets, used_tickets)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
serialisation_revision,
|
||||
data,
|
||||
expiration_date,
|
||||
typ,
|
||||
epoch_id,
|
||||
total_tickets,
|
||||
used_tickets,
|
||||
@@ -92,7 +95,7 @@ impl SqliteEcashTicketbookManager {
|
||||
) -> Result<Vec<BasicTicketbookInformation>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, expiration_date, epoch_id, total_tickets, used_tickets
|
||||
SELECT id, expiration_date, ticketbook_type, epoch_id, total_tickets, used_tickets
|
||||
FROM ecash_ticketbook
|
||||
"#,
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct RetrievedPendingTicketbook {
|
||||
pub struct BasicTicketbookInformation {
|
||||
pub id: i64,
|
||||
pub expiration_date: Date,
|
||||
pub ticketbook_type: String,
|
||||
pub epoch_id: u32,
|
||||
pub total_tickets: u32,
|
||||
pub used_tickets: u32,
|
||||
@@ -31,6 +32,8 @@ pub struct StoredIssuedTicketbook {
|
||||
|
||||
pub serialization_revision: u8,
|
||||
|
||||
pub ticketbook_type: String,
|
||||
|
||||
pub ticketbook_data: Vec<u8>,
|
||||
|
||||
#[zeroize(skip)]
|
||||
|
||||
@@ -114,6 +114,7 @@ impl Storage for PersistentStorage {
|
||||
serialisation_revision,
|
||||
&data,
|
||||
ticketbook.expiration_date(),
|
||||
&ticketbook.ticketbook_type().to_string(),
|
||||
ticketbook.epoch_id() as u32,
|
||||
ticketbook.params_total_tickets() as u32,
|
||||
ticketbook.spent_tickets() as u32,
|
||||
|
||||
@@ -14,9 +14,9 @@ time.workspace = true
|
||||
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credentials-interface = { path = "../../common/credentials-interface" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage", features = ["persistent-storage"] }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-client-core = { path = "../../common/client-core" }
|
||||
nym-compact-ecash = { path = "../../common/nym_offline_compact_ecash" }
|
||||
nym-ecash-time = { path = "../../common/ecash-time" }
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_time::ecash_default_expiration_date;
|
||||
use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
@@ -16,7 +18,12 @@ use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub async fn issue_credential<C, S>(client: &C, storage: &S, client_id: &[u8]) -> Result<()>
|
||||
pub async fn issue_credential<C, S>(
|
||||
client: &C,
|
||||
storage: &S,
|
||||
client_id: &[u8],
|
||||
typ: TicketType,
|
||||
) -> Result<()>
|
||||
where
|
||||
C: DkgQueryClient + EcashSigningClient + EcashQueryClient + Send + Sync,
|
||||
S: Storage,
|
||||
@@ -36,19 +43,16 @@ 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,
|
||||
client_id,
|
||||
Some(ticketbook_expiration),
|
||||
typ,
|
||||
)
|
||||
.await?;
|
||||
info!("Deposit done");
|
||||
@@ -65,7 +69,7 @@ where
|
||||
}).map_err(Error::storage_error)?
|
||||
}
|
||||
|
||||
info!("Succeeded adding a ticketbook");
|
||||
info!("Succeeded adding a ticketbook of type '{typ}'");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ license.workspace = true
|
||||
bls12_381 = { workspace = true, default-features = false }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
time = { workspace = true, features = ["serde"] }
|
||||
rand = { workspace = true }
|
||||
|
||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_network_defaults::TicketTypeRepr;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
pub use nym_compact_ecash::{
|
||||
@@ -15,7 +17,6 @@ pub use nym_compact_ecash::{
|
||||
PartialCoinIndexSignature,
|
||||
},
|
||||
scheme::expiration_date_signatures::aggregate_expiration_signatures,
|
||||
scheme::expiration_date_signatures::date_scalar,
|
||||
scheme::expiration_date_signatures::{
|
||||
AnnotatedExpirationDateSignature, ExpirationDateSignature, ExpirationDateSignatureShare,
|
||||
PartialExpirationDateSignature,
|
||||
@@ -24,10 +25,10 @@ pub use nym_compact_ecash::{
|
||||
scheme::withdrawal::RequestInfo,
|
||||
scheme::Payment,
|
||||
scheme::{Wallet, WalletSignatures},
|
||||
withdrawal_request, Base58, BlindedSignature, Bytable, PartialWallet, PayInfo, PublicKeyUser,
|
||||
SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
|
||||
withdrawal_request, Base58, BlindedSignature, Bytable, EncodedDate, EncodedTicketType,
|
||||
PartialWallet, PayInfo, PublicKeyUser, SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
|
||||
};
|
||||
use nym_ecash_time::EcashTime;
|
||||
use nym_ecash_time::{ecash_today, EcashTime};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CredentialSigningData {
|
||||
@@ -38,6 +39,8 @@ pub struct CredentialSigningData {
|
||||
pub ecash_pub_key: PublicKeyUser,
|
||||
|
||||
pub expiration_date: Date,
|
||||
|
||||
pub ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
@@ -58,7 +61,7 @@ impl CredentialSpendingData {
|
||||
self.payment.spend_verify(
|
||||
verification_key,
|
||||
&self.pay_info,
|
||||
date_scalar(self.spend_date.ecash_unix_timestamp()),
|
||||
self.spend_date.ecash_unix_timestamp(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -216,3 +219,112 @@ impl From<PayInfo> for NymPayInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum TicketType {
|
||||
#[default]
|
||||
V1MixnetEntry,
|
||||
V1MixnetExit,
|
||||
V1WireguardEntry,
|
||||
V1WireguardExit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Error)]
|
||||
#[error("provided unknown ticketbook type")]
|
||||
pub struct UnknownTicketType;
|
||||
|
||||
impl TicketType {
|
||||
pub fn to_repr(&self) -> TicketTypeRepr {
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> EncodedTicketType {
|
||||
self.to_repr() as EncodedTicketType
|
||||
}
|
||||
|
||||
pub fn try_from_encoded(val: EncodedTicketType) -> Result<Self, UnknownTicketType> {
|
||||
match val {
|
||||
n if n == TicketTypeRepr::V1MixnetEntry as u8 => {
|
||||
Ok(TicketTypeRepr::V1MixnetEntry.into())
|
||||
}
|
||||
n if n == TicketTypeRepr::V1MixnetExit as u8 => Ok(TicketTypeRepr::V1MixnetExit.into()),
|
||||
n if n == TicketTypeRepr::V1WireguardEntry as u8 => {
|
||||
Ok(TicketTypeRepr::V1WireguardEntry.into())
|
||||
}
|
||||
n if n == TicketTypeRepr::V1WireguardExit as u8 => {
|
||||
Ok(TicketTypeRepr::V1WireguardExit.into())
|
||||
}
|
||||
_ => Err(UnknownTicketType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TicketType> for TicketTypeRepr {
|
||||
fn from(value: TicketType) -> Self {
|
||||
match value {
|
||||
TicketType::V1MixnetEntry => TicketTypeRepr::V1MixnetEntry,
|
||||
TicketType::V1MixnetExit => TicketTypeRepr::V1MixnetExit,
|
||||
TicketType::V1WireguardEntry => TicketTypeRepr::V1WireguardEntry,
|
||||
TicketType::V1WireguardExit => TicketTypeRepr::V1WireguardExit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TicketTypeRepr> for TicketType {
|
||||
fn from(value: TicketTypeRepr) -> Self {
|
||||
match value {
|
||||
TicketTypeRepr::V1MixnetEntry => TicketType::V1MixnetEntry,
|
||||
TicketTypeRepr::V1MixnetExit => TicketType::V1MixnetExit,
|
||||
TicketTypeRepr::V1WireguardEntry => TicketType::V1WireguardEntry,
|
||||
TicketTypeRepr::V1WireguardExit => TicketType::V1WireguardExit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientTicket {
|
||||
pub spending_data: CredentialSpendingData,
|
||||
pub ticket_id: i64,
|
||||
}
|
||||
|
||||
impl ClientTicket {
|
||||
pub fn new(spending_data: CredentialSpendingData, ticket_id: i64) -> Self {
|
||||
ClientTicket {
|
||||
spending_data,
|
||||
ticket_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AvailableBandwidth {
|
||||
pub bytes: i64,
|
||||
pub expiration: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl AvailableBandwidth {
|
||||
pub fn expired(&self) -> bool {
|
||||
self.expiration < ecash_today()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AvailableBandwidth {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bytes: 0,
|
||||
expiration: OffsetDateTime::UNIX_EPOCH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ecash::bandwidth::issued::IssuedTicketBook;
|
||||
use crate::ecash::bandwidth::serialiser::VersionedSerialise;
|
||||
use crate::ecash::bandwidth::CredentialSigningData;
|
||||
use crate::ecash::utils::cred_exp_date;
|
||||
use crate::error::Error;
|
||||
use nym_api_requests::ecash::BlindSignRequestBody;
|
||||
use nym_credentials_interface::{
|
||||
aggregate_wallets, generate_keypair_user_from_seed, issue_verify, withdrawal_request,
|
||||
BlindedSignature, KeyPairUser, PartialWallet, VerificationKeyAuth, WalletSignatures,
|
||||
WithdrawalRequest,
|
||||
BlindedSignature, KeyPairUser, PartialWallet, TicketType, VerificationKeyAuth,
|
||||
WalletSignatures, WithdrawalRequest,
|
||||
};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
@@ -18,7 +19,6 @@ use nym_validator_client::nym_api::EpochId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
|
||||
use crate::ecash::bandwidth::serialiser::VersionedSerialise;
|
||||
pub use nym_validator_client::nyxd::{Coin, Hash};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -32,6 +32,9 @@ pub struct IssuanceTicketBook {
|
||||
/// ecash keypair related to the credential
|
||||
ecash_keypair: KeyPairUser,
|
||||
|
||||
/// the type of the ticketbook to be issued
|
||||
ticketbook_type: TicketType,
|
||||
|
||||
/// expiration_date of that credential
|
||||
expiration_date: Date,
|
||||
}
|
||||
@@ -41,12 +44,14 @@ impl IssuanceTicketBook {
|
||||
deposit_id: DepositId,
|
||||
identifier: M,
|
||||
signing_key: identity::PrivateKey,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Self {
|
||||
//this expiration date will get fed to the ecash library, force midnight to be set
|
||||
Self::new_with_expiration(
|
||||
deposit_id,
|
||||
identifier,
|
||||
signing_key,
|
||||
ticketbook_type,
|
||||
ecash_default_expiration_date(),
|
||||
)
|
||||
}
|
||||
@@ -55,6 +60,7 @@ impl IssuanceTicketBook {
|
||||
deposit_id: DepositId,
|
||||
identifier: M,
|
||||
signing_key: identity::PrivateKey,
|
||||
ticketbook_type: TicketType,
|
||||
expiration_date: Date,
|
||||
) -> Self {
|
||||
let ecash_keypair = generate_keypair_user_from_seed(identifier);
|
||||
@@ -62,6 +68,7 @@ impl IssuanceTicketBook {
|
||||
deposit_id,
|
||||
signing_key,
|
||||
ecash_keypair,
|
||||
ticketbook_type,
|
||||
expiration_date,
|
||||
}
|
||||
}
|
||||
@@ -76,6 +83,10 @@ impl IssuanceTicketBook {
|
||||
self.expiration_date
|
||||
}
|
||||
|
||||
pub fn ticketbook_type(&self) -> TicketType {
|
||||
self.ticketbook_type
|
||||
}
|
||||
|
||||
pub fn request_plaintext(request: &WithdrawalRequest, deposit_id: DepositId) -> Vec<u8> {
|
||||
let mut message = request.to_bytes();
|
||||
message.extend_from_slice(&deposit_id.to_be_bytes());
|
||||
@@ -99,6 +110,7 @@ impl IssuanceTicketBook {
|
||||
request_signature,
|
||||
signing_request.ecash_pub_key.clone(),
|
||||
signing_request.expiration_date,
|
||||
signing_request.ticketbook_type,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,6 +145,7 @@ impl IssuanceTicketBook {
|
||||
let (withdrawal_request, request_info) = withdrawal_request(
|
||||
self.ecash_keypair.secret_key(),
|
||||
self.expiration_date.ecash_unix_timestamp(),
|
||||
self.ticketbook_type.encode(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -141,6 +154,7 @@ impl IssuanceTicketBook {
|
||||
request_info,
|
||||
ecash_pub_key: self.ecash_keypair.public_key(),
|
||||
expiration_date: self.expiration_date,
|
||||
ticketbook_type: self.ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +232,7 @@ impl IssuanceTicketBook {
|
||||
wallet,
|
||||
epoch_id,
|
||||
self.ecash_keypair.secret_key().clone(),
|
||||
self.ticketbook_type,
|
||||
self.expiration_date,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::ecash::bandwidth::CredentialSpendingData;
|
||||
use crate::ecash::utils::ecash_today;
|
||||
use crate::error::Error;
|
||||
use nym_credentials_interface::{
|
||||
CoinIndexSignature, ExpirationDateSignature, PayInfo, SecretKeyUser, VerificationKeyAuth,
|
||||
Wallet, WalletSignatures,
|
||||
CoinIndexSignature, ExpirationDateSignature, NymPayInfo, PayInfo, SecretKeyUser, TicketType,
|
||||
VerificationKeyAuth, Wallet, WalletSignatures,
|
||||
};
|
||||
use nym_ecash_time::EcashTime;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
@@ -36,6 +36,10 @@ pub struct IssuedTicketBook {
|
||||
/// expiration_date for easier discarding
|
||||
#[zeroize(skip)]
|
||||
expiration_date: Date,
|
||||
|
||||
/// the type of the ticketbook to got issued
|
||||
#[zeroize(skip)]
|
||||
ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
impl IssuedTicketBook {
|
||||
@@ -43,6 +47,7 @@ impl IssuedTicketBook {
|
||||
wallet: WalletSignatures,
|
||||
epoch_id: EpochId,
|
||||
ecash_secret_key: SecretKeyUser,
|
||||
ticketbook_type: TicketType,
|
||||
expiration_date: Date,
|
||||
) -> Self {
|
||||
IssuedTicketBook {
|
||||
@@ -51,6 +56,7 @@ impl IssuedTicketBook {
|
||||
epoch_id,
|
||||
ecash_secret_key,
|
||||
expiration_date,
|
||||
ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +64,7 @@ impl IssuedTicketBook {
|
||||
signatures_wallet: WalletSignatures,
|
||||
epoch_id: EpochId,
|
||||
ecash_secret_key: SecretKeyUser,
|
||||
ticketbook_type: TicketType,
|
||||
expiration_date: Date,
|
||||
spent_tickets: u64,
|
||||
) -> Self {
|
||||
@@ -67,6 +74,7 @@ impl IssuedTicketBook {
|
||||
epoch_id,
|
||||
ecash_secret_key,
|
||||
expiration_date,
|
||||
ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +86,10 @@ impl IssuedTicketBook {
|
||||
self.epoch_id
|
||||
}
|
||||
|
||||
pub fn ticketbook_type(&self) -> TicketType {
|
||||
self.ticketbook_type
|
||||
}
|
||||
|
||||
pub fn current_serialization_revision(&self) -> u8 {
|
||||
CURRENT_SERIALIZATION_REVISION
|
||||
}
|
||||
@@ -102,6 +114,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,
|
||||
|
||||
@@ -36,22 +36,6 @@ pub fn aggregate_verification_keys(
|
||||
)?)
|
||||
}
|
||||
|
||||
pub fn obtain_aggregated_verification_key(
|
||||
_api_clients: &[EcashApiClient],
|
||||
) -> Result<VerificationKeyAuth, Error> {
|
||||
// TODO:
|
||||
// let total = api_clients.len();
|
||||
// let mut rng = thread_rng();
|
||||
// let indices = sample(&mut rng, total, total);
|
||||
// for index in indices {
|
||||
// // randomly try apis until we succeed
|
||||
// // if let Ok(res) = api_clients[index].api_client.get_aggregated_verification_key().await {
|
||||
// // //
|
||||
// // }
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn obtain_expiration_date_signatures(
|
||||
ecash_api_clients: &[EcashApiClient],
|
||||
verification_key: &VerificationKeyAuth,
|
||||
@@ -63,7 +47,7 @@ pub async fn obtain_expiration_date_signatures(
|
||||
|
||||
let mut signatures_shares: Vec<_> = Vec::with_capacity(ecash_api_clients.len());
|
||||
|
||||
let expiration_date = cred_exp_date().unix_timestamp() as u64;
|
||||
let expiration_date = cred_exp_date().ecash_unix_timestamp();
|
||||
for ecash_api_client in ecash_api_clients.iter() {
|
||||
match ecash_api_client
|
||||
.api_client
|
||||
|
||||
@@ -6,13 +6,16 @@ use time::{Duration, PrimitiveDateTime, Time};
|
||||
pub use time::{Date, OffsetDateTime};
|
||||
|
||||
pub trait EcashTime {
|
||||
fn ecash_unix_timestamp(&self) -> u64 {
|
||||
fn ecash_unix_timestamp(&self) -> u32 {
|
||||
let ts = self.ecash_datetime().unix_timestamp();
|
||||
|
||||
// just panic on pre-1970 timestamps...
|
||||
assert!(ts > 0);
|
||||
|
||||
ts as u64
|
||||
// and on anything in 22nd century...
|
||||
assert!(ts <= u32::MAX as i64);
|
||||
|
||||
ts as u32
|
||||
}
|
||||
|
||||
fn ecash_date(&self) -> Date {
|
||||
|
||||
@@ -21,12 +21,12 @@ thiserror = { workspace = true }
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-pemstore = { path = "../../common/pemstore" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credentials-interface = { path = "../../common/credentials-interface" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
workspace = true
|
||||
@@ -41,6 +41,4 @@ workspace = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
nym-compact-ecash = { path = "../../common/nym_offline_compact_ecash" } # we need specific imports in tests
|
||||
|
||||
|
||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" } # we need specific imports in tests
|
||||
@@ -64,6 +64,7 @@ mod tests {
|
||||
};
|
||||
use nym_credentials::ecash::utils::EcashTime;
|
||||
use nym_credentials::IssuanceTicketBook;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
@@ -75,7 +76,7 @@ mod tests {
|
||||
let mut rng = OsRng;
|
||||
let signing_key = ed25519::PrivateKey::new(&mut rng);
|
||||
|
||||
let issuance = IssuanceTicketBook::new(42, [], signing_key);
|
||||
let issuance = IssuanceTicketBook::new(42, [], signing_key, TicketType::V1MixnetEntry);
|
||||
let expiration_date = issuance.expiration_date();
|
||||
let sig_req = issuance.prepare_for_signing();
|
||||
let exp_date_sigs = generate_expiration_date_signatures(
|
||||
@@ -91,6 +92,7 @@ mod tests {
|
||||
sig_req.ecash_pub_key.clone(),
|
||||
&sig_req.withdrawal_request,
|
||||
expiration_date.ecash_unix_timestamp(),
|
||||
issuance.ticketbook_type().encode(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
[package]
|
||||
name = "nym-gateway-storage"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bincode = { workspace = true, optional = true }
|
||||
defguard_wireguard_rs = { workspace = true, optional = true }
|
||||
log = { workspace = true }
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
"time",
|
||||
] }
|
||||
time = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
|
||||
[features]
|
||||
wireguard = ["defguard_wireguard_rs", "bincode"]
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
CREATE TABLE wireguard_peer
|
||||
(
|
||||
public_key TEXT NOT NULL PRIMARY KEY UNIQUE,
|
||||
preshared_key TEXT,
|
||||
protocol_version INTEGER,
|
||||
endpoint TEXT,
|
||||
last_handshake TIMESTAMP,
|
||||
tx_bytes BIGINT NOT NULL,
|
||||
rx_bytes BIGINT NOT NULL,
|
||||
persistent_keepalive_interval INTEGER,
|
||||
allowed_ips BLOB NOT NULL,
|
||||
suspended BOOLEAN NOT NULL
|
||||
);
|
||||
@@ -1,11 +1,28 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node::storage::models::PersistedBandwidth;
|
||||
use crate::models::PersistedBandwidth;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct AvailableBandwidth {
|
||||
pub bytes: i64,
|
||||
pub freepass_expiration: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
impl AvailableBandwidth {
|
||||
pub fn freepass_expired(&self) -> bool {
|
||||
if let Some(expiration) = self.freepass_expiration {
|
||||
if expiration < OffsetDateTime::now_utc() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BandwidthManager {
|
||||
pub struct BandwidthManager {
|
||||
connection_pool: sqlx::SqlitePool,
|
||||
}
|
||||
|
||||
@@ -15,15 +32,18 @@ impl BandwidthManager {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `connection_pool`: database connection pool to use.
|
||||
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
pub fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
BandwidthManager { connection_pool }
|
||||
}
|
||||
|
||||
/// Creates a new bandwidth entry for the particular client.
|
||||
pub(crate) async fn insert_new_client(&self, client_id: i64) -> Result<(), sqlx::Error> {
|
||||
pub(crate) async fn insert_new_client_if_doesnt_exist(
|
||||
&self,
|
||||
client_id: i64,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
// FIXME: hack; we need to change api slightly
|
||||
sqlx::query!(
|
||||
"INSERT INTO available_bandwidth(client_id, available, expiration) VALUES (?, 0, ?)",
|
||||
"INSERT OR IGNORE INTO available_bandwidth(client_id, available, expiration) VALUES (?, 0, ?)",
|
||||
client_id,
|
||||
OffsetDateTime::UNIX_EPOCH,
|
||||
)
|
||||
@@ -16,4 +16,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("the stored data associated with ticket {ticket_id} is malformed!")]
|
||||
MalformedStoredTicketData { ticket_id: i64 },
|
||||
|
||||
#[error("Failed to convert from type of database: {0}")]
|
||||
TypeConversion(String),
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node::storage::models::StoredMessage;
|
||||
use crate::models::StoredMessage;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct InboxManager {
|
||||
@@ -1,29 +1,32 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node::client_handling::websocket::connection_handler::ecash::ClientTicket;
|
||||
use crate::node::storage::bandwidth::BandwidthManager;
|
||||
use crate::node::storage::error::StorageError;
|
||||
use crate::node::storage::inboxes::InboxManager;
|
||||
use crate::node::storage::models::{
|
||||
PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage, VerifiedTicket,
|
||||
};
|
||||
use crate::node::storage::shared_keys::SharedKeysManager;
|
||||
use crate::node::storage::tickets::TicketStorageManager;
|
||||
use async_trait::async_trait;
|
||||
use bandwidth::BandwidthManager;
|
||||
use error::StorageError;
|
||||
use inboxes::InboxManager;
|
||||
use models::{
|
||||
PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage, VerifiedTicket,
|
||||
WireguardPeer,
|
||||
};
|
||||
use nym_credentials_interface::ClientTicket;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_sphinx::DestinationAddressBytes;
|
||||
use shared_keys::SharedKeysManager;
|
||||
use sqlx::ConnectOptions;
|
||||
use std::path::Path;
|
||||
use tickets::TicketStorageManager;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{debug, error};
|
||||
|
||||
mod bandwidth;
|
||||
pub(crate) mod error;
|
||||
pub mod bandwidth;
|
||||
pub mod error;
|
||||
mod inboxes;
|
||||
pub(crate) mod models;
|
||||
mod shared_keys;
|
||||
mod tickets;
|
||||
#[cfg(feature = "wireguard")]
|
||||
mod wireguard_peers;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
@@ -32,6 +35,13 @@ pub trait Storage: Send + Sync {
|
||||
client_address: DestinationAddressBytes,
|
||||
) -> Result<i64, StorageError>;
|
||||
|
||||
/// Creates all relevant database entries for the newly registered client
|
||||
async fn insert_new_client(
|
||||
&self,
|
||||
client_address: DestinationAddressBytes,
|
||||
shared_keys: &SharedKeys,
|
||||
) -> Result<i64, StorageError>;
|
||||
|
||||
/// Inserts provided derived shared keys into the database.
|
||||
/// If keys previously existed for the provided client, they are overwritten with the new data.
|
||||
///
|
||||
@@ -207,6 +217,42 @@ pub trait Storage: Send + Sync {
|
||||
async fn get_votes(&self, ticket_id: i64) -> Result<Vec<i64>, StorageError>;
|
||||
|
||||
async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, StorageError>;
|
||||
|
||||
/// Insert a wireguard peer in the storage.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `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,
|
||||
suspended: bool,
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
/// Tries to retrieve available bandwidth for the particular peer.
|
||||
///
|
||||
/// # 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.
|
||||
///
|
||||
/// # 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>;
|
||||
}
|
||||
|
||||
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||
@@ -216,6 +262,8 @@ pub struct PersistentStorage {
|
||||
inbox_manager: InboxManager,
|
||||
bandwidth_manager: BandwidthManager,
|
||||
ticket_manager: TicketStorageManager,
|
||||
#[cfg(feature = "wireguard")]
|
||||
wireguard_peer_manager: wireguard_peers::WgPeerManager,
|
||||
}
|
||||
|
||||
impl PersistentStorage {
|
||||
@@ -259,6 +307,8 @@ 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),
|
||||
bandwidth_manager: BandwidthManager::new(connection_pool.clone()),
|
||||
@@ -279,6 +329,19 @@ impl Storage for PersistentStorage {
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn insert_new_client(
|
||||
&self,
|
||||
client_address: DestinationAddressBytes,
|
||||
shared_keys: &SharedKeys,
|
||||
) -> Result<i64, StorageError> {
|
||||
let id = self.insert_shared_keys(client_address, shared_keys).await?;
|
||||
self.bandwidth_manager
|
||||
.insert_new_client_if_doesnt_exist(id)
|
||||
.await?;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
async fn insert_shared_keys(
|
||||
&self,
|
||||
client_address: DestinationAddressBytes,
|
||||
@@ -347,7 +410,9 @@ impl Storage for PersistentStorage {
|
||||
}
|
||||
|
||||
async fn create_bandwidth_entry(&self, client_id: i64) -> Result<(), StorageError> {
|
||||
self.bandwidth_manager.insert_new_client(client_id).await?;
|
||||
self.bandwidth_manager
|
||||
.insert_new_client_if_doesnt_exist(client_id)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -576,4 +641,42 @@ impl Storage for PersistentStorage {
|
||||
async fn get_signers(&self, epoch_id: i64) -> Result<Vec<i64>, StorageError> {
|
||||
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,
|
||||
suspended: bool,
|
||||
) -> Result<(), StorageError> {
|
||||
let mut peer = WireguardPeer::from(peer.clone());
|
||||
peer.suspended = suspended;
|
||||
self.wireguard_peer_manager.insert_peer(&peer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "wireguard")]
|
||||
async fn get_wireguard_peer(
|
||||
&self,
|
||||
peer_public_key: &str,
|
||||
) -> Result<Option<WireguardPeer>, StorageError> {
|
||||
let peer = self
|
||||
.wireguard_peer_manager
|
||||
.retrieve_peer(peer_public_key)
|
||||
.await?;
|
||||
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)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user