Compare commits

..

25 Commits

Author SHA1 Message Date
mfahampshire 50d998bdf8 test update 2024-11-06 11:30:07 +01:00
mfahampshire f84a605536 test update 2024-11-06 11:20:43 +01:00
mfahampshire 6cf967b06d push component update 2024-11-06 11:19:07 +01:00
mfahampshire 0ca44ed309 push component update 2024-11-06 11:14:30 +01:00
mfahampshire 0064edbca5 push component update 2024-11-06 11:13:00 +01:00
mfahampshire 95322535c6 push component update 2024-11-06 11:07:12 +01:00
mx c7d025baba try fix scan redirect failures (#5100)
* try fix scan redirect failures

* yet more redirects
2024-11-06 09:53:49 +00:00
Simon Wicky ec7482e417 publishing list of actvie client hashes (#5084) 2024-11-05 09:28:17 +01:00
Dinko Zdravac 307d326f82 Merge pull request #5085 from nymtech/develop-release-merge
Merge release into develop
2024-11-05 08:58:57 +01:00
mx 2e746e9890 [DOCs]: Fix all redirects (#5086)
* add new redirect

* add new redirect fix

* add new redirect fix

* try remove prepend from redirects

* more tweaks config redirects

* new tweaks

* move root redirect to bottom

* tweak

* tweak

* test the difference in config

* correct all redirects based on testing - ready to review

* adding one more redirect

---------

Co-authored-by: import this <97586125+serinko@users.noreply.github.com>
2024-11-04 18:36:10 +00:00
dynco-nym e840c1fe93 Merge branch 'release/2024.13-magura' into develop-release-merge 2024-11-04 16:50:44 +01:00
mx d6599b2933 [DOCs]: New docs framework (#5017)
* started todo list for rework

* startd long todo list

* startd long todo list

* remove ts docs from ts sdk dir

* started new docs draft

* rearranged code example dir structure

* modified code component filepaths

* first pass rust sdk

* small shift typescript org

* updated todo list

* consolidating images folders in one place

* first pass @ operator docs

* gen updates

* sdk in its own dir

* first pass developers structure

* first pass network structure

* structure

* add licensing

* moved old docs -> old_docs dir for clarity when devving

* moving around new docs - think this is the final dir structure

* updated todo list

* new autodoc version (#4781)

* Update rework_todo.md

* quick first sketch of landing page

* rework of structure of developers

* added arch and concepts stubs

* add new bits to todo list

* new list

* tweak to overview

* mixnet node overview

* tweak overview

* first pass new arch

* first pass concepts

* first pass traffic

* more network docs

* moved some chain files to the dev portal stubs

* removed old reference to archive

* note to client

* concepts 2nd pass

* crypto first proper pass, sphinx

* stub for not p2p

* structure change

* traffic 2nd pass

* misc

* hid root index

* overhaul arch

* overhaul arch

* add links + tweaks

* update todo list

* updating nyx section

* added zknym docs

* added zknym docs

* note on where to find deployed info

* smart contracts done

* started moving integrations docs over from ts sdk

* pass @ integration page

* todo for the tldr overview

* added ffi stub files

* updated todo list

* move sdks to developers

* initial pass at new clients overview for developers

* rework intro

* add echo serv to tools

* sidebar autocollapse

* integration overview work + tools

* concepts overview for devporta

* stub

* more for networking pages

* added to concepts in dev portal

* updated arch

* crypto overview page

* typo fix

* add credential stub

* first pass concepts done

* start reorg of rust sdk docs

* reorg + added FFI table

* added no scroll to inline code

* finished ffi overview page

* first pass @ rest of rust sdk doc

* first pass ffi

* tweaks

* added testnet example + note to custom topology example overview

* stripped unnecessary stuff from TS

* tweaks to ffi

* updated faq

* first pass tcpproxy

* commit before moving image dir

* moved images/ to correct place

* started on client redo

* chain first pass

* moved cli wallet out of tools

* first pass new ws client

* new chain info, left todo links in

* links

* more links

* chain registry

* added echo server to tools

* rust sdk links

* ts sdk links

* final linkchecks

* redo acks diagram as mermaid

* add mermaid flow diagram

* added links for codecs + full flow diagram

* removed todo

* remove forced dark mode

* diagram + concepts overview

* small correction re tcpproxy ffi

* remove diagram title

* new sock5 diagram, minor client docs tweaks

* diagrams

* change order in list

* added note for standalone: can be accessed via sdk

* tweaks

* replaced old diagram with mermaid

* fixed link

* hardcoded import version for the moment

* update deps

* remove test component

* recreated tools dir

* remove tools dir moved to wrong palce

* prebuild and predev script for autodoc commands

* make script own command instead of prebuild

* made code blocks sh

* updated autogenerated docs

* temp

* auto commit generated command files

* add link to autodoc generated files

* updated autodoc for committing changing else exit

* auto commit generated command files

* updated readme

* make subcommand headers smaller

* removed mdbook related scripts

* update readme

* update readme

* removed backups of root meta.json

* cherry pick yana commits + some extra config in theme

* update readme

* update theme: width of page and padding

* some more themeing

* changed erroneous note

* docs redirects first pass

* tweaking

* new pages + rest of redirects for old docs/

* brought in archive + done rewrites for devportal

* cherry pick yana landingpage

* tweaked landing page component

* changed theme of mermaid diagram to match everything else

* updated todo list

* [DOCs]: Operators rework to next.js (#4930)

* initialise operators guides v2

* new introduction page

* add variables csv and page

* add baseurl to allow short path

* add sandbox page

* added building from source page

* add binary pages

* add preliminary steps

* clean preliminary steps dir

* syntax edit

* syntax edit

* add configuration page

* create new proxy configuration page

* create new proxy configuration page

* create bonding.mdx page

* correct images path

* syntax edit

* add new validator setup page

* add api setup page

* add nyx configuration page

* add nym node and maintenance pages

* finish maintenance and add nymvisor conf page

* add manual upgrade page

* add nymvisor upgrade page

* add performance testing page and dir

* add node api check page

* add explore nym scripts page

* add testing pages

* fix menu issue by moving snippets to coomponents

* add all troubleshooting pages

* add general faq page

* add nym node faq page

* add nyx faq page

* revamp legal forum to community counsel and add all pages

* rewire relative paths to new structure

* simplify setup and remove lock file

* syntax fix

* rm package.json

* re add package.json, rm package-lock.json

* removed old books from commit

* address review comments

---------

Co-authored-by: mfahampshire <maxhampshire@pm.me>
Co-authored-by: mx <33262279+mfahampshire@users.noreply.github.com>

* tweak client links

* also moved matrix images to correct place

* Max/fix links new docs framework (#4989)

* tweak client links
* standardise images in public/
* old images move to public/archive

* rename overview to more descriptive

* links (#4990)

* links
* removed todos
* updated todo list

* minor themeing

* operator redirects

* pick yana's edits: remove specified callout theming

* added todo comments for old ts sdk redirects

* [new/docs/operators]: Create archive section - PR ready to merge (#5004)

* [new-docs/operators] : Fix callout syntax (#5006)

* fix callout syntax from color to type

* correct callout from danger to warning

* update footer

* updated footer

* finalised rewrites

* tweaks to clients and reintroduced old examples page

* update todo

* Max/individual command autodocs (#5015)


* auto commit generated command files

* added to autodoc.sh: build all binaries before running

* autodoc move individual command outputs to components

* Max/individual command autodocs (#5018)


* updated autodoc script

* updated autodoc script for fix + reintroduced gitignore file for generated markdown

* auto commit generated command files

* auto commit generated command files

* added command-outputs to autodoc script

* fix merge conflicts

* repush components

* remove old docs dirs

* auto commit generated command files

* auto commit generated command files

* updated messages paradigm with the standalone proxies

* [NEW-DOCs/operators]: Command output, accordion, api scraping & all final tasks (#5026)

* add custom scripts, create prebuild to import data to pages

* update after latest prebuild

* auto commit generated command files

* add accordion component

* add changbelog page

* add node_api_check outputs

* finish all command outputs

* more accordions beautifications

* finish accordion

* PR ready to go

* address review comments

---------

Co-authored-by: mfahampshire <maxhampshire@pm.me>

* Adjust padding

* Fix responsive design

* cherry pick yana landingpage flex update

* reremove old docs

* added dependencies to readme

* pushing build attempt changes

* fix merge errors, path errors, dump uselss dinosaurs - BUILT THE F*N DOCS w success

* moved prebuild to its own script

* generate timenow

* auto commit generated command files

* remove comment

* auto commit generated command files

* auto commit generated command files

* auto commit generated command files

* build from new configs

* add mdx type as explicit dep

* remove rc from version in package

* change predev script

* update readme with scripts

* update general info

* add license

* auto commit generated command files

* add updated components

* removed old examples page for the moment

* remove old list will reintroduce hidden behind gitignore for future

* reintroduce todo list behind gitignore

* added standalone tcpproxy binary info

* nothing change for redeploy test

* make build standalone

* updated readme

* working on new cd

* remove export

* updated ci/cd for docs

* added ci script for dist

* hide text on laptop wide screen

* add pnpm to ci/cd

* add pnpm version to ci/cd

* add default dir to ci/cd

* change path to script

* update projct name ci

* lint ci branch ignore

* add basePath to next.config.js

* update doc rewrites

* revert basePath addition

* update basePath

* add mobile styles

* fix responsive style

* remove old ts sdk docs workflow

* temp remove autodoc from workspace

* update sidebar for clarity: crypto = cryptography

* ignore documentation in pr-validation workflow

---------

Co-authored-by: Yana <yanok87@users.noreply.github.com>
Co-authored-by: import this <97586125+serinko@users.noreply.github.com>
Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
2024-11-04 11:42:56 +00:00
Bogdan-Ștefan Neacşu 5cefa7fdd4 Don't increase bandwidth again (#5081) 2024-11-04 13:15:27 +02:00
Mark Sinclair 80b590d50d bug-fix: nym-credential-proxy webhook request is the correct shape and added reporting errors via webhook (#5077)
Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2024-11-01 21:48:04 +01:00
Bogdan-Ștefan Neacşu f9b363648f Fix expiration date as today + 7 days (#5076) 2024-11-01 16:01:24 +02:00
Bogdan-Ștefan Neacşu b73561f1c9 Fix gateway decreasing bandwidth (#5075)
* Update storage peers after periodic check

* Reset storage bytes on restart

* Fix clippy
2024-11-01 15:40:22 +02:00
Dinko Zdravac 09b68a8204 Cherry pick NS API from develop (#5074)
* Revert "NS API with directory v2 (#5068)"

This reverts commit cf4fe5f875.

* Merge pull request #5050 from nymtech/dz-node-status-api

Node Status API

* Ns agent workflow (#5055)

* feat: add dockerfile

* add github workflow for node status agent

---------

Co-authored-by: Fran Arbanas <arbanasfran@gmail.com>

* NS API with directory v2 (#5058)

* Use unstable explorer client

* Clean up stale testruns & logging
- log gw identity key
- better agent testrun logging
- log responses
- change response code for agents

* Better logging on agent

* Testrun stores gw identity key instead of gw pk

* Agent 0.1.3

* Agent 0.1.4

* Sqlx offline query data + clippy

* Compatible with directory v2

* Point to internal deps + rebase + v0.1.5

* self described field not null

* Fix build.rs typo

* Fix clippy

---------

Co-authored-by: Fran Arbanas <arbanasfran@gmail.com>
2024-11-01 01:24:41 +01:00
Fouad 0374626960 Allow custom http port to be reset (#5073)
* allow custom port to be reset in wallet
2024-10-31 16:53:55 +00:00
Dinko Zdravac cf4fe5f875 NS API with directory v2 (#5068)
* Use unstable explorer client

* Clean up stale testruns & logging
- log gw identity key
- better agent testrun logging
- log responses
- change response code for agents

* Better logging on agent

* Testrun stores gw identity key instead of gw pk

* Agent 0.1.3

* Agent 0.1.4

* Sqlx offline query data + clippy

* Compatible with directory v2

* Point to internal deps + rebase + v0.1.5

* self described field not null

* Fix build.rs typo
2024-10-31 13:52:20 +01:00
Jędrzej Stuczyński 9f8bf2d080 bugfix: wallet backend fixes (#5070)
* fixed simulation arguments

* make sure 'try_convert_pubkey_to_node_id' checks for native nymnodes
2024-10-31 12:23:20 +00:00
Jędrzej Stuczyński b9d1fc40e7 deprecated old nym-api client methods and replaced them when possible (#5069) 2024-10-31 12:08:58 +00:00
Jędrzej Stuczyński be67234093 bugfix: credential-proxy obtain-async (#5067)
* removed foreign key constraint on deposit table

* fixed sql nullability

* fixed swagger arguments for '/api/v1/ticketbook/shares/device/{device_id}/credential/{credential_id}' route

* fixed missing swagger component definitions
2024-10-31 10:33:38 +00:00
Fouad 8b0b70a727 allow nym node config updates (#5066) 2024-10-31 09:59:22 +00:00
Fouad c90ebf0a6a Feature/wallet bonding fixes (#5064)
* bonding and unbonding for nym nodes
2024-10-30 17:15:38 +00:00
Jędrzej Stuczyński 07ff2639ec bugfix: use corrext axum extractors for ecash route arguments (#5065) 2024-10-30 16:05:16 +00:00
71 changed files with 6760 additions and 449 deletions
Generated
+4 -23
View File
@@ -430,14 +430,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "autodoc"
version = "0.1.0"
dependencies = [
"env_logger 0.11.5",
"log",
]
[[package]]
name = "axum"
version = "0.6.20"
@@ -2362,19 +2354,6 @@ dependencies = [
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime 2.1.0",
"log",
]
[[package]]
name = "envy"
version = "0.4.2"
@@ -5101,7 +5080,7 @@ dependencies = [
[[package]]
name = "nym-credential-proxy"
version = "0.1.1"
version = "0.1.3"
dependencies = [
"anyhow",
"async-trait",
@@ -5481,6 +5460,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
"sha2 0.10.8",
"si-scale",
"sqlx",
"subtle-encoding",
@@ -7488,7 +7468,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger 0.7.1",
"env_logger",
"log",
]
@@ -10704,6 +10684,7 @@ dependencies = [
"quote",
"regex",
"syn 2.0.82",
"uuid",
]
[[package]]
+3 -1
View File
@@ -1,3 +1,5 @@
# Nym Desktop Client
The Nym Desktop Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
The Nym Desktop Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
TEST CI PICKUP
@@ -18,7 +18,7 @@ use nym_api_requests::ecash::{
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
};
use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
ApiHealthResponse, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
};
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
@@ -192,6 +192,8 @@ impl<C, S> Client<C, S> {
}
// validator-api wrappers
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
#[allow(deprecated)]
impl<C, S> Client<C, S> {
pub fn api_url(&self) -> &Url {
self.nym_api.current_url()
@@ -201,46 +203,54 @@ impl<C, S> Client<C, S> {
self.nym_api.change_base_url(new_endpoint)
}
#[deprecated]
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes_detailed().await?)
}
#[deprecated]
pub async fn get_cached_mixnodes_detailed_unfiltered(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes_detailed_unfiltered().await?)
}
#[deprecated]
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes_detailed().await?)
}
#[deprecated]
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes_detailed().await?)
}
#[deprecated]
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.nym_api.get_gateways().await?)
}
@@ -304,6 +314,8 @@ pub struct NymApiClient {
// we could re-implement the communication with the REST API on port 1317
}
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
#[allow(deprecated)]
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
@@ -424,6 +436,38 @@ impl NymApiClient {
Ok(nodes)
}
/// retrieve basic information for nodes are capable of operating as a mixnode
/// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_mixing_capable_nodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
loop {
let mut res = self
.nym_api
.get_basic_mixing_capable_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.await?;
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(nodes)
}
/// retrieve basic information for all bonded nodes on the network
pub async fn get_all_basic_nodes(
&self,
@@ -450,26 +494,35 @@ impl NymApiClient {
Ok(nodes)
}
pub async fn health(&self) -> Result<ApiHealthResponse, ValidatorClientError> {
Ok(self.nym_api.health().await?)
}
#[deprecated]
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.nym_api.get_gateways().await?)
}
#[deprecated]
pub async fn get_cached_described_gateways(
&self,
) -> Result<Vec<LegacyDescribedGateway>, ValidatorClientError> {
@@ -518,6 +571,7 @@ impl NymApiClient {
Ok(bonds)
}
#[deprecated]
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
@@ -529,6 +583,7 @@ impl NymApiClient {
.await?)
}
#[deprecated]
pub async fn get_mixnode_core_status_count(
&self,
mix_id: NodeId,
@@ -540,6 +595,7 @@ impl NymApiClient {
.await?)
}
#[deprecated]
pub async fn get_mixnode_status(
&self,
mix_id: NodeId,
@@ -547,6 +603,7 @@ impl NymApiClient {
Ok(self.nym_api.get_mixnode_status(mix_id).await?)
}
#[deprecated]
pub async fn get_mixnode_reward_estimation(
&self,
mix_id: NodeId,
@@ -554,6 +611,7 @@ impl NymApiClient {
Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?)
}
#[deprecated]
pub async fn get_mixnode_stake_saturation(
&self,
mix_id: NodeId,
@@ -585,6 +643,7 @@ impl NymApiClient {
.await?)
}
#[deprecated]
pub async fn spent_credentials_filter(
&self,
) -> Result<SpentCredentialsResponse, ValidatorClientError> {
@@ -164,7 +164,7 @@ async fn test_nym_api_connection(
) -> ConnectionResult {
let result = match timeout(
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
client.get_cached_mixnodes(),
client.health(),
)
.await
{
@@ -11,7 +11,8 @@ use nym_api_requests::ecash::models::{
};
use nym_api_requests::ecash::VerificationKeyResponse;
use nym_api_requests::models::{
AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse, NymNodeDescription,
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
NymNodeDescription,
};
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
use nym_api_requests::pagination::PaginatedResponse;
@@ -54,12 +55,26 @@ pub fn rfc_3339_date() -> Vec<BorrowedFormatItem<'static>> {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait NymApiClientExt: ApiClient {
async fn health(&self) -> Result<ApiHealthResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::API_STATUS_ROUTES,
routes::HEALTH,
],
NO_PARAMS,
)
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
@@ -74,6 +89,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.get_json(
@@ -88,6 +104,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnodes_detailed_unfiltered(
&self,
@@ -104,12 +121,14 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateways_described(&self) -> Result<Vec<LegacyDescribedGateway>, NymAPIError> {
self.get_json(
@@ -119,6 +138,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnodes_described(&self) -> Result<Vec<LegacyDescribedMixNode>, NymAPIError> {
self.get_json(
@@ -128,6 +148,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_nodes_described(
&self,
page: Option<u32>,
@@ -147,6 +168,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_nym_nodes(
&self,
page: Option<u32>,
@@ -166,6 +188,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[tracing::instrument(level = "debug", skip_all)]
async fn get_basic_mixnodes(
&self,
@@ -190,6 +213,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_basic_gateways(
&self,
@@ -298,6 +322,49 @@ pub trait NymApiClientExt: ApiClient {
.await
}
/// retrieve basic information for nodes that got assigned 'mixing' node in this epoch
/// this includes legacy mixnodes and nym-nodes
#[instrument(level = "debug", skip(self))]
async fn get_basic_mixing_capable_nodes(
&self,
semver_compatibility: Option<String>,
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy {
params.push(("no_legacy", "true".to_string()))
}
if let Some(page) = page {
params.push(("page", page.to_string()))
}
if let Some(per_page) = per_page {
params.push(("per_page", per_page.to_string()))
}
self.get_json(
&[
routes::API_VERSION,
"unstable",
"nym-nodes",
"skimmed",
"mixnodes",
"all",
],
&params,
)
.await
}
#[instrument(level = "debug", skip(self))]
async fn get_basic_nodes(
&self,
semver_compatibility: Option<String>,
@@ -330,6 +397,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
@@ -339,6 +407,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_active_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
@@ -354,6 +423,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
@@ -363,6 +433,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_report(
&self,
@@ -381,6 +452,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateway_report(
&self,
@@ -399,6 +471,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_history(
&self,
@@ -417,6 +490,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateway_history(
&self,
@@ -435,6 +509,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_rewarded_mixnodes_detailed(
&self,
@@ -452,6 +527,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateway_core_status_count(
&self,
@@ -484,6 +560,7 @@ pub trait NymApiClientExt: ApiClient {
}
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_core_status_count(
&self,
@@ -517,6 +594,7 @@ pub trait NymApiClientExt: ApiClient {
}
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_status(
&self,
@@ -535,6 +613,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_reward_estimation(
&self,
@@ -553,6 +632,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn compute_mixnode_reward_estimation(
&self,
@@ -573,6 +653,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_stake_saturation(
&self,
@@ -591,6 +672,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnode_inclusion_probability(
&self,
@@ -626,6 +708,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
async fn get_mixnode_avg_uptime(&self, mix_id: NodeId) -> Result<UptimeResponse, NymAPIError> {
self.get_json(
&[
@@ -640,6 +723,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnodes_blacklisted(&self) -> Result<Vec<NodeId>, NymAPIError> {
self.get_json(
@@ -649,6 +733,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateways_blacklisted(&self) -> Result<Vec<IdentityKey>, NymAPIError> {
self.get_json(
@@ -709,6 +794,7 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn double_spending_filter_v1(&self) -> Result<SpentCredentialsResponse, NymAPIError> {
self.get_json(
@@ -36,6 +36,8 @@ pub mod ecash {
}
pub const STATUS_ROUTES: &str = "status";
pub const API_STATUS_ROUTES: &str = "api-status";
pub const HEALTH: &str = "health";
pub const MIXNODE: &str = "mixnode";
pub const GATEWAY: &str = "gateway";
pub const NYM_NODES: &str = "nym-nodes";
@@ -2,10 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_cosmwasm_coin, show_error};
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
#[derive(Debug, Parser)]
pub struct Args {
@@ -15,12 +14,11 @@ pub struct Args {
}
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
match client.nym_api.get_gateways().await {
match client.get_all_cached_described_nodes().await {
Ok(res) => match args.identity_key {
Some(identity_key) => {
let node = res.iter().find(|node| {
node.gateway
.identity_key
node.ed25519_identity_key()
.to_string()
.eq_ignore_ascii_case(&identity_key)
});
@@ -32,14 +30,16 @@ pub async fn query(args: Args, client: &QueryClientWithNyxd) {
None => {
let mut table = Table::new();
table.set_header(vec!["Identity Key", "Owner", "Host", "Bond", "Version"]);
for node in res {
table.set_header(vec!["Node Id", "Identity Key", "Version", "Is Legacy"]);
for node in res
.into_iter()
.filter(|node| node.description.declared_role.entry)
{
table.add_row(vec![
node.gateway.identity_key.to_string(),
node.owner.to_string(),
node.gateway.host.to_string(),
pretty_cosmwasm_coin(&node.pledge_amount),
node.gateway.version.clone(),
node.node_id.to_string(),
node.ed25519_identity_key().to_base58_string(),
node.description.build_information.build_version,
(!node.contract_node_type.is_nym_node()).to_string(),
]);
}
@@ -2,10 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_decimal_with_denom, show_error};
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
#[derive(Debug, Parser)]
pub struct Args {
@@ -15,13 +14,11 @@ pub struct Args {
}
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
match client.nym_api.get_mixnodes().await {
match client.get_all_cached_described_nodes().await {
Ok(res) => match args.identity_key {
Some(identity_key) => {
let node = res.iter().find(|node| {
node.bond_information
.mix_node
.identity_key
node.ed25519_identity_key()
.to_string()
.eq_ignore_ascii_case(&identity_key)
});
@@ -33,25 +30,16 @@ pub async fn query(args: Args, client: &QueryClientWithNyxd) {
None => {
let mut table = Table::new();
table.set_header(vec![
"Mix id",
"Identity Key",
"Owner",
"Host",
"Bond",
"Total Delegations",
"Version",
]);
for node in res {
let denom = &node.bond_information.original_pledge().denom;
table.set_header(vec!["Node Id", "Identity Key", "Version", "Is Legacy"]);
for node in res
.into_iter()
.filter(|node| node.description.declared_role.mixnode)
{
table.add_row(vec![
node.mix_id().to_string(),
node.bond_information.mix_node.identity_key.clone(),
node.bond_information.owner.clone().into_string(),
node.bond_information.mix_node.host.clone(),
pretty_decimal_with_denom(node.rewarding_details.operator, denom),
pretty_decimal_with_denom(node.rewarding_details.delegates, denom),
node.bond_information.mix_node.version,
node.node_id.to_string(),
node.ed25519_identity_key().to_base58_string(),
node.description.build_information.build_version,
(!node.contract_node_type.is_nym_node()).to_string(),
]);
}
+2 -2
View File
@@ -6,7 +6,7 @@ use std::sync::Arc;
use time::{Date, OffsetDateTime};
use tracing::*;
use nym_credentials::ecash::utils::{ecash_today, EcashTime};
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
use nym_gateway_requests::models::CredentialSpendingRequest;
use nym_gateway_storage::Storage;
@@ -131,7 +131,7 @@ impl<S: Storage + Clone + 'static> CredentialVerifier<S> {
let bandwidth = Bandwidth::ticket_amount(credential_type.into());
self.bandwidth_storage_manager
.increase_bandwidth(bandwidth, spend_date)
.increase_bandwidth(bandwidth, cred_exp_date())
.await?;
Ok(self
+2 -2
View File
@@ -104,8 +104,8 @@ impl PersistentStatsStorage {
.await?)
}
pub async fn get_unique_users_count(&self, date: Date) -> Result<i32, StatsStorageError> {
Ok(self.session_manager.get_unique_users_count(date).await?)
pub async fn get_unique_users(&self, date: Date) -> Result<Vec<String>, StatsStorageError> {
Ok(self.session_manager.get_unique_users(date).await?)
}
pub async fn delete_unique_users(&self, before_date: Date) -> Result<(), StatsStorageError> {
+5 -6
View File
@@ -71,14 +71,13 @@ impl SessionManager {
Ok(())
}
pub(crate) async fn get_unique_users_count(&self, date: Date) -> Result<i32> {
Ok(sqlx::query!(
"SELECT COUNT(*) as count FROM sessions_unique_users WHERE day = ?",
pub(crate) async fn get_unique_users(&self, date: Date) -> Result<Vec<String>> {
sqlx::query_scalar!(
"SELECT client_address as count FROM sessions_unique_users WHERE day = ?",
date
)
.fetch_one(&self.connection_pool)
.await?
.count)
.fetch_all(&self.connection_pool)
.await
}
pub(crate) async fn delete_unique_users(&self, before_date: Date) -> Result<()> {
+12 -10
View File
@@ -534,17 +534,19 @@ where
}
if res.status().is_success() {
let text = res.text().await?;
match serde_json::from_str(&text) {
Ok(res) => Ok(res),
Err(source) => {
#[cfg(debug_assertions)]
{
tracing::trace!("Result:\n{:#?}", text);
}
Err(HttpClientError::ResponseDeserialisationFailure { source })
}
#[cfg(debug_assertions)]
{
let text = res.text().await.inspect_err(|err| {
tracing::error!("Couldn't even get response text: {err}");
})?;
tracing::trace!("Result:\n{:#?}", text);
serde_json::from_str(&text)
.map_err(|err| HttpClientError::GenericRequestFailure(err.to_string()))
}
#[cfg(not(debug_assertions))]
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(HttpClientError::NotFound)
} else {
+6 -15
View File
@@ -14,7 +14,6 @@ use nym_task::TaskClient;
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::time::Duration;
use tokio::task::JoinHandle;
@@ -313,7 +312,7 @@ impl VerlocMeasurer {
info!("Starting verloc measurements");
// TODO: should we also measure gateways?
let all_mixes = match self.validator_client.get_cached_mixnodes().await {
let all_mixes = match self.validator_client.get_all_described_nodes().await {
Ok(nodes) => nodes,
Err(err) => {
error!(
@@ -332,22 +331,14 @@ impl VerlocMeasurer {
// we only care about address and identity
let tested_nodes = all_mixes
.into_iter()
.filter(|n| n.description.declared_role.mixnode)
.filter_map(|node| {
let mix_node = node.bond_information.mix_node;
// check if the node has sufficient version to be able to understand the packets
let node_version = parse_version(&mix_node.version).ok()?;
if node_version < self.config.minimum_compatible_node_version {
return None;
}
// try to parse the identity and host
let node_identity =
identity::PublicKey::from_base58_string(mix_node.identity_key).ok()?;
let node_identity = node.ed25519_identity_key();
let verloc_host = (&*mix_node.host, mix_node.verloc_port)
.to_socket_addrs()
.ok()?
.next()?;
let ip = node.description.host_information.ip_address.first()?;
let verloc_port = node.description.verloc_port();
let verloc_host = SocketAddr::new(*ip, verloc_port);
// TODO: possible problem in the future, this does name resolution and theoretically
// if a lot of nodes maliciously mis-configured themselves, it might take a while to resolve them all
+14 -1
View File
@@ -99,12 +99,25 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
let peers = all_peers
.into_iter()
.map(Peer::try_from)
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|mut peer| {
// since WGApi doesn't set those values on init, let's set them to 0
peer.rx_bytes = 0;
peer.tx_bytes = 0;
peer
})
.collect::<Vec<_>>();
for peer in peers.iter() {
let bandwidth_manager =
PeerController::generate_bandwidth_manager(storage.clone(), &peer.public_key)
.await?
.map(|bw_m| Arc::new(RwLock::new(bw_m)));
// Update storage with *x_bytes set to 0, as in kernel peers we can't set those values
// so we need to restart counting. Hopefully the bandwidth was counted in available_bandwidth
storage
.insert_wireguard_peer(peer, bandwidth_manager.is_some())
.await?;
peer_bandwidth_managers.insert(peer.public_key.clone(), bandwidth_manager);
}
wg_api.create_interface()?;
+6 -3
View File
@@ -75,8 +75,8 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
async fn active_peer(
&mut self,
storage_peer: WireguardPeer,
kernel_peer: Peer,
storage_peer: &WireguardPeer,
kernel_peer: &Peer,
) -> Result<bool, Error> {
if let Some(bandwidth_manager) = &self.bandwidth_storage_manager {
let spent_bandwidth = (kernel_peer.rx_bytes + kernel_peer.tx_bytes)
@@ -136,9 +136,12 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
log::debug!("Peer {:?} not in storage anymore, shutting down handle", self.public_key);
return Ok(());
};
if !self.active_peer(storage_peer, kernel_peer).await? {
if !self.active_peer(&storage_peer, &kernel_peer).await? {
log::debug!("Peer {:?} doesn't have bandwidth anymore, shutting down handle", self.public_key);
return Ok(());
} else {
// Update storage values
self.storage.insert_wireguard_peer(&kernel_peer, self.bandwidth_storage_manager.is_some()).await?;
}
}
-3
View File
@@ -46,9 +46,6 @@ pnpm run build
> **Only run this script on branches where you want to push e.g. the build info of a binary to production docs**; it will build the monorepo binaries and use their command output for the produced markdown files.
## CI/CD
TODO
## Licensing and copyright information
This is a monorepo and components that make up Nym as a system are licensed individually, so for accurate information, please check individual files.
@@ -1 +1 @@
Tuesday, October 29th 2024, 09:51:52 UTC
Wednesday, November 6th 2024, 10:18:58 UTC
@@ -1,5 +1,5 @@
```sh
2024-10-29T09:51:53.363364Z  INFO nym-api/src/main.rs:41: Starting nym api...
2024-11-06T10:12:52.501648Z  INFO nym-api/src/main.rs:41: Starting nym api...
Usage: nym-api [OPTIONS] <COMMAND>
Commands:
File diff suppressed because it is too large Load Diff
+3
View File
@@ -8,6 +8,9 @@ use nym_contracts_common::truncate_decimal;
use nym_mixnet_contract_common::NodeId;
use nym_validator_client::client::NymApiClientExt;
// use deprecated method as hopefully this whole API will be sunset soon-enough...
// and we're only getting info for legacy node so the relevant data should still exist
#[allow(deprecated)]
pub(crate) async fn retrieve_mixnode_econ_stats(
client: &ThreadsafeValidatorClient,
mix_id: NodeId,
+2
View File
@@ -17,6 +17,8 @@ pub(crate) struct ExplorerApiTasks {
shutdown: TaskClient,
}
// allow usage of deprecated methods here as we actually want to be explicitly querying for legacy data
#[allow(deprecated)]
impl ExplorerApiTasks {
pub(crate) fn new(state: ExplorerApiStateContext, shutdown: TaskClient) -> Self {
ExplorerApiTasks { state, shutdown }
+1
View File
@@ -40,6 +40,7 @@ once_cell = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = { workspace = true }
si-scale = { workspace = true }
subtle-encoding = { workspace = true, features = ["bech32-preview"] }
thiserror = { workspace = true }
+4 -4
View File
@@ -632,7 +632,7 @@ impl<St> Gateway<St> {
// TODO: if anything, this should be getting data directly from the contract
// as opposed to the validator API
let validator_client = self.random_api_client()?;
let existing_nodes = match validator_client.get_cached_gateways().await {
let existing_nodes = match validator_client.get_all_basic_nodes(None).await {
Ok(nodes) => nodes,
Err(err) => {
error!("failed to grab initial network gateways - {err}\n Please try to startup again in few minutes");
@@ -640,9 +640,9 @@ impl<St> Gateway<St> {
}
};
Ok(existing_nodes.iter().any(|node| {
node.gateway.identity_key == self.identity_keypair.public_key().to_base58_string()
}))
Ok(existing_nodes
.iter()
.any(|node| &node.ed25519_identity_pubkey == self.identity_keypair.public_key()))
}
pub async fn run(mut self) -> Result<(), GatewayError>
+8 -2
View File
@@ -7,6 +7,7 @@ use nym_gateway_stats_storage::PersistentStatsStorage;
use nym_gateway_stats_storage::{error::StatsStorageError, models::ActiveSession};
use nym_node_http_api::state::metrics::SharedSessionStats;
use nym_sphinx::DestinationAddressBytes;
use sha2::{Digest, Sha256};
use time::{Date, Duration, OffsetDateTime};
use nym_statistics_common::events::SessionEvent;
@@ -114,12 +115,17 @@ impl SessionStatsHandler {
//update shared state once a day has passed, with data from the previous day
async fn publish_stats(&mut self, stats_date: Date) -> Result<(), StatsStorageError> {
let finished_sessions = self.storage.get_finished_sessions(stats_date).await?;
let user_count = self.storage.get_unique_users_count(stats_date).await?;
let unique_users = self.storage.get_unique_users(stats_date).await?;
let unique_users_hash = unique_users
.into_iter()
.map(|address| format!("{:x}", Sha256::digest(address)))
.collect::<Vec<_>>();
let session_started = self.storage.get_started_sessions_count(stats_date).await? as u32;
{
let mut shared_state = self.shared_session_stats.write().await;
shared_state.update_time = stats_date;
shared_state.unique_active_users = user_count as u32;
shared_state.unique_active_users_count = unique_users_hash.len() as u32;
shared_state.unique_active_users_hashes = unique_users_hash;
shared_state.session_started = session_started;
shared_state.sessions = finished_sessions.iter().map(|s| s.serialize()).collect();
}
+4 -5
View File
@@ -234,7 +234,7 @@ impl MixNode {
// TODO: if anything, this should be getting data directly from the contract
// as opposed to the validator API
let validator_client = self.random_api_client();
let existing_nodes = match validator_client.get_cached_mixnodes().await {
let existing_nodes = match validator_client.get_all_basic_nodes(None).await {
Ok(nodes) => nodes,
Err(err) => {
error!(
@@ -245,10 +245,9 @@ impl MixNode {
}
};
existing_nodes.iter().any(|node| {
node.bond_information.mix_node.identity_key
== self.identity_keypair.public_key().to_base58_string()
})
existing_nodes
.iter()
.any(|node| &node.ed25519_identity_pubkey == self.identity_keypair.public_key())
}
async fn wait_for_interrupt(&self, shutdown: TaskHandle) {
+6
View File
@@ -897,6 +897,12 @@ pub enum DescribedNodeType {
NymNode,
}
impl DescribedNodeType {
pub fn is_nym_node(&self) -> bool {
matches!(self, DescribedNodeType::NymNode)
}
}
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -7,7 +7,7 @@ use crate::ecash::helpers::blind_sign;
use crate::ecash::state::EcashState;
use crate::node_status_api::models::AxumResult;
use crate::support::http::state::AppState;
use axum::extract::Path;
use axum::extract::Query;
use axum::{Json, Router};
use nym_api_requests::ecash::{
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
@@ -134,7 +134,7 @@ struct ExpirationDateParam {
)
)]
async fn partial_expiration_date_signatures(
Path(ExpirationDateParam { expiration_date }): Path<ExpirationDateParam>,
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
state: Arc<EcashState>,
) -> AxumResult<Json<PartialExpirationDateSignatureResponse>> {
state.ensure_signer().await?;
@@ -172,7 +172,7 @@ async fn partial_expiration_date_signatures(
)
)]
async fn partial_coin_indices_signatures(
Path(EpochIdParam { epoch_id }): Path<EpochIdParam>,
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
state: Arc<EcashState>,
) -> AxumResult<Json<PartialCoinIndicesSignatureResponse>> {
state.ensure_signer().await?;
+5417
View File
File diff suppressed because it is too large Load Diff
@@ -22,7 +22,7 @@ reqwest = { workspace = true, features = ["json"] }
wasm-bindgen = { workspace = true, optional = true }
## openapi:
utoipa = { workspace = true, optional = true }
utoipa = { workspace = true, optional = true, features = ["uuid"] }
nym-credentials = { path = "../../common/credentials" }
nym-credentials-interface = { path = "../../common/credentials-interface" }
@@ -10,7 +10,7 @@ use schemars::schema::Schema;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
use time::Date;
use time::{Date, OffsetDateTime};
#[cfg(feature = "query-types")]
use nym_http_api_common::Output;
@@ -40,6 +40,7 @@ pub struct TicketbookRequest {
/// you **MUST** provide a valid value otherwise blacklisting won't work
#[schemars(with = "String")]
#[serde(with = "bs58_ecash")]
#[cfg_attr(feature = "openapi", schema(value_type = String))]
pub ecash_pubkey: PublicKeyUser,
// needs to be explicit in case user creates request at 23:59:59.999, but it reaches vpn-api at 00:00:00.001
@@ -48,6 +49,7 @@ pub struct TicketbookRequest {
pub expiration_date: Date,
#[schemars(with = "String")]
#[cfg_attr(feature = "openapi", schema(value_type = String))]
pub ticketbook_type: TicketType,
pub is_freepass_request: bool,
@@ -234,22 +236,28 @@ pub struct TicketbookWalletSharesAsyncResponse {
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct BlindedWalletSharesResponse {
pub struct WebhookTicketbookWalletShares {
pub id: i64,
pub status: String,
pub device_id: String,
pub credential_id: String,
pub data: Option<TicketbookWalletSharesResponse>,
pub error_message: Option<String>,
pub created: String,
pub updated: String,
#[schemars(with = "String")]
#[serde(with = "time::serde::rfc3339")]
pub created: OffsetDateTime,
#[schemars(with = "String")]
#[serde(with = "time::serde::rfc3339")]
pub updated: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct WebhookBlindedSharesResponse {
pub blinded_shares: BlindedWalletSharesResponse,
pub struct WebhookTicketbookWalletSharesRequest {
pub ticketbook_wallet_shares: WebhookTicketbookWalletShares,
pub secret: String,
}
@@ -1,6 +1,6 @@
[package]
name = "nym-credential-proxy"
version = "0.1.1"
version = "0.1.3"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -0,0 +1,20 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
DROP TABLE blinded_shares;
CREATE TABLE blinded_shares
(
id INTEGER NOT NULL PRIMARY KEY,
-- removed reference to `ticketbook_deposit` as the deposit wouldn't actually have been made before the pending share is inserted
request_uuid TEXT NOT NULL,
status TEXT NOT NULL,
device_id TEXT NOT NULL,
credential_id TEXT NOT NULL,
available_shares INTEGER NOT NULL DEFAULT 0,
error_message TEXT DEFAULT NULL,
created TIMESTAMP WITHOUT TIME ZONE NOT NULL,
updated TIMESTAMP WITHOUT TIME ZONE NOT NULL
);
@@ -3,10 +3,12 @@
use crate::error::VpnApiError;
use crate::http::state::ApiState;
use crate::storage::models::BlindedShares;
use futures::{stream, StreamExt};
use nym_credential_proxy_requests::api::v1::ticketbook::models::{
TicketbookAsyncRequest, TicketbookObtainQueryParams, TicketbookRequest,
TicketbookWalletSharesResponse, WalletShare,
TicketbookWalletSharesResponse, WalletShare, WebhookTicketbookWalletShares,
WebhookTicketbookWalletSharesRequest,
};
use nym_credentials::IssuanceTicketBook;
use nym_credentials_interface::Base58;
@@ -217,11 +219,13 @@ async fn try_obtain_blinded_ticketbook_async_inner(
requested_on: OffsetDateTime,
request_data: TicketbookAsyncRequest,
params: TicketbookObtainQueryParams,
pending: &BlindedShares,
) -> Result<(), VpnApiError> {
let epoch_id = state.current_epoch_id().await?;
let device_id = &request_data.device_id;
let credential_id = &request_data.credential_id;
let secret = request_data.secret.clone();
// 1. try to obtain global data
let (
@@ -259,19 +263,70 @@ async fn try_obtain_blinded_ticketbook_async_inner(
error!(uuid = %request, "failed to update db with issued information: {err}")
}
// 4. build the response
let response = TicketbookWalletSharesResponse {
// 4. build the webhook request body
let data = Some(TicketbookWalletSharesResponse {
epoch_id,
shares,
master_verification_key,
aggregated_coin_index_signatures,
aggregated_expiration_date_signatures,
});
let ticketbook_wallet_shares = WebhookTicketbookWalletShares {
id: pending.id,
status: pending.status.to_string(),
device_id: device_id.clone(),
credential_id: credential_id.clone(),
data,
error_message: None,
created: pending.created,
updated: pending.updated,
};
let webhook_request = WebhookTicketbookWalletSharesRequest {
ticketbook_wallet_shares,
secret,
};
// 5. call the webhook
state
.zk_nym_web_hook()
.try_trigger(request, &response)
.try_trigger(request, &webhook_request)
.await;
Ok(())
}
async fn try_trigger_webhook_request_for_error(
state: &ApiState,
request: Uuid,
request_data: TicketbookAsyncRequest,
pending: &BlindedShares,
error_message: String,
) -> Result<(), VpnApiError> {
let device_id = &request_data.device_id;
let credential_id = &request_data.credential_id;
let secret = request_data.secret.clone();
let ticketbook_wallet_shares = WebhookTicketbookWalletShares {
id: pending.id,
status: "error".to_string(),
device_id: device_id.clone(),
credential_id: credential_id.clone(),
data: None,
error_message: Some(error_message),
created: pending.created,
updated: pending.updated,
};
let webhook_request = WebhookTicketbookWalletSharesRequest {
ticketbook_wallet_shares,
secret,
};
state
.zk_nym_web_hook()
.try_trigger(request, &webhook_request)
.await;
Ok(())
@@ -285,16 +340,30 @@ pub(crate) async fn try_obtain_blinded_ticketbook_async(
requested_on: OffsetDateTime,
request_data: TicketbookAsyncRequest,
params: TicketbookObtainQueryParams,
pending: BlindedShares,
) {
if let Err(err) = try_obtain_blinded_ticketbook_async_inner(
&state,
request,
requested_on,
request_data,
request_data.clone(),
params,
&pending,
)
.await
{
// post to the webhook to notify of errors on this side
if let Err(webhook_err) = try_trigger_webhook_request_for_error(
&state,
request,
request_data,
&pending,
format!("Failed to get ticketbook: {err}"),
)
.await
{
error!(uuid = %request, "failed to make webhook request to report error: {webhook_err}")
}
error!(uuid = %request, "failed to resolve the blinded ticketbook issuance: {err}")
} else {
info!(uuid = %request, "managed to resolve the blinded ticketbook issuance")
@@ -84,8 +84,8 @@ pub(crate) struct ApiDoc;
api_requests::v1::ticketbook::models::WalletShare,
api_requests::v1::ticketbook::models::TicketbookWalletSharesResponse,
api_requests::v1::ticketbook::models::TicketbookWalletSharesAsyncResponse,
api_requests::v1::ticketbook::models::BlindedWalletSharesResponse,
api_requests::v1::ticketbook::models::WebhookBlindedSharesResponse,
api_requests::v1::ticketbook::models::WebhookTicketbookWalletShares,
api_requests::v1::ticketbook::models::WebhookTicketbookWalletSharesRequest,
api_requests::v1::ticketbook::models::TicketbookObtainQueryParams,
api_requests::v1::ticketbook::models::SharesQueryParams,
api_requests::v1::ticketbook::models::PlaceholderJsonSchemaImpl,
@@ -192,6 +192,7 @@ pub(crate) async fn obtain_ticketbook_shares_async(
}
Ok(pending) => pending,
};
let id = pending.id;
// 3. try to spawn a new task attempting to resolve the request
if state
@@ -201,6 +202,7 @@ pub(crate) async fn obtain_ticketbook_shares_async(
requested_on,
payload,
params,
pending,
))
.is_none()
{
@@ -213,10 +215,7 @@ pub(crate) async fn obtain_ticketbook_shares_async(
}
// 4. in the meantime, return the id to the user
Ok(output.to_response(TicketbookWalletSharesAsyncResponse {
id: pending.id,
uuid,
}))
Ok(output.to_response(TicketbookWalletSharesAsyncResponse { id, uuid }))
}
/// Obtain the current value of the bandwidth voucher deposit
@@ -149,7 +149,7 @@ pub(crate) async fn query_for_shares_by_id(
(status = 401, description = "authentication token is missing or is invalid"),
(status = 500, body = ErrorResponse, description = "failed to query for bandwidth blinded shares"),
),
params(OutputParams),
params(SharesQueryParams),
security(
("auth_token" = [])
)
@@ -83,7 +83,11 @@ impl SqliteStorageManager {
sqlx::query_as!(
MinimalWalletShare,
r#"
SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date as "expiration_date!: Date"
SELECT
t1.node_id as "node_id!",
t1.blinded_signature as "blinded_signature!",
t1.epoch_id as "epoch_id!",
t1.expiration_date as "expiration_date!: Date"
FROM partial_blinded_wallet as t1
JOIN ticketbook_deposit as t2
on t1.corresponding_deposit = t2.deposit_id
+2
View File
@@ -1,3 +1,5 @@
#![allow(deprecated)]
use crate::db::models::{
gateway, mixnode, GatewayRecord, MixnodeRecord, NetworkSummary, GATEWAYS_BLACKLISTED_COUNT,
GATEWAYS_BONDED_COUNT, GATEWAYS_EXPLORER_COUNT, GATEWAYS_HISTORICAL_COUNT,
@@ -159,7 +159,8 @@ type FinishedSessions = Vec<(u64, String)>;
#[derive(Debug, Clone)]
pub struct SessionStatsState {
pub update_time: Date,
pub unique_active_users: u32,
pub unique_active_users_count: u32,
pub unique_active_users_hashes: Vec<String>,
pub session_started: u32,
pub sessions: FinishedSessions,
}
@@ -174,7 +175,8 @@ impl SessionStatsState {
.collect();
SessionStats {
update_time: self.update_time.with_time(time!(0:00)).assume_utc(),
unique_active_users: self.unique_active_users,
unique_active_users: self.unique_active_users_count,
unique_active_users_hashes: self.unique_active_users_hashes.clone(),
sessions,
sessions_started: self.session_started,
sessions_finished: self.sessions.len() as u32,
@@ -186,7 +188,8 @@ impl Default for SessionStatsState {
fn default() -> Self {
SessionStatsState {
update_time: OffsetDateTime::UNIX_EPOCH.date(),
unique_active_users: 0,
unique_active_users_count: 0,
unique_active_users_hashes: Default::default(),
session_started: 0,
sessions: Default::default(),
}
@@ -50,6 +50,8 @@ pub struct SessionStats {
pub unique_active_users: u32,
pub unique_active_users_hashes: Vec<String>,
pub sessions: Vec<Session>,
pub sessions_started: u32,
+35 -35
View File
@@ -173,7 +173,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -658,7 +658,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1022,7 +1022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1083,7 +1083,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1751,7 +1751,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1931,7 +1931,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -3428,7 +3428,6 @@ dependencies = [
"flate2",
"futures",
"itertools 0.13.0",
"log",
"nym-api-requests",
"nym-coconut-bandwidth-contract-common",
"nym-coconut-dkg-common",
@@ -3452,6 +3451,7 @@ dependencies = [
"thiserror",
"time",
"tokio",
"tracing",
"url",
"wasmtimer",
"zeroize",
@@ -3667,7 +3667,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -3854,7 +3854,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -3983,7 +3983,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4140,7 +4140,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4151,9 +4151,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.79"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@@ -4178,7 +4178,7 @@ dependencies = [
"itertools 0.11.0",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4704,7 +4704,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4798,9 +4798,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
@@ -4825,13 +4825,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4842,14 +4842,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
name = "serde_json"
version = "1.0.128"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [
"itoa 1.0.9",
"memchr",
@@ -4865,7 +4865,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -5219,7 +5219,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -5262,9 +5262,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.55"
version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
dependencies = [
"proc-macro2",
"quote",
@@ -5769,7 +5769,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -5856,7 +5856,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -6021,7 +6021,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -6097,7 +6097,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
"termcolor",
]
@@ -6201,7 +6201,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -6316,7 +6316,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
"wasm-bindgen-shared",
]
@@ -6350,7 +6350,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -6979,7 +6979,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
+3 -3
View File
@@ -115,7 +115,7 @@ fn main() {
utils::owns_mixnode,
utils::owns_nym_node,
utils::get_env,
utils::try_convert_pubkey_to_mix_id,
utils::try_convert_pubkey_to_node_id,
utils::default_mixnode_cost_params,
nym_api::status::compute_mixnode_reward_estimation,
nym_api::status::gateway_core_node_status,
@@ -178,8 +178,8 @@ fn main() {
simulate::mixnet::simulate_update_mixnode_config,
simulate::mixnet::simulate_update_mixnode_cost_params,
simulate::mixnet::simulate_update_gateway_config,
simulate::mixnet::simulate_delegate_to_mixnode,
simulate::mixnet::simulate_undelegate_from_mixnode,
simulate::mixnet::simulate_delegate_to_node,
simulate::mixnet::simulate_undelegate_from_node,
simulate::vesting::simulate_vesting_delegate_to_mixnode,
simulate::vesting::simulate_vesting_undelegate_from_mixnode,
simulate::vesting::simulate_vesting_bond_gateway,
@@ -403,6 +403,8 @@ pub async fn update_gateway_config(
)?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn get_mixnode_avg_uptime(
state: tauri::State<'_, WalletState>,
@@ -605,6 +607,8 @@ pub async fn get_nym_node_description(
)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn get_mixnode_uptime(
mix_id: NodeId,
@@ -141,6 +141,8 @@ pub async fn undelegate_all_from_mixnode(
Ok(res)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn get_all_mix_delegations(
state: tauri::State<'_, WalletState>,
@@ -14,6 +14,8 @@ use nym_validator_client::models::{
MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse,
};
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn mixnode_core_node_status(
mix_id: NodeId,
@@ -25,6 +27,8 @@ pub async fn mixnode_core_node_status(
.await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn gateway_core_node_status(
identity: IdentityKeyRef<'_>,
@@ -36,6 +40,8 @@ pub async fn gateway_core_node_status(
.await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn gateway_report(
identity: IdentityKeyRef<'_>,
@@ -44,6 +50,8 @@ pub async fn gateway_report(
Ok(api_client!(state).get_gateway_report(identity).await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn mixnode_status(
mix_id: NodeId,
@@ -52,6 +60,8 @@ pub async fn mixnode_status(
Ok(api_client!(state).get_mixnode_status(mix_id).await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn mixnode_reward_estimation(
mix_id: NodeId,
@@ -62,6 +72,8 @@ pub async fn mixnode_reward_estimation(
.await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn compute_mixnode_reward_estimation(
mix_id: u32,
@@ -85,6 +97,8 @@ pub async fn compute_mixnode_reward_estimation(
.await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn mixnode_stake_saturation(
mix_id: NodeId,
@@ -95,6 +109,8 @@ pub async fn mixnode_stake_saturation(
.await?)
}
// TODO: fix later (yeah...)
#[allow(deprecated)]
#[tauri::command]
pub async fn mixnode_inclusion_probability(
mix_id: NodeId,
@@ -116,12 +116,12 @@ pub async fn generate_gateway_bonding_msg_payload(
#[tauri::command]
pub async fn generate_nym_node_bonding_msg_payload(
nym_node: NymNode,
nymnode: NymNode,
cost_params: NodeCostParams,
pledge: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<String, BackendError> {
nym_node_bonding_msg_payload(nym_node, cost_params, pledge, state).await
nym_node_bonding_msg_payload(nymnode, cost_params, pledge, state).await
}
#[tauri::command]
@@ -184,7 +184,7 @@ pub async fn simulate_update_gateway_config(
}
#[tauri::command]
pub async fn simulate_delegate_to_mixnode(
pub async fn simulate_delegate_to_node(
node_id: NodeId,
amount: DecCoin,
state: tauri::State<'_, WalletState>,
@@ -193,7 +193,7 @@ pub async fn simulate_delegate_to_mixnode(
}
#[tauri::command]
pub async fn simulate_undelegate_from_mixnode(
pub async fn simulate_undelegate_from_node(
node_id: NodeId,
state: tauri::State<'_, WalletState>,
) -> Result<FeeDetails, BackendError> {
+24 -6
View File
@@ -55,16 +55,34 @@ pub async fn owns_nym_node(state: tauri::State<'_, WalletState>) -> Result<bool,
}
#[tauri::command]
pub async fn try_convert_pubkey_to_mix_id(
pub async fn try_convert_pubkey_to_node_id(
state: tauri::State<'_, WalletState>,
mix_identity: IdentityKey,
) -> Result<Option<NodeId>, BackendError> {
let res = nyxd_client!(state)
.get_mixnode_details_by_identity(mix_identity)
.await?;
Ok(res
let guard = state.read().await;
let client = guard.current_client()?;
// first try native nym-node
if let Some(node) = client
.nyxd
.get_nymnode_details_by_identity(mix_identity.clone())
.await?
.details
{
return Ok(Some(node.node_id()));
}
// fallback to legacy mixnode
if let Some(node) = client
.nyxd
.get_mixnode_details_by_identity(mix_identity.clone())
.await?
.mixnode_details
.map(|mixnode_details| mixnode_details.mix_id()))
{
return Ok(Some(node.mix_id()));
}
Ok(None)
}
#[tauri::command]
@@ -2,15 +2,15 @@ import React, { createContext, useContext, useMemo, useState } from 'react';
import { CurrencyDenom } from '@nymproject/types';
import { TBondNymNodeArgs, TBondMixNodeArgs } from 'src/types';
const defaultNymNodeValues: TBondNymNodeArgs['nymNode'] = {
identity_key: 'H6rXWgsW89QsVyaNSS3qBe9zZFLhBS6Gn3YRkGFSoFW9',
custom_http_port: 1,
const defaultNymNodeValues: TBondNymNodeArgs['nymnode'] = {
identity_key: '',
custom_http_port: null,
host: '1.1.1.1',
};
const defaultCostParams = (denom: CurrencyDenom): TBondNymNodeArgs['costParams'] => ({
interval_operating_cost: { amount: '40', denom },
profit_margin_percent: '10',
profit_margin_percent: '40',
});
const defaultAmount = (denom: CurrencyDenom): TBondMixNodeArgs['pledge'] => ({
@@ -21,14 +21,14 @@ const defaultAmount = (denom: CurrencyDenom): TBondMixNodeArgs['pledge'] => ({
interface FormContextType {
step: 1 | 2 | 3 | 4;
setStep: React.Dispatch<React.SetStateAction<1 | 2 | 3 | 4>>;
nymNodeData: TBondNymNodeArgs['nymNode'];
setNymNodeData: React.Dispatch<React.SetStateAction<TBondNymNodeArgs['nymNode']>>;
nymNodeData: TBondNymNodeArgs['nymnode'];
setNymNodeData: React.Dispatch<React.SetStateAction<TBondNymNodeArgs['nymnode']>>;
costParams: TBondNymNodeArgs['costParams'];
setCostParams: React.Dispatch<React.SetStateAction<TBondNymNodeArgs['costParams']>>;
amountData: TBondMixNodeArgs['pledge'];
setAmountData: React.Dispatch<React.SetStateAction<TBondMixNodeArgs['pledge']>>;
signature?: string;
setSignature: React.Dispatch<React.SetStateAction<string | undefined>>;
signature: string;
setSignature: React.Dispatch<React.SetStateAction<string>>;
onError: (e: string) => void;
}
@@ -41,9 +41,8 @@ const FormContext = createContext<FormContextType>({
setCostParams: () => {},
amountData: defaultAmount('nym'),
setAmountData: () => {},
signature: undefined,
signature: '',
setSignature: () => {},
onError: () => {},
});
@@ -52,10 +51,10 @@ const FormContextProvider = ({ children }: { children: React.ReactNode }) => {
const denom = 'nym';
const [step, setStep] = useState<1 | 2 | 3 | 4>(1);
const [nymNodeData, setNymNodeData] = useState<TBondNymNodeArgs['nymNode']>(defaultNymNodeValues);
const [nymNodeData, setNymNodeData] = useState<TBondNymNodeArgs['nymnode']>(defaultNymNodeValues);
const [costParams, setCostParams] = useState<TBondNymNodeArgs['costParams']>(defaultCostParams(denom));
const [amountData, setAmountData] = useState<TBondNymNodeArgs['pledge']>(defaultAmount(denom));
const [signature, setSignature] = useState<string>();
const [signature, setSignature] = useState('');
const onError = (e: string) => {
console.error(e);
@@ -1,22 +1,12 @@
import React from 'react';
import { Stack, TextField, Box, FormHelperText } from '@mui/material';
import { useForm } from 'react-hook-form';
import { TBondNymNodeArgs } from 'src/types';
import { yupResolver } from '@hookform/resolvers/yup';
import { SimpleModal } from 'src/components/Modals/SimpleModal';
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
import { checkHasEnoughFunds } from 'src/utils';
import { nymNodeAmountSchema } from './amountValidationSchema';
const defaultNymNodeCostParamValues: TBondNymNodeArgs['costParams'] = {
profit_margin_percent: '10',
interval_operating_cost: { amount: '40', denom: 'nym' },
};
const defaultNymNodePledgeValue: TBondNymNodeArgs['pledge'] = {
amount: '100',
denom: 'nym',
};
import { useFormContext } from './FormContext';
type NymNodeDataProps = {
onClose: () => void;
@@ -26,6 +16,7 @@ type NymNodeDataProps = {
};
const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
const { setAmountData, setCostParams, amountData, costParams } = useFormContext();
const {
formState: { errors },
register,
@@ -36,21 +27,26 @@ const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
} = useForm({
mode: 'all',
defaultValues: {
pledge: defaultNymNodePledgeValue,
...defaultNymNodeCostParamValues,
pledge: amountData,
...costParams,
},
resolver: yupResolver(nymNodeAmountSchema()),
});
console.log(errors, 'errors');
const handleRequestValidation = async () => {
const values = getValues();
const hasSufficientTokens = await checkHasEnoughFunds(values.pledge.amount);
if (hasSufficientTokens) {
handleSubmit(onNext)();
handleSubmit((args) => {
setAmountData(args.pledge);
setCostParams({
profit_margin_percent: args.profit_margin_percent,
interval_operating_cost: args.interval_operating_cost,
});
onNext();
})();
} else {
setError('pledge.amount', { message: 'Not enough tokens' });
}
@@ -77,8 +73,8 @@ const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
setValue('pledge.amount', newValue.amount, { shouldValidate: true });
}}
validationError={errors.pledge?.amount?.message}
denom={defaultNymNodePledgeValue.denom}
initialValue={defaultNymNodePledgeValue.amount}
denom={amountData.denom}
initialValue={amountData.amount}
/>
<Box>
@@ -90,8 +86,8 @@ const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
setValue('interval_operating_cost', newValue, { shouldValidate: true });
}}
validationError={errors.interval_operating_cost?.amount?.message}
denom={defaultNymNodeCostParamValues.interval_operating_cost.denom}
initialValue={defaultNymNodeCostParamValues.interval_operating_cost.amount}
denom={costParams.interval_operating_cost.denom}
initialValue={costParams.interval_operating_cost.amount}
/>
<FormHelperText>
Monthly operational costs of running your node. If your node is in the active set the amount will be paid
@@ -1,32 +1,12 @@
import React from 'react';
import * as Yup from 'yup';
import { Stack, TextField, FormControlLabel, Checkbox } from '@mui/material';
import { useForm } from 'react-hook-form';
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
import { TBondNymNodeArgs } from 'src/types';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { isValidHostname, validateRawPort } from 'src/utils';
import { SimpleModal } from 'src/components/Modals/SimpleModal';
const defaultNymNodeValues: TBondNymNodeArgs['nymNode'] = {
identity_key: 'H6rXWgsW89QsVyaNSS3qBe9zZFLhBS6Gn3YRkGFSoFW9',
custom_http_port: 1,
host: '1.1.1.1',
};
const yupValidationSchema = yup.object().shape({
identity_key: yup.string().required('Identity key is required'),
host: yup
.string()
.required('A host is required')
.test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || ''))
.test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)),
custom_http_port: yup
.number()
.required('A custom http port is required')
.test('valid-http', 'A valid http port is required', (value) => (value ? validateRawPort(value) : false)),
});
import { useFormContext } from './FormContext';
import { settingsValidationSchema } from './settingsValidationSchema';
type NymNodeDataProps = {
onClose: () => void;
@@ -35,7 +15,13 @@ type NymNodeDataProps = {
step: number;
};
const validationSchema = Yup.object().shape({
identity_key: Yup.string().required('Identity key is required'),
...settingsValidationSchema.fields,
});
const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => {
const { setNymNodeData, nymNodeData } = useFormContext();
const {
formState: { errors },
register,
@@ -43,14 +29,17 @@ const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => {
handleSubmit,
} = useForm({
mode: 'all',
defaultValues: defaultNymNodeValues,
resolver: yupResolver(yupValidationSchema),
defaultValues: nymNodeData,
resolver: yupResolver(validationSchema),
});
const [showAdvancedOptions, setShowAdvancedOptions] = React.useState(false);
const handleNext = async () => {
handleSubmit(onNext)();
handleSubmit((args) => {
setNymNodeData(args);
onNext();
})();
};
return (
@@ -69,7 +58,7 @@ const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => {
required
fullWidth
label="Identity Key"
initialValue={defaultNymNodeValues.identity_key}
initialValue={nymNodeData.identity_key}
errorText={errors.identity_key?.message?.toString()}
onChanged={(value) => setValue('identity_key', value, { shouldValidate: true })}
showTickOnValid={false}
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import * as yup from 'yup';
import { Stack, TextField, Typography } from '@mui/material';
import { useForm } from 'react-hook-form';
import { CopyToClipboard } from 'src/components/CopyToClipboard';
@@ -7,9 +8,11 @@ import { SimpleModal } from 'src/components/Modals/SimpleModal';
import { useBondingContext } from 'src/context';
import { TBondNymNodeArgs } from 'src/types';
import { Signature } from 'src/pages/bonding/types';
import { yupResolver } from '@hookform/resolvers/yup';
import { useFormContext } from './FormContext';
const NymNodeSignature = ({
nymNode,
nymnode,
pledge,
costParams,
step,
@@ -17,27 +20,38 @@ const NymNodeSignature = ({
onClose,
onBack,
}: {
nymNode: TBondNymNodeArgs['nymNode'];
nymnode: TBondNymNodeArgs['nymnode'];
pledge: TBondNymNodeArgs['pledge'];
costParams: TBondNymNodeArgs['costParams'];
step: number;
onNext: (data: Signature) => void;
onNext: () => void;
onClose: () => void;
onBack: () => void;
}) => {
const [message, setMessage] = useState<string>('');
const [error, setError] = useState<string>();
const { generateNymNodeMsgPayload } = useBondingContext();
const { signature, setSignature } = useFormContext();
const yupValidationSchema = yup.object().shape({
signature: yup.string().required('Signature is required'),
});
const {
register,
formState: { errors },
} = useForm<Signature>();
handleSubmit,
} = useForm<Signature>({
defaultValues: {
signature,
},
resolver: yupResolver(yupValidationSchema),
});
const generateMessage = async () => {
try {
const msg = await generateNymNodeMsgPayload({
nymNode,
nymnode,
pledge,
costParams,
});
@@ -53,10 +67,10 @@ const NymNodeSignature = ({
useEffect(() => {
generateMessage();
}, [nymNode, pledge, costParams]);
}, []);
const onSubmit = async (data: Signature) => {
onNext(data);
const handleNext = async () => {
handleSubmit(onNext)();
};
if (error) {
@@ -66,11 +80,7 @@ const NymNodeSignature = ({
return (
<SimpleModal
open
onOk={() =>
onSubmit({
signature: 'signature',
})
}
onOk={handleNext}
onClose={onClose}
header="Bond Nym Node"
subHeader={`Step ${step}/3`}
@@ -95,10 +105,13 @@ const NymNodeSignature = ({
</Stack>
<TextField
{...register('signature')}
onChange={(e) => setSignature(e.target.value)}
id="outlined-multiline-static"
name="signature"
rows={3}
placeholder="Paste Signature"
helperText={errors.signature?.message}
error={Boolean(errors.signature)}
multiline
fullWidth
required
@@ -5,8 +5,8 @@ import { isLessThan, isGreaterThan, validateAmount } from 'src/utils';
const operatingCostAndPmValidation = (params?: TauriContractStateParams) => {
const defaultParams = {
profit_margin_percent: {
minimum: parseFloat(params?.profit_margin.minimum || '0%'),
maximum: parseFloat(params?.profit_margin.maximum || '100%'),
minimum: parseFloat(params?.profit_margin.minimum || '20%'),
maximum: parseFloat(params?.profit_margin.maximum || '50%'),
},
interval_operating_cost: {
@@ -0,0 +1,29 @@
import { isValidHostname, validateRawPort } from 'src/utils';
import * as Yup from 'yup';
const settingsValidationSchema = Yup.object().shape({
host: Yup.string()
.required('A host is required')
.test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || ''))
.test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)),
custom_http_port: Yup.number()
.nullable()
.transform((numberVal, stringVal) => {
if (stringVal === '') {
return null;
}
if (!Number(stringVal)) {
return stringVal;
}
return numberVal;
})
.test('valid-http', 'A valid http port is required', (value) => {
if (value === null) {
return true;
}
return value ? validateRawPort(value) : false;
}),
});
export { settingsValidationSchema };
@@ -2,18 +2,24 @@ import React, { useContext, useEffect } from 'react';
import { ConfirmTx } from 'src/components/ConfirmTX';
import { ModalListItem } from 'src/components/Modals/ModalListItem';
import { useGetFee } from 'src/hooks/useGetFee';
import { Signature } from 'src/pages/bonding/types';
import { BalanceWarning } from 'src/components/FeeWarning';
import { AppContext } from 'src/context';
import { TBondNymNodeArgs } from 'src/types';
import FormContextProvider, { useFormContext } from '../forms/nym-node/FormContext';
import NymNodeData from '../forms/nym-node/NymNodeData';
import NymNodeAmount from '../forms/nym-node/NymNodeAmount';
import NymNodeSignature from '../forms/nym-node/NymNodeSignature';
export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
export const BondNymNodeModal = ({
onClose,
onBond,
}: {
onClose: () => void;
onBond: (data: TBondNymNodeArgs) => Promise<void>;
}) => {
const { fee, resetFeeState, feeError } = useGetFee();
const { userBalance } = useContext(AppContext);
const { setStep, step, onError, setSignature, amountData, costParams, nymNodeData } = useFormContext();
const { setStep, step, onError, signature, amountData, costParams, nymNodeData } = useFormContext();
useEffect(() => {
if (feeError) {
@@ -21,12 +27,17 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
}
}, [feeError]);
const handleUpdateMixnodeData = async () => {
const handleUpdateNymnodeData = async () => {
setStep(2);
};
const handleUpdateSignature = async (data: Signature) => {
setSignature(data.signature);
const handleBond = async () => {
onBond({
nymnode: nymNodeData,
pledge: amountData,
costParams,
msgSignature: signature,
});
};
const handleConfirm = async () => {};
@@ -51,7 +62,7 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
}
if (step === 1) {
return <NymNodeData onClose={onClose} onBack={onClose} onNext={handleUpdateMixnodeData} step={step} />;
return <NymNodeData onClose={onClose} onBack={onClose} onNext={handleUpdateNymnodeData} step={step} />;
}
if (step === 2) {
@@ -61,10 +72,10 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
if (step === 3) {
return (
<NymNodeSignature
nymNode={nymNodeData}
nymnode={nymNodeData}
pledge={amountData}
costParams={costParams}
onNext={handleUpdateSignature}
onNext={handleBond}
onClose={onClose}
onBack={() => setStep(2)}
step={step}
@@ -75,14 +86,22 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
return null;
};
export const BondNymNodeModalWithState = ({ open, onClose }: { open: boolean; onClose: () => void }) => {
export const BondNymNode = ({
open,
onClose,
onBond,
}: {
open: boolean;
onClose: () => void;
onBond: (data: TBondNymNodeArgs) => Promise<void>;
}) => {
if (!open) {
return null;
}
return (
<FormContextProvider>
<BondNymNodeModal onClose={onClose} />
<BondNymNodeModal onClose={onClose} onBond={onBond} />
</FormContextProvider>
);
};
@@ -1,10 +1,9 @@
import * as React from 'react';
import { useEffect } from 'react';
import { Typography } from '@mui/material';
import { TBondedNode } from 'src/context';
import { useGetFee } from 'src/hooks/useGetFee';
import { isGateway, isMixnode } from 'src/types';
import { TBondedGateway } from 'src/requests/gatewayDetails';
import { TBondedMixnode } from 'src/requests/mixnodeDetails';
import { ModalFee } from '../../Modals/ModalFee';
import { ModalListItem } from '../../Modals/ModalListItem';
import { SimpleModal } from '../../Modals/SimpleModal';
@@ -16,7 +15,7 @@ import {
} from '../../../requests';
interface Props {
node: TBondedMixnode | TBondedGateway;
node: TBondedNode;
onConfirm: () => Promise<void>;
onClose: () => void;
onError: (e: string) => void;
@@ -5,7 +5,7 @@ import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'
import { CurrencyDenom, FeeDetails, DecCoin, decimalToFloatApproximation } from '@nymproject/types';
import { Console } from 'src/utils/console';
import { useGetFee } from 'src/hooks/useGetFee';
import { simulateDelegateToMixnode, simulateVestingDelegateToMixnode, tryConvertIdentityToMixId } from 'src/requests';
import { simulateDelegateToNode, simulateVestingDelegateToMixnode, tryConvertIdentityToNodeId } from 'src/requests';
import { debounce } from 'lodash';
import { AppContext } from 'src/context';
import { SimpleModal } from '../Modals/SimpleModal';
@@ -152,7 +152,7 @@ export const DelegateModal: FCWithChildren<{
}
if (tokenPool === 'balance') {
getFee(simulateDelegateToMixnode, { mixId: id, amount: value });
getFee(simulateDelegateToNode, { nodeId: id, amount: value });
}
if (tokenPool === 'locked') {
@@ -187,16 +187,16 @@ export const DelegateModal: FCWithChildren<{
}
let res;
try {
res = await tryConvertIdentityToMixId(idKey);
res = await tryConvertIdentityToNodeId(idKey);
} catch (e) {
Console.warn(`failed to resolve mix_id for "${idKey}": ${e}`);
Console.warn(`failed to resolve node_id for "${idKey}": ${e}`);
return;
}
if (res) {
setMixId(res);
setMixIdError(undefined);
} else {
setMixIdError('Mixnode with this identity does not seem to be currently bonded');
setMixIdError('Node with this identity does not seem to be currently bonded');
}
}, 500),
[],
@@ -2,7 +2,7 @@ import React, { useContext, useEffect } from 'react';
import { Box, SxProps } from '@mui/material';
import { FeeDetails } from '@nymproject/types';
import { useGetFee } from 'src/hooks/useGetFee';
import { simulateUndelegateFromMixnode, simulateVestingUndelegateFromMixnode } from 'src/requests';
import { simulateUndelegateFromNode, simulateVestingUndelegateFromMixnode } from 'src/requests';
import { AppContext } from 'src/context';
import { ModalFee } from '../Modals/ModalFee';
import { ModalListItem } from '../Modals/ModalListItem';
@@ -27,7 +27,7 @@ export const UndelegateModal: FCWithChildren<{
useEffect(() => {
if (usesVestingContractTokens) getFee(simulateVestingUndelegateFromMixnode, { mixId });
else {
getFee(simulateUndelegateFromMixnode, mixId);
getFee(simulateUndelegateFromNode, mixId);
}
}, []);
+61 -4
View File
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { FeeDetails, TransactionExecuteResult } from '@nymproject/types';
import { isGateway, isMixnode, TUpdateBondArgs, isNymNode, TNymNodeSignatureArgs } from 'src/types';
import { FeeDetails, NodeConfigUpdate, TransactionExecuteResult } from '@nymproject/types';
import { isGateway, isMixnode, TUpdateBondArgs, isNymNode, TNymNodeSignatureArgs, TBondNymNodeArgs } from 'src/types';
import { Console } from 'src/utils/console';
import useGetNodeDetails from 'src/hooks/useGetNodeDetails';
import { TBondedNymNode } from 'src/requests/nymNodeDetails';
@@ -20,6 +20,8 @@ import {
migrateVestedMixnode as tauriMigrateVestedMixnode,
migrateLegacyMixnode as migrateLegacyMixnodeReq,
migrateLegacyGateway as migrateLegacyGatewayReq,
bondNymNode,
updateNymNodeConfig as updateNymNodeConfigReq,
} from '../requests';
export type TBondedNode = TBondedMixnode | TBondedGateway | TBondedNymNode;
@@ -31,7 +33,9 @@ export type TBondingContext = {
isVestingAccount: boolean;
refresh: () => void;
unbond: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
bond: (args: TBondNymNodeArgs) => Promise<TransactionExecuteResult | undefined>;
updateBondAmount: (data: TUpdateBondArgs) => Promise<TransactionExecuteResult | undefined>;
updateNymNodeConfig: (data: NodeConfigUpdate) => Promise<TransactionExecuteResult | undefined>;
redeemRewards: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
generateNymNodeMsgPayload: (data: TNymNodeSignatureArgs) => Promise<string | undefined>;
migrateVestedMixnode: () => Promise<TransactionExecuteResult | undefined>;
@@ -41,12 +45,18 @@ export type TBondingContext = {
export const BondingContext = createContext<TBondingContext>({
isLoading: true,
refresh: async () => undefined,
bond: async () => {
throw new Error('Not implemented');
},
unbond: async () => {
throw new Error('Not implemented');
},
updateBondAmount: async () => {
throw new Error('Not implemented');
},
updateNymNodeConfig: async () => {
throw new Error('Not implemented');
},
redeemRewards: async () => {
throw new Error('Not implemented');
},
@@ -70,7 +80,11 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
const { userBalance, clientDetails, network } = useContext(AppContext);
const { bondedNode, isLoading: isBondedNodeLoading } = useGetNodeDetails(clientDetails?.client_address, network);
const {
bondedNode,
isLoading: isBondedNodeLoading,
getNodeDetails,
} = useGetNodeDetails(clientDetails?.client_address, network);
useEffect(() => {
userBalance.fetchBalance();
@@ -91,6 +105,30 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
resetState();
};
const bond = async (data: TBondNymNodeArgs) => {
let tx;
setIsLoading(true);
try {
tx = await bondNymNode({
...data,
costParams: {
...data.costParams,
profit_margin_percent: toPercentFloatString(data.costParams.profit_margin_percent),
},
});
if (clientDetails?.client_address) {
await getNodeDetails(clientDetails?.client_address);
}
} catch (e) {
Console.warn(e);
setError(`an error occurred: ${e as string}`);
} finally {
setIsLoading(false);
}
return tx;
};
const unbond = async (fee?: FeeDetails) => {
let tx;
setIsLoading(true);
@@ -107,6 +145,23 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
return tx;
};
const updateNymNodeConfig = async (data: NodeConfigUpdate) => {
let tx;
setIsLoading(true);
try {
tx = await updateNymNodeConfigReq(data);
if (clientDetails?.client_address) {
await getNodeDetails(clientDetails?.client_address);
}
} catch (e) {
Console.warn(e);
setError(`an error occurred: ${e}`);
} finally {
setIsLoading(false);
}
return tx;
};
const redeemRewards = async (fee?: FeeDetails) => {
let tx;
setIsLoading(true);
@@ -143,7 +198,7 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
try {
const message = await generateNymNodeMsgPayloadReq({
nymNode: data.nymNode,
nymnode: data.nymnode,
pledge: data.pledge,
costParams: {
...data.costParams,
@@ -187,10 +242,12 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
isLoading: isLoading || isBondedNodeLoading,
error,
bondedNode,
bond,
unbond,
refresh,
redeemRewards,
updateBondAmount,
updateNymNodeConfig,
generateNymNodeMsgPayload,
migrateVestedMixnode,
migrateLegacyNode,
+18
View File
@@ -133,6 +133,14 @@ export const MockBondingContextProvider = ({
return TxResultMock;
};
const bond = async (): Promise<TransactionExecuteResult> => {
setIsLoading(true);
await mockSleep(SLEEP_MS);
setBondedData(bondedMixnodeMock);
setIsLoading(false);
return TxResultMock;
};
const unbond = async (): Promise<TransactionExecuteResult> => {
setIsLoading(true);
await mockSleep(SLEEP_MS);
@@ -141,6 +149,14 @@ export const MockBondingContextProvider = ({
return TxResultMock;
};
const updateNymNodeConfig = async (): Promise<TransactionExecuteResult> => {
setIsLoading(true);
await mockSleep(SLEEP_MS);
triggerStateUpdate();
setIsLoading(false);
return TxResultMock;
};
const redeemRewards = async (): Promise<TransactionExecuteResult | undefined> => {
setIsLoading(true);
await mockSleep(SLEEP_MS);
@@ -189,6 +205,7 @@ export const MockBondingContextProvider = ({
error,
bondMixnode,
bondGateway,
bond,
unbond,
refresh,
redeemRewards,
@@ -203,6 +220,7 @@ export const MockBondingContextProvider = ({
isVestingAccount: false,
migrateVestedMixnode: async () => undefined,
migrateLegacyNode: async () => undefined,
updateNymNodeConfig,
}),
[isLoading, error, bondedMixnode, bondedGateway, trigger, fee],
);
@@ -63,6 +63,7 @@ const useGetNodeDetails = (clientAddress?: string, network?: string) => {
bondedNode,
isLoading,
isError,
getNodeDetails,
};
};
+16 -3
View File
@@ -11,14 +11,14 @@ import { ConfirmationDetailProps, ConfirmationDetailsModal } from 'src/component
import { ErrorModal } from 'src/components/Modals/ErrorModal';
import { LoadingModal } from 'src/components/Modals/LoadingModal';
import { AppContext, urls } from 'src/context/main';
import { isGateway, isMixnode, isNymNode, TUpdateBondArgs } from 'src/types';
import { isGateway, isMixnode, isNymNode, TBondNymNodeArgs, TUpdateBondArgs } from 'src/types';
import { BondedGateway } from 'src/components/Bonding/BondedGateway';
import { RedeemRewardsModal } from 'src/components/Bonding/modals/RedeemRewardsModal';
import { VestingWarningModal } from 'src/components/VestingWarningModal';
import MigrateLegacyNode from 'src/components/Bonding/modals/MigrateLegacyNode';
import { BondedNymNode } from 'src/components/Bonding/BondedNymNode';
import { UpdateBondAmountNymNode } from 'src/components/Bonding/modals/UpdateBondAmountNymNode';
import { BondNymNodeModalWithState } from 'src/components/Bonding/modals/BondNymNodeModal';
import { BondNymNode } from 'src/components/Bonding/modals/BondNymNodeModal';
import { BondingContextProvider, useBondingContext } from '../../context';
export const Bonding = () => {
@@ -44,6 +44,7 @@ export const Bonding = () => {
redeemRewards,
updateBondAmount,
refresh,
bond,
migrateVestedMixnode,
migrateLegacyNode,
} = useBondingContext();
@@ -74,6 +75,18 @@ export const Bonding = () => {
setShowMigrateLegacyNodeModal(shouldShowMigrateLegacyNodeModal());
}, [bondedNode]);
const handleBondNymNode = async (data: TBondNymNodeArgs) => {
setShowModal(undefined);
const tx = await bond(data);
if (tx) {
setConfirmationDetails({
status: 'success',
title: 'Bonding successful',
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
});
}
};
const handleMigrateVestedMixnode = async () => {
setShowMigrationModal(false);
const tx = await migrateVestedMixnode();
@@ -250,7 +263,7 @@ export const Bonding = () => {
/>
)}
<BondNymNodeModalWithState open={showModal === 'bond-nymnode'} onClose={handleCloseModal} />
<BondNymNode open={showModal === 'bond-nymnode'} onClose={handleCloseModal} onBond={handleBondNymNode} />
{showModal === 'update-bond-oversaturated' && uncappedSaturation && (
<BondOversaturatedModal
@@ -7,7 +7,6 @@ import { isMixnode, isNymNode } from 'src/types';
interface Props {
bondedNode: TBondedNode;
onConfirm: () => Promise<void>;
onError: (e: string) => void;
}
@@ -71,7 +70,7 @@ export const NodeUnbondPage = ({ bondedNode, onConfirm, onError }: Props) => {
</Stack>
</Grid>
</Grid>
{isConfirmed && !isNymNode(bondedNode) && (
{isConfirmed && (
<UnbondModal
node={bondedNode}
onConfirm={async () => {
@@ -3,22 +3,21 @@ import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { Box, Button, Divider, Grid, Stack, TextField, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { updateNymNodeConfig } from 'src/requests';
import { SimpleModal } from 'src/components/Modals/SimpleModal';
import { bondedInfoParametersValidationSchema } from 'src/components/Bonding/forms/legacyForms/mixnodeValidationSchema';
import { Console } from 'src/utils/console';
import { Alert } from 'src/components/Alert';
import { ConfirmTx } from 'src/components/ConfirmTX';
import { useGetFee } from 'src/hooks/useGetFee';
import { LoadingModal } from 'src/components/Modals/LoadingModal';
import { BalanceWarning } from 'src/components/FeeWarning';
import { AppContext } from 'src/context';
import { AppContext, useBondingContext } from 'src/context';
import { TBondedNymNode } from 'src/requests/nymNodeDetails';
import { settingsValidationSchema } from 'src/components/Bonding/forms/nym-node/settingsValidationSchema';
export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymNode }) => {
const [openConfirmationModal, setOpenConfirmationModal] = useState<boolean>(false);
const { fee, resetFeeState } = useGetFee();
const { userBalance } = useContext(AppContext);
const { updateNymNodeConfig } = useBondingContext();
const theme = useTheme();
@@ -27,28 +26,28 @@ export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymN
handleSubmit,
formState: { errors, isSubmitting, isDirty, isValid },
} = useForm({
resolver: yupResolver(bondedInfoParametersValidationSchema),
resolver: yupResolver(settingsValidationSchema),
mode: 'onChange',
defaultValues: {
host: bondedNode.host,
customHttpPort: bondedNode.customHttpPort,
custom_http_port: bondedNode.customHttpPort,
},
});
const onSubmit = async (data: { host?: string; customHttpPort?: number | null }) => {
const onSubmit = async ({ host, custom_http_port }: { host: string; custom_http_port: number | null }) => {
resetFeeState();
const { host, customHttpPort } = data;
if (host && customHttpPort) {
try {
const NymNodeConfigParams = {
host,
custom_http_port: customHttpPort,
custom_http_port,
restore_default_http_port: custom_http_port === null,
};
try {
await updateNymNodeConfig(NymNodeConfigParams);
setOpenConfirmationModal(true);
} catch (error) {
Console.error(error);
}
await updateNymNodeConfig(NymNodeConfigParams);
setOpenConfirmationModal(true);
} catch (error) {
Console.error(error);
}
};
@@ -59,7 +58,7 @@ export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymN
open
header="Update node settings"
fee={fee}
onConfirm={handleSubmit((d) => onSubmit(d))}
onConfirm={handleSubmit(onSubmit)}
onPrev={resetFeeState}
onClose={resetFeeState}
>
@@ -70,7 +69,6 @@ export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymN
)}
</ConfirmTx>
)}
{isSubmitting && <LoadingModal />}
<Alert
title={
<Stack>
@@ -93,12 +91,12 @@ export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymN
<Grid spacing={3} item container alignItems="center" xs={12} md={6}>
<Grid item width={1}>
<TextField
{...register('customHttpPort')}
name="customHttpPort"
{...register('custom_http_port')}
name="custom_http_port"
label="Custom HTTP port"
fullWidth
error={!!errors.customHttpPort}
helperText={errors.customHttpPort?.message}
error={!!errors.custom_http_port}
helperText={errors.custom_http_port?.message}
InputLabelProps={{ shrink: true }}
/>
</Grid>
@@ -134,7 +132,7 @@ export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymN
size="large"
variant="contained"
disabled={isSubmitting || !isDirty || !isValid}
onClick={handleSubmit(() => undefined)}
onClick={handleSubmit(onSubmit)}
sx={{ m: 3, mr: 0 }}
fullWidth
>
@@ -152,7 +150,7 @@ export const GeneralNymNodeSettings = ({ bondedNode }: { bondedNode: TBondedNymN
hideCloseIcon
displayInfoIcon
onOk={async () => {
await setOpenConfirmationModal(false);
setOpenConfirmationModal(false);
}}
buttonFullWidth
sx={{
+2 -1
View File
@@ -4,6 +4,7 @@ import {
SendTxResult,
TransactionExecuteResult,
MixNodeConfigUpdate,
NodeConfigUpdate,
GatewayConfigUpdate,
} from '@nymproject/types';
import { TBondGatewayArgs, TBondGatewaySignatureArgs, TNodeConfigUpdateArgs } from '../types';
@@ -18,7 +19,7 @@ export const generateGatewayMsgPayload = async (args: Omit<TBondGatewaySignature
export const updateMixnodeConfig = async (update: MixNodeConfigUpdate, fee?: Fee) =>
invokeWrapper<TransactionExecuteResult>('update_mixnode_config', { update, fee });
export const updateNymNodeConfig = async (update: TNodeConfigUpdateArgs, fee?: Fee) =>
export const updateNymNodeConfig = async (update: NodeConfigUpdate, fee?: Fee) =>
invokeWrapper<TransactionExecuteResult>('update_nymnode_config', { update, fee });
export const updateGatewayConfig = async (update: GatewayConfigUpdate, fee?: Fee) =>
+6 -6
View File
@@ -28,14 +28,14 @@ export const simulateUpdateMixnodeConfig = async (update: MixNodeConfigUpdate) =
export const simulateUpdateGatewayConfig = async (update: GatewayConfigUpdate) =>
invokeWrapper<FeeDetails>('simulate_update_gateway_config', { update });
export const simulateDelegateToMixnode = async (args: { mixId: number; amount: DecCoin }) =>
invokeWrapper<FeeDetails>('simulate_delegate_to_mixnode', args);
export const simulateDelegateToNode = async (args: { nodeId: number; amount: DecCoin }) =>
invokeWrapper<FeeDetails>('simulate_delegate_to_node', args);
export const simulateUndelegateFromMixnode = async (mixId: number) =>
invokeWrapper<FeeDetails>('simulate_undelegate_from_mixnode', { mixId });
export const simulateUndelegateFromNode = async (nodeId: number) =>
invokeWrapper<FeeDetails>('simulate_undelegate_from_node', { nodeId });
export const simulateClaimDelegatorReward = async (mixId: number) =>
invokeWrapper<FeeDetails>('simulate_claim_delegator_reward', { mixId });
export const simulateClaimDelegatorReward = async (nodeId: number) =>
invokeWrapper<FeeDetails>('simulate_claim_delegator_reward', { nodeId });
export const simulateVestingClaimDelegatorReward = async (mixId: number) =>
invokeWrapper<FeeDetails>('simulate_vesting_claim_delegator_reward', { mixId });
+2 -2
View File
@@ -4,8 +4,8 @@ import { invokeWrapper } from './wrapper';
export const getEnv = async () => invokeWrapper<AppEnv>('get_env');
export const tryConvertIdentityToMixId = async (mixIdentity: string) =>
invokeWrapper<number | null>('try_convert_pubkey_to_mix_id', { mixIdentity });
export const tryConvertIdentityToNodeId = async (mixIdentity: string) =>
invokeWrapper<number | null>('try_convert_pubkey_to_node_id', { mixIdentity });
export const getDefaultNodeCostParams = async (profitMarginPercent: string) =>
invokeWrapper<NodeCostParams>('default_mixnode_cost_params', { profitMarginPercent });
+2 -2
View File
@@ -36,7 +36,7 @@ export type TBondNymNodeArgs = TNymNodeSignatureArgs & {
};
export type TNymNodeSignatureArgs = {
nymNode: NymNode;
nymnode: NymNode;
costParams: NodeCostParams;
pledge: DecCoin;
};
@@ -86,7 +86,7 @@ export type TNodeDescription = {
export type TNodeConfigUpdateArgs = {
host: string;
custom_http_port: number;
custom_http_port: number | null;
};
export type TDelegateArgs = {
@@ -27,7 +27,7 @@ use nym_credential_verification::{
bandwidth_storage_manager::BandwidthStorageManager, ecash::EcashManager,
BandwidthFlushingBehaviourConfig, ClientBandwidth, CredentialVerifier,
};
use nym_credentials_interface::{CredentialSpendingData, TicketType};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::x25519::KeyPair;
use nym_gateway_requests::models::CredentialSpendingRequest;
use nym_gateway_storage::Storage;
@@ -362,7 +362,6 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
"bandwidth entry should have just been created".to_string(),
))?;
let t_type = credential.payment.t_type;
let client_bandwidth = ClientBandwidth::new(bandwidth.into());
let mut verifier = CredentialVerifier::new(
CredentialSpendingRequest::new(credential),
@@ -375,20 +374,7 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
true,
),
);
verifier.verify().await?;
let amount = TicketType::try_from_encoded(t_type)
.map_err(|e| {
AuthenticatorError::CredentialVerificationError(
nym_credential_verification::Error::UnknownTicketType(e),
)
})?
.to_repr()
.bandwidth_value() as i64;
let available_bandwidth = ecash_verifier
.storage()
.increase_bandwidth(client_id, amount)
.await?;
let available_bandwidth = verifier.verify().await?;
Ok(AuthenticatorResponse::new_topup_bandwidth(
RemainingBandwidthData {
@@ -7,7 +7,6 @@ use crate::manager::network::LoadedNetwork;
use crate::manager::NetworkManager;
use console::style;
use nym_config::{must_get_home, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR};
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::NymApiClient;
use rand::{thread_rng, RngCore};
use std::fs;
@@ -97,8 +96,8 @@ impl NetworkManager {
let wait_fut = async {
let inner_fut = async {
loop {
let mut gateways = match api_client.nym_api.get_basic_gateways(None).await {
Ok(gateways) => gateways,
let mut nodes = match api_client.get_all_basic_nodes(None).await {
Ok(nodes) => nodes,
Err(err) => {
ctx.println(format!(
"{} {err}",
@@ -110,8 +109,7 @@ impl NetworkManager {
// if we explicitly specified some identity, find THIS node
if let Some(identity) = ctx.gateway.as_ref() {
if let Some(node) = gateways
.nodes
if let Some(node) = nodes
.iter()
.find(|gw| &gw.ed25519_identity_pubkey.to_base58_string() == identity)
{
@@ -123,7 +121,7 @@ impl NetworkManager {
}
// otherwise look for ANY node
if let Some(node) = gateways.nodes.pop() {
if let Some(node) = nodes.pop() {
return SocketAddr::new(node.ip_addresses[0], node.entry.unwrap().ws_port);
}
sleep(Duration::from_secs(10)).await;
@@ -32,6 +32,7 @@ export * from './MixNodeDetails';
export * from './MixNodeRewarding';
export * from './MixnodeStatus';
export * from './MixnodeStatusResponse';
export * from './NodeConfigUpdate';
export * from './NymNodeDetails';
export * from './OriginalVestingResponse';
export * from './PendingEpochEvent';