Merge branch 'develop' into feature/vouchers

This commit is contained in:
AniaPiotrowska
2021-11-04 14:08:50 +00:00
committed by GitHub
108 changed files with 4077 additions and 4013 deletions
+32
View File
@@ -0,0 +1,32 @@
---
name: Feature request
about: Suggest an enhancement to the product
title: "[Feature Request]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is...
**Is your request a feature not related to an existing problem? A new feature.**
For example.
- Given I am using the nym wallet
- When I transfer nym tokens across the network
- Then I want to have an url link in the wallet which navigates outside the application to the nym-explorer
**Where does the feature fit in the Nym real estate?**
- Application / UI
**What is this solving?**
How will this improve the product...
**Is this an update to packages or libraries?**
If so, please list them. If not, please ignore this section.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+43
View File
@@ -0,0 +1,43 @@
---
name: Report
about: To help identify and reproduce issues
title: "[Issue]"
labels: bug, bug-needs-triage, qa
assignees: tommyv1987
---
**Describe the issue**
A clear and concise description of what the issue is...
**Expected behaviour**
A clear and concise description of what you expected to happen...
**Stack Traces**
If there are stack traces or logs, please provide them here...
**Steps to Reproduce**
Steps to reproduce the behaviour, if you're familiar with BDD syntax, please write it in this style:
- Given I was doing X
- And I installed Y
- When I actioned Y
- Then I expect Z
*An example:*
- Given I was setting up a mix-node following the instructions in the docs
- And I successfully bonded my node via the the wallet
- When I went to start my mixnode
- Then I was presented with an error
**Screenshots**
If applicable, add screenshots to help explain your problem...
**Which area of Nym were you using?**
- UI: [e.g. Websites - network-explorer, nym-website]
- Application: [e.g Gateway, Client, Wallet]
- OS: [e.g. Ubuntu 20.x, MacOs Big Sur, Windows 10]
- Browser: [e.g Chrome (if applicable)]
- Version: [e.g. nym binary(0.11.0), browser(94.0)]
**Additional context**
Please provide any other information
-1
View File
@@ -1 +0,0 @@
+34 -8
View File
@@ -1,9 +1,10 @@
name: webdriverio tests for nym wallet
name: Webdriverio tests for nym wallet
on:
push:
paths:
- '/tauri-wallet/*'
- 'tauri-wallet/**'
defaults:
run:
working-directory: tauri-wallet
@@ -12,7 +13,6 @@ jobs:
test:
name: wallet tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -27,18 +27,44 @@ jobs:
webkit2gtk-driver
xvfb
- name: Install minimal stable
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Node v16
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Install yarn for building application
run: yarn install
- name: Build application
run: yarn run webpack:build & yarn run tauri:build
working-directory: tauri-wallet
- name: Check binary exists
run: |
cd target/release/
(test -f nym-wallet && echo nym binary exists) || echo wallet does not exist
- name: Yarn install
- name: Install dependencies
run: yarn install
working-directory: webdriver/
working-directory: tauri-wallet/webdriver
- name: Remove existing user datafile
uses: JesseTG/rm@v1.0.2
with:
path: tauri-wallet/webdriver/common/data/user-data.json
- name: Create user data json file
id: create-json
uses: jsdaniell/create-json@1.1.2
with:
name: "user-data.json"
json: ${{ secrets.WALLET_USERDATA }}
dir: 'tauri-wallet/webdriver/common/data/'
- name: Install tauri-driver
uses: actions-rs/cargo@v1
@@ -47,5 +73,5 @@ jobs:
args: tauri-driver
- name: Launch tests
run: xvfb-run yarn test:login
working-directory: webdriver/
run: xvfb-run yarn test:newuser
working-directory: tauri-wallet/webdriver
Generated
+86 -104
View File
@@ -12,15 +12,6 @@ dependencies = [
"regex",
]
[[package]]
name = "addr2line"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
@@ -66,15 +57,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "anomaly"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "550632e31568ae1a5f47998c3aa48563030fc49b9ec91913ca337cf64fbc5ccb"
dependencies = [
"backtrace",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
@@ -246,21 +228,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
dependencies = [
"addr2line",
"cc",
"cfg-if 1.0.0",
"libc",
"miniz_oxide 0.4.4",
"object",
"rustc-demangle",
]
[[package]]
name = "base-x"
version = "0.2.8"
@@ -864,9 +831,9 @@ dependencies = [
[[package]]
name = "cosmos-sdk-proto"
version = "0.6.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7115eae4b8518967b8e737a3ef76025f2d007de7906d88c18f00b8bbfc70f51e"
checksum = "edb5204c6ddc4352c74297638b5561f2929d6334866c156e5f3c75e1e1a1436a"
dependencies = [
"prost",
"prost-types",
@@ -875,9 +842,9 @@ dependencies = [
[[package]]
name = "cosmrs"
version = "0.1.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "609eba7eee6d9929927cbdf15f9fe96d250c316c5e1b983c4a47a10f60da5ea7"
checksum = "d31147fe89e547e74e2692e4bec35387e1ea406fe8cfb14c0ea177b58fd2a8a9"
dependencies = [
"bip32",
"cosmos-sdk-proto",
@@ -888,6 +855,8 @@ dependencies = [
"prost",
"prost-types",
"rand_core 0.6.3",
"serde",
"serde_json",
"subtle-encoding",
"tendermint",
"tendermint-rpc",
@@ -1527,7 +1496,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc"
dependencies = [
"serde",
"signature",
]
@@ -1541,7 +1509,6 @@ dependencies = [
"ed25519",
"rand 0.7.3",
"serde",
"serde_bytes",
"sha2",
"zeroize",
]
@@ -1738,6 +1705,16 @@ dependencies = [
"miniz_oxide 0.4.4",
]
[[package]]
name = "flex-error"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b"
dependencies = [
"eyre",
"paste",
]
[[package]]
name = "fluvio-wasm-timer"
version = "0.2.5"
@@ -2120,12 +2097,6 @@ dependencies = [
"syn",
]
[[package]]
name = "gimli"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
[[package]]
name = "gio"
version = "0.14.6"
@@ -2608,6 +2579,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"webpki",
"webpki-roots",
]
[[package]]
@@ -2768,15 +2740,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.10.1"
@@ -3716,15 +3679,6 @@ dependencies = [
"objc",
]
[[package]]
name = "object"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
dependencies = [
"memchr",
]
[[package]]
name = "okapi"
version = "0.6.0-alpha-1"
@@ -3874,6 +3828,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "paste"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
[[package]]
name = "pbkdf2"
version = "0.8.0"
@@ -3906,6 +3866,33 @@ dependencies = [
"syn",
]
[[package]]
name = "peg"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a"
dependencies = [
"peg-macros",
"peg-runtime",
]
[[package]]
name = "peg-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c"
dependencies = [
"peg-runtime",
"proc-macro2",
"quote",
]
[[package]]
name = "peg-runtime"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"
[[package]]
name = "pem"
version = "0.8.3"
@@ -4283,9 +4270,9 @@ dependencies = [
[[package]]
name = "prost"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2"
checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
dependencies = [
"bytes",
"prost-derive",
@@ -4293,12 +4280,12 @@ dependencies = [
[[package]]
name = "prost-derive"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4"
checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
dependencies = [
"anyhow",
"itertools 0.9.0",
"itertools",
"proc-macro2",
"quote",
"syn",
@@ -4306,9 +4293,9 @@ dependencies = [
[[package]]
name = "prost-types"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb"
checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a"
dependencies = [
"bytes",
"prost",
@@ -4927,12 +4914,6 @@ dependencies = [
"quote",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc_version"
version = "0.2.3"
@@ -5513,7 +5494,7 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
dependencies = [
"itertools 0.10.1",
"itertools",
"nom",
"unicode_categories",
]
@@ -5999,7 +5980,7 @@ dependencies = [
"anyhow",
"cfg-expr",
"heck",
"itertools 0.10.1",
"itertools",
"pkg-config",
"strum 0.21.0",
"strum_macros 0.21.1",
@@ -6214,16 +6195,16 @@ dependencies = [
[[package]]
name = "tendermint"
version = "0.21.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0752808f59b612916614e92e43be64e9ba566b762a266bcfcf7dce1b324e9319"
checksum = "50d1cdb0236becb17ab35a2ed1566503e412fd910944dc940239857bb7663652"
dependencies = [
"anomaly",
"async-trait",
"bytes",
"chrono",
"ed25519",
"ed25519-dalek",
"flex-error",
"futures",
"k256",
"num-traits",
@@ -6240,21 +6221,32 @@ dependencies = [
"subtle 2.4.1",
"subtle-encoding",
"tendermint-proto",
"thiserror",
"toml",
"url",
"zeroize",
]
[[package]]
name = "tendermint-proto"
version = "0.21.0"
name = "tendermint-config"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd0371c6b0c7fc4f6b053b8a1bc868a2a47c49a1408c4cd6f595d9f3bd3c238"
checksum = "2a1f94250d30e3011130a09756b05985d8dbfbd562cf261b5a17e36d89a37992"
dependencies = [
"flex-error",
"serde",
"serde_json",
"tendermint",
"toml",
"url",
]
[[package]]
name = "tendermint-proto"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff16a7b42bdbcf31c8cd10a4cffc7631f2a301360ba3a3f71dde48eabfa5bced"
dependencies = [
"anomaly",
"bytes",
"chrono",
"flex-error",
"num-derive",
"num-traits",
"prost",
@@ -6262,30 +6254,32 @@ dependencies = [
"serde",
"serde_bytes",
"subtle-encoding",
"thiserror",
]
[[package]]
name = "tendermint-rpc"
version = "0.21.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fa8d172e3129802d28de980531993dd25cc01841c5bc8fe4a87c7750a7f222"
checksum = "c7842dcd5edb60b077572aa92ff8b29fc810b9b463310f9810a2607474130db4"
dependencies = [
"async-trait",
"bytes",
"chrono",
"flex-error",
"futures",
"getrandom 0.1.16",
"http",
"hyper",
"hyper-proxy",
"hyper-rustls",
"peg",
"pin-project",
"serde",
"serde_bytes",
"serde_json",
"subtle-encoding",
"tendermint",
"tendermint-config",
"tendermint-proto",
"thiserror",
"tokio",
@@ -6577,21 +6571,9 @@ checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.20"
@@ -6812,7 +6794,7 @@ dependencies = [
"cosmrs",
"cosmwasm-std",
"flate2",
"itertools 0.10.1",
"itertools",
"log",
"mixnet-contract",
"network-defaults",
+1
View File
@@ -4,6 +4,7 @@
[profile.release]
panic = "abort"
opt-level = "s"
overflow-checks = true
[profile.dev]
panic = "abort"
@@ -257,7 +257,7 @@ impl TopologyRefresher {
Ok(mixes) => mixes,
};
let gateways = match self.validator_client.get_cached_active_gateways().await {
let gateways = match self.validator_client.get_cached_gateways().await {
Err(err) => {
error!("failed to get network gateways - {}", err);
return None;
+1
View File
@@ -3,6 +3,7 @@ name = "nym-client"
version = "0.11.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-socks5-client"
version = "0.11.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2018"
rust-version = "1.56"
[lib]
name = "nym_socks5"
+1
View File
@@ -7,6 +7,7 @@ keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
repository = "https://github.com/nymtech/nym"
description = "A webassembly client which can be used to interact with the the Nym privacy platform. Wasm is used for Sphinx packet generation."
rust-version = "1.56"
[lib]
crate-type = ["cdylib", "rlib"]
+1 -1
View File
@@ -301,7 +301,7 @@ impl NymClient {
};
let gateways = match validator_client.get_cached_gateways().await {
Err(err) => panic!("{:?}", err),
Err(err) => panic!("{}", err),
Ok(gateways) => gateways,
};
@@ -173,9 +173,9 @@ impl PartiallyDelegated {
// this call failing is incredibly unlikely, but not impossible.
// basically the gateway connection must have failed after executing previous line but
// before starting execution of this one.
if notify.send(()).is_err() {
return Err(GatewayClientError::ConnectionAbruptlyClosed);
}
notify
.send(())
.map_err(|_| GatewayClientError::ConnectionAbruptlyClosed)?;
let stream_results: Result<_, GatewayClientError> = stream_receiver
.await
@@ -3,6 +3,7 @@ name = "validator-client"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -25,8 +26,8 @@ network-defaults = { path = "../../network-defaults" }
async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
config = { path = "../../config", optional = true }
cosmrs = { version = "0.1", features = ["rpc", "bip32", "cosmwasm"], optional = true }
prost = { version = "0.7", default-features = false, optional = true }
cosmrs = { version = "0.3", features = ["rpc", "bip32", "cosmwasm"], optional = true }
prost = { version = "0.9", default-features = false, optional = true }
flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
@@ -24,7 +24,6 @@ pub struct Config {
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
gateway_delegations_page_limit: Option<u32>,
}
#[cfg(feature = "nymd-client")]
@@ -41,7 +40,6 @@ impl Config {
mixnode_page_limit: None,
gateway_page_limit: None,
mixnode_delegations_page_limit: None,
gateway_delegations_page_limit: None,
}
}
@@ -59,11 +57,6 @@ impl Config {
self.mixnode_delegations_page_limit = limit;
self
}
pub fn with_gateway_delegations_page_limit(mut self, limit: Option<u32>) -> Config {
self.gateway_delegations_page_limit = limit;
self
}
}
#[cfg(feature = "nymd-client")]
@@ -74,7 +67,6 @@ pub struct Client<C> {
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
gateway_delegations_page_limit: Option<u32>,
// ideally they would have been read-only, but unfortunately rust doesn't have such features
pub validator_api: validator_api::Client,
@@ -100,7 +92,6 @@ impl Client<SigningNymdClient> {
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
gateway_delegations_page_limit: config.gateway_delegations_page_limit,
validator_api: validator_api_client,
nymd: nymd_client,
})
@@ -136,7 +127,6 @@ impl Client<QueryNymdClient> {
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
gateway_delegations_page_limit: config.gateway_delegations_page_limit,
validator_api: validator_api_client,
nymd: nymd_client,
})
@@ -339,116 +329,6 @@ impl<C> Client<C> {
Ok(delegations)
}
pub async fn get_all_nymd_single_gateway_delegations(
&self,
identity: mixnet_contract::IdentityKey,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_gateway_delegations(
identity.clone(),
start_after.take(),
self.gateway_delegations_page_limit,
)
.await?;
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
pub async fn get_all_nymd_gateway_delegations(
&self,
) -> Result<Vec<mixnet_contract::UnpackedDelegation<RawDelegationData>>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_all_gateway_delegations(
start_after.take(),
self.gateway_delegations_page_limit,
)
.await?;
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
pub async fn get_all_nymd_reverse_gateway_delegations(
&self,
delegation_owner: &cosmrs::AccountId,
) -> Result<Vec<mixnet_contract::IdentityKey>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_reverse_gateway_delegations_paged(
mixnet_contract::Addr::unchecked(delegation_owner.as_ref()),
start_after.take(),
self.mixnode_delegations_page_limit,
)
.await?;
delegations.append(&mut paged_response.delegated_nodes);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
pub async fn get_all_nymd_gateway_delegations_of_owner(
&self,
delegation_owner: &cosmrs::AccountId,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
for node_identity in self
.get_all_nymd_reverse_gateway_delegations(delegation_owner)
.await?
{
let delegation = self
.nymd
.get_gateway_delegation(node_identity, delegation_owner)
.await?;
delegations.push(delegation);
}
Ok(delegations)
}
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
@@ -488,12 +368,6 @@ impl ApiClient {
Ok(self.validator_api.get_active_mixnodes().await?)
}
pub async fn get_cached_active_gateways(
&self,
) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.validator_api.get_active_gateways().await?)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?)
}
@@ -22,7 +22,7 @@ use cosmrs::rpc::query::Query;
use cosmrs::rpc::{self, HttpClient, Order};
use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{AccountId, Coin, Denom};
use cosmrs::{tx, AccountId, Coin, Denom};
use prost::Message;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
@@ -153,12 +153,9 @@ pub trait CosmWasmClient: rpc::Client {
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
}
// disabled until https://github.com/tendermint/tendermint/issues/6802
// and consequently https://github.com/informalsystems/tendermint-rs/issues/942 is resolved
//
// async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError> {
// Ok(self.tx(id, false).await?)
// }
async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError> {
Ok(self.tx(id, false).await?)
}
async fn search_tx(&self, query: Query) -> Result<Vec<TxResponse>, NymdError> {
// according to https://docs.tendermint.com/master/rpc/#/Info/tx_search
@@ -13,8 +13,8 @@ use cosmrs::distribution::MsgWithdrawDelegatorReward;
use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
use cosmrs::tx::{Fee, Msg, MsgType, SignDoc, SignerInfo};
use cosmrs::{cosmwasm, rpc, tx, AccountId, Coin};
use cosmrs::tx::{Fee, Msg, SignDoc, SignerInfo};
use cosmrs::{cosmwasm, rpc, tx, AccountId, Any, Coin};
use log::debug;
use serde::Serialize;
use sha2::Digest;
@@ -52,7 +52,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.unwrap_or_default(),
instantiate_permission: Default::default(),
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?;
let tx_res = self
@@ -114,7 +114,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
init_msg: serde_json::to_vec(msg)?,
funds: options.map(|options| options.funds).unwrap_or_default(),
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?;
let tx_res = self
@@ -154,7 +154,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
new_admin: new_admin.clone(),
contract: contract_address.clone(),
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?;
let tx_res = self
@@ -179,7 +179,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
sender: sender_address.clone(),
contract: contract_address.clone(),
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?;
let tx_res = self
@@ -211,7 +211,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
code_id,
migrate_msg: serde_json::to_vec(msg)?,
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?;
let tx_res = self
@@ -243,7 +243,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
msg: serde_json::to_vec(msg)?,
funds,
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
let tx_res = self
@@ -278,7 +278,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
msg: serde_json::to_vec(&msg)?,
funds,
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
})
.collect::<Result<_, _>>()?;
@@ -312,7 +312,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
to_address: recipient_address.clone(),
amount,
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
self.sign_and_broadcast_commit(sender_address, vec![send_msg], fee, memo)
@@ -332,7 +332,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
validator_address: validator_address.to_owned(),
amount,
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
self.sign_and_broadcast_commit(delegator_address, vec![delegate_msg], fee, memo)
@@ -352,7 +352,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
validator_address: validator_address.to_owned(),
amount: Some(amount),
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
self.sign_and_broadcast_commit(delegator_address, vec![undelegate_msg], fee, memo)
@@ -370,7 +370,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(),
}
.to_msg()
.to_any()
.map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?;
self.sign_and_broadcast_commit(delegator_address, vec![withdraw_msg], fee, memo)
@@ -381,7 +381,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign_and_broadcast_async(
&self,
signer_address: &AccountId,
messages: Vec<Msg>,
messages: Vec<Any>,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<broadcast::tx_async::Response, NymdError> {
@@ -397,7 +397,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign_and_broadcast_sync(
&self,
signer_address: &AccountId,
messages: Vec<Msg>,
messages: Vec<Any>,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<broadcast::tx_sync::Response, NymdError> {
@@ -413,7 +413,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign_and_broadcast_commit(
&self,
signer_address: &AccountId,
messages: Vec<Msg>,
messages: Vec<Any>,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<broadcast::tx_commit::Response, NymdError> {
@@ -428,7 +428,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
fn sign_direct(
&self,
signer_address: &AccountId,
messages: Vec<Msg>,
messages: Vec<Any>,
fee: Fee,
memo: impl Into<String> + Send + 'static,
signer_data: SignerData,
@@ -466,7 +466,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign(
&self,
signer_address: &AccountId,
messages: Vec<Msg>,
messages: Vec<Any>,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<tx::Raw, NymdError> {
@@ -506,7 +506,7 @@ impl Client {
#[async_trait]
impl rpc::Client for Client {
async fn perform<R>(&self, request: R) -> rpc::Result<R::Response>
async fn perform<R>(&self, request: R) -> Result<R::Response, rpc::Error>
where
R: SimpleRequest,
{
@@ -7,7 +7,10 @@ use cosmrs::{bip32, tx, AccountId};
use std::io;
use thiserror::Error;
pub use cosmrs::rpc::error::{Code, Error as TendermintRpcError};
pub use cosmrs::rpc::error::{
Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail,
};
pub use cosmrs::rpc::response_error::{Code, ResponseError};
#[derive(Debug, Error)]
pub enum NymdError {
@@ -102,17 +105,21 @@ pub enum NymdError {
}
impl NymdError {
pub fn is_tendermint_timeout(&self) -> bool {
pub fn is_tendermint_response_timeout(&self) -> bool {
match &self {
NymdError::TendermintError(tm_err) => {
if tm_err.code() == Code::InternalError {
NymdError::TendermintError(TendermintRpcError(
TendermintRpcErrorDetail::Response(err),
_,
)) => {
let response = &err.source;
if response.code() == Code::InternalError {
// 0.34 (and earlier) versions of tendermint seemed to be using phrase "timed out waiting ..."
// (https://github.com/tendermint/tendermint/blob/v0.34.13/rpc/core/mempool.go#L124)
// while 0.35+ has "timeout waiting for ..."
// https://github.com/tendermint/tendermint/blob/v0.35.0-rc3/internal/rpc/core/mempool.go#L99
// note that as of the time of writing this comment (08.10.2021), the most recent version
// of cosmos-sdk (v0.44.1) uses tendermint 0.34.13
if let Some(data) = tm_err.data() {
if let Some(data) = response.data() {
data.contains("timed out") || data.contains("timeout")
} else {
false
@@ -125,14 +132,18 @@ impl NymdError {
}
}
pub fn is_tendermint_duplicate(&self) -> bool {
pub fn is_tendermint_response_duplicate(&self) -> bool {
match &self {
NymdError::TendermintError(tm_err) => {
if tm_err.code() == Code::InternalError {
NymdError::TendermintError(TendermintRpcError(
TendermintRpcErrorDetail::Response(err),
_,
)) => {
let response = &err.source;
if response.code() == Code::InternalError {
// this particular error message seems to be unchanged between 0.34 and newer versions
// https://github.com/tendermint/tendermint/blob/v0.34.13/mempool/errors.go#L10
// https://github.com/tendermint/tendermint/blob/v0.35.0-rc3/types/mempool.go#L10
if let Some(data) = tm_err.data() {
if let Some(data) = response.data() {
data.contains("tx already exists in cache")
} else {
false
@@ -15,8 +15,7 @@ use cosmwasm_std::Coin;
use mixnet_contract::{
Addr, Delegation, ExecuteMsg, Gateway, GatewayOwnershipResponse, IdentityKey,
LayerDistribution, MixNode, MixOwnershipResponse, PagedAllDelegationsResponse,
PagedGatewayDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
PagedMixnodeResponse, PagedReverseGatewayDelegationsResponse,
PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse,
PagedReverseMixDelegationsResponse, QueryMsg, RawDelegationData, StateParams,
};
use serde::Serialize;
@@ -354,82 +353,6 @@ impl<C> NymdClient<C> {
.await
}
/// Gets list of all delegations towards particular gateway on particular page.
pub async fn get_gateway_delegations(
&self,
gateway_identity: IdentityKey,
start_after: Option<Addr>,
page_limit: Option<u32>,
) -> Result<PagedGatewayDelegationsResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetGatewayDelegations {
gateway_identity,
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Gets list of all gateway delegations on particular page.
pub async fn get_all_gateway_delegations(
&self,
start_after: Option<Vec<u8>>,
page_limit: Option<u32>,
) -> Result<PagedAllDelegationsResponse<RawDelegationData>, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetAllGatewayDelegations {
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Gets list of all the gateways on which a particular address delegated.
pub async fn get_reverse_gateway_delegations_paged(
&self,
delegation_owner: Addr,
start_after: Option<IdentityKey>,
page_limit: Option<u32>,
) -> Result<PagedReverseGatewayDelegationsResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetReverseGatewayDelegations {
delegation_owner,
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Checks value of delegation of given client towards particular gateway.
pub async fn get_gateway_delegation(
&self,
gateway_identity: IdentityKey,
delegator: &AccountId,
) -> Result<Delegation, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetGatewayDelegation {
gateway_identity,
address: Addr::unchecked(delegator.as_ref()),
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Send funds from one address to another
pub async fn send(
&self,
@@ -698,57 +621,6 @@ impl<C> NymdClient<C> {
.await
}
/// Delegates specified amount of stake to particular gateway.
pub async fn delegate_to_gateway(
&self,
gateway_identity: &str,
amount: &Coin,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::DelegateToGateway);
let req = ExecuteMsg::DelegateToGateway {
gateway_identity: gateway_identity.to_string(),
};
self.client
.execute(
self.address(),
self.contract_address()?,
&req,
fee,
"Delegating to gateway from rust!",
vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
)
.await
}
/// Removes stake delegation from a particular gateway.
pub async fn remove_gateway_delegation(
&self,
gateway_identity: &str,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::UndelegateFromGateway);
let req = ExecuteMsg::UndelegateFromGateway {
gateway_identity: gateway_identity.to_string(),
};
self.client
.execute(
self.address(),
self.contract_address()?,
&req,
fee,
"Removing gateway delegation from rust!",
Vec::new(),
)
.await
}
pub async fn update_state_params(
&self,
new_params: StateParams,
@@ -73,11 +73,6 @@ impl Client {
.await
}
pub async fn get_active_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS, routes::ACTIVE])
.await
}
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
+2 -9
View File
@@ -5,18 +5,13 @@ use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::ArrayLength;
use hkdf::Hkdf;
#[derive(Debug)]
pub enum HkdfError {
InvalidOkmLength,
}
/// Perform HKDF `extract` then `expand` as a single step.
pub fn extract_then_expand<D>(
salt: Option<&[u8]>,
ikm: &[u8],
info: Option<&[u8]>,
okm_length: usize,
) -> Result<Vec<u8>, HkdfError>
) -> Result<Vec<u8>, hkdf::InvalidLength>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
@@ -27,9 +22,7 @@ where
let hkdf = Hkdf::<D>::new(salt, ikm);
let mut okm = vec![0u8; okm_length];
if hkdf.expand(info.unwrap_or_else(|| &[]), &mut okm).is_err() {
return Err(HkdfError::InvalidOkmLength);
}
hkdf.expand(info.unwrap_or_else(|| &[]), &mut okm)?;
Ok(okm)
}
-42
View File
@@ -120,48 +120,6 @@ impl PagedReverseMixDelegationsResponse {
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedGatewayDelegationsResponse {
pub node_identity: IdentityKey,
pub delegations: Vec<Delegation>,
pub start_next_after: Option<Addr>,
}
impl PagedGatewayDelegationsResponse {
pub fn new(
node_identity: IdentityKey,
delegations: Vec<Delegation>,
start_next_after: Option<Addr>,
) -> Self {
PagedGatewayDelegationsResponse {
node_identity,
delegations,
start_next_after,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedReverseGatewayDelegationsResponse {
pub delegation_owner: Addr,
pub delegated_nodes: Vec<IdentityKey>,
pub start_next_after: Option<IdentityKey>,
}
impl PagedReverseGatewayDelegationsResponse {
pub fn new(
delegation_owner: Addr,
delegated_nodes: Vec<IdentityKey>,
start_next_after: Option<IdentityKey>,
) -> Self {
PagedReverseGatewayDelegationsResponse {
delegation_owner,
delegated_nodes,
start_next_after,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedAllDelegationsResponse<T> {
pub delegations: Vec<UnpackedDelegation<T>>,
+11 -46
View File
@@ -2,15 +2,13 @@
#![allow(clippy::field_reassign_with_default)]
use crate::{IdentityKey, SphinxKey};
use cosmwasm_std::{coin, Addr, Coin};
use cosmwasm_std::{Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::Display;
use ts_rs::TS;
use crate::current_block_height;
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema, TS)]
pub struct Gateway {
pub host: String,
@@ -26,9 +24,7 @@ pub struct Gateway {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct GatewayBond {
pub bond_amount: Coin,
pub total_delegation: Coin,
pub owner: Addr,
#[serde(default = "current_block_height")]
pub block_height: u64,
pub gateway: Gateway,
}
@@ -36,7 +32,6 @@ pub struct GatewayBond {
impl GatewayBond {
pub fn new(bond_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
GatewayBond {
total_delegation: coin(0, &bond_amount.denom),
bond_amount,
owner,
block_height,
@@ -64,27 +59,11 @@ impl GatewayBond {
impl PartialOrd for GatewayBond {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// first remove invalid cases
if self.bond_amount.denom != self.total_delegation.denom {
return None;
}
if other.bond_amount.denom != other.total_delegation.denom {
return None;
}
if self.bond_amount.denom != other.bond_amount.denom {
return None;
}
// try to order by total bond + delegation
let total_cmp = (self.bond_amount.amount + self.total_delegation.amount)
.partial_cmp(&(self.bond_amount.amount + self.total_delegation.amount))?;
if total_cmp != Ordering::Equal {
return Some(total_cmp);
}
// then if those are equal, prefer higher bond over delegation
// try to order by total bond
let bond_cmp = self
.bond_amount
.amount
@@ -93,15 +72,6 @@ impl PartialOrd for GatewayBond {
return Some(bond_cmp);
}
// then look at delegation (I'm not sure we can get here, but better safe than sorry)
let delegation_cmp = self
.total_delegation
.amount
.partial_cmp(&other.total_delegation.amount)?;
if delegation_cmp != Ordering::Equal {
return Some(delegation_cmp);
}
// then check block height
let height_cmp = self.block_height.partial_cmp(&other.block_height)?;
if height_cmp != Ordering::Equal {
@@ -175,20 +145,19 @@ mod tests {
#[test]
fn gateway_bond_partial_ord() {
let _150foos = Coin::new(150, "foo");
let _140foos = Coin::new(140, "foo");
let _50foos = Coin::new(50, "foo");
let _0foos = Coin::new(0, "foo");
let gate1 = GatewayBond {
bond_amount: _150foos.clone(),
total_delegation: _50foos.clone(),
owner: Addr::unchecked("foo1"),
block_height: 100,
gateway: gateway_fixture(),
};
let gate2 = GatewayBond {
bond_amount: _150foos.clone(),
total_delegation: _50foos.clone(),
bond_amount: _150foos,
owner: Addr::unchecked("foo2"),
block_height: 120,
gateway: gateway_fixture(),
@@ -196,15 +165,13 @@ mod tests {
let gate3 = GatewayBond {
bond_amount: _50foos,
total_delegation: _150foos.clone(),
owner: Addr::unchecked("foo3"),
block_height: 120,
gateway: gateway_fixture(),
};
let gate4 = GatewayBond {
bond_amount: _150foos.clone(),
total_delegation: _0foos.clone(),
bond_amount: _140foos,
owner: Addr::unchecked("foo4"),
block_height: 120,
gateway: gateway_fixture(),
@@ -212,21 +179,19 @@ mod tests {
let gate5 = GatewayBond {
bond_amount: _0foos,
total_delegation: _150foos,
owner: Addr::unchecked("foo5"),
block_height: 120,
gateway: gateway_fixture(),
};
// summary:
// gate1: 150bond + 50delegation, foo1, 100
// gate2: 150bond + 50delegation, foo2, 120
// gate3: 50bond + 150delegation, foo3, 120
// gate4: 150bond + 0delegation, foo4, 120
// gate5: 0bond + 150delegation, foo5, 120
// gate1: 150bond, foo1, 100
// gate2: 150bond, foo2, 120
// gate3: 50bond, foo3, 120
// gate4: 140bond, foo4, 120
// gate5: 0bond, foo5, 120
// highest total bond+delegation is used
// then bond followed by delegation
// highest total bond is used
// finally just the rest of the fields
// gate1 has higher total than gate4 or gate5
+1 -9
View File
@@ -9,18 +9,10 @@ mod types;
pub use cosmwasm_std::{Addr, Coin};
pub use delegation::{
Delegation, PagedAllDelegationsResponse, PagedGatewayDelegationsResponse,
PagedMixDelegationsResponse, PagedReverseGatewayDelegationsResponse,
Delegation, PagedAllDelegationsResponse, PagedMixDelegationsResponse,
PagedReverseMixDelegationsResponse, RawDelegationData, UnpackedDelegation,
};
pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse};
pub use mixnode::{Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
pub use msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
pub use types::{IdentityKey, IdentityKeyRef, LayerDistribution, SphinxKey, StateParams};
use std::sync::atomic::{AtomicU64, Ordering};
pub static CURRENT_BLOCK_HEIGHT: AtomicU64 = AtomicU64::new(0);
pub fn current_block_height() -> u64 {
CURRENT_BLOCK_HEIGHT.load(Ordering::Relaxed)
}
-3
View File
@@ -10,8 +10,6 @@ use std::cmp::Ordering;
use std::fmt::Display;
use ts_rs::TS;
use crate::current_block_height;
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema, TS)]
pub struct MixNode {
pub host: String,
@@ -41,7 +39,6 @@ pub struct MixNodeBond {
pub total_delegation: Coin,
pub owner: Addr,
pub layer: Layer,
#[serde(default = "current_block_height")]
pub block_height: u64,
pub mix_node: MixNode,
}
-32
View File
@@ -31,25 +31,11 @@ pub enum ExecuteMsg {
mix_identity: IdentityKey,
},
DelegateToGateway {
gateway_identity: IdentityKey,
},
UndelegateFromGateway {
gateway_identity: IdentityKey,
},
RewardMixnode {
identity: IdentityKey,
// percentage value in range 0-100
uptime: u32,
},
RewardGateway {
identity: IdentityKey,
// percentage value in range 0-100
uptime: u32,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -88,24 +74,6 @@ pub enum QueryMsg {
mix_identity: IdentityKey,
address: Addr,
},
GetGatewayDelegations {
gateway_identity: IdentityKey,
start_after: Option<Addr>,
limit: Option<u32>,
},
GetAllGatewayDelegations {
start_after: Option<Vec<u8>>,
limit: Option<u32>,
},
GetReverseGatewayDelegations {
delegation_owner: Addr,
start_after: Option<IdentityKey>,
limit: Option<u32>,
},
GetGatewayDelegation {
gateway_identity: IdentityKey,
address: Addr,
},
LayerDistribution {},
}
+1 -18
View File
@@ -32,12 +32,10 @@ pub struct StateParams {
pub minimum_mixnode_bond: Uint128, // minimum amount a mixnode must bond to get into the system
pub minimum_gateway_bond: Uint128, // minimum amount a gateway must bond to get into the system
pub mixnode_bond_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
pub gateway_bond_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
pub mixnode_delegation_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
pub gateway_delegation_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
pub mixnode_active_set_size: u32,
pub gateway_active_set_size: u32,
}
impl Display for StateParams {
@@ -51,30 +49,15 @@ impl Display for StateParams {
"mixnode bond reward rate: {}; ",
self.mixnode_bond_reward_rate
)?;
write!(
f,
"gateway bond reward rate: {}; ",
self.gateway_bond_reward_rate
)?;
write!(
f,
"mixnode delegation reward rate: {}; ",
self.mixnode_delegation_reward_rate
)?;
write!(
f,
"gateway delegation reward rate: {}; ",
self.gateway_delegation_reward_rate
)?;
write!(
f,
"mixnode active set size: {}",
self.mixnode_active_set_size
)?;
write!(
f,
"gateway active set size: {} ]",
self.gateway_active_set_size
)
}
}
+3 -6
View File
@@ -156,11 +156,9 @@ impl MessageReceiver {
};
// Finally, remove the zero padding from the message
if Self::remove_padding(&mut message).is_err() {
return Err(MessageRecoveryError::MalformedReconstructedMessage(
used_sets,
));
};
Self::remove_padding(&mut message).map_err(|_| {
MessageRecoveryError::MalformedReconstructedMessage(used_sets.clone())
})?;
Ok(Some((
ReconstructedMessage {
@@ -270,7 +268,6 @@ mod message_receiver {
vec![gateway::Node {
owner: "foomp4".to_string(),
stake: 123,
delegation: 456,
location: "unknown".to_string(),
host: "1.2.3.4".parse().unwrap(),
mix_host: "1.2.3.4:1789".parse().unwrap(),
-2
View File
@@ -72,7 +72,6 @@ pub struct Node {
// somebody correct me if I'm wrong, but we should only ever have a single denom of currency
// on the network at a type, right?
pub stake: u128,
pub delegation: u128,
pub location: String,
pub host: NetworkAddress,
// we're keeping this as separate resolved field since we do not want to be resolving the potential
@@ -127,7 +126,6 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
Ok(Node {
owner: bond.owner.as_str().to_owned(),
stake: bond.bond_amount.amount.into(),
delegation: bond.total_delegation.amount.into(),
location: bond.gateway.location.clone(),
host,
mix_host,
-56
View File
@@ -22,18 +22,13 @@ pub const INITIAL_MIXNODE_BOND: Uint128 = Uint128(100_000000);
// percentage annual increase. Given starting value of x, we expect to have 1.1x at the end of the year
pub const INITIAL_MIXNODE_BOND_REWARD_RATE: u64 = 110;
pub const INITIAL_GATEWAY_BOND_REWARD_RATE: u64 = 110;
pub const INITIAL_MIXNODE_DELEGATION_REWARD_RATE: u64 = 110;
pub const INITIAL_GATEWAY_DELEGATION_REWARD_RATE: u64 = 110;
pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
pub const INITIAL_GATEWAY_ACTIVE_SET_SIZE: u32 = 20;
fn default_initial_state(owner: Addr) -> State {
let mixnode_bond_reward_rate = Decimal::percent(INITIAL_MIXNODE_BOND_REWARD_RATE);
let gateway_bond_reward_rate = Decimal::percent(INITIAL_GATEWAY_BOND_REWARD_RATE);
let mixnode_delegation_reward_rate = Decimal::percent(INITIAL_MIXNODE_DELEGATION_REWARD_RATE);
let gateway_delegation_reward_rate = Decimal::percent(INITIAL_GATEWAY_DELEGATION_REWARD_RATE);
State {
owner,
@@ -43,28 +38,17 @@ fn default_initial_state(owner: Addr) -> State {
minimum_mixnode_bond: INITIAL_MIXNODE_BOND,
minimum_gateway_bond: INITIAL_GATEWAY_BOND,
mixnode_bond_reward_rate,
gateway_bond_reward_rate,
mixnode_delegation_reward_rate,
gateway_delegation_reward_rate,
mixnode_active_set_size: INITIAL_MIXNODE_ACTIVE_SET_SIZE,
gateway_active_set_size: INITIAL_GATEWAY_ACTIVE_SET_SIZE,
},
mixnode_epoch_bond_reward: calculate_epoch_reward_rate(
INITIAL_DEFAULT_EPOCH_LENGTH,
mixnode_bond_reward_rate,
),
gateway_epoch_bond_reward: calculate_epoch_reward_rate(
INITIAL_DEFAULT_EPOCH_LENGTH,
gateway_bond_reward_rate,
),
mixnode_epoch_delegation_reward: calculate_epoch_reward_rate(
INITIAL_DEFAULT_EPOCH_LENGTH,
mixnode_delegation_reward_rate,
),
gateway_epoch_delegation_reward: calculate_epoch_reward_rate(
INITIAL_DEFAULT_EPOCH_LENGTH,
gateway_delegation_reward_rate,
),
}
}
@@ -110,21 +94,12 @@ pub fn execute(
ExecuteMsg::RewardMixnode { identity, uptime } => {
transactions::try_reward_mixnode(deps, env, info, identity, uptime)
}
ExecuteMsg::RewardGateway { identity, uptime } => {
transactions::try_reward_gateway(deps, env, info, identity, uptime)
}
ExecuteMsg::DelegateToMixnode { mix_identity } => {
transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
}
ExecuteMsg::UndelegateFromMixnode { mix_identity } => {
transactions::try_remove_delegation_from_mixnode(deps, info, mix_identity)
}
ExecuteMsg::DelegateToGateway { gateway_identity } => {
transactions::try_delegate_to_gateway(deps, env, info, gateway_identity)
}
ExecuteMsg::UndelegateFromGateway { gateway_identity } => {
transactions::try_remove_delegation_from_gateway(deps, info, gateway_identity)
}
}
}
@@ -176,37 +151,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
mix_identity,
address,
)?),
QueryMsg::GetGatewayDelegations {
gateway_identity,
start_after,
limit,
} => to_binary(&queries::query_gateway_delegations_paged(
deps,
gateway_identity,
start_after,
limit,
)?),
QueryMsg::GetAllGatewayDelegations { start_after, limit } => to_binary(
&queries::query_all_gateway_delegations_paged(deps, start_after, limit)?,
),
QueryMsg::GetReverseGatewayDelegations {
delegation_owner,
start_after,
limit,
} => to_binary(&queries::query_reverse_gateway_delegations_paged(
deps,
delegation_owner,
start_after,
limit,
)?),
QueryMsg::GetGatewayDelegation {
gateway_identity,
address,
} => to_binary(&queries::query_gateway_delegation(
deps,
gateway_identity,
address,
)?),
};
Ok(query_res?)
-12
View File
@@ -48,15 +48,9 @@ pub enum ContractError {
#[error("The bond reward rate for mixnode was set to be lower than 1")]
DecreasingMixnodeBondReward,
#[error("The bond reward rate for gateway was set to be lower than 1")]
DecreasingGatewayBondReward,
#[error("The delegation reward rate for mixnode was set to be lower than 1")]
DecreasingMixnodeDelegationReward,
#[error("The delegation reward rate for gateway was set to be lower than 1")]
DecreasingGatewayDelegationReward,
#[error("The node had uptime larger than 100%")]
UnexpectedUptime,
@@ -83,10 +77,4 @@ pub enum ContractError {
identity: IdentityKey,
address: Addr,
},
#[error("Could not find any delegation information associated with gateway {identity} for {address}")]
NoGatewayDelegationFound {
identity: IdentityKey,
address: Addr,
},
}
+1 -66
View File
@@ -209,10 +209,7 @@ impl<'a, T: Clone + Serialize + DeserializeOwned> Iterator for Delegations<'a, T
mod tests {
use super::*;
use crate::queries::tests::store_n_mix_delegations;
use crate::storage::{
all_gateway_delegations_read, all_mix_delegations_read, gateway_delegations,
mix_delegations,
};
use crate::storage::{all_mix_delegations_read, mix_delegations};
use crate::support::tests::helpers;
use cosmwasm_std::testing::mock_dependencies;
use mixnet_contract::RawDelegationData;
@@ -389,66 +386,4 @@ mod tests {
UnpackedDelegation::new(delegation_owner2, node_identity2, raw_delegation.clone()),
);
}
#[test]
fn all_gateway_delegations() {
let mut deps = mock_dependencies(&[]);
let node_identity1: IdentityKey = "foo1".into();
let delegation_owner1 = Addr::unchecked("bar1");
let node_identity2: IdentityKey = "foo2".into();
let delegation_owner2 = Addr::unchecked("bar2");
let raw_delegation = RawDelegationData::new(1000u128.into(), 42);
let mut start_after = None;
gateway_delegations(&mut deps.storage, &node_identity1)
.save(delegation_owner1.as_bytes(), &raw_delegation)
.unwrap();
let bucket = all_gateway_delegations_read::<RawDelegationData>(&deps.storage);
let response =
get_all_delegations_paged::<RawDelegationData>(&bucket, &start_after, 10).unwrap();
start_after = response.start_next_after;
let delegations = response.delegations;
assert_eq!(delegations.len(), 1);
assert_eq!(
delegations[0],
UnpackedDelegation::new(
delegation_owner1.clone(),
node_identity1.clone(),
raw_delegation.clone()
)
);
gateway_delegations(&mut deps.storage, &node_identity2)
.save(delegation_owner2.as_bytes(), &raw_delegation)
.unwrap();
let bucket = all_gateway_delegations_read::<RawDelegationData>(&deps.storage);
let response =
get_all_delegations_paged::<RawDelegationData>(&bucket, &start_after, 10).unwrap();
start_after = response.start_next_after;
let delegations = response.delegations;
assert_eq!(delegations.len(), 2);
assert_eq!(
delegations[1],
UnpackedDelegation::new(
delegation_owner2.clone(),
node_identity2.clone(),
raw_delegation.clone()
)
);
gateway_delegations(&mut deps.storage, &node_identity1)
.remove(delegation_owner1.as_bytes());
let bucket = all_gateway_delegations_read::<RawDelegationData>(&deps.storage);
let response =
get_all_delegations_paged::<RawDelegationData>(&bucket, &start_after, 10).unwrap();
let delegations = response.delegations;
assert_eq!(delegations.len(), 1);
assert_eq!(
delegations[0],
UnpackedDelegation::new(delegation_owner2, node_identity2, raw_delegation.clone()),
);
}
}
+6 -636
View File
@@ -4,19 +4,17 @@
use crate::error::ContractError;
use crate::helpers::get_all_delegations_paged;
use crate::storage::{
all_gateway_delegations_read, all_mix_delegations_read, gateway_delegations_read,
gateways_owners_read, gateways_read, mix_delegations_read, mixnodes_owners_read, mixnodes_read,
read_layer_distribution, read_state_params, reverse_gateway_delegations_read,
all_mix_delegations_read, gateways_owners_read, gateways_read, mix_delegations_read,
mixnodes_owners_read, mixnodes_read, read_layer_distribution, read_state_params,
reverse_mix_delegations_read,
};
use config::defaults::DENOM;
use cosmwasm_std::{coin, Addr, Deps, Order, StdResult};
use mixnet_contract::{
Delegation, GatewayBond, GatewayOwnershipResponse, IdentityKey, LayerDistribution, MixNodeBond,
MixOwnershipResponse, PagedAllDelegationsResponse, PagedGatewayDelegationsResponse,
PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse,
PagedReverseGatewayDelegationsResponse, PagedReverseMixDelegationsResponse, RawDelegationData,
StateParams,
MixOwnershipResponse, PagedAllDelegationsResponse, PagedGatewayResponse,
PagedMixDelegationsResponse, PagedMixnodeResponse, PagedReverseMixDelegationsResponse,
RawDelegationData, StateParams,
};
const BOND_PAGE_MAX_LIMIT: u32 = 100;
@@ -211,114 +209,11 @@ pub(crate) fn query_mixnode_delegation(
}
}
pub(crate) fn query_gateway_delegations_paged(
deps: Deps,
gateway_identity: IdentityKey,
start_after: Option<Addr>,
limit: Option<u32>,
) -> StdResult<PagedGatewayDelegationsResponse> {
let limit = limit
.unwrap_or(DELEGATION_PAGE_DEFAULT_LIMIT)
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
let start = calculate_start_value(start_after);
let delegations = gateway_delegations_read(deps.storage, &gateway_identity)
.range(start.as_deref(), None, Order::Ascending)
.take(limit)
.map(|res| {
res.map(|entry| {
Delegation::new(
Addr::unchecked(String::from_utf8(entry.0).expect(
"Non-UTF8 address used as key in bucket. The storage is corrupted!",
)),
coin(entry.1.amount.u128(), DENOM),
entry.1.block_height,
)
})
})
.collect::<StdResult<Vec<Delegation>>>()?;
let start_next_after = delegations.last().map(|delegation| delegation.owner());
Ok(PagedGatewayDelegationsResponse::new(
gateway_identity,
delegations,
start_next_after,
))
}
pub(crate) fn query_all_gateway_delegations_paged(
deps: Deps,
start_after: Option<Vec<u8>>,
limit: Option<u32>,
) -> StdResult<PagedAllDelegationsResponse<RawDelegationData>> {
let limit = limit
.unwrap_or(DELEGATION_PAGE_DEFAULT_LIMIT)
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
let bucket = all_gateway_delegations_read::<RawDelegationData>(deps.storage);
let start = start_after.map(|mut v| {
v.push(0);
v
});
get_all_delegations_paged::<RawDelegationData>(&bucket, &start, limit)
}
pub(crate) fn query_reverse_gateway_delegations_paged(
deps: Deps,
delegation_owner: Addr,
start_after: Option<IdentityKey>,
limit: Option<u32>,
) -> StdResult<PagedReverseGatewayDelegationsResponse> {
let limit = limit
.unwrap_or(DELEGATION_PAGE_DEFAULT_LIMIT)
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
let start = calculate_start_value(start_after);
let delegations = reverse_gateway_delegations_read(deps.storage, &delegation_owner)
.range(start.as_deref(), None, Order::Ascending)
.take(limit)
.map(|res| {
res.map(|entry| {
String::from_utf8(entry.0)
.expect("Non-UTF8 address used as key in bucket. The storage is corrupted!")
})
})
.collect::<StdResult<Vec<IdentityKey>>>()?;
let start_next_after = delegations.last().cloned();
Ok(PagedReverseGatewayDelegationsResponse::new(
delegation_owner,
delegations,
start_next_after,
))
}
// queries for delegation value of given address for particular node
pub(crate) fn query_gateway_delegation(
deps: Deps,
gateway_identity: IdentityKey,
address: Addr,
) -> Result<Delegation, ContractError> {
match gateway_delegations_read(deps.storage, &gateway_identity).may_load(address.as_bytes())? {
Some(delegation_value) => Ok(Delegation::new(
address,
coin(delegation_value.amount.u128(), DENOM),
delegation_value.block_height,
)),
None => Err(ContractError::NoGatewayDelegationFound {
identity: gateway_identity,
address,
}),
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::state::State;
use crate::storage::{config, gateway_delegations, gateways, mix_delegations, mixnodes};
use crate::storage::{config, gateways, mix_delegations, mixnodes};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{
good_gateway_bond, good_mixnode_bond, raw_delegation_fixture,
@@ -673,16 +568,11 @@ pub(crate) mod tests {
minimum_mixnode_bond: 123u128.into(),
minimum_gateway_bond: 456u128.into(),
mixnode_bond_reward_rate: "1.23".parse().unwrap(),
gateway_bond_reward_rate: "4.56".parse().unwrap(),
mixnode_delegation_reward_rate: "7.89".parse().unwrap(),
gateway_delegation_reward_rate: "0.12".parse().unwrap(),
mixnode_active_set_size: 1000,
gateway_active_set_size: 20,
},
mixnode_epoch_bond_reward: "1.23".parse().unwrap(),
gateway_epoch_bond_reward: "4.56".parse().unwrap(),
mixnode_epoch_delegation_reward: "7.89".parse().unwrap(),
gateway_epoch_delegation_reward: "0.12".parse().unwrap(),
};
config(deps.as_mut().storage).save(&dummy_state).unwrap();
@@ -1203,524 +1093,4 @@ pub(crate) mod tests {
assert_eq!(2, page2.delegated_nodes.len());
}
}
pub fn store_n_gateway_delegations(
n: u32,
storage: &mut dyn Storage,
node_identity: &IdentityKey,
) {
for i in 0..n {
let address = format!("address{}", i);
gateway_delegations(storage, node_identity)
.save(address.as_bytes(), &raw_delegation_fixture(42))
.unwrap();
}
}
#[cfg(test)]
mod querying_for_gateway_delegations_paged {
use super::*;
use crate::storage::gateway_delegations;
#[test]
fn retrieval_obeys_limits() {
let mut deps = helpers::init_contract();
let limit = 2;
let node_identity: IdentityKey = "foo".into();
store_n_gateway_delegations(100, &mut deps.storage, &node_identity);
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity,
None,
Option::from(limit),
)
.unwrap();
assert_eq!(limit, page1.delegations.len() as u32);
}
#[test]
fn retrieval_has_default_limit() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
store_n_gateway_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_identity,
);
// query without explicitly setting a limit
let page1 =
query_gateway_delegations_paged(deps.as_ref(), node_identity, None, None).unwrap();
assert_eq!(
DELEGATION_PAGE_DEFAULT_LIMIT,
page1.delegations.len() as u32
);
}
#[test]
fn retrieval_has_max_limit() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
store_n_gateway_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_identity,
);
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * DELEGATION_PAGE_DEFAULT_LIMIT;
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity,
None,
Option::from(crazy_limit),
)
.unwrap();
// we default to a decent sized upper bound instead
let expected_limit = DELEGATION_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.delegations.len() as u32);
}
#[test]
fn pagination_works() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
gateway_delegations(&mut deps.storage, &node_identity)
.save("1".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
let per_page = 2;
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity.clone(),
None,
Option::from(per_page),
)
.unwrap();
// page should have 1 result on it
assert_eq!(1, page1.delegations.len());
// save another
gateway_delegations(&mut deps.storage, &node_identity)
.save("2".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
// page1 should have 2 results on it
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity.clone(),
None,
Option::from(per_page),
)
.unwrap();
assert_eq!(2, page1.delegations.len());
gateway_delegations(&mut deps.storage, &node_identity)
.save("3".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
// page1 still has 2 results
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity.clone(),
None,
Option::from(per_page),
)
.unwrap();
assert_eq!(2, page1.delegations.len());
// retrieving the next page should start after the last key on this page
let start_after = Addr::unchecked("2");
let page2 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity.clone(),
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
assert_eq!(1, page2.delegations.len());
// save another one
gateway_delegations(&mut deps.storage, &node_identity)
.save("4".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
let start_after = Addr::unchecked("2");
let page2 = query_gateway_delegations_paged(
deps.as_ref(),
node_identity,
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.delegations.len());
}
}
#[test]
fn gateway_deletion_query_returns_current_delegation_value() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
let delegation_owner = Addr::unchecked("bar");
gateway_delegations(&mut deps.storage, &node_identity)
.save(
delegation_owner.as_bytes(),
&&RawDelegationData::new(42u128.into(), 12_345),
)
.unwrap();
assert_eq!(
Ok(Delegation::new(
delegation_owner.clone(),
coin(42, DENOM),
12_345
)),
query_gateway_delegation(deps.as_ref(), node_identity, delegation_owner)
)
}
#[test]
fn gateway_deletion_query_returns_error_if_delegation_doesnt_exist() {
let mut deps = helpers::init_contract();
let node_identity1: IdentityKey = "foo1".into();
let node_identity2: IdentityKey = "foo2".into();
let delegation_owner1 = Addr::unchecked("bar");
let delegation_owner2 = Addr::unchecked("bar2");
assert_eq!(
Err(ContractError::NoGatewayDelegationFound {
identity: node_identity1.clone(),
address: delegation_owner1.clone(),
}),
query_gateway_delegation(
deps.as_ref(),
node_identity1.clone(),
delegation_owner1.clone()
)
);
// add delegation from a different address
gateway_delegations(&mut deps.storage, &node_identity1)
.save(delegation_owner2.as_bytes(), &raw_delegation_fixture(42))
.unwrap();
assert_eq!(
Err(ContractError::NoGatewayDelegationFound {
identity: node_identity1.clone(),
address: delegation_owner1.clone(),
}),
query_gateway_delegation(
deps.as_ref(),
node_identity1.clone(),
delegation_owner1.clone()
)
);
// add delegation for a different node
gateway_delegations(&mut deps.storage, &node_identity2)
.save(delegation_owner1.as_bytes(), &raw_delegation_fixture(42))
.unwrap();
assert_eq!(
Err(ContractError::NoGatewayDelegationFound {
identity: node_identity1.clone(),
address: delegation_owner1.clone()
}),
query_gateway_delegation(deps.as_ref(), node_identity1, delegation_owner1)
)
}
#[cfg(test)]
mod querying_for_all_gateway_delegations_paged {
use super::*;
use crate::helpers::identity_and_owner_to_bytes;
use crate::storage::gateway_delegations;
#[test]
fn retrieval_obeys_limits() {
let mut deps = helpers::init_contract();
let limit = 2;
let node_identity: IdentityKey = "foo".into();
store_n_gateway_delegations(100, &mut deps.storage, &node_identity);
let page1 =
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(limit))
.unwrap();
assert_eq!(limit, page1.delegations.len() as u32);
}
#[test]
fn retrieval_has_default_limit() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
store_n_gateway_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_identity,
);
// query without explicitly setting a limit
let page1 = query_all_gateway_delegations_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(
DELEGATION_PAGE_DEFAULT_LIMIT,
page1.delegations.len() as u32
);
}
#[test]
fn retrieval_has_max_limit() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
store_n_gateway_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_identity,
);
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * DELEGATION_PAGE_DEFAULT_LIMIT;
let page1 =
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(crazy_limit))
.unwrap();
// we default to a decent sized upper bound instead
let expected_limit = DELEGATION_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.delegations.len() as u32);
}
#[test]
fn pagination_works() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
gateway_delegations(&mut deps.storage, &node_identity)
.save("1".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
let per_page = 2;
let page1 =
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(per_page))
.unwrap();
// page should have 1 result on it
assert_eq!(1, page1.delegations.len());
// save another
gateway_delegations(&mut deps.storage, &node_identity)
.save("2".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
// page1 should have 2 results on it
let page1 =
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(per_page))
.unwrap();
assert_eq!(2, page1.delegations.len());
gateway_delegations(&mut deps.storage, &node_identity)
.save("3".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
// page1 still has 2 results
let page1 =
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(per_page))
.unwrap();
assert_eq!(2, page1.delegations.len());
// retrieving the next page should start after the last key on this page
let start_after = identity_and_owner_to_bytes(&node_identity, &Addr::unchecked("2"));
let page2 = query_all_gateway_delegations_paged(
deps.as_ref(),
Option::from(start_after.clone()),
Option::from(per_page),
)
.unwrap();
assert_eq!(1, page2.delegations.len());
// save another one
gateway_delegations(&mut deps.storage, &node_identity)
.save("4".as_bytes(), &raw_delegation_fixture(42))
.unwrap();
let page2 = query_all_gateway_delegations_paged(
deps.as_ref(),
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.delegations.len());
}
}
#[cfg(test)]
mod querying_for_reverse_gateway_delegations_paged {
use super::*;
use crate::storage::reverse_gateway_delegations;
fn store_n_reverse_delegations(n: u32, storage: &mut dyn Storage, delegation_owner: &Addr) {
for i in 0..n {
let node_identity = format!("node{}", i);
reverse_gateway_delegations(storage, delegation_owner)
.save(node_identity.as_bytes(), &())
.unwrap();
}
}
#[test]
fn retrieval_obeys_limits() {
let mut deps = helpers::init_contract();
let limit = 2;
let delegation_owner = Addr::unchecked("foo");
store_n_reverse_delegations(100, &mut deps.storage, &delegation_owner);
let page1 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner,
None,
Option::from(limit),
)
.unwrap();
assert_eq!(limit, page1.delegated_nodes.len() as u32);
}
#[test]
fn retrieval_has_default_limit() {
let mut deps = helpers::init_contract();
let delegation_owner = Addr::unchecked("foo");
store_n_reverse_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&delegation_owner,
);
// query without explicitly setting a limit
let page1 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner,
None,
None,
)
.unwrap();
assert_eq!(
DELEGATION_PAGE_DEFAULT_LIMIT,
page1.delegated_nodes.len() as u32
);
}
#[test]
fn retrieval_has_max_limit() {
let mut deps = helpers::init_contract();
let delegation_owner = Addr::unchecked("foo");
store_n_reverse_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&delegation_owner,
);
// query with a crazy high limit in an attempt to use too many resources
let crazy_limit = 1000 * DELEGATION_PAGE_DEFAULT_LIMIT;
let page1 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner,
None,
Option::from(crazy_limit),
)
.unwrap();
// we default to a decent sized upper bound instead
let expected_limit = DELEGATION_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.delegated_nodes.len() as u32);
}
#[test]
fn pagination_works() {
let mut deps = helpers::init_contract();
let delegation_owner = Addr::unchecked("bar");
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
.save("1".as_bytes(), &())
.unwrap();
let per_page = 2;
let page1 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner.clone(),
None,
Option::from(per_page),
)
.unwrap();
// page should have 1 result on it
assert_eq!(1, page1.delegated_nodes.len());
// save another
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
.save("2".as_bytes(), &())
.unwrap();
// page1 should have 2 results on it
let page1 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner.clone(),
None,
Option::from(per_page),
)
.unwrap();
assert_eq!(2, page1.delegated_nodes.len());
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
.save("3".as_bytes(), &())
.unwrap();
// page1 still has 2 results
let page1 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner.clone(),
None,
Option::from(per_page),
)
.unwrap();
assert_eq!(2, page1.delegated_nodes.len());
// retrieving the next page should start after the last key on this page
let start_after: IdentityKey = String::from("2");
let page2 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner.clone(),
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
assert_eq!(1, page2.delegated_nodes.len());
// save another one
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
.save("4".as_bytes(), &())
.unwrap();
let start_after = String::from("2");
let page2 = query_reverse_gateway_delegations_paged(
deps.as_ref(),
delegation_owner,
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.delegated_nodes.len());
}
}
}
-2
View File
@@ -14,7 +14,5 @@ pub struct State {
// helper values to avoid having to recalculate them on every single payment operation
pub mixnode_epoch_bond_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
pub gateway_epoch_bond_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
pub mixnode_epoch_delegation_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
pub gateway_epoch_delegation_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
}
-435
View File
@@ -32,9 +32,7 @@ const PREFIX_GATEWAYS: &[u8] = b"gt";
const PREFIX_GATEWAYS_OWNERS: &[u8] = b"go";
const PREFIX_MIX_DELEGATION: &[u8] = b"md";
const PREFIX_GATEWAY_DELEGATION: &[u8] = b"gd";
const PREFIX_REVERSE_MIX_DELEGATION: &[u8] = b"dm";
const PREFIX_REVERSE_GATEWAY_DELEGATION: &[u8] = b"dg";
// Contract-level stuff
@@ -63,14 +61,6 @@ pub(crate) fn read_mixnode_epoch_bond_reward_rate(storage: &dyn Storage) -> Deci
.mixnode_epoch_bond_reward
}
pub(crate) fn read_gateway_epoch_bond_reward_rate(storage: &dyn Storage) -> Decimal {
// same justification as in `read_state_params` for the unwrap
config_read(storage)
.load()
.unwrap()
.gateway_epoch_bond_reward
}
pub(crate) fn read_mixnode_epoch_delegation_reward_rate(storage: &dyn Storage) -> Decimal {
// same justification as in `read_state_params` for the unwrap
config_read(storage)
@@ -79,14 +69,6 @@ pub(crate) fn read_mixnode_epoch_delegation_reward_rate(storage: &dyn Storage) -
.mixnode_epoch_delegation_reward
}
pub(crate) fn read_gateway_epoch_delegation_reward_rate(storage: &dyn Storage) -> Decimal {
// same justification as in `read_state_params` for the unwrap
config_read(storage)
.load()
.unwrap()
.gateway_epoch_delegation_reward
}
pub fn layer_distribution(storage: &mut dyn Storage) -> Singleton<LayerDistribution> {
singleton(storage, LAYER_DISTRIBUTION_KEY)
}
@@ -214,56 +196,6 @@ pub(crate) fn increase_mix_delegated_stakes(
Ok(total_rewarded)
}
pub(crate) fn increase_gateway_delegated_stakes(
storage: &mut dyn Storage,
gateway_identity: IdentityKeyRef,
scaled_reward_rate: Decimal,
reward_blockstamp: u64,
) -> StdResult<Uint128> {
let chunk_size = queries::DELEGATION_PAGE_MAX_LIMIT as usize;
let mut total_rewarded = Uint128::zero();
let mut chunk_start: Option<Vec<_>> = None;
loop {
// get `chunk_size` of delegations
let delegations_chunk = gateway_delegations_read(storage, gateway_identity)
.range(chunk_start.as_deref(), None, Order::Ascending)
.take(chunk_size)
.collect::<StdResult<Vec<_>>>()?;
if delegations_chunk.is_empty() {
break;
}
// append 0 byte to the last value to start with whatever is the next suceeding key
chunk_start = Some(
delegations_chunk
.last()
.unwrap()
.0
.iter()
.cloned()
.chain(std::iter::once(0u8))
.collect(),
);
// and for each of them increase the stake proportionally to the reward
// if at least `MINIMUM_BLOCK_AGE_FOR_REWARDING` blocks have been created
// since they delegated
for (delegator_address, mut delegation) in delegations_chunk.into_iter() {
if delegation.block_height + MINIMUM_BLOCK_AGE_FOR_REWARDING <= reward_blockstamp {
let reward = delegation.amount * scaled_reward_rate;
delegation.amount += reward;
total_rewarded += reward;
gateway_delegations(storage, gateway_identity)
.save(&delegator_address, &delegation)?;
}
}
}
Ok(total_rewarded)
}
// currently not used outside tests
#[cfg(test)]
pub(crate) fn read_mixnode_bond(
@@ -338,53 +270,6 @@ pub fn reverse_mix_delegations_read<'a>(
ReadonlyBucket::multilevel(storage, &[PREFIX_REVERSE_MIX_DELEGATION, owner.as_bytes()])
}
pub fn all_gateway_delegations_read<T>(storage: &dyn Storage) -> ReadonlyBucket<T>
where
T: Serialize + DeserializeOwned,
{
bucket_read(storage, PREFIX_GATEWAY_DELEGATION)
}
pub fn gateway_delegations<'a>(
storage: &'a mut dyn Storage,
gateway_identity: IdentityKeyRef,
) -> Bucket<'a, RawDelegationData> {
Bucket::multilevel(
storage,
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
)
}
pub fn gateway_delegations_read<'a>(
storage: &'a dyn Storage,
gateway_identity: IdentityKeyRef,
) -> ReadonlyBucket<'a, RawDelegationData> {
ReadonlyBucket::multilevel(
storage,
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
)
}
pub fn reverse_gateway_delegations<'a>(
storage: &'a mut dyn Storage,
owner: &Addr,
) -> Bucket<'a, ()> {
Bucket::multilevel(
storage,
&[PREFIX_REVERSE_GATEWAY_DELEGATION, owner.as_bytes()],
)
}
pub fn reverse_gateway_delegations_read<'a>(
storage: &'a dyn Storage,
owner: &Addr,
) -> ReadonlyBucket<'a, ()> {
ReadonlyBucket::multilevel(
storage,
&[PREFIX_REVERSE_GATEWAY_DELEGATION, owner.as_bytes()],
)
}
// currently not used outside tests
#[cfg(test)]
pub(crate) fn read_gateway_bond(
@@ -396,17 +281,6 @@ pub(crate) fn read_gateway_bond(
Ok(node.bond_amount.amount)
}
// currently not used outside tests
#[cfg(test)]
pub(crate) fn read_gateway_delegation(
storage: &dyn Storage,
identity: &[u8],
) -> StdResult<cosmwasm_std::Uint128> {
let bucket = gateways_read(storage);
let node = bucket.load(identity)?;
Ok(node.total_delegation.amount)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -499,7 +373,6 @@ mod tests {
let gateway_bond = GatewayBond {
bond_amount: coin(bond_value, DENOM),
total_delegation: coin(0, DENOM),
owner: node_owner.clone(),
block_height: 12_345,
gateway: Gateway {
@@ -551,39 +424,6 @@ mod tests {
assert_eq!(raw_delegation2, res2);
}
#[test]
fn all_gateway_delegations_read_retrieval() {
let mut deps = mock_dependencies(&[]);
let node_identity1: IdentityKey = "foo1".into();
let delegation_owner1 = Addr::unchecked("bar1");
let node_identity2: IdentityKey = "foo2".into();
let delegation_owner2 = Addr::unchecked("bar2");
let raw_delegation1 = RawDelegationData::new(1u128.into(), 1000);
let raw_delegation2 = RawDelegationData::new(2u128.into(), 2000);
gateway_delegations(&mut deps.storage, &node_identity1)
.save(delegation_owner1.as_bytes(), &raw_delegation1)
.unwrap();
gateway_delegations(&mut deps.storage, &node_identity2)
.save(delegation_owner2.as_bytes(), &raw_delegation2)
.unwrap();
let res1 = all_gateway_delegations_read::<RawDelegationData>(&deps.storage)
.load(&*identity_and_owner_to_bytes(
&node_identity1,
&delegation_owner1,
))
.unwrap();
let res2 = all_gateway_delegations_read::<RawDelegationData>(&deps.storage)
.load(&*identity_and_owner_to_bytes(
&node_identity2,
&delegation_owner2,
))
.unwrap();
assert_eq!(raw_delegation1, res1);
assert_eq!(raw_delegation2, res2);
}
#[cfg(test)]
mod increasing_mix_delegated_stakes {
use super::*;
@@ -858,279 +698,4 @@ mod tests {
);
}
}
#[cfg(test)]
mod increasing_gateway_delegated_stakes {
use super::*;
use crate::queries::query_gateway_delegations_paged;
use cosmwasm_std::testing::mock_dependencies;
#[test]
fn when_there_are_no_delegations() {
let mut deps = mock_dependencies(&[]);
let node_identity: IdentityKey = "nodeidentity".into();
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
42,
)
.unwrap();
// there was no increase
assert!(total_increase.is_zero());
// there are no 'new' delegations magically added
assert!(
query_gateway_delegations_paged(deps.as_ref(), node_identity, None, None)
.unwrap()
.delegations
.is_empty()
)
}
#[test]
fn when_there_is_a_single_delegation() {
let mut deps = mock_dependencies(&[]);
let node_identity: IdentityKey = "nodeidentity".into();
let delegation_blockstamp = 42;
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
let delegator_address = Addr::unchecked("bob");
gateway_delegations(&mut deps.storage, &node_identity)
.save(
delegator_address.as_bytes(),
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
)
.unwrap();
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
delegation_blockstamp + 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING,
)
.unwrap();
assert_eq!(Uint128(1), total_increase);
// amount is incremented, block height remains the same
assert_eq!(
RawDelegationData::new(1001u128.into(), 42),
gateway_delegations_read(&mut deps.storage, &node_identity)
.load(delegator_address.as_bytes())
.unwrap()
)
}
#[test]
fn when_there_is_a_single_delegation_depending_on_blockstamp() {
let mut deps = mock_dependencies(&[]);
let node_identity: IdentityKey = "nodeidentity".into();
let delegation_blockstamp = 42;
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
let delegator_address = Addr::unchecked("bob");
gateway_delegations(&mut deps.storage, &node_identity)
.save(
delegator_address.as_bytes(),
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
)
.unwrap();
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
delegation_blockstamp + MINIMUM_BLOCK_AGE_FOR_REWARDING - 1,
)
.unwrap();
// there was no increase
assert!(total_increase.is_zero());
// amount is not incremented
assert_eq!(
RawDelegationData::new(1000u128.into(), delegation_blockstamp),
gateway_delegations_read(&mut deps.storage, &node_identity)
.load(delegator_address.as_bytes())
.unwrap()
);
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
delegation_blockstamp + MINIMUM_BLOCK_AGE_FOR_REWARDING,
)
.unwrap();
// there is an increase now, that the lock period has passed
assert_eq!(Uint128(1), total_increase);
// amount is incremented
assert_eq!(
RawDelegationData::new(1001u128.into(), delegation_blockstamp),
gateway_delegations_read(&mut deps.storage, &node_identity)
.load(delegator_address.as_bytes())
.unwrap()
)
}
#[test]
fn when_there_are_multiple_delegations() {
let mut deps = mock_dependencies(&[]);
let node_identity: IdentityKey = "nodeidentity".into();
let delegation_blockstamp = 42;
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
for i in 0..100 {
let delegator_address = Addr::unchecked(format!("address{}", i));
gateway_delegations(&mut deps.storage, &node_identity)
.save(
delegator_address.as_bytes(),
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
)
.unwrap();
}
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
delegation_blockstamp + 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING,
)
.unwrap();
assert_eq!(Uint128(100), total_increase);
for i in 0..100 {
let delegator_address = Addr::unchecked(format!("address{}", i));
assert_eq!(
raw_delegation_fixture(1001),
gateway_delegations_read(&mut deps.storage, &node_identity)
.load(delegator_address.as_bytes())
.unwrap()
)
}
}
#[test]
fn when_there_are_more_delegations_than_page_size() {
let mut deps = mock_dependencies(&[]);
let node_identity: IdentityKey = "nodeidentity".into();
let delegation_blockstamp = 42;
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
let delegator_address = Addr::unchecked(format!("address{}", i));
gateway_delegations(&mut deps.storage, &node_identity)
.save(
delegator_address.as_bytes(),
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
)
.unwrap();
}
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
delegation_blockstamp + 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING,
)
.unwrap();
assert_eq!(
Uint128(queries::DELEGATION_PAGE_MAX_LIMIT as u128 * 10),
total_increase
);
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
let delegator_address = Addr::unchecked(format!("address{}", i));
assert_eq!(
raw_delegation_fixture(1001),
gateway_delegations_read(&mut deps.storage, &node_identity)
.load(delegator_address.as_bytes())
.unwrap()
)
}
}
}
#[cfg(test)]
mod reverse_gateway_delegations {
use super::*;
use crate::support::tests::helpers;
#[test]
fn reverse_gateway_delegation_exists() {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "foo".into();
let delegation_owner = Addr::unchecked("bar");
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
.save(node_identity.as_bytes(), &())
.unwrap();
assert!(
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner)
.may_load(node_identity.as_bytes())
.unwrap()
.is_some(),
);
}
#[test]
fn reverse_gateway_delegation_returns_none_if_delegation_doesnt_exist() {
let mut deps = helpers::init_contract();
let node_identity1: IdentityKey = "foo1".into();
let node_identity2: IdentityKey = "foo2".into();
let delegation_owner1 = Addr::unchecked("bar");
let delegation_owner2 = Addr::unchecked("bar2");
assert!(
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner1)
.may_load(node_identity1.as_bytes())
.unwrap()
.is_none()
);
// add delegation for a different node
reverse_gateway_delegations(&mut deps.storage, &delegation_owner1)
.save(node_identity2.as_bytes(), &())
.unwrap();
assert!(
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner1)
.may_load(node_identity1.as_bytes())
.unwrap()
.is_none()
);
// add delegation from a different owner
reverse_gateway_delegations(&mut deps.storage, &delegation_owner2)
.save(node_identity1.as_bytes(), &())
.unwrap();
assert!(
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner1)
.may_load(node_identity1.as_bytes())
.unwrap()
.is_none()
);
}
}
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -6,6 +6,7 @@ name = "nym-gateway"
version = "0.11.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -10,6 +10,7 @@ authors = [
"Drazen Urch <durch@users.noreply.github.com>",
]
edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+94 -108
View File
@@ -12,15 +12,6 @@ dependencies = [
"regex",
]
[[package]]
name = "addr2line"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
@@ -42,15 +33,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "anomaly"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "550632e31568ae1a5f47998c3aa48563030fc49b9ec91913ca337cf64fbc5ccb"
dependencies = [
"backtrace",
]
[[package]]
name = "anyhow"
version = "1.0.43"
@@ -148,21 +130,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
dependencies = [
"addr2line",
"cc",
"cfg-if 1.0.0",
"libc",
"miniz_oxide 0.4.4",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.0"
@@ -430,12 +397,9 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"serde",
"time 0.1.43",
"winapi",
]
[[package]]
@@ -632,9 +596,9 @@ dependencies = [
[[package]]
name = "cosmos-sdk-proto"
version = "0.6.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7115eae4b8518967b8e737a3ef76025f2d007de7906d88c18f00b8bbfc70f51e"
checksum = "edb5204c6ddc4352c74297638b5561f2929d6334866c156e5f3c75e1e1a1436a"
dependencies = [
"prost",
"prost-types",
@@ -643,9 +607,9 @@ dependencies = [
[[package]]
name = "cosmrs"
version = "0.1.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "609eba7eee6d9929927cbdf15f9fe96d250c316c5e1b983c4a47a10f60da5ea7"
checksum = "d31147fe89e547e74e2692e4bec35387e1ea406fe8cfb14c0ea177b58fd2a8a9"
dependencies = [
"bip32",
"cosmos-sdk-proto",
@@ -656,6 +620,8 @@ dependencies = [
"prost",
"prost-types",
"rand_core 0.6.3",
"serde",
"serde_json",
"subtle-encoding",
"tendermint",
"tendermint-rpc",
@@ -1077,7 +1043,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc"
dependencies = [
"serde",
"signature",
]
@@ -1089,9 +1054,6 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"serde",
"serde_bytes",
"sha2",
"zeroize",
]
@@ -1228,6 +1190,16 @@ dependencies = [
"miniz_oxide 0.4.4",
]
[[package]]
name = "flex-error"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b"
dependencies = [
"eyre",
"paste",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -1531,12 +1503,6 @@ dependencies = [
"syn",
]
[[package]]
name = "gimli"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
[[package]]
name = "gio"
version = "0.14.5"
@@ -1976,6 +1942,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"webpki",
"webpki-roots",
]
[[package]]
@@ -2098,15 +2065,6 @@ dependencies = [
"syn",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.10.1"
@@ -2610,15 +2568,6 @@ dependencies = [
"objc",
]
[[package]]
name = "object"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.8.0"
@@ -2744,6 +2693,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "paste"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
[[package]]
name = "pbkdf2"
version = "0.8.0"
@@ -2753,6 +2708,33 @@ dependencies = [
"crypto-mac",
]
[[package]]
name = "peg"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a"
dependencies = [
"peg-macros",
"peg-runtime",
]
[[package]]
name = "peg-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c"
dependencies = [
"peg-runtime",
"proc-macro2",
"quote",
]
[[package]]
name = "peg-runtime"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -3061,9 +3043,9 @@ dependencies = [
[[package]]
name = "prost"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2"
checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
dependencies = [
"bytes",
"prost-derive",
@@ -3071,12 +3053,12 @@ dependencies = [
[[package]]
name = "prost-derive"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4"
checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
dependencies = [
"anyhow",
"itertools 0.9.0",
"itertools",
"proc-macro2",
"quote",
"syn",
@@ -3084,9 +3066,9 @@ dependencies = [
[[package]]
name = "prost-types"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb"
checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a"
dependencies = [
"bytes",
"prost",
@@ -3476,12 +3458,6 @@ dependencies = [
"opaque-debug 0.3.0",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc_version"
version = "0.3.3"
@@ -4193,7 +4169,7 @@ dependencies = [
"anyhow",
"cfg-expr",
"heck",
"itertools 0.10.1",
"itertools",
"pkg-config",
"strum 0.21.0",
"strum_macros 0.21.1",
@@ -4408,16 +4384,16 @@ dependencies = [
[[package]]
name = "tendermint"
version = "0.21.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0752808f59b612916614e92e43be64e9ba566b762a266bcfcf7dce1b324e9319"
checksum = "50d1cdb0236becb17ab35a2ed1566503e412fd910944dc940239857bb7663652"
dependencies = [
"anomaly",
"async-trait",
"bytes",
"chrono",
"ed25519",
"ed25519-dalek",
"flex-error",
"futures",
"k256",
"num-traits",
@@ -4434,21 +4410,32 @@ dependencies = [
"subtle",
"subtle-encoding",
"tendermint-proto",
"thiserror",
"toml",
"url",
"zeroize",
]
[[package]]
name = "tendermint-proto"
version = "0.21.0"
name = "tendermint-config"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd0371c6b0c7fc4f6b053b8a1bc868a2a47c49a1408c4cd6f595d9f3bd3c238"
checksum = "2a1f94250d30e3011130a09756b05985d8dbfbd562cf261b5a17e36d89a37992"
dependencies = [
"flex-error",
"serde",
"serde_json",
"tendermint",
"toml",
"url",
]
[[package]]
name = "tendermint-proto"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff16a7b42bdbcf31c8cd10a4cffc7631f2a301360ba3a3f71dde48eabfa5bced"
dependencies = [
"anomaly",
"bytes",
"chrono",
"flex-error",
"num-derive",
"num-traits",
"prost",
@@ -4456,30 +4443,32 @@ dependencies = [
"serde",
"serde_bytes",
"subtle-encoding",
"thiserror",
]
[[package]]
name = "tendermint-rpc"
version = "0.21.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fa8d172e3129802d28de980531993dd25cc01841c5bc8fe4a87c7750a7f222"
checksum = "c7842dcd5edb60b077572aa92ff8b29fc810b9b463310f9810a2607474130db4"
dependencies = [
"async-trait",
"bytes",
"chrono",
"flex-error",
"futures",
"getrandom 0.1.16",
"http",
"hyper",
"hyper-proxy",
"hyper-rustls",
"peg",
"pin-project",
"serde",
"serde_bytes",
"serde_json",
"subtle-encoding",
"tendermint",
"tendermint-config",
"tendermint-proto",
"thiserror",
"tokio",
@@ -4656,21 +4645,9 @@ checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.19"
@@ -4812,7 +4789,7 @@ dependencies = [
"cosmrs",
"cosmwasm-std",
"flate2",
"itertools 0.10.1",
"itertools",
"log",
"mixnet-contract",
"network-defaults",
@@ -5023,6 +5000,15 @@ dependencies = [
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
"webpki",
]
[[package]]
name = "webview2"
version = "0.1.1"
+8 -1
View File
@@ -7,12 +7,19 @@ SPDX-License-Identifier: Apache-2.0
A Rust and Tauri desktop wallet implementation.
## Installation prerequisites
## Installation prerequisites Linux / Mac
- `Yarn`
- `NodeJS >= v16.8.0`
- `Rust & cargo >= v1.51`
## Installation prerequisites Windows
- When running on Windows you will need to install c++ build tools
- An easy guide to get rust up and running [Installation]("http://kennykerr.ca/2019/11/18/rust-getting-started/")
- When installing NodeJS please use the `current features` version
- Using a package manager like [Chocolatey]("chocolatey.org") is recommended
## Installation
Inside of the `tauri-wallet` folder, run the following commands
+3 -2
View File
@@ -8,6 +8,7 @@ repository = ""
default-run = "nym_wallet"
edition = "2018"
build = "src/build.rs"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -23,12 +24,12 @@ dirs = "3.0"
# url = "2.2"
bip39 = "1.0"
thiserror = "1.0"
tendermint-rpc = "0.21.0"
tendermint-rpc = "0.23.0"
ts-rs = "3.0"
url = "2.0"
rand = "0.6.5"
cosmrs = { version = "0.1", features = ["rpc", "bip32", "cosmwasm"] }
cosmrs = { version = "0.3", features = ["rpc", "bip32", "cosmwasm"] }
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch = "0.14.1-updatedk256" }
validator-client = { path = "../../common/client-libs/validator-client", features = [
+8 -3
View File
@@ -5,6 +5,7 @@
use mixnet_contract::{Gateway, MixNode};
use std::sync::Arc;
use tauri::{Menu, MenuItem};
use tokio::sync::RwLock;
use ts_rs::export;
use validator_client::nymd::fee_helpers::Operation;
@@ -35,6 +36,12 @@ macro_rules! format_err {
};
}
pub fn create_menu_items() -> Menu {
Menu::new()
.add_native_item(MenuItem::Copy)
.add_native_item(MenuItem::Paste)
}
fn main() {
tauri::Builder::default()
.manage(Arc::new(RwLock::new(State::default())))
@@ -51,16 +58,14 @@ fn main() {
unbond_gateway,
delegate_to_mixnode,
undelegate_from_mixnode,
delegate_to_gateway,
undelegate_from_gateway,
send,
create_new_account,
get_fee,
get_state_params,
update_state_params,
get_reverse_mix_delegations_paged,
get_reverse_gateway_delegations_paged,
])
.menu(create_menu_items())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
@@ -16,11 +16,8 @@ pub struct TauriStateParams {
minimum_mixnode_bond: String,
minimum_gateway_bond: String,
mixnode_bond_reward_rate: String,
gateway_bond_reward_rate: String,
mixnode_delegation_reward_rate: String,
gateway_delegation_reward_rate: String,
mixnode_active_set_size: u32,
gateway_active_set_size: u32,
}
impl From<StateParams> for TauriStateParams {
@@ -30,11 +27,8 @@ impl From<StateParams> for TauriStateParams {
minimum_mixnode_bond: p.minimum_mixnode_bond.to_string(),
minimum_gateway_bond: p.minimum_gateway_bond.to_string(),
mixnode_bond_reward_rate: p.mixnode_bond_reward_rate.to_string(),
gateway_bond_reward_rate: p.gateway_bond_reward_rate.to_string(),
mixnode_delegation_reward_rate: p.mixnode_delegation_reward_rate.to_string(),
gateway_delegation_reward_rate: p.gateway_delegation_reward_rate.to_string(),
mixnode_active_set_size: p.mixnode_active_set_size,
gateway_active_set_size: p.gateway_active_set_size,
}
}
}
@@ -48,11 +42,8 @@ impl TryFrom<TauriStateParams> for StateParams {
minimum_mixnode_bond: Uint128::try_from(p.minimum_mixnode_bond.as_str())?,
minimum_gateway_bond: Uint128::try_from(p.minimum_gateway_bond.as_str())?,
mixnode_bond_reward_rate: Decimal::from_str(p.mixnode_bond_reward_rate.as_str())?,
gateway_bond_reward_rate: Decimal::from_str(p.gateway_bond_reward_rate.as_str())?,
mixnode_delegation_reward_rate: Decimal::from_str(p.mixnode_delegation_reward_rate.as_str())?,
gateway_delegation_reward_rate: Decimal::from_str(p.gateway_delegation_reward_rate.as_str())?,
mixnode_active_set_size: p.mixnode_active_set_size,
gateway_active_set_size: p.gateway_active_set_size,
})
}
}
@@ -2,9 +2,7 @@ use crate::coin::Coin;
use crate::format_err;
use crate::state::State;
use cosmwasm_std::Coin as CosmWasmCoin;
use mixnet_contract::{
Addr, PagedReverseGatewayDelegationsResponse, PagedReverseMixDelegationsResponse,
};
use mixnet_contract::{Addr, PagedReverseMixDelegationsResponse};
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::sync::Arc;
@@ -57,45 +55,6 @@ pub async fn undelegate_from_mixnode(
}
}
#[tauri::command]
pub async fn delegate_to_gateway(
identity: &str,
amount: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, String> {
let r_state = state.read().await;
let bond: CosmWasmCoin = match amount.try_into() {
Ok(b) => b,
Err(e) => return Err(format_err!(e)),
};
let client = r_state.client()?;
match client.delegate_to_gateway(identity, &bond).await {
Ok(_result) => Ok(DelegationResult {
source_address: client.address().to_string(),
target_address: identity.to_string(),
amount: Some(bond.into()),
}),
Err(e) => Err(format_err!(e)),
}
}
#[tauri::command]
pub async fn undelegate_from_gateway(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.remove_gateway_delegation(identity).await {
Ok(_result) => Ok(DelegationResult {
source_address: client.address().to_string(),
target_address: identity.to_string(),
amount: None,
}),
Err(e) => Err(format_err!(e)),
}
}
#[tauri::command]
pub async fn get_reverse_mix_delegations_paged(
state: tauri::State<'_, Arc<RwLock<State>>>,
@@ -104,16 +63,6 @@ pub async fn get_reverse_mix_delegations_paged(
let client = r_state.client()?;
client
.get_reverse_mix_delegations_paged(Addr::unchecked(client.address().as_ref()), None, None)
.await.map_err(|err| format_err!(err))
}
#[tauri::command]
pub async fn get_reverse_gateway_delegations_paged(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<PagedReverseGatewayDelegationsResponse, String> {
let r_state = state.read().await;
let client = r_state.client()?;
client
.get_reverse_gateway_delegations_paged(Addr::unchecked(client.address().as_ref()), None, None)
.await.map_err(|err| format_err!(err))
.await
.map_err(|err| format_err!(err))
}
+1 -1
View File
@@ -33,7 +33,7 @@ export const Confirmation = ({
{error === null ? (
SuccessMessage
) : (
<Alert severity="error">
<Alert severity="error" data-testid="error-message">
<AlertTitle>{error.name}</AlertTitle>
<strong>{failureMessage}</strong> - {error.message}
</Alert>
@@ -40,6 +40,7 @@ export const CopyToClipboard = ({ text }: { text: string }) => {
size="small"
variant={copied ? 'text' : 'outlined'}
aria-label="save"
data-testid="copy-button"
onClick={() => handleCopy({ text, cb: updateCopyStatus })}
endIcon={copied && <Check />}
style={copied ? { background: green[500], color: 'white' } : {}}
+2 -2
View File
@@ -6,11 +6,11 @@ import { Button } from '@material-ui/core'
export const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
return (
<div>
<Alert severity="error">
<Alert severity="error" data-testid="error-message">
<AlertTitle>{error.name}</AlertTitle>
{error.message}
</Alert>
<Alert severity="error">
<Alert severity="error" data-testid="stack-trace">
<AlertTitle>Stack trace</AlertTitle>
{error.stack}
</Alert>
+1 -1
View File
@@ -131,7 +131,7 @@ export const Nav = () => {
)}
<ListItem button onClick={logOut}>
<ListItemIcon className={classes.navItem}>
<ListItemIcon data-testid="log-out" className={classes.navItem}>
<ExitToApp />
</ListItemIcon>
<ListItemText
@@ -28,7 +28,7 @@ export const BalanceCard = () => {
noPadding
Action={
<Tooltip title="Refresh balance">
<IconButton onClick={getBalance.fetchBalance} size="small">
<IconButton data-testid="refresh-balance" onClick={getBalance.fetchBalance} size="small">
<Refresh />
</IconButton>
</Tooltip>
@@ -43,7 +43,7 @@ export const BalanceCard = () => {
{getBalance.error}
</Alert>
) : (
<Typography variant="h6">
<Typography variant="h6" data-testid="account-balance">
{getBalance.balance?.printable_balance}
</Typography>
)}
@@ -71,6 +71,7 @@ export const AddressCard = () => {
title="Address"
subheader="Wallet payments address"
noPadding
data-testid="wallet-address-header"
Action={
<Tooltip title={!copyState ? 'Copy address' : 'Copied'}>
<span>
@@ -106,7 +107,7 @@ export const AddressCard = () => {
}
>
<CardContent>
<Typography
<Typography data-testid="wallet-address"
style={{ fontWeight: theme.typography.fontWeightRegular }}
>
{truncate(clientDetails?.client_address!, 35)}
@@ -5,7 +5,7 @@ import { Alert, AlertTitle } from '@material-ui/lab'
export const NoClientError = () => {
return (
<Alert severity="error">
<AlertTitle>No client detected</AlertTitle>
<AlertTitle data-testid="client-error">No client detected</AlertTitle>
Have you signed in? Try to go back to{' '}
<Link to="/signin">the main page</Link> and try again
</Alert>
@@ -34,11 +34,13 @@ export const NodeTypeSelector = ({
value={EnumNodeType.mixnode}
control={<Radio />}
label="Mixnode"
data-testid="mix-node"
disabled={disabled}
/>
<FormControlLabel
value={EnumNodeType.gateway}
control={<Radio />}
data-testid="gate-way"
label="Gateway"
disabled={disabled}
/>
+1
View File
@@ -13,6 +13,7 @@ export const NymCard: React.FC<{
<CardHeader
title={title}
subheader={subheader}
data-testid={title}
titleTypographyProps={{ variant: 'h5' }}
subheaderTypographyProps={{ variant: 'subtitle1' }}
action={Action}
+4 -1
View File
@@ -18,6 +18,7 @@ export const Balance = () => {
size="small"
color="primary"
type="submit"
data-testid="refresh-button"
onClick={fetchBalance}
disabled={isLoading}
disableElevation
@@ -31,12 +32,13 @@ export const Balance = () => {
return (
<Layout>
<NymCard title="Check Balance">
<NymCard title="Check Balance" data-testid="check-balance">
<Grid container direction="column" spacing={2}>
<Grid item>
{error && (
<Alert
severity="error"
data-testid="error-refresh"
action={<RefreshAction />}
style={{ padding: theme.spacing(2) }}
>
@@ -46,6 +48,7 @@ export const Balance = () => {
{!error && (
<Alert
severity="success"
data-testid="refresh-success"
style={{ padding: theme.spacing(2, 3) }}
action={<RefreshAction />}
>
+2 -1
View File
@@ -142,7 +142,7 @@ export const BondForm = ({
</Grid>
{fees && (
<Grid item>
<Alert severity="info">
<Alert severity="info" data-testid="fee-amount">
{`A fee of ${
watchNodeType === EnumNodeType.mixnode
? fees.mixnode.amount
@@ -369,6 +369,7 @@ export const BondForm = ({
color="primary"
type="submit"
size="large"
data-testid="submit-button"
disableElevation
onClick={handleSubmit(onSubmit)}
endIcon={isSubmitting && <CircularProgress size={20} />}
+4 -3
View File
@@ -43,7 +43,7 @@ export const Bond = () => {
<NymCard title="Bond" subheader="Bond a node or gateway" noPadding>
{ownership?.hasOwnership && (
<Alert
severity="warning"
severity="warning"
action={
<Button
disabled={status === EnumRequestStatus.loading}
@@ -53,6 +53,7 @@ export const Bond = () => {
getBalance.fetchBalance()
setStatus(EnumRequestStatus.initial)
}}
data-testid="unBond"
>
Unbond
</Button>
@@ -93,10 +94,10 @@ export const Bond = () => {
<RequestStatus
status={status}
Success={
<Alert severity="success">Successfully bonded node</Alert>
<Alert severity="success" data-testid="bond-success">Successfully bonded node</Alert>
}
Error={
<Alert severity="error">
<Alert severity="error" data-testid="bond-error">
An error occurred with the request: {message}
</Alert>
}
@@ -97,7 +97,7 @@ export const DelegateForm = ({
/>
</Grid>
<Grid item>
<Alert severity="info">
<Alert severity="info" data-testid="fee-amount">
{`A fee of ${
watchNodeType === EnumNodeType.mixnode
? fees.mixnode.amount
@@ -153,6 +153,7 @@ export const DelegateForm = ({
<Button
onClick={handleSubmit(onSubmit)}
disabled={isSubmitting}
data-testid="delegate-button"
variant="contained"
color="primary"
type="submit"
+6 -3
View File
@@ -41,6 +41,7 @@ export const Delegate = () => {
title="Delegate"
subheader="Delegate to mixnode or gateway"
noPadding
data-testid="delegateCard"
>
{isLoading && (
<Box
@@ -72,15 +73,16 @@ export const Delegate = () => {
<RequestStatus
status={status}
Error={
<Alert severity="error">
<Alert severity="error" data-testid="delegate-error">
<AlertTitle>Delegation failed</AlertTitle>
An error occurred with the request:
<Box style={{ wordBreak: 'break-word' }}>{message}</Box>
</Alert>
}
Success={
<Alert severity="success">
<Alert severity="success" data-testid="delegate-success">
<AlertTitle>Delegation complete</AlertTitle>
{message}
<Box style={{ wordBreak: 'break-word' }}>{message}</Box>
</Alert>
}
/>
@@ -95,6 +97,7 @@ export const Delegate = () => {
}}
>
<Button
data-testid="finish-button"
onClick={() => {
setStatus(EnumRequestStatus.initial)
}}
+3 -2
View File
@@ -17,7 +17,7 @@ export const Receive = () => {
<NymCard title="Receive Nym">
<Grid container direction="column" spacing={1}>
<Grid item>
<Alert severity="info">
<Alert severity="info" data-testid="receive-nym">
You can receive tokens by providing this address to the sender
</Alert>
</Grid>
@@ -40,6 +40,7 @@ export const Receive = () => {
>
<Grid item>
<Typography
data-testid="client-address"
variant={matches ? 'h5' : 'subtitle1'}
style={{
wordBreak: 'break-word',
@@ -61,7 +62,7 @@ export const Receive = () => {
component="div"
>
{clientDetails && (
<QRCode value={clientDetails.client_address} />
<QRCode data-testid="qr-code" value={clientDetails.client_address} />
)}
</Box>
</Grid>
@@ -46,7 +46,7 @@ export const SendConfirmation = ({
marginBottom: theme.spacing(1),
}}
/>
<Typography>Transaction complete</Typography>
<Typography data-testid="transaction-complete">Transaction complete</Typography>
</div>
<Card
@@ -60,7 +60,7 @@ export const SendConfirmation = ({
</Typography>
</div>
<div style={{ wordBreak: 'break-all' }}>
<Typography>{data.to_address}</Typography>
<Typography data-testid="to-address">{data.to_address}</Typography>
</div>
</div>
<div style={{ display: 'flex' }}>
@@ -70,7 +70,7 @@ export const SendConfirmation = ({
</Typography>
</div>
<div>
<Typography>{data.amount.amount + ' punks'}</Typography>
<Typography data-testid="send-amount">{data.amount.amount + ' punks'}</Typography>
</div>
</div>
</Card>
+1 -1
View File
@@ -37,7 +37,7 @@ export const SendError = ({ message }: { message?: string }) => {
variant="outlined"
style={{ width: '100%', padding: theme.spacing(2) }}
>
<Alert severity="error">
<Alert severity="error" data-testid="transaction-error">
An error occured during the request {message}
</Alert>
</Card>
+2 -2
View File
@@ -44,7 +44,7 @@ export const SendReview = () => {
) : (
<Grid container spacing={2}>
<Grid item xs={12}>
<SendReviewField title="From" subtitle={values.from} />
<SendReviewField title="From" subtitle={values.from}/>
</Grid>
<Grid item xs={12}>
<Divider light />
@@ -86,7 +86,7 @@ export const SendReviewField = ({
<Typography style={{ color: theme.palette.grey[600] }}>
{title}
</Typography>
<Typography style={{ wordBreak: 'break-all' }}>{subtitle}</Typography>
<Typography data-testid={title} style={{ wordBreak: 'break-all' }}>{subtitle}</Typography>
</>
)
}
@@ -148,6 +148,7 @@ export const SendWizard = () => {
disableElevation
style={{ marginRight: theme.spacing(1) }}
onClick={handlePreviousStep}
data-testid="back-button"
>
Back
</Button>
@@ -156,6 +157,7 @@ export const SendWizard = () => {
variant={activeStep > 0 ? 'contained' : 'text'}
color={activeStep > 0 ? 'primary' : 'default'}
disableElevation
data-testid="button"
onClick={
activeStep === 0
? handleNextStep
+7 -5
View File
@@ -100,7 +100,7 @@ const SignInContent = ({
return (
<SignInCard>
<>
<Typography variant="h4">Sign in</Typography>
<Typography variant="h4" data-testid="sign-in">Sign in</Typography>
<form noValidate onSubmit={handleSignIn}>
<Grid container direction="column" spacing={1}>
<Grid item>
@@ -239,7 +239,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
/>
<Typography>Wallet setup complete</Typography>
</div>
<Alert severity="info" style={{ marginBottom: theme.spacing(2) }}>
<Alert severity="info" style={{ marginBottom: theme.spacing(2) }} data-testid="mnemonic-warning">
Please store your <strong>mnemonic</strong> in a safe place.
You'll need it to access your wallet
</Alert>
@@ -257,7 +257,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
</Typography>
</Grid>
<Grid item>
<Typography>{accountDetails.mnemonic}</Typography>
<Typography data-testid="mnemonic-phrase">{accountDetails.mnemonic}</Typography>
<div
style={{ display: 'flex', justifyContent: 'flex-end' }}
>
@@ -273,7 +273,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
</Typography>
</Grid>
<Grid item>
<Typography>{accountDetails.client_address}</Typography>
<Typography data-testid="wallet-address">{accountDetails.client_address}</Typography>
</Grid>
</Grid>
</Card>
@@ -282,7 +282,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
</Grid>
{error && (
<Grid item style={{ marginTop: theme.spacing(1) }}>
<Alert severity="error">{error}</Alert>
<Alert severity="error" data-testid="error">{error}</Alert>
</Grid>
)}
<Grid item>
@@ -293,6 +293,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
variant="contained"
color="primary"
type="submit"
data-testid="create-button"
disableElevation
style={{ marginBottom: theme.spacing(1) }}
disabled={isLoading}
@@ -304,6 +305,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
fullWidth
variant="text"
onClick={showSignIn}
data-testid="sign-in-button"
startIcon={<ArrowBack />}
>
Sign in
+3 -1
View File
@@ -29,8 +29,10 @@ export const Unbond = () => {
{ownership?.hasOwnership && (
<Alert
severity="warning"
data-testid="bond-noded"
action={
<Button
data-testid="un-bond"
disabled={isLoading}
onClick={async () => {
setIsLoading(true)
@@ -48,7 +50,7 @@ export const Unbond = () => {
</Alert>
)}
{!ownership.hasOwnership && (
<Alert severity="info" style={{ margin: theme.spacing(3) }}>
<Alert severity="info" style={{ margin: theme.spacing(3) }} data-testid="no-bond">
You don't currently have a bonded node
</Alert>
)}
@@ -84,7 +84,7 @@ export const UndelegateForm = ({
/>
</Grid>
<Grid item>
<Alert severity="info">
<Alert severity="info" data-testid="fee-amount">
{`A fee of ${
watchNodeType === EnumNodeType.mixnode
? fees.mixnode.amount
@@ -140,6 +140,7 @@ export const UndelegateForm = ({
variant="contained"
color="primary"
type="submit"
data-testid="submit-button"
disableElevation
disabled={isSubmitting}
endIcon={isSubmitting && <CircularProgress size={20} />}
+3 -2
View File
@@ -102,14 +102,14 @@ export const Undelegate = () => {
<RequestStatus
status={status}
Error={
<Alert severity="error">
<Alert severity="error" data-testid="request-error">
An error occurred with the request: {message}
</Alert>
}
Success={
<Alert severity="success">
{' '}
<AlertTitle>Undelegation complete</AlertTitle>
<AlertTitle data-testid="undelegate-success">Undelegation complete</AlertTitle>
{message}
</Alert>
}
@@ -125,6 +125,7 @@ export const Undelegate = () => {
}}
>
<Button
data-testid="finish-button"
onClick={() => {
setStatus(EnumRequestStatus.initial)
initialize()
@@ -3,9 +3,6 @@ export interface TauriStateParams {
minimum_mixnode_bond: string;
minimum_gateway_bond: string;
mixnode_bond_reward_rate: string;
gateway_bond_reward_rate: string;
mixnode_delegation_reward_rate: string;
gateway_delegation_reward_rate: string;
mixnode_active_set_size: number;
gateway_active_set_size: number;
}
+5
View File
@@ -0,0 +1,5 @@
reports
allure-results
node_modules
.vscode
.idea
+79
View File
@@ -0,0 +1,79 @@
<!--
Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
SPDX-License-Identifier: Apache-2.0
-->
# Nym Wallet Webdriverio testsuite
A webdriverio test suite implementation using tauri driver
with a page object model design. This is to provide quick iterative feedback
on the UI of the nym wallet. Currently, tauri-driver is available to run on Windows and Linux machines.
## Installation prerequisites
* `Yarn`
* `NodeJS >= v16.8.0`
* `Rust & cargo >= v1.51`
* `tauri-driver`
* `That you have an existing mnemonic and you can login to the app`
* `Have the details listed below to provide the user-data.json file`
## Key Information
* Please read the instructions on the `nym/tauri-wallet/README.md` in the root of the project on how to build the application
* Please ensure you have the relevant Webdriver kits installed on your machine -
```
linux:
sudo apt-get install -y webkit2gtk-driver
```
```
windows:
download msedgedriver.exe from https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
```
please visit [Tauri Studio](https://tauri.studio/en/docs/usage/guides/webdriver/introduction), this will specify the additional drivers you need
* The path to run the application is set in the `wdio.conf.js` which lives in the root directory
* Before running the suite you need to build the application and check that the application has
built successfully, if so, you will have an executable sitting in the target directory in `tauri-wallet/target/*/nym_wallet` (refer to point 1)
* The suite will not be able to detect elements on screen if you select a release build, however you can run tests against a release target
## Installation & usage
* `test excution happens inside /webdriver directory`
* `test data needs to be provided inside the user-data.json`
* `check the wdio.conf.cjs to see the test execution along with the path location of the binary`
```
example:
//mnemonic is a base64 enconded value, which is your 24 character passphrase, these values are for illustration purposes
{
"mnemonic" : "dGhpcyBpcyBhIHBhc3NwaHJhc2UK",
"punk_address" : "punk1f3dzkhmunma5ze5q952daxca6371989189",
"receiver_address" : "punk1p0ce82jxxglpmutvhq4mdwgcwf4avm5n1821982",
"amount_to_send" : "1",
"identity_key_to_delegate_mix_node": "value",
"identity_key_to_delegate_gateway" : "value",
"delegate_amount" : "1"
}
```
* `yarn test:runall` - the first test run will take some time to spin up be patient
* You can run tests individually by passing through the script situated in the package.json for example `yarn test:newuser`
Tests are categorised and run by their pages, they follow a sequential flow, if one test case fails before the next execution it may derail the next test.
//todo improve in near future
## Test reporting
Currently the tests use allure reporting, the configuration can be altered in the `wdio.conf.cjs`. At present it takes snapshots of any failing tests, the test output run can be seen in the allure-results directory
Tests ouput:
* <guid-testuite.xml>
* <guid-attachment.png>
If any tests fail in their test run it will produce the stack trace error along with the test in question
## TODO
*Disclaimer*: Still WIP
Implement error handling/ beforeTest() - validating json file exists with data for test execution
Currently this is dev'd against a Linux based OS, not tested against windows yet.
+12
View File
@@ -0,0 +1,12 @@
module.exports = {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "14"
}
}
]
]
}
@@ -0,0 +1,28 @@
module.exports = {
//receivePage
"recievePageInformation" : "You can receive tokens by providing this address to the sender",
"receivePageHeaderText": "Receive Nym",
//sendPage
"sendPunk" : "Send PUNK",
//homePage
"homePageErrorMnemonic": "Error parsing bip39 mnemonic",
"homePageSignIn" : "Sign in",
"createOne" : "Create one",
"walletSuccess" : "Please store your mnemonic in a safe place. You'll need it to access your wallet",
//bondPage // unbondPage
"bondAlreadyNoded" : "Looks like you already have a mixnode bonded.",
"bondNodeHeaderText": "Bond a node or gateway",
"unbondNodeHeaderText" : "Unbond a mixnode or gateway",
"unbondMixNodeText": "Looks like you already have a mixnode bonded.",
"unbondMixNode": "UNBOND",
//delegatePage // undelegatePage
"delegateHeaderText" : "Delegate\nDelegate to mixnode or gateway",
"nodeIdentityValidationText" : "identity is a required field",
"amountValidationText": "amount is a required field",
"undelegateHeaderText" : "Undelegate from a mixnode or gateway",
"delegationComplete" : "Delegation complete"
}
@@ -0,0 +1,9 @@
{
"mnemonic": "value",
"punk_address": "",
"receiver_address": "",
"amount_to_send": "",
"identity_key_to_delegate_mix_node": "",
"identity_key_to_delegate_gateway" : "",
"delegate_amount": ""
}
@@ -0,0 +1,45 @@
class Helpers {
//helper to decode mnemonic so plain 24 character passphrase isn't in sight albeit it is presented when ruunning the scripts
//maybe a show passphrase toggle button?
decodeBase = async (input) => {
var m = Buffer.from(input, 'base64').toString()
return m
}
navigateAndClick = async (element) => {
await element.click()
}
scrollIntoView = async (element) => {
await element.scrollIntoView()
}
currentBalance = async (value) => {
return parseFloat(value.split(/\s+/)[0].toString()).toFixed(5)
}
//todo need to improve calculation - WIP
calculateFees = async (beforeBalance, transactionFee, amount, isSend) => {
let fee
if (isSend) {
//send transaction
fee = transactionFee.split(/\s+/)[0]
} else {
//delegate transaction
fee = transactionFee.split(/\s+/)[3]
}
const currentBalance = beforeBalance.split(/\s+/)[0]
const castCurrentBalance = parseFloat(currentBalance).toFixed(5)
const transCost = +parseFloat(amount) + +parseFloat(fee).toFixed(5)
let sum = parseFloat(castCurrentBalance) - parseFloat(transCost)
return sum.toFixed(5)
}
}
module.exports = new Helpers()
+25
View File
@@ -0,0 +1,25 @@
{
"name": "tauri_nym_wallet",
"version": "1.0.0",
"private": false,
"license": "MIT",
"scripts": {
"test:runall": "wdio run wdio.conf.cjs",
"test:sendreceive": "wdio run wdio.conf.cjs --suite sendreceive",
"test:home": "wdio run wdio.conf.cjs --suite home",
"test:bond": "wdio run wdio.conf.cjs --suite bond",
"test:delegate": "wdio run wdio.conf.cjs --suite delegate",
"test:newuser": "wdio run wdio.conf.cjs --suite newuser"
},
"dependencies": {
"@types/node": "^16.11.0",
"@wdio/allure-reporter": "^7.16.1",
"@wdio/cli": "^7.9.1",
"@zxing/browser": "^0.0.9"
},
"devDependencies": {
"@wdio/local-runner": "^7.14.1",
"@wdio/mocha-framework": "^7.14.1",
"@wdio/spec-reporter": "^7.14.1"
}
}
@@ -0,0 +1,20 @@
class WalletBond {
get header() { return $("#root > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div.MuiCardHeader-root > div > span.MuiTypography-root.MuiCardHeader-subheader.MuiTypography-subtitle1.MuiTypography-colorTextSecondary.MuiTypography-displayBlock") }
get identityKey() { return $("#identityKey") }
get sphinxKey() { return $("#sphinxKey") }
get amountToBond() { return $("#amount") }
get hostInput() { return $("#host") }
get versionInput() { return $("version") }
get selectAdvancedOptions() { return $("[type='checkbox']") }
get mixPort() { return $("#mixPort") }
get verlocPort() { return $("#verlocPort") }
get httpApiPort() { return $("#httpApiPort") }
get bondButton() { return $("[data-testid='bond-button']") }
get unBondButton() { return $("[data-testid='un-bond']") }
get unBond() { return $("[data-testid='bond-noded']") }
get unBondWarning() {return $("div.MuiAlert-message")}
}
module.exports = new WalletBond()
@@ -0,0 +1,12 @@
class WalletCreate {
get createAccount() { return $("[href='#']") }
get create() { return $("[data-testid='create-button']") }
get accountCreatedSuccessfully() { return $("[data-testid='mnemonic-warning']") }
get walletMnemonicValue() { return $("[data-testid='mnemonic-phrase']") }
get punkAddress() { return $("[data-testid='wallet-address']") }
get backToSignIn() { return $("[data-testid='sign-in-button']") }
get signInButton() { return $("[type='submit']") }
}
module.exports = new WalletCreate()
@@ -0,0 +1,26 @@
class WalletDelegate {
get header() { return $("[data-testid='Delegate']") }
get nodeIdentity() { return $("#identity") }
get amountToDelegate() { return $("#amount") }
get identityValidation() { return $("#identity-helper-text") }
get amountToDelegateValidation() { return $("#amount-helper-text") }
get delegateStakeButton() { return $("[data-testid='delegate-button']") }
get mixNodeRadioButton() { return $("[data-testid='mix-node']") }
get gateWayRadioButton() { return $("[data-testid='gate-way']") }
get successfullyDelegate() { return $("[data-testid='delegate-success']") }
get finishButton() { return $("[data-testid='finish-button']") }
get transactionFeeAmount() { return $("[data-testid='fee-amount']") }
get accountBalance() { return $("[data-testid='account-balance']") }
//Undelegate
get unDelegateHeader() { return $("[data-testid='Undelegate']") }
get unNodeIdentity() { return $("[name='identity']") }
get unDelegateFeeText() { return $("[data-testid='fee-amount']") }
get unDelegateGatewayRadioButton() { return $("[data-testid='gate-way']") }
get unMixNodeRadioButton() { return $("[data-testid='mix-node']") }
get unDelegateButton() { return $("[data-testid='submit-button']") }
}
module.exports = new WalletDelegate()
@@ -0,0 +1,18 @@
class WalletHome {
get balanceCheck() { return $("#root > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div.MuiCardHeader-root > div > span") }
get punkBalance() { return $("") }
get punkAddress() { return $("[data-testid='wallet-address']") }
get accountBalance() { return $("[data-testid='account-balance']") }
get balanceButton() { return $("[href='/balance']") }
get sendButton() { return $("[href='/send']"); }
get receiveButton() { return $("[href='/receive']") }
get bondButton() { return $("[href='/bond']") }
get unBondButton() { return $("[href='/unbond']") }
get delegateButton() { return $("[href='/delegate']") }
get unDelegateButton() { return $("[href='/undelegate']") }
get logOutButton() { return $("[data-testid='log-out']") }
}
module.exports = new WalletHome()
@@ -0,0 +1,18 @@
class WalletLogin {
get signInLabel() {return $("[data-testid='sign-in']")}
get mnemonic() { return $("#mnemonic")}
get signInButton() {return $("[type='submit']") }
get errorValidation() {return $("[class='MuiAlert-message']") }
get accountBalance() {return $("[data-test-id='account-balance']")}
get accountBalanceText() {return $("[class='MuiAlert-message']")}
get walletAddress() {return $("[data-testid='wallet-address']")}
//login to the application
enterMnemonic = async(mnemonic) => {
await this.mnemonic.addValue(mnemonic);
await this.signInButton.click();
await this.accountBalance.isExisting();
}
}
module.exports = new WalletLogin()
@@ -0,0 +1,24 @@
class WalletReceive {
get receiveNymHeader() { return $("#root > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div.MuiCardHeader-root > div > span") }
get receiveNymText() { return $("[data-testid='receive-nym']") }
get walletAddress() { return $("[data-testid='client-address']"); }
get copyButton() { return $("[data-testid='copy-button']") }
get qrCode() { return $("[data-testid='qr-code']") }
WaitForButtonChangeOnCopy = async () => {
await this.copyButton.click()
await this.copyButton.waitForDisplayed({ timeout: 1500 })
await this.copyButton.waitUntil(async function () {
return (await this.getText()) === 'COPIED'
}, {
timeout: 1500,
timeoutMsg: 'expected text to be different after 1.5s'
})
}
}
module.exports = new WalletReceive()
@@ -0,0 +1,22 @@
class WalletSend {
get fromAddress() { return $("#from") }
get toAddress() { return $("#to") }
get amount() { return $("#amount") }
get nextButton() { return $("[data-testid='button") }
get sendHeader() { return $("[data-testid='Send PUNK']") }
get accountBalance() { return $("[data-testid='account-balance']") }
get amountReviewAndSend() { return $("[data-testid='Amount']") }
get toAddressReviewAndSend() { return $("[data-testid='To']") }
get fromAddressReviewAndSend() { return $("[data-testid='From']") }
get transferFeeAmount() { return $("[data-testid='Transfer fee']") }
get reviewAndSendBackButton() { return $("[data-testid='back-button']") }
get sendButton() { return $("[data-testid='button']") }
get transactionComplete() { return $("[data-testid='transaction-complete']") }
get transactionCompleteRecipient() { return $("[data-testid='to-address']") }
get transactionCompleteAmount() { return $("[data-testid='send-amount']") }
get finishButton() { return $("[data-testid='button']") }
}
module.exports = new WalletSend()
@@ -0,0 +1,12 @@
class WallentUndelegate {
get transactionFee() { return $("[data-testid='fee-amount']") }
get mixNodeRadioButton() { return $("[value='mixnode']") }
get gatewayRadionButton() { return $("[value='gateway']") }
get nodeIdentity() { return $("#mui-55011") }
get identityHelper() { return $("#identity-helper-text") }
get delegateButton() { return $("[data-testid='submit-button']") }
}
module.exports = new WallentUndelegate()
@@ -0,0 +1,55 @@
const userData = require('../../../common/data/user-data.json')
const helper = require('../../../common/helpers/helper')
const walletLogin = require('../../pages/wallet.login')
const textConstants = require('../../../common/constants/text-constants')
const walletHomepage = require('../../pages/wallet.homepage')
const bondPage = require('../../pages/wallet.bond')
describe("bonding and unbonding nodes", () => {
it("should have a node already bonded and validate no input fields are enabled", async () => {
const mnemonic = await helper.decodeBase(userData.mnemonic);
await walletLogin.enterMnemonic(mnemonic);
await helper.navigateAndClick(walletHomepage.bondButton);
await helper.scrollIntoView(bondPage.selectAdvancedOptions);
await bondPage.selectAdvancedOptions.click();
//as bond node is mixed expect all the fields to be disabled
const getText = await bondPage.header.getText()
const getIdentity = await bondPage.identityKey.isEnabled()
const getSphinxKey = await bondPage.sphinxKey.isEnabled()
const amountToBond = await bondPage.amountToBond.isEnabled()
const hostInput = await bondPage.hostInput.isEnabled()
const verlocPort = await bondPage.verlocPort.isEnabled()
const httpApiPort = await bondPage.httpApiPort.isEnabled()
const mixPort = await bondPage.mixPort.isEnabled()
//assert all field are not functional
expect(getText).toEqual(textConstants.bondNodeHeaderText)
expect(getIdentity).toEqual(false)
expect(getSphinxKey).toEqual(false)
expect(amountToBond).toEqual(false)
expect(hostInput).toEqual(false)
expect(verlocPort).toEqual(false)
expect(httpApiPort).toEqual(false)
expect(mixPort).toEqual(false)
})
it("unbond mix monde screen should be present with the option to unbond", async () => {
//we do not want to unbond our node, check that elements are selectable
await helper.scrollIntoView(walletHomepage.unBondButton)
await helper.navigateAndClick(walletHomepage.unBondButton)
const getText = await bondPage.header.getText()
const unbondText = await bondPage.unBondWarning.getText()
await bondPage.unBondButton.isClickable()
//assert all field are not functional
expect(getText).toEqual(textConstants.unbondNodeHeaderText)
expect(unbondText).toEqual(textConstants.unbondMixNodeText)
})
})
@@ -0,0 +1,90 @@
const userData = require('../../../common/data/user-data.json')
const helper = require('../../../common/helpers/helper')
const walletLogin = require('../../pages/wallet.login')
const textConstants = require('../../../common/constants/text-constants')
const walletHomepage = require('../../pages/wallet.homepage')
const delegatePage = require('../../pages/wallet.delegate')
describe("delegate to a mix node or gateway", () => {
it("ensure that fields are enabled for existing user", async () => {
const mnemonic = await helper.decodeBase(userData.mnemonic)
await walletLogin.enterMnemonic(mnemonic)
await helper.navigateAndClick(walletHomepage.delegateButton)
const getText = await delegatePage.header.getText()
expect(getText).toEqual(textConstants.delegateHeaderText)
})
it("submitting the form without input prompts validation errors", async () => {
await delegatePage.delegateStakeButton.click()
const getIdentityValidation = await delegatePage.identityValidation.getText()
const getAmountValidation = await delegatePage.amountToDelegateValidation.getText()
expect(getIdentityValidation).toEqual(textConstants.nodeIdentityValidationText)
expect(getAmountValidation).toEqual(textConstants.amountValidationText)
})
it("input delegate amount to a mix node then broadcast the transaction then check account balances", async () => {
const balanceText = await delegatePage.accountBalance.getText()
const getTransfeeAmount = await delegatePage.transactionFeeAmount.getText()
await delegatePage.nodeIdentity.setValue(userData.identity_key_to_delegate_mix_node)
await delegatePage.amountToDelegate.setValue(userData.delegate_amount)
//transfer fee + amount delegation
const sumCost = await helper.calculateFees(balanceText, getTransfeeAmount, userData.delegate_amount, false)
await delegatePage.delegateStakeButton.click()
await delegatePage.successfullyDelegate.waitForClickable({ timeout: 10000 })
const getConfirmationText = await delegatePage.successfullyDelegate.getText()
expect(getConfirmationText).toContain(textConstants.delegationComplete)
const availablePunk = await delegatePage.accountBalance.getText()
//expect new account balance - the fee calculation above
await delegatePage.finishButton.click()
expect(await helper.currentBalance(availablePunk)).toEqual(sumCost)
})
it("input amount to stake to a gateway then broadcast the transaction then check account balances", async () => {
const balanceText = await delegatePage.accountBalance.getText()
const getTransfeeAmount = await delegatePage.transactionFeeAmount.getText()
await delegatePage.gateWayRadioButton.click()
await delegatePage.nodeIdentity.setValue(userData.identity_key_to_delegate_gateway)
await delegatePage.amountToDelegate.setValue(userData.delegate_amount)
//transfer fee + amount delegation
const sumCost = await helper.calculateFees(balanceText, getTransfeeAmount, userData.delegate_amount, false)
await delegatePage.delegateStakeButton.click()
await delegatePage.successfullyDelegate.waitForClickable({ timeout: 10000 })
const getConfirmationText = await delegatePage.successfullyDelegate.getText()
expect(getConfirmationText).toContain(textConstants.delegationComplete)
const availablePunk = await delegatePage.accountBalance.getText()
//expect new account balance - the fee calculation above
expect(await helper.currentBalance(availablePunk)).toEqual(sumCost)
})
})
@@ -0,0 +1,51 @@
const userData = require('../../../common/data/user-data.json')
const helper = require('../../../common/helpers/helper')
const walletLogin = require('../../pages/wallet.login')
const homepPage = require('../../pages/wallet.homepage')
const textConstants = require('../../../common/constants/text-constants')
describe("wallet splash screen", () => {
it("should have the sign in header present", async () => {
const signInText = await walletLogin.signInLabel.getText()
expect(signInText).toEqual(textConstants.homePageSignIn)
})
it("submitting the sign in button with no input throws a validation error", async () => {
await walletLogin.signInButton.click()
const errorResponseText = await walletLogin.errorValidation.getText()
expect(errorResponseText).toEqual(textConstants.homePageErrorMnemonic)
})
//currently the punk_address is not fully displayed on the wallet UI
//trim the punk address
it("successfully input mnemonic and log in", async () => {
const mnemonic = await helper.decodeBase(userData.mnemonic)
await walletLogin.enterMnemonic(mnemonic)
await walletLogin.walletAddress.waitForEnabled({ timeout: 5000 })
const getWalletAddress = await walletLogin.walletAddress.getText()
//currently 35 characters are displayed along with three ...
//current hack we can assume this is the correct wallet
const walletTruncated = userData.punk_address.substring(0,35)
expect(walletTruncated + '...').toContain(getWalletAddress)
})
it("successfully log out the application", async () => {
await helper.scrollIntoView(homepPage.logOutButton)
await homepPage.logOutButton.click()
await walletLogin.signInLabel.waitForEnabled({ timeout: 1500 })
expect(await walletLogin.signInLabel.isDisplayed()).toEqual(true)
})
})
@@ -0,0 +1,29 @@
const userData = require('../../../common/data/user-data.json')
const textConstants = require('../../../common/constants/text-constants')
const helper = require('../../../common/helpers/helper')
const walletLogin = require('../../pages/wallet.login')
const receive = require('../../pages/wallet.receive')
const walletHomepage = require('../../pages/wallet.homepage')
describe("provide the relevant information about a user nym wallet address", () => {
it("should have the receivers address and a qr code present", async () => {
const mnemonic = await helper.decodeBase(userData.mnemonic)
await walletLogin.enterMnemonic(mnemonic)
await helper.navigateAndClick(walletHomepage.receiveButton)
await receive.receiveNymHeader.waitForDisplayed({ timeout: 1500 })
await receive.WaitForButtonChangeOnCopy()
const textHeader = await receive.receiveNymHeader.getText();
const getInformationText = await receive.receiveNymText.getText()
const getPunkAddress = await receive.walletAddress.getText()
expect(getPunkAddress).toEqual(userData.punk_address)
expect(getInformationText).toEqual(textConstants.recievePageInformation)
expect(textConstants.receivePageHeaderText).toEqual(textHeader)
})
})
@@ -0,0 +1,55 @@
const userData = require('../../../common/data/user-data.json');
const helper = require('../../../common/helpers/helper')
const textConstants = require('../../../common/constants/text-constants')
const walletLogin = require('../../pages/wallet.login')
const sendWallet = require('../../pages/wallet.send')
const walletHomepage = require('../../pages/wallet.homepage')
describe("send punk to another a wallet", () => {
it("expect send screen to display the data", async () => {
const mnemonic = await helper.decodeBase(userData.mnemonic)
await walletLogin.enterMnemonic(mnemonic)
await helper.navigateAndClick(walletHomepage.sendButton)
const textHeader = await sendWallet.sendHeader.getText()
expect(textHeader).toContain(textConstants.sendPunk)
})
it("send funds correctly to another punk address", async () => {
//already logged in due to the previous test
const getCurrentBalance = await walletHomepage.accountBalance.getText()
await sendWallet.toAddress.addValue(userData.receiver_address)
await sendWallet.amount.addValue(userData.amount_to_send)
await sendWallet.nextButton.waitForEnabled({ timeout: 3000 })
await sendWallet.nextButton.click()
const transFee = await sendWallet.transferFeeAmount.getText()
await sendWallet.sendButton.click()
await sendWallet.finishButton.waitForClickable({ timeout: 10000 })
let sumCost = await helper.calculateFees(getCurrentBalance, transFee, userData.amount_to_send, true)
await walletHomepage.accountBalance.isDisplayed()
const availablePunk = await walletHomepage.accountBalance.getText()
await sendWallet.finishButton.click()
//expect new account balance - the fee calculation above
expect(await helper.currentBalance(availablePunk)).toEqual(sumCost)
})
})
@@ -0,0 +1,32 @@
const userData = require('../../../common/data/user-data.json')
const helper = require('../../../common/helpers/helper')
const walletLogin = require('../../pages/wallet.login')
const walletHomepage = require('../../pages/wallet.homepage')
const unDelegatePage = require('../../pages/wallet.delegate')
describe("un-delegate a mix node or gateway", () => {
it("ensure that fields are enabled for existing user", async () => {
//we are ensuring that the fields are selectable for undelegation
//not proceeding to undelegate a node or gateway
const mnemonic = await helper.decodeBase(userData.mnemonic)
await walletLogin.enterMnemonic(mnemonic)
await helper.scrollIntoView(walletHomepage.unDelegateButton)
await helper.navigateAndClick(walletHomepage.unDelegateButton)
await unDelegatePage.unDelegateButton.waitForClickable({ timeout: 1500})
await unDelegatePage.unDelegateButton.isEnabled()
await unDelegatePage.unDelegateGatewayRadioButton.click()
await unDelegatePage.unDelegateGatewayRadioButton.isSelected()
const mixNodeRadioButton = await unDelegatePage.unMixNodeRadioButton.isSelected()
expect(mixNodeRadioButton).toEqual(false)
})
})
@@ -0,0 +1,38 @@
const walletLogin = require('../../pages/wallet.login')
const walletSignUp = require('../../pages/wallet.create')
const textConstants = require('../../../common/constants/text-constants')
describe("non existing wallet holder", () => {
//wallet mnemonic gets pushed here
const DATA = []
it("create a new account and wallet", async () => {
const signInText = await walletLogin.signInLabel.getText();
expect(signInText).toEqual(textConstants.homePageSignIn);
await walletSignUp.createAccount.click();
//wallet generation takes some time - apply wait
await walletSignUp.create.click()
await walletSignUp.accountCreatedSuccessfully.waitForEnabled({ timeout: 10000 })
const getWalletText = await walletSignUp.punkAddress.getText()
expect(getWalletText.length).toEqual(43)
const accountCreated = await walletSignUp.accountCreatedSuccessfully.getText()
expect(accountCreated).toEqual(textConstants.walletSuccess)
const getMnemonic = await walletSignUp.walletMnemonicValue.getText()
DATA.push(getMnemonic)
})
it("navigate back to sign in screen and validate mnemonic works", async () => {
await walletSignUp.backToSignIn.click()
await walletLogin.enterMnemonic(DATA[0])
await walletLogin.walletAddress.isDisplayed()
})
})
+80
View File
@@ -0,0 +1,80 @@
const os = require("os");
const path = require("path");
const { spawn, spawnSync } = require("child_process");
//insert path to binary
const nym_path = "../target/release/nym_wallet";
exports.config = {
//run sequentially, as using one default user may cause issues for parallel test runs for now
specs: [
"./tests/specs/existinguser/test.wallet.home.js",
"./tests/specs/existinguser/test.wallet.send.js",
"./tests/specs/existinguser/test.wallet.receive.js",
"./tests/specs/existinguser/test.wallet.bond.js",
"./tests/specs/existinguser/test.wallet.delegate.js",
"./tests/specs/newuser/test.wallet.create.js"
],
//run tests by providing --suite {{login}}
suites: {
home: ["./tests/specs/existinguser/test.wallet.home.js"],
sendreceive: ["./tests/specs/existinguser/test.wallet.send.js",
"./tests/specs/existinguser/test.wallet.receive.js"],
bond: ["./tests/specs/existinguser/test.wallet.bond.js"],
delegate: ["./tests/specs/existinguser/test.wallet.delegate.js",
"./tests/specs/existinguser/test.wallet.undelegate.js"],
newuser: ["./tests/specs/newuser/test.wallet.create.js"]
},
maxInstances: 1,
capabilities: [
{
maxInstances: 1,
"tauri:options": {
application: nym_path,
},
},
],
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
// Level of logging verbosity: trace | debug | info | warn | error | silent
bail: 0,
framework: 'mocha',
reporters: ['spec'],
mochaOpts: {
ui: 'bdd',
timeout: 60000
},
logLevel: 'silent',
// ===================
// Test Reporters
// ===================
reporters: [['allure', {
outputDir: 'allure-results',
disableWebdriverStepsReporting: true,
disableWebdriverScreenshotsReporting: true,
}]],
// this is documentented in the readme - you will need to build the project first
// ensure the rust project is built since we expect this binary to exist for the webdriver sessions
//onPrepare: () => spawnSync("cargo", ["build", "--release"]),
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
beforeSession: () =>
(tauriDriver = spawn(
path.resolve(os.homedir(), ".cargo", "bin", "tauri-driver"),
[],
{ stdio: [null, process.stdout, process.stderr] }
)),
afterTest: function (test, context, { error, result, duration, passed, retries }) {
if (error) {
browser.takeScreenshot()
}
},
// clean up the `tauri-driver` process we spawned at the start of the session
afterSession: () => tauriDriver.kill()
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -10,6 +10,7 @@ authors = [
"Drazen Urch <durch@users.noreply.github.com>",
]
edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -0,0 +1,25 @@
/*
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE rewarding_report RENAME TO _rewarding_report_old;
CREATE TABLE rewarding_report
(
epoch_rewarding_id INTEGER NOT NULL,
eligible_mixnodes INTEGER NOT NULL,
possibly_unrewarded_mixnodes INTEGER NOT NULL,
FOREIGN KEY (epoch_rewarding_id) REFERENCES epoch_rewarding (id)
);
INSERT INTO rewarding_report (epoch_rewarding_id, eligible_mixnodes, possibly_unrewarded_mixnodes)
SELECT epoch_rewarding_id, eligible_mixnodes, possibly_unrewarded_mixnodes
FROM _rewarding_report_old;
DROP TABLE _rewarding_report_old;
DROP TABLE failed_gateway_reward_chunk;
DROP TABLE possibly_unrewarded_gateway;
+1 -66
View File
@@ -33,10 +33,7 @@ struct ValidatorCacheInner {
gateways: RwLock<Cache<Vec<GatewayBond>>>,
active_mixnodes_available: AtomicBool,
active_gateways_available: AtomicBool,
current_mixnode_active_set_size: AtomicUsize,
current_gateway_active_set_size: AtomicUsize,
}
#[derive(Default, Serialize, Clone)]
@@ -131,7 +128,6 @@ impl ValidatorCache {
routes::get_mixnodes,
routes::get_gateways,
routes::get_active_mixnodes,
routes::get_active_gateways
],
)
})
@@ -155,28 +151,10 @@ impl ValidatorCache {
true
}
// TODO: check if all nodes can be compared together,
// i.e. they all have the same denom for bonds and delegations
fn verify_gateways(&self, gateways: &[GatewayBond]) -> bool {
if gateways.is_empty() {
return true;
}
let expected_denom = &gateways[0].bond_amount.denom;
for gateway in gateways {
if &gateway.bond_amount.denom != expected_denom
|| &gateway.total_delegation.denom != expected_denom
{
return false;
}
}
true
}
async fn update_cache(
&self,
mut mixnodes: Vec<MixNodeBond>,
mut gateways: Vec<GatewayBond>,
gateways: Vec<GatewayBond>,
state: StateParams,
) {
// if our data is valid, it means the active sets are available,
@@ -199,23 +177,6 @@ impl ValidatorCache {
.store(false, Ordering::SeqCst);
}
if self.verify_gateways(&gateways) {
// partial_cmp can only fail if the nodes have different denomination,
// but we just checked for that hence the unwraps are fine here
// Note the reverse order of comparison so that the "highest" node would be first
gateways.sort_by(|a, b| b.partial_cmp(a).unwrap());
self.inner
.active_gateways_available
.store(true, Ordering::SeqCst);
self.inner
.current_gateway_active_set_size
.store(state.gateway_active_set_size as usize, Ordering::SeqCst);
} else {
self.inner
.active_gateways_available
.store(false, Ordering::SeqCst);
}
self.inner.mixnodes.write().await.set(mixnodes);
self.inner.gateways.write().await.set(gateways);
}
@@ -252,30 +213,6 @@ impl ValidatorCache {
}
}
pub async fn active_gateways(&self) -> Option<Cache<Vec<GatewayBond>>> {
// if active set is available, it means it is already sorted
if self.inner.active_gateways_available.load(Ordering::SeqCst) {
let cache = self.inner.gateways.read().await;
let timestamp = cache.as_at;
let nodes = cache
.value
.iter()
.take(
self.inner
.current_gateway_active_set_size
.load(Ordering::SeqCst),
)
.cloned()
.collect();
Some(Cache {
value: nodes,
as_at: timestamp,
})
} else {
None
}
}
pub fn initialised(&self) -> bool {
self.inner.initialised.load(Ordering::Relaxed)
}
@@ -300,9 +237,7 @@ impl ValidatorCacheInner {
mixnodes: RwLock::new(Cache::default()),
gateways: RwLock::new(Cache::default()),
active_mixnodes_available: AtomicBool::new(false),
active_gateways_available: AtomicBool::new(false),
current_mixnode_active_set_size: Default::default(),
current_gateway_active_set_size: Default::default(),
}
}
}
-7
View File
@@ -22,10 +22,3 @@ pub(crate) async fn get_active_mixnodes(
) -> Option<Json<Vec<MixNodeBond>>> {
cache.active_mixnodes().await.map(|cache| Json(cache.value))
}
#[get("/gateways/active")]
pub(crate) async fn get_active_gateways(
cache: &State<ValidatorCache>,
) -> Option<Json<Vec<GatewayBond>>> {
cache.active_gateways().await.map(|cache| Json(cache.value))
}
+10 -3
View File
@@ -8,10 +8,11 @@ use crate::cache::ValidatorCacheRefresher;
use crate::config::Config;
use crate::network_monitor::tested_network::good_topology::parse_topology_file;
use crate::network_monitor::NetworkMonitorBuilder;
use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater;
use crate::nymd_client::Client;
use crate::rewarding::epoch::Epoch;
use crate::rewarding::Rewarder;
use crate::storage::NodeStatusStorage;
use crate::storage::ValidatorApiStorage;
use ::config::NymConfig;
use anyhow::Result;
use cache::ValidatorCache;
@@ -335,7 +336,7 @@ fn setup_network_monitor<'a>(
}
// get instances of managed states
let node_status_storage = rocket.state::<NodeStatusStorage>().unwrap().clone();
let node_status_storage = rocket.state::<ValidatorApiStorage>().unwrap().clone();
let validator_cache = rocket.state::<ValidatorCache>().unwrap().clone();
let v4_topology = parse_topology_file(config.get_v4_good_topology_file());
@@ -367,7 +368,7 @@ fn setup_rewarder(
) -> Option<Rewarder> {
if config.get_rewarding_enabled() && config.get_network_monitor_enabled() {
// get instances of managed states
let node_status_storage = rocket.state::<NodeStatusStorage>().unwrap().clone();
let node_status_storage = rocket.state::<ValidatorApiStorage>().unwrap().clone();
let validator_cache = rocket.state::<ValidatorCache>().unwrap().clone();
let first_epoch = Epoch::new(
@@ -475,6 +476,12 @@ async fn main() -> Result<()> {
// spawn our cacher
tokio::spawn(async move { validator_cache_refresher.run().await });
// setup our daily uptime updater. Note that if network monitor is disabled, then we have
// no data for the updates and hence we don't need to start it up
let storage = rocket.state::<ValidatorApiStorage>().unwrap().clone();
let uptime_updater = HistoricalUptimeUpdater::new(storage);
tokio::spawn(async move { uptime_updater.run().await });
if let Some(rewarder) = setup_rewarder(&config, &rocket, &nymd_client) {
info!("Periodic rewarding is starting...");
tokio::spawn(async move { rewarder.run().await });
+10 -2
View File
@@ -31,7 +31,15 @@ use crate::network_monitor::monitor::receiver::{
use crate::network_monitor::monitor::sender::PacketSender;
use crate::network_monitor::monitor::summary_producer::SummaryProducer;
use crate::network_monitor::tested_network::TestedNetwork;
use crate::storage::NodeStatusStorage;
use crate::storage::ValidatorApiStorage;
use crypto::asymmetric::{encryption, identity};
use futures::channel::mpsc;
use log::info;
use nymsphinx::addressing::clients::Recipient;
use std::sync::Arc;
use topology::NymTopology;
pub(crate) mod chunker;
pub(crate) mod gateways_reader;
@@ -42,7 +50,7 @@ pub(crate) mod tested_network;
pub(crate) struct NetworkMonitorBuilder<'a> {
config: &'a Config,
tested_network: TestedNetwork,
node_status_storage: NodeStatusStorage,
node_status_storage: ValidatorApiStorage,
validator_cache: ValidatorCache,
}
@@ -51,7 +59,7 @@ impl<'a> NetworkMonitorBuilder<'a> {
config: &'a Config,
v4_topology: NymTopology,
v6_topology: NymTopology,
node_status_storage: NodeStatusStorage,
node_status_storage: ValidatorApiStorage,
validator_cache: ValidatorCache,
) -> Self {
let tested_network = TestedNetwork::new_good(v4_topology, v6_topology);
@@ -8,7 +8,7 @@ use crate::network_monitor::monitor::sender::PacketSender;
use crate::network_monitor::monitor::summary_producer::{NodeResult, SummaryProducer, TestReport};
use crate::network_monitor::test_packet::NodeType;
use crate::network_monitor::tested_network::TestedNetwork;
use crate::storage::NodeStatusStorage;
use crate::storage::ValidatorApiStorage;
use log::{debug, error, info, warn};
use std::process;
use tokio::time::{sleep, Duration, Instant};
@@ -25,7 +25,7 @@ pub(super) struct Monitor {
packet_sender: PacketSender,
received_processor: ReceivedProcessor,
summary_producer: SummaryProducer,
node_status_storage: NodeStatusStorage,
node_status_storage: ValidatorApiStorage,
tested_network: TestedNetwork,
run_interval: Duration,
gateway_ping_interval: Duration,
@@ -39,7 +39,7 @@ impl Monitor {
packet_sender: PacketSender,
received_processor: ReceivedProcessor,
summary_producer: SummaryProducer,
node_status_storage: NodeStatusStorage,
node_status_storage: ValidatorApiStorage,
tested_network: TestedNetwork,
) -> Self {
Monitor {
+2 -1
View File
@@ -9,6 +9,7 @@ use std::time::Duration;
pub(crate) mod local_guard;
pub(crate) mod models;
pub(crate) mod routes;
pub(crate) mod uptime_updater;
pub(crate) mod utils;
pub(crate) const FIFTEEN_MINUTES: Duration = Duration::from_secs(900);
@@ -18,7 +19,7 @@ pub(crate) const ONE_DAY: Duration = Duration::from_secs(86400);
pub(crate) fn stage(database_path: PathBuf) -> AdHoc {
AdHoc::on_ignite("SQLx Stage", |rocket| async {
rocket
.attach(storage::NodeStatusStorage::stage(database_path))
.attach(storage::ValidatorApiStorage::stage(database_path))
.mount(
"/v1/status",
routes![
+9 -9
View File
@@ -213,12 +213,12 @@ pub struct HistoricalUptime {
}
pub(crate) struct ErrorResponse {
error: NodeStatusApiError,
error: ValidatorApiStorageError,
status: Status,
}
impl ErrorResponse {
pub(crate) fn new(error: NodeStatusApiError, status: Status) -> Self {
pub(crate) fn new(error: ValidatorApiStorageError, status: Status) -> Self {
ErrorResponse { error, status }
}
}
@@ -235,7 +235,7 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for ErrorResponse {
}
#[derive(Debug)]
pub enum NodeStatusApiError {
pub enum ValidatorApiStorageError {
MixnodeReportNotFound(String),
GatewayReportNotFound(String),
MixnodeUptimeHistoryNotFound(String),
@@ -245,30 +245,30 @@ pub enum NodeStatusApiError {
InternalDatabaseError,
}
impl Display for NodeStatusApiError {
impl Display for ValidatorApiStorageError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
NodeStatusApiError::MixnodeReportNotFound(identity) => write!(
ValidatorApiStorageError::MixnodeReportNotFound(identity) => write!(
f,
"Could not find status report associated with mixnode {}",
identity
),
NodeStatusApiError::GatewayReportNotFound(identity) => write!(
ValidatorApiStorageError::GatewayReportNotFound(identity) => write!(
f,
"Could not find status report associated with gateway {}",
identity
),
NodeStatusApiError::MixnodeUptimeHistoryNotFound(identity) => write!(
ValidatorApiStorageError::MixnodeUptimeHistoryNotFound(identity) => write!(
f,
"Could not find uptime history associated with mixnode {}",
identity
),
NodeStatusApiError::GatewayUptimeHistoryNotFound(identity) => write!(
ValidatorApiStorageError::GatewayUptimeHistoryNotFound(identity) => write!(
f,
"Could not find uptime history associated with gateway {}",
identity
),
NodeStatusApiError::InternalDatabaseError => {
ValidatorApiStorageError::InternalDatabaseError => {
write!(f, "The internal database has experienced an issue")
}
}
+5 -5
View File
@@ -5,14 +5,14 @@ use crate::node_status_api::models::{
ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport,
MixnodeUptimeHistory,
};
use crate::storage::NodeStatusStorage;
use crate::storage::ValidatorApiStorage;
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::State;
#[get("/mixnode/<pubkey>/report")]
pub(crate) async fn mixnode_report(
storage: &State<NodeStatusStorage>,
storage: &State<ValidatorApiStorage>,
pubkey: &str,
) -> Result<Json<MixnodeStatusReport>, ErrorResponse> {
storage
@@ -24,7 +24,7 @@ pub(crate) async fn mixnode_report(
#[get("/gateway/<pubkey>/report")]
pub(crate) async fn gateway_report(
storage: &State<NodeStatusStorage>,
storage: &State<ValidatorApiStorage>,
pubkey: &str,
) -> Result<Json<GatewayStatusReport>, ErrorResponse> {
storage
@@ -36,7 +36,7 @@ pub(crate) async fn gateway_report(
#[get("/mixnode/<pubkey>/history")]
pub(crate) async fn mixnode_uptime_history(
storage: &State<NodeStatusStorage>,
storage: &State<ValidatorApiStorage>,
pubkey: &str,
) -> Result<Json<MixnodeUptimeHistory>, ErrorResponse> {
storage
@@ -48,7 +48,7 @@ pub(crate) async fn mixnode_uptime_history(
#[get("/gateway/<pubkey>/history")]
pub(crate) async fn gateway_uptime_history(
storage: &State<NodeStatusStorage>,
storage: &State<ValidatorApiStorage>,
pubkey: &str,
) -> Result<Json<GatewayUptimeHistory>, ErrorResponse> {
storage

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