Compare commits

..

2 Commits

200 changed files with 2508 additions and 22925 deletions
+24 -2
View File
@@ -3,7 +3,7 @@ name: Continuous integration
on: [push, pull_request]
jobs:
build:
ci:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' }}
strategy:
@@ -25,13 +25,28 @@ jobs:
override: true
components: rustfmt, clippy
# Exclude validator API on Windows
- uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: build
args: --all --exclude nym-validator-api
- uses: actions-rs/cargo@v1
if: ${{ matrix.os != 'windows-latest' }}
with:
command: build
args: --all
# Exclude validator API on Windows
- uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: test
args: --all --exclude nym-validator-api
- uses: actions-rs/cargo@v1
if: ${{ matrix.os != 'windows-latest' }}
with:
command: test
args: --all
@@ -41,8 +56,15 @@ jobs:
command: fmt
args: --all -- --check
# Exclude validator API on Windows
- uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
if: ${{ matrix.rust != 'nightly' && matrix.os == 'windows-latest' }}
with:
command: clippy
args: --all --exclude nym-validator-api -- -D warnings
- uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' && matrix.os != 'windows-latest' }}
with:
command: clippy
args: -- -D warnings
+1 -1
View File
@@ -3,7 +3,7 @@ name: Mixnet Contract
on: [push, pull_request]
jobs:
mixnet-contract:
ci:
# since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es
runs-on: ubuntu-latest
-22
View File
@@ -1,22 +0,0 @@
name: Generate TS types
on: push
jobs:
tauri-wallet-types:
runs-on: ubuntu-latest
steps:
- name: Prepare
run: sudo apt-get update && sudo apt-get install -y libpango1.0-dev libatk1.0-dev libgdk-pixbuf2.0-dev libsoup2.4-dev librust-gdk-dev libwebkit2gtk-4.0-dev
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Generate TS
run: cd tauri-wallet/src-tauri && cargo test
- uses: EndBug/add-and-commit@v7.2.1 # https://github.com/marketplace/actions/add-commit
with:
add: '["tauri-wallet"]'
message: '[ci skip] Generate TS types'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -3,7 +3,7 @@ name: Wasm Client
on: [push, pull_request]
jobs:
wasm:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Generated
+626 -946
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -15,7 +15,6 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
* nym-wallet (currently in development)- a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)
[![Build Status](https://img.shields.io/github/workflow/status/nymtech/nym/Continuous%20integration/develop?style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
@@ -18,7 +18,7 @@ pub enum ReplyKeyStorageError {
/// Permanent storage for keys in all sent [`ReplySURB`]
///
/// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for
/// payload encryption. In order to -decrypt whatever reply we receive, we need to know which
/// payload encryption. In order to decrypt whatever reply we receive, we need to know which
/// key to use for that purpose. We do it based on received `H(t)` which has to be included
/// with each reply.
/// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to
+2
View File
@@ -31,6 +31,8 @@ tokio-tungstenite = "0.14" # websocket
## internal
client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
+30
View File
@@ -22,6 +22,9 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::{
@@ -163,6 +166,30 @@ impl NymClient {
.start(self.runtime.handle())
}
async fn prepare_credential(&self) -> Credential {
let verification_key = obtain_aggregate_verification_key(
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain bandwidth credential");
// the above would presumably be loaded from a file
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
prepare_for_spending(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&bandwidth_credential,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
}
fn start_gateway_client(
&mut self,
mixnet_message_sender: MixnetMessageSender,
@@ -181,6 +208,8 @@ impl NymClient {
.expect("provided gateway id is invalid!");
self.runtime.block_on(async {
let coconut_credential = self.prepare_credential().await;
let mut gateway_client = GatewayClient::new(
gateway_address,
self.key_manager.identity_keypair(),
@@ -189,6 +218,7 @@ impl NymClient {
mixnet_message_sender,
ack_sender,
self.config.get_base().get_gateway_response_timeout(),
coconut_credential,
);
gateway_client
+29 -2
View File
@@ -6,7 +6,10 @@ use crate::commands::override_config;
use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential;
use config::NymConfig;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
@@ -57,15 +60,34 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
)
}
// this behaviour should definitely be changed, we shouldn't
// need to get bandwidth credential for registration
async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
let verification_key = obtain_aggregate_verification_key(validators)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(raw_identity, validators)
.await
.expect("could not obtain bandwidth credential");
prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key)
.expect("could not prepare out bandwidth credential for spending")
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
validator_urls: Vec<Url>,
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let coconut_credential =
prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await;
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
our_identity.clone(),
coconut_credential,
timeout,
);
gateway_client
@@ -188,8 +210,13 @@ pub fn execute(matches: &ArgMatches) {
config
.get_base_mut()
.with_gateway_id(gate_details.identity_key.to_base58_string());
let shared_keys =
register_with_gateway(&gate_details, key_manager.identity_keypair()).await;
let validator_urls = config.get_base().get_validator_api_endpoints();
let shared_keys = register_with_gateway(
&gate_details,
key_manager.identity_keypair(),
validator_urls,
)
.await;
(shared_keys, gate_details.clients_address())
};
+2
View File
@@ -24,6 +24,8 @@ url = "2.2"
# internal
client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
+30
View File
@@ -23,6 +23,9 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::{
@@ -151,6 +154,30 @@ impl NymClient {
.start(self.runtime.handle())
}
async fn prepare_credential(&self) -> Credential {
let verification_key = obtain_aggregate_verification_key(
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain bandwidth credential");
// the above would presumably be loaded from a file
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
prepare_for_spending(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&bandwidth_credential,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
}
fn start_gateway_client(
&mut self,
mixnet_message_sender: MixnetMessageSender,
@@ -169,6 +196,8 @@ impl NymClient {
.expect("provided gateway id is invalid!");
self.runtime.block_on(async {
let coconut_credential = self.prepare_credential().await;
let mut gateway_client = GatewayClient::new(
gateway_address,
self.key_manager.identity_keypair(),
@@ -177,6 +206,7 @@ impl NymClient {
mixnet_message_sender,
ack_sender,
self.config.get_base().get_gateway_response_timeout(),
coconut_credential,
);
gateway_client
+29 -2
View File
@@ -6,7 +6,10 @@ use crate::commands::override_config;
use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential;
use config::NymConfig;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
@@ -57,15 +60,34 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
)
}
// this behaviour should definitely be changed, we shouldn't
// need to get bandwidth credential for registration
async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
let verification_key = obtain_aggregate_verification_key(validators)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(raw_identity, validators)
.await
.expect("could not obtain bandwidth credential");
prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key)
.expect("could not prepare out bandwidth credential for spending")
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
validator_urls: Vec<Url>,
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let coconut_credential =
prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await;
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
our_identity.clone(),
coconut_credential,
timeout,
);
gateway_client
@@ -189,8 +211,13 @@ pub fn execute(matches: &ArgMatches) {
config
.get_base_mut()
.with_gateway_id(gate_details.identity_key.to_base58_string());
let shared_keys =
register_with_gateway(&gate_details, key_manager.identity_keypair()).await;
let validator_urls = config.get_base().get_validator_api_endpoints();
let shared_keys = register_with_gateway(
&gate_details,
key_manager.identity_keypair(),
validator_urls,
)
.await;
(shared_keys, gate_details.clients_address())
};
@@ -8,6 +8,7 @@ pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use coconut_interface::Credential;
use crypto::asymmetric::identity;
use futures::{FutureExt, SinkExt, StreamExt};
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
@@ -35,6 +36,8 @@ const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayClient {
authenticated: bool,
// TODO: This should be replaced by an actual bandwidth value, with 0 meaning no bandwidth
has_bandwidth: bool,
gateway_address: String,
gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>,
@@ -51,6 +54,7 @@ pub struct GatewayClient {
reconnection_attempts: usize,
/// Delay between each subsequent reconnection attempt.
reconnection_backoff: Duration,
coconut_credential: Credential,
}
impl GatewayClient {
@@ -64,9 +68,11 @@ impl GatewayClient {
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
coconut_credential: Credential,
) -> Self {
GatewayClient {
authenticated: false,
has_bandwidth: false,
gateway_address,
gateway_identity,
local_identity,
@@ -77,6 +83,7 @@ impl GatewayClient {
should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
coconut_credential,
}
}
@@ -97,6 +104,7 @@ impl GatewayClient {
gateway_address: String,
gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>,
coconut_credential: Credential,
response_timeout_duration: Duration,
) -> Self {
use futures::channel::mpsc;
@@ -109,6 +117,7 @@ impl GatewayClient {
GatewayClient {
authenticated: false,
has_bandwidth: false,
gateway_address,
gateway_identity,
local_identity,
@@ -119,6 +128,7 @@ impl GatewayClient {
should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
coconut_credential,
}
}
@@ -446,6 +456,32 @@ impl GatewayClient {
}
}
pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> {
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
if self.shared_key.is_none() {
return Err(GatewayClientError::NoSharedKeyAvailable);
}
let mut rng = OsRng;
let iv = IV::new_random(&mut rng);
let msg = ClientControlRequest::new_enc_bandwidth_credential(
&self.coconut_credential,
self.shared_key.as_ref().unwrap(),
iv,
)
.ok_or(GatewayClientError::SerializeCredential)?
.into();
self.has_bandwidth = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { status } => Ok(status),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
}?;
Ok(())
}
pub async fn batch_send_mix_packets(
&mut self,
packets: Vec<MixPacket>,
@@ -453,6 +489,9 @@ impl GatewayClient {
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
if !self.has_bandwidth {
return Err(GatewayClientError::NotEnoughBandwidth);
}
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
@@ -511,6 +550,9 @@ impl GatewayClient {
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
if !self.has_bandwidth {
return Err(GatewayClientError::NotEnoughBandwidth);
}
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
@@ -556,6 +598,9 @@ impl GatewayClient {
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
if !self.has_bandwidth {
return Err(GatewayClientError::NotEnoughBandwidth);
}
if self.connection.is_partially_delegated() {
return Ok(());
}
@@ -588,6 +633,7 @@ impl GatewayClient {
self.establish_connection().await?;
}
let shared_key = self.perform_initial_authentication().await?;
self.claim_bandwidth().await?;
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
@@ -31,7 +31,6 @@ flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-updatedk256", optional = true }
ts-rs = "3.0"
[features]
nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"]
@@ -158,10 +158,6 @@ impl<C> Client<C> {
self.mixnet_contract_address = Some(mixnet_contract_address)
}
pub fn get_mixnet_contract_address(&self) -> Option<cosmrs::AccountId> {
self.mixnet_contract_address.clone()
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?)
}
@@ -15,7 +15,6 @@ use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, Simple
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
use cosmrs::tx::{Fee, Msg, MsgType, SignDoc, SignerInfo};
use cosmrs::{cosmwasm, rpc, tx, AccountId, Coin};
use log::debug;
use serde::Serialize;
use sha2::Digest;
use sha2::Sha256;
@@ -257,48 +256,6 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
})
}
async fn execute_multiple<I, M>(
&self,
sender_address: &AccountId,
contract_address: &AccountId,
msgs: I,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<ExecuteResult, NymdError>
where
I: IntoIterator<Item = (M, Vec<Coin>)> + Send,
M: Serialize,
{
let messages = msgs
.into_iter()
.map(|(msg, funds)| {
cosmwasm::MsgExecuteContract {
sender: sender_address.clone(),
contract: contract_address.clone(),
msg: serde_json::to_vec(&msg)?,
funds,
}
.to_msg()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
})
.collect::<Result<_, _>>()?;
let tx_res = self
.sign_and_broadcast_commit(sender_address, messages, fee, memo)
.await?
.check_response()?;
debug!(
"gas wanted: {:?}, gas used: {:?}",
tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used
);
Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash,
})
}
async fn send_tokens(
&self,
sender_address: &AccountId,
@@ -485,7 +442,6 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
}
}
#[derive(Debug)]
pub struct Client {
rpc_client: HttpClient,
signer: DirectSecp256k1HdWallet,
@@ -3,12 +3,10 @@
use crate::nymd::cosmwasm_client::types::ContractCodeId;
use cosmrs::tendermint::block;
use cosmrs::{bip32, tx, AccountId};
use cosmrs::{bip32, rpc, tx, AccountId};
use std::io;
use thiserror::Error;
pub use cosmrs::rpc::error::{Code, Error as TendermintRpcError};
#[derive(Debug, Error)]
pub enum NymdError {
#[error("No contract address is available to perform the call")]
@@ -33,7 +31,7 @@ pub enum NymdError {
InvalidTxHash(String),
#[error("There was an issue with a tendermint RPC request - {0}")]
TendermintError(#[from] TendermintRpcError),
TendermintError(#[from] rpc::Error),
#[error("There was an issue when attempting to serialize data")]
SerializationError(String),
@@ -100,48 +98,3 @@ pub enum NymdError {
#[error("The provided gas price is malformed")]
MalformedGasPrice,
}
impl NymdError {
pub fn is_tendermint_timeout(&self) -> bool {
match &self {
NymdError::TendermintError(tm_err) => {
if tm_err.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() {
data.contains("timed out") || data.contains("timeout")
} else {
false
}
} else {
false
}
}
_ => false,
}
}
pub fn is_tendermint_duplicate(&self) -> bool {
match &self {
NymdError::TendermintError(tm_err) => {
if tm_err.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() {
data.contains("tx already exists in cache")
} else {
false
}
} else {
false
}
}
_ => false,
}
}
}
@@ -4,11 +4,9 @@
use crate::nymd::GasPrice;
use cosmrs::tx::{Fee, Gas};
use cosmrs::Coin;
use serde::{Deserialize, Serialize};
use std::fmt;
use ts_rs::TS;
use cosmwasm_std::Uint128;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, TS)]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum Operation {
Upload,
Init,
@@ -30,33 +28,18 @@ pub enum Operation {
}
pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin {
gas_price * gas_limit
}
impl fmt::Display for Operation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Operation::Upload => f.write_str("Upload"),
Operation::Init => f.write_str("Init"),
Operation::Migrate => f.write_str("Migrate"),
Operation::ChangeAdmin => f.write_str("ChangeAdmin"),
Operation::Send => f.write_str("Send"),
Operation::BondMixnode => f.write_str("BondMixnode"),
Operation::UnbondMixnode => f.write_str("UnbondMixnode"),
Operation::DelegateToMixnode => f.write_str("DelegateToMixnode"),
Operation::UndelegateFromMixnode => f.write_str("UndelegateFromMixnode"),
Operation::BondGateway => f.write_str("BondGateway"),
Operation::UnbondGateway => f.write_str("UnbondGateway"),
Operation::DelegateToGateway => f.write_str("DelegateToGateway"),
Operation::UndelegateFromGateway => f.write_str("UndelegateFromGateway"),
Operation::UpdateStateParams => f.write_str("UpdateStateParams"),
}
let limit_uint128 = Uint128::from(gas_limit.value());
let amount = gas_price.amount * limit_uint128;
assert!(amount.u128() <= u64::MAX as u128);
Coin {
denom: gas_price.denom.clone(),
amount: (amount.u128() as u64).into(),
}
}
impl Operation {
// TODO: some value tweaking
pub fn default_gas_limit(&self) -> Gas {
pub(crate) fn default_gas_limit(&self) -> Gas {
match self {
Operation::Upload => 2_500_000u64.into(),
Operation::Init => 500_000u64.into(),
@@ -78,20 +61,16 @@ impl Operation {
}
}
pub(crate) fn determine_custom_fee(gas_price: &GasPrice, gas_limit: Gas) -> Fee {
pub(crate) fn determine_fee(&self, gas_price: &GasPrice, gas_limit: Option<Gas>) -> Fee {
// we need to know 2 of the following 3 parameters (the third one is being implicit) in order to construct Fee:
// (source: https://docs.cosmos.network/v0.42/basics/gas-fees.html)
// - gas price
// - gas limit
// - fees
let gas_limit = gas_limit.unwrap_or_else(|| self.default_gas_limit());
let fee = calculate_fee(gas_price, gas_limit);
Fee::from_amount_and_gas(fee, gas_limit)
}
pub(crate) fn determine_fee(&self, gas_price: &GasPrice, gas_limit: Option<Gas>) -> Fee {
let gas_limit = gas_limit.unwrap_or_else(|| self.default_gas_limit());
Self::determine_custom_fee(gas_price, gas_limit)
}
}
#[cfg(test)]
@@ -3,10 +3,8 @@
use crate::nymd::error::NymdError;
use config::defaults;
use cosmrs::tx::Gas;
use cosmrs::{Coin, Denom};
use cosmwasm_std::{Decimal, Fraction, Uint128};
use std::ops::Mul;
use cosmrs::Denom;
use cosmwasm_std::Decimal;
use std::str::FromStr;
/// A gas price, i.e. the price of a single unit of gas. This is typically a fraction of
@@ -20,36 +18,6 @@ pub struct GasPrice {
pub denom: Denom,
}
impl<'a> Mul<Gas> for &'a GasPrice {
type Output = Coin;
fn mul(self, gas_limit: Gas) -> Self::Output {
let limit_uint128 = Uint128::from(gas_limit.value());
let mut amount = self.amount * limit_uint128;
let gas_price_numerator = self.amount.numerator();
let gas_price_denominator = self.amount.denominator();
// gas price is a fraction of the smallest fee token unit, so we must ensure that
// for any multiplication, we have rounded up
//
// I don't really like the this solution as it has a theoretical chance of
// overflowing (internally cosmwasm uses U256 to avoid that)
// however, realistically that is impossible to happen as the resultant value
// would have to be way higher than our token limit of 10^15 (1 billion of tokens * 1 million for denomination)
// and max value of u128 is approximately 10^38
if limit_uint128.u128() * gas_price_numerator > amount.u128() * gas_price_denominator {
amount += Uint128::new(1);
}
assert!(amount.u128() <= u64::MAX as u128);
Coin {
denom: self.denom.clone(),
amount: (amount.u128() as u64).into(),
}
}
}
impl FromStr for GasPrice {
type Err = NymdError;
@@ -110,15 +78,4 @@ mod tests {
assert!("0.025 upunk".parse::<GasPrice>().is_err());
assert!("0.025UPUNK".parse::<GasPrice>().is_err());
}
#[test]
fn gas_limit_multiplication() {
// real world example that caused an issue when the result was rounded down
let gas_price: GasPrice = "0.025upunk".parse().unwrap();
let gas_limit: Gas = 157500u64.into();
let fee = &gas_price * gas_limit;
// the failing behaviour was result value of 3937
assert_eq!(fee.amount, 3938u64.into());
}
}
@@ -4,13 +4,14 @@
use crate::nymd::cosmwasm_client::signing_client;
use crate::nymd::cosmwasm_client::types::{
ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult,
MigrateResult, SequenceResponse, UploadMeta, UploadResult,
MigrateResult, UploadMeta, UploadResult,
};
use crate::nymd::error::NymdError;
use crate::nymd::fee_helpers::Operation;
use crate::nymd::wallet::DirectSecp256k1HdWallet;
use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl};
use cosmrs::tx::{Fee, Gas};
use cosmwasm_std::Coin;
use mixnet_contract::{
@@ -28,20 +29,16 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
pub use crate::nymd::gas_price::GasPrice;
pub use cosmrs::rpc::HttpClient as QueryNymdClient;
use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::{Fee, Gas};
pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::{AccountId, Denom};
pub use signing_client::Client as SigningNymdClient;
pub mod cosmwasm_client;
pub mod error;
pub mod fee_helpers;
pub(crate) mod fee_helpers;
pub mod gas_price;
pub mod wallet;
#[derive(Debug)]
pub struct NymdClient<C> {
client: C,
contract_address: Option<AccountId>,
@@ -127,14 +124,6 @@ impl<C> NymdClient<C> {
self.custom_gas_limits.insert(operation, limit);
}
pub fn get_gas_price(&self) -> GasPrice {
self.gas_price.clone()
}
pub fn get_custom_gas_limits(&self) -> HashMap<Operation, Gas> {
self.custom_gas_limits.clone()
}
pub fn contract_address(&self) -> Result<&AccountId, NymdError> {
self.contract_address
.as_ref()
@@ -156,36 +145,11 @@ impl<C> NymdClient<C> {
&self.client_address.as_ref().unwrap()[0]
}
pub async fn account_sequence(&self) -> Result<SequenceResponse, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
self.client.get_sequence(self.address()).await
}
pub fn get_fee(&self, operation: Operation) -> Fee {
fn get_fee(&self, operation: Operation) -> Fee {
let gas_limit = self.custom_gas_limits.get(&operation).cloned();
operation.determine_fee(&self.gas_price, gas_limit)
}
pub async fn get_current_block_height(&self) -> Result<Height, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_height().await
}
pub fn calculate_custom_fee(&self, gas_limit: impl Into<Gas>) -> Fee {
Operation::determine_custom_fee(&self.gas_price, gas_limit.into())
}
pub async fn get_current_block_timestamp(&self) -> Result<TendermintTime, NymdError>
where
C: CosmWasmClient + Sync,
{
Ok(self.client.get_block(None).await?.block.header.time)
}
pub async fn get_balance(&self, address: &AccountId) -> Result<Option<CosmosCoin>, NymdError>
where
C: CosmWasmClient + Sync,
@@ -427,23 +391,6 @@ impl<C> NymdClient<C> {
.await
}
pub async fn execute_multiple<I, M>(
&self,
contract_address: &AccountId,
msgs: I,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
I: IntoIterator<Item = (M, Vec<CosmosCoin>)> + Send,
M: Serialize,
{
self.client
.execute_multiple(self.address(), contract_address, msgs, fee, memo)
.await
}
pub async fn upload(
&self,
wasm_code: Vec<u8>,
@@ -570,17 +517,15 @@ impl<C> NymdClient<C> {
/// Delegates specified amount of stake to particular mixnode.
pub async fn delegate_to_mixnode(
&self,
mix_identity: &str,
amount: &Coin,
mix_identity: IdentityKey,
amount: Coin,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::DelegateToMixnode);
let req = ExecuteMsg::DelegateToMixnode {
mix_identity: mix_identity.to_string(),
};
let req = ExecuteMsg::DelegateToMixnode { mix_identity };
self.client
.execute(
self.address(),
@@ -588,7 +533,7 @@ impl<C> NymdClient<C> {
&req,
fee,
"Delegating to mixnode from rust!",
vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
vec![cosmwasm_coin_to_cosmos_coin(amount)],
)
.await
}
@@ -596,16 +541,14 @@ impl<C> NymdClient<C> {
/// Removes stake delegation from a particular mixnode.
pub async fn remove_mixnode_delegation(
&self,
mix_identity: &str,
mix_identity: IdentityKey,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::UndelegateFromMixnode);
let req = ExecuteMsg::UndelegateFromMixnode {
mix_identity: mix_identity.to_string(),
};
let req = ExecuteMsg::UndelegateFromMixnode { mix_identity };
self.client
.execute(
self.address(),
@@ -665,17 +608,15 @@ impl<C> NymdClient<C> {
/// Delegates specified amount of stake to particular gateway.
pub async fn delegate_to_gateway(
&self,
gateway_identity: &str,
amount: &Coin,
gateway_identity: IdentityKey,
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(),
};
let req = ExecuteMsg::DelegateToGateway { gateway_identity };
self.client
.execute(
self.address(),
@@ -683,7 +624,7 @@ impl<C> NymdClient<C> {
&req,
fee,
"Delegating to gateway from rust!",
vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
vec![cosmwasm_coin_to_cosmos_coin(amount)],
)
.await
}
@@ -691,16 +632,14 @@ impl<C> NymdClient<C> {
/// Removes stake delegation from a particular gateway.
pub async fn remove_gateway_delegation(
&self,
gateway_identity: &str,
gateway_identity: IdentityKey,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::UndelegateFromGateway);
let req = ExecuteMsg::UndelegateFromGateway {
gateway_identity: gateway_identity.to_string(),
};
let req = ExecuteMsg::UndelegateFromGateway { gateway_identity };
self.client
.execute(
self.address(),
@@ -743,11 +682,3 @@ fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin {
amount: (coin.amount.u128() as u64).into(),
}
}
fn cosmwasm_coin_ptr_to_cosmos_coin(coin: &Coin) -> CosmosCoin {
CosmosCoin {
denom: coin.denom.parse().unwrap(),
// this might be a bit iffy, cosmwasm coin stores value as u128, while cosmos does it as u64
amount: (coin.amount.u128() as u64).into(),
}
}
@@ -10,7 +10,6 @@ use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId};
/// Derivation information required to derive a keypair and an address from a mnemonic.
#[derive(Debug)]
struct Secp256k1Derivation {
hd_path: DerivationPath,
prefix: String,
@@ -24,23 +23,8 @@ pub struct AccountData {
pub(crate) private_key: SigningKey,
}
impl AccountData {
pub fn address(&self) -> &AccountId {
&self.address
}
pub fn public_key(&self) -> PublicKey {
self.public_key
}
pub fn private_key(&self) -> &SigningKey {
&self.private_key
}
}
type Secp256k1Keypair = (SigningKey, PublicKey);
#[derive(Debug)]
pub struct DirectSecp256k1HdWallet {
/// Base secret
secret: bip39::Mnemonic,
+8 -7
View File
@@ -7,14 +7,15 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aes = { version = "0.7.4", features = ["ctr"] }
bs58 = "0.4.0"
blake3 = { version = "1.0.0", features = ["traits-preview"] }
digest = "0.9.0"
aes-ctr = "0.6.0"
bs58 = "0.4"
blake3 = "0.3"
#blake3 = { version = "0.3", features = ["traits-preview"]}
digest = "0.9"
generic-array = "0.14"
hkdf = "0.11.0"
hmac = "0.11.0"
cipher = "0.3.0"
hkdf = "0.10"
hmac = "0.8"
cipher = "0.2"
x25519-dalek = "1.1"
ed25519-dalek = "1.0"
log = "0.4"
+2 -2
View File
@@ -18,7 +18,7 @@ where
D::OutputSize: ArrayLength<u8>,
{
let mut hmac =
Hmac::<D>::new_from_slice(key).expect("HMAC should be able to take key of any size!");
Hmac::<D>::new_varkey(key).expect("HMAC should be able to take key of any size!");
hmac.update(data);
hmac.finalize()
}
@@ -31,7 +31,7 @@ where
D::OutputSize: ArrayLength<u8>,
{
let mut hmac =
Hmac::<D>::new_from_slice(key).expect("HMAC should be able to take key of any size!");
Hmac::<D>::new_varkey(key).expect("HMAC should be able to take key of any size!");
hmac.update(data);
// note, under the hood ct_eq is called
hmac.verify(tag).is_ok()
+1 -1
View File
@@ -13,7 +13,7 @@ pub use generic_array;
// with the below my idea was to try to introduce having a single place of importing all hashing, encryption,
// etc. algorithms and import them elsewhere as needed via common/crypto
pub use aes;
pub use aes_ctr;
pub use blake3;
// TODO: this function uses all three modules: asymmetric crypto, symmetric crypto and derives key...,
+7 -7
View File
@@ -3,7 +3,7 @@
use crate::asymmetric::encryption;
use crate::hkdf;
use cipher::{CipherKey, NewCipher, StreamCipher};
use cipher::stream::{Key, NewStreamCipher, SyncStreamCipher};
use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::{typenum::Unsigned, ArrayLength};
use rand::{CryptoRng, RngCore};
@@ -13,9 +13,9 @@ use rand::{CryptoRng, RngCore};
pub fn new_ephemeral_shared_key<C, D, R>(
rng: &mut R,
remote_key: &encryption::PublicKey,
) -> (encryption::KeyPair, CipherKey<C>)
) -> (encryption::KeyPair, Key<C>)
where
C: StreamCipher + NewCipher,
C: SyncStreamCipher + NewStreamCipher,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
@@ -31,7 +31,7 @@ where
.expect("somehow too long okm was provided");
let derived_shared_key =
CipherKey::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!");
Key::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!");
(ephemeral_keypair, derived_shared_key)
}
@@ -40,9 +40,9 @@ where
pub fn recompute_shared_key<C, D>(
remote_key: &encryption::PublicKey,
local_key: &encryption::PrivateKey,
) -> CipherKey<C>
) -> Key<C>
where
C: StreamCipher + NewCipher,
C: SyncStreamCipher + NewStreamCipher,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
@@ -53,5 +53,5 @@ where
let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::KeySize::to_usize())
.expect("somehow too long okm was provided");
CipherKey::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!")
Key::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!")
}
+18 -23
View File
@@ -1,14 +1,13 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cipher::{Nonce, StreamCipher};
use cipher::stream::{Nonce, StreamCipher, SyncStreamCipher};
use generic_array::{typenum::Unsigned, GenericArray};
use rand::{CryptoRng, RngCore};
// re-export this for ease of use
pub use cipher::{CipherKey, NewCipher};
pub use cipher::stream::{Key, NewStreamCipher};
// SECURITY:
// TODO: note that this is not the most secure approach here
// we are not using nonces properly but instead "kinda" thinking of them as IVs.
// Nonce require, as the name suggest, being only seen once. Ever.
@@ -21,9 +20,9 @@ pub use cipher::{CipherKey, NewCipher};
#[allow(clippy::upper_case_acronyms)]
pub type IV<C> = Nonce<C>;
pub fn generate_key<C, R>(rng: &mut R) -> CipherKey<C>
pub fn generate_key<C, R>(rng: &mut R) -> Key<C>
where
C: NewCipher,
C: NewStreamCipher,
R: RngCore + CryptoRng,
{
let mut key = GenericArray::default();
@@ -33,7 +32,7 @@ where
pub fn random_iv<C, R>(rng: &mut R) -> IV<C>
where
C: NewCipher,
C: NewStreamCipher,
R: RngCore + CryptoRng,
{
let mut iv = GenericArray::default();
@@ -43,14 +42,14 @@ where
pub fn zero_iv<C>() -> IV<C>
where
C: NewCipher,
C: NewStreamCipher,
{
GenericArray::default()
}
pub fn iv_from_slice<C>(b: &[u8]) -> &IV<C>
where
C: NewCipher,
C: NewStreamCipher,
{
if b.len() != C::NonceSize::to_usize() {
// `from_slice` would have caused a panic about this issue anyway.
@@ -67,42 +66,38 @@ where
// TODO: there's really no way to use more parts of the keystream if it was required at some point.
// However, do we really expect to ever need it?
#[inline]
pub fn encrypt<C>(key: &CipherKey<C>, iv: &IV<C>, data: &[u8]) -> Vec<u8>
pub fn encrypt<C>(key: &Key<C>, iv: &IV<C>, data: &[u8]) -> Vec<u8>
where
C: StreamCipher + NewCipher,
C: SyncStreamCipher + NewStreamCipher,
{
let mut ciphertext = data.to_vec();
encrypt_in_place::<C>(key, iv, &mut ciphertext);
ciphertext
}
#[inline]
pub fn encrypt_in_place<C>(key: &CipherKey<C>, iv: &IV<C>, data: &mut [u8])
pub fn encrypt_in_place<C>(key: &Key<C>, iv: &IV<C>, data: &mut [u8])
where
C: StreamCipher + NewCipher,
C: SyncStreamCipher + NewStreamCipher,
{
let mut cipher = C::new(key, iv);
cipher.apply_keystream(data)
cipher.encrypt(data)
}
#[inline]
pub fn decrypt<C>(key: &CipherKey<C>, iv: &IV<C>, ciphertext: &[u8]) -> Vec<u8>
pub fn decrypt<C>(key: &Key<C>, iv: &IV<C>, ciphertext: &[u8]) -> Vec<u8>
where
C: StreamCipher + NewCipher,
C: SyncStreamCipher + NewStreamCipher,
{
let mut data = ciphertext.to_vec();
decrypt_in_place::<C>(key, iv, &mut data);
data
}
#[inline]
pub fn decrypt_in_place<C>(key: &CipherKey<C>, iv: &IV<C>, data: &mut [u8])
pub fn decrypt_in_place<C>(key: &Key<C>, iv: &IV<C>, data: &mut [u8])
where
C: StreamCipher + NewCipher,
C: SyncStreamCipher + NewStreamCipher,
{
let mut cipher = C::new(key, iv);
cipher.apply_keystream(data)
cipher.decrypt(data)
}
#[cfg(test)]
@@ -113,7 +108,7 @@ mod tests {
#[cfg(test)]
mod aes_ctr128 {
use super::*;
use aes::Aes128Ctr;
use aes_ctr::Aes128Ctr;
#[test]
fn zero_iv_is_actually_zero() {
-1
View File
@@ -14,4 +14,3 @@ cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-up
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
schemars = "0.8"
ts-rs = "3.0"
-4
View File
@@ -45,10 +45,6 @@ impl Delegation {
pub fn owner(&self) -> Addr {
self.owner.clone()
}
pub fn block_height(&self) -> u64 {
self.block_height
}
}
impl Display for Delegation {
+1 -2
View File
@@ -6,11 +6,10 @@ use cosmwasm_std::{coin, Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use ts_rs::TS;
use crate::current_block_height;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema, TS)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct Gateway {
pub host: String,
pub mix_port: u16,
+1 -2
View File
@@ -7,11 +7,10 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::fmt::Display;
use ts_rs::TS;
use crate::current_block_height;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema, TS)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNode {
pub host: String,
pub mix_port: u16,
-2
View File
@@ -7,6 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = {version = "1.0", features = ["derive"]}
url = "2.2"
time = { version = "0.3", features = ["macros"] }
+16 -30
View File
@@ -1,26 +1,19 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::time::Duration;
use time::OffsetDateTime;
use url::Url;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ValidatorDetails {
pub struct ValidatorDetails<'a> {
// it is assumed those values are always valid since they're being provided in our defaults file
pub nymd_url: String,
pub nymd_url: &'a str,
// Right now api_url is optional as we are not running the api reliably on all validators
// however, later on it should be a mandatory field
pub api_url: Option<String>,
pub api_url: Option<&'a str>,
}
impl ValidatorDetails {
pub fn new(nymd_url: &str, api_url: Option<&str>) -> Self {
let api_url = api_url.map(|api_url_str| api_url_str.to_string());
ValidatorDetails {
nymd_url: nymd_url.to_string(),
api_url,
}
impl<'a> ValidatorDetails<'a> {
pub const fn new(nymd_url: &'a str, api_url: Option<&'a str>) -> Self {
ValidatorDetails { nymd_url, api_url }
}
pub fn nymd_url(&self) -> Url {
@@ -31,30 +24,27 @@ impl ValidatorDetails {
pub fn api_url(&self) -> Option<Url> {
self.api_url
.as_ref()
.map(|url| url.parse().expect("the provided api url is invalid!"))
}
}
pub fn default_validators() -> Vec<ValidatorDetails> {
vec![
ValidatorDetails::new(
"https://testnet-milhon-validator1.nymtech.net",
Some("https://testnet-milhon-validator1.nymtech.net/api"),
),
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
]
}
pub const DEFAULT_VALIDATORS: &[ValidatorDetails] = &[
ValidatorDetails::new(
"https://testnet-milhon-validator1.nymtech.net",
Some("https://testnet-milhon-validator1.nymtech.net/api"),
),
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
];
pub fn default_nymd_endpoints() -> Vec<Url> {
default_validators()
DEFAULT_VALIDATORS
.iter()
.map(|validator| validator.nymd_url())
.collect()
}
pub fn default_api_endpoints() -> Vec<Url> {
default_validators()
DEFAULT_VALIDATORS
.iter()
.filter_map(|validator| validator.api_url())
.collect()
@@ -90,7 +80,3 @@ pub const DEFAULT_SOCKS5_LISTENING_PORT: u16 = 1080;
pub const DEFAULT_VALIDATOR_API_PORT: u16 = 8080;
pub const VALIDATOR_API_VERSION: &str = "v1";
// REWARDING
pub const DEFAULT_FIRST_EPOCH_START: OffsetDateTime = time::macros::datetime!(2021-08-23 12:00 UTC);
pub const DEFAULT_EPOCH_LENGTH: Duration = Duration::from_secs(24 * 60 * 60); // 24h
@@ -3,7 +3,7 @@
use crate::AckKey;
use crypto::generic_array::typenum::Unsigned;
use crypto::symmetric::stream_cipher::{self, encrypt, iv_from_slice, random_iv, NewCipher};
use crypto::symmetric::stream_cipher::{self, encrypt, iv_from_slice, random_iv, NewStreamCipher};
use nymsphinx_params::{
packet_sizes::PacketSize, AckEncryptionAlgorithm, SerializedFragmentIdentifier, FRAG_ID_LEN,
};
@@ -33,7 +33,7 @@ pub fn recover_identifier(
return None;
}
let iv_size = <AckEncryptionAlgorithm as NewCipher>::NonceSize::to_usize();
let iv_size = <AckEncryptionAlgorithm as NewStreamCipher>::NonceSize::to_usize();
let iv = iv_from_slice::<AckEncryptionAlgorithm>(&iv_id_ciphertext[..iv_size]);
let id = stream_cipher::decrypt::<AckEncryptionAlgorithm>(
+4 -4
View File
@@ -2,13 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
use crypto::generic_array::{typenum::Unsigned, GenericArray};
use crypto::symmetric::stream_cipher::{generate_key, CipherKey, NewCipher};
use crypto::symmetric::stream_cipher::{generate_key, Key, NewStreamCipher};
use nymsphinx_params::AckEncryptionAlgorithm;
use pemstore::traits::PemStorableKey;
use rand::{CryptoRng, RngCore};
use std::fmt::{self, Display, Formatter};
pub struct AckKey(CipherKey<AckEncryptionAlgorithm>);
pub struct AckKey(Key<AckEncryptionAlgorithm>);
#[derive(Debug)]
pub enum AckKeyConversionError {
@@ -33,7 +33,7 @@ impl AckKey {
}
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, AckKeyConversionError> {
if bytes.len() != <AckEncryptionAlgorithm as NewCipher>::KeySize::to_usize() {
if bytes.len() != <AckEncryptionAlgorithm as NewStreamCipher>::KeySize::to_usize() {
return Err(AckKeyConversionError::BytesOfInvalidLengthError);
}
@@ -48,7 +48,7 @@ impl AckKey {
self.0.as_ref()
}
pub fn inner(&self) -> &CipherKey<AckEncryptionAlgorithm> {
pub fn inner(&self) -> &Key<AckEncryptionAlgorithm> {
&self.0
}
}
@@ -5,7 +5,7 @@ pub use crypto::generic_array::typenum::Unsigned;
use crypto::{
crypto_hash,
generic_array::GenericArray,
symmetric::stream_cipher::{generate_key, CipherKey, NewCipher},
symmetric::stream_cipher::{generate_key, Key, NewStreamCipher},
Digest,
};
use nymsphinx_params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorithm};
@@ -15,10 +15,10 @@ use std::fmt::{self, Display, Formatter};
pub type EncryptionKeyDigest =
GenericArray<u8, <ReplySurbKeyDigestAlgorithm as Digest>::OutputSize>;
pub type SurbEncryptionKeySize = <ReplySurbEncryptionAlgorithm as NewCipher>::KeySize;
pub type SurbEncryptionKeySize = <ReplySurbEncryptionAlgorithm as NewStreamCipher>::KeySize;
#[derive(Clone, Debug)]
pub struct SurbEncryptionKey(CipherKey<ReplySurbEncryptionAlgorithm>);
pub struct SurbEncryptionKey(Key<ReplySurbEncryptionAlgorithm>);
#[derive(Debug)]
pub enum SurbEncryptionKeyError {
@@ -64,7 +64,7 @@ impl SurbEncryptionKey {
self.0.as_ref()
}
pub fn inner(&self) -> &CipherKey<ReplySurbEncryptionAlgorithm> {
pub fn inner(&self) -> &Key<ReplySurbEncryptionAlgorithm> {
&self.0
}
}
@@ -196,7 +196,7 @@ impl ReplySurb {
message: &[u8],
packet_size: Option<PacketSize>,
) -> Result<(SphinxPacket, NymNodeRoutingAddress), ReplySurbError> {
let packet_size = packet_size.unwrap_or_default();
let packet_size = packet_size.unwrap_or_else(Default::default);
if message.len() != packet_size.plaintext_size() {
return Err(ReplySurbError::UnpaddedMessageError);
+1 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::aes::Aes128Ctr;
use crypto::aes_ctr::Aes128Ctr;
use crypto::blake3;
// Re-export for ease of use
+9 -616
View File
@@ -2,45 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ast_node"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93f52ce8fac3d0e6720a92b0576d737c01b1b5db4dd786e962e5925f00bf755"
dependencies = [
"darling",
"pmutil",
"proc-macro2",
"quote",
"swc_macros_common",
"syn",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
version = "0.13.0"
@@ -77,12 +38,6 @@ dependencies = [
"byte-tools",
]
[[package]]
name = "bumpalo"
version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
[[package]]
name = "byte-tools"
version = "0.3.1"
@@ -95,12 +50,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -229,41 +178,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "darling"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "der"
version = "0.4.0"
@@ -291,45 +205,6 @@ dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "dprint-core"
version = "0.35.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93bd44f40b1881477837edc7112695d4b174f058c36c1cbc4c50f8d0482e2ac8"
dependencies = [
"bumpalo",
"fnv",
"serde",
]
[[package]]
name = "dprint-plugin-typescript"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ba0077bd2ab9235848e793fbbfb563e6a04b4c8e4149827802a84063c15805"
dependencies = [
"dprint-core",
"dprint-swc-ecma-ast-view",
"fnv",
"serde",
"swc_common",
"swc_ecmascript",
]
[[package]]
name = "dprint-swc-ecma-ast-view"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecf692a2ee5c5f699ed0e95f21686cf6367f3a591e5d8e7bd3041bbf184651f9"
dependencies = [
"bumpalo",
"fnv",
"num-bigint",
"swc_atoms",
"swc_common",
"swc_ecmascript",
]
[[package]]
name = "dyn-clone"
version = "1.0.4"
@@ -362,12 +237,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "elliptic-curve"
version = "0.10.4"
@@ -384,18 +253,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "enum_kind"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b940da354ae81ef0926c5eaa428207b8f4f091d3956c891dfbd124162bed99"
dependencies = [
"pmutil",
"proc-macro2",
"swc_macros_common",
"syn",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
@@ -412,12 +269,6 @@ dependencies = [
"subtle",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
@@ -428,27 +279,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "from_variant"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0951635027ca477be98f8774abd6f0345233439d63f307e47101acb40c7cc63d"
dependencies = [
"pmutil",
"proc-macro2",
"swc_macros_common",
"syn",
]
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]]
name = "generic-array"
version = "0.12.4"
@@ -474,7 +304,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@@ -485,7 +315,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
]
@@ -547,12 +377,6 @@ dependencies = [
"serde",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.2.3"
@@ -564,19 +388,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "is-macro"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a322dd16d960e322c3d92f541b4c1a4f0a2e81e1fdeee430d8cecc8b72e8015f"
dependencies = [
"Inflector",
"pmutil",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "itoa"
version = "0.4.7"
@@ -589,23 +400,17 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b0281ca8032567c9711cd48631781c15228301860a39b32deb28d63125e46"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"ecdsa",
"elliptic-curve",
"sha2",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.100"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
[[package]]
name = "log"
@@ -613,7 +418,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@@ -628,12 +433,6 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "mixnet-contract"
version = "0.1.0"
@@ -642,7 +441,6 @@ dependencies = [
"schemars",
"serde",
"serde_repr",
"ts-rs",
]
[[package]]
@@ -663,54 +461,9 @@ dependencies = [
name = "network-defaults"
version = "0.1.0"
dependencies = [
"serde",
"time",
"url",
]
[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "num-bigint"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
"serde",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "opaque-debug"
version = "0.2.3"
@@ -723,15 +476,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -781,25 +525,6 @@ dependencies = [
"sha-1",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "pkcs8"
version = "0.7.5"
@@ -810,29 +535,6 @@ dependencies = [
"spki",
]
[[package]]
name = "pmutil"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "precomputed-hash"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro2"
version = "1.0.24"
@@ -857,30 +559,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha",
"rand_core 0.5.1",
"rand_hc",
"rand_pcg",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
name = "rand_core"
version = "0.5.1"
@@ -899,41 +577,6 @@ dependencies = [
"getrandom 0.2.3",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ryu"
version = "1.0.5"
@@ -964,12 +607,6 @@ dependencies = [
"syn",
]
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "serde"
version = "1.0.122"
@@ -1051,7 +688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
dependencies = [
"block-buffer 0.9.0",
"cfg-if 1.0.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug 0.3.0",
@@ -1067,18 +704,6 @@ dependencies = [
"rand_core 0.6.3",
]
[[package]]
name = "siphasher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "spki"
version = "0.4.0"
@@ -1088,211 +713,23 @@ dependencies = [
"der",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "string_cache"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"phf_shared",
"precomputed-hash",
"serde",
]
[[package]]
name = "string_cache_codegen"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
]
[[package]]
name = "string_enum"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f584cc881e9e5f1fd6bf827b0444aa94c30d8fe6378cf241071b5f5700b2871f"
dependencies = [
"pmutil",
"proc-macro2",
"quote",
"swc_macros_common",
"syn",
]
[[package]]
name = "strsim"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "subtle"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
[[package]]
name = "swc_atoms"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "837a3ef86c2817228e733b6f173c821fd76f9eb21a0bc9001a826be48b00b4e7"
dependencies = [
"string_cache",
"string_cache_codegen",
]
[[package]]
name = "swc_common"
version = "0.10.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c93df65683ec1a001e15ce1de438c7c2c226c0c2462d1cb93fa1bd2a7664170b"
dependencies = [
"ast_node",
"cfg-if 0.1.10",
"either",
"from_variant",
"fxhash",
"log",
"num-bigint",
"once_cell",
"owning_ref",
"scoped-tls",
"serde",
"string_cache",
"swc_eq_ignore_macros",
"swc_visit",
"unicode-width",
]
[[package]]
name = "swc_ecma_ast"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83eb6a73820660a5af3c24ae1d436e84e4d4c13822021140011361e678df247b"
dependencies = [
"is-macro",
"num-bigint",
"serde",
"string_enum",
"swc_atoms",
"swc_common",
]
[[package]]
name = "swc_ecma_parser"
version = "0.52.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03250697857164f16fa98f8e1726f566652d13e52ea3f0c3ecea9deb63ee327"
dependencies = [
"either",
"enum_kind",
"fxhash",
"log",
"num-bigint",
"serde",
"smallvec",
"swc_atoms",
"swc_common",
"swc_ecma_ast",
"swc_ecma_visit",
"unicode-xid",
]
[[package]]
name = "swc_ecma_visit"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3d60b9dc97ae4f181d4d60f43142d8ac9669953db410bcedefb29a14627e19"
dependencies = [
"num-bigint",
"swc_atoms",
"swc_common",
"swc_ecma_ast",
"swc_visit",
]
[[package]]
name = "swc_ecmascript"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ffb53afe008c15d4dc4957e80148c4b457659f93e4d4e8736eaeae352e48ec8"
dependencies = [
"swc_ecma_ast",
"swc_ecma_parser",
]
[[package]]
name = "swc_eq_ignore_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c8f200a2eaed938e7c1a685faaa66e6d42fa9e17da5f62572d3cbc335898f5e"
dependencies = [
"pmutil",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "swc_macros_common"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08ed2e930f5a1a4071fe62c90fd3a296f6030e5d94bfe13993244423caf59a78"
dependencies = [
"pmutil",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "swc_visit"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a423caa0b4585118164dbad8f1ad52b592a9a9370b25decc4d84c6b4309132c0"
dependencies = [
"either",
"swc_visit_macros",
]
[[package]]
name = "swc_visit_macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b2825fee79f10d0166e8e650e79c7a862fb991db275743083f07555d7641f0"
dependencies = [
"Inflector",
"pmutil",
"proc-macro2",
"quote",
"swc_macros_common",
"syn",
]
[[package]]
name = "syn"
version = "1.0.65"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
dependencies = [
"proc-macro2",
"quote",
@@ -1319,22 +756,6 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a776787d9c5d455bec3db044586ccdd8a9c74d5da5dc319fb80f3db08808fe6"
dependencies = [
"libc",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04a153416002296880a3b51329a0e3df31c779c53ec827993e865ce427982843"
[[package]]
name = "tinyvec"
version = "1.3.1"
@@ -1359,28 +780,6 @@ dependencies = [
"serde",
]
[[package]]
name = "ts-rs"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "369e48de67506679b3a576b0faf666fa9f9acf2fd00b4c61e28bdb6c8e08ec06"
dependencies = [
"dprint-plugin-typescript",
"ts-rs-macros",
]
[[package]]
name = "ts-rs-macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f269e8fd28e26b4cdbd01f81f345aaf666131511e54a735a76a614b5062d0a5a"
dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.13.0"
@@ -1423,12 +822,6 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.1"
-349
View File
@@ -1,349 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExecuteMsg",
"anyOf": [
{
"type": "object",
"required": [
"bond_mixnode"
],
"properties": {
"bond_mixnode": {
"type": "object",
"required": [
"mix_node"
],
"properties": {
"mix_node": {
"$ref": "#/definitions/MixNode"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"unbond_mixnode"
],
"properties": {
"unbond_mixnode": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"bond_gateway"
],
"properties": {
"bond_gateway": {
"type": "object",
"required": [
"gateway"
],
"properties": {
"gateway": {
"$ref": "#/definitions/Gateway"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"unbond_gateway"
],
"properties": {
"unbond_gateway": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"update_state_params"
],
"properties": {
"update_state_params": {
"$ref": "#/definitions/StateParams"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"delegate_to_mixnode"
],
"properties": {
"delegate_to_mixnode": {
"type": "object",
"required": [
"mix_identity"
],
"properties": {
"mix_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"undelegate_from_mixnode"
],
"properties": {
"undelegate_from_mixnode": {
"type": "object",
"required": [
"mix_identity"
],
"properties": {
"mix_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"delegate_to_gateway"
],
"properties": {
"delegate_to_gateway": {
"type": "object",
"required": [
"gateway_identity"
],
"properties": {
"gateway_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"undelegate_from_gateway"
],
"properties": {
"undelegate_from_gateway": {
"type": "object",
"required": [
"gateway_identity"
],
"properties": {
"gateway_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"reward_mixnode"
],
"properties": {
"reward_mixnode": {
"type": "object",
"required": [
"identity",
"uptime"
],
"properties": {
"identity": {
"type": "string"
},
"uptime": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"reward_gateway"
],
"properties": {
"reward_gateway": {
"type": "object",
"required": [
"identity",
"uptime"
],
"properties": {
"identity": {
"type": "string"
},
"uptime": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
}
},
"additionalProperties": false
}
],
"definitions": {
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Gateway": {
"type": "object",
"required": [
"clients_port",
"host",
"identity_key",
"location",
"mix_port",
"sphinx_key",
"version"
],
"properties": {
"clients_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"host": {
"type": "string"
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients",
"type": "string"
},
"location": {
"type": "string"
},
"mix_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"sphinx_key": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"MixNode": {
"type": "object",
"required": [
"host",
"http_api_port",
"identity_key",
"mix_port",
"sphinx_key",
"verloc_port",
"version"
],
"properties": {
"host": {
"type": "string"
},
"http_api_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key.",
"type": "string"
},
"mix_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"sphinx_key": {
"type": "string"
},
"verloc_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"version": {
"type": "string"
}
}
},
"StateParams": {
"type": "object",
"required": [
"epoch_length",
"gateway_bond_reward_rate",
"gateway_delegation_reward_rate",
"minimum_gateway_bond",
"minimum_mixnode_bond",
"mixnode_active_set_size",
"mixnode_bond_reward_rate",
"mixnode_delegation_reward_rate"
],
"properties": {
"epoch_length": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"gateway_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"gateway_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"minimum_gateway_bond": {
"$ref": "#/definitions/Uint128"
},
"minimum_mixnode_bond": {
"$ref": "#/definitions/Uint128"
},
"mixnode_active_set_size": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"mixnode_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"mixnode_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
+134
View File
@@ -0,0 +1,134 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "HandleMsg",
"anyOf": [
{
"type": "object",
"required": [
"register_mixnode"
],
"properties": {
"register_mixnode": {
"type": "object",
"required": [
"mix_node"
],
"properties": {
"mix_node": {
"$ref": "#/definitions/MixNode"
}
}
}
}
},
{
"type": "object",
"required": [
"un_register_mixnode"
],
"properties": {
"un_register_mixnode": {
"type": "object"
}
}
},
{
"type": "object",
"required": [
"bond_gateway"
],
"properties": {
"bond_gateway": {
"type": "object",
"required": [
"gateway"
],
"properties": {
"gateway": {
"$ref": "#/definitions/Gateway"
}
}
}
}
},
{
"type": "object",
"required": [
"unbond_gateway"
],
"properties": {
"unbond_gateway": {
"type": "object"
}
}
}
],
"definitions": {
"Gateway": {
"type": "object",
"required": [
"clients_host",
"identity_key",
"location",
"mix_host",
"sphinx_key",
"version"
],
"properties": {
"clients_host": {
"type": "string"
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients",
"type": "string"
},
"location": {
"type": "string"
},
"mix_host": {
"type": "string"
},
"sphinx_key": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"MixNode": {
"type": "object",
"required": [
"host",
"identity_key",
"layer",
"location",
"sphinx_key",
"version"
],
"properties": {
"host": {
"type": "string"
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key.",
"type": "string"
},
"layer": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"location": {
"type": "string"
},
"sphinx_key": {
"type": "string"
},
"version": {
"type": "string"
}
}
}
}
}
@@ -1,5 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InstantiateMsg",
"title": "InitMsg",
"type": "object"
}
+17 -47
View File
@@ -3,40 +3,25 @@
"title": "MixNodeBond",
"type": "object",
"required": [
"bond_amount",
"layer",
"amount",
"mix_node",
"owner",
"total_delegation"
"owner"
],
"properties": {
"block_height": {
"default": 0,
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"bond_amount": {
"$ref": "#/definitions/Coin"
},
"layer": {
"$ref": "#/definitions/Layer"
"amount": {
"type": "array",
"items": {
"$ref": "#/definitions/Coin"
}
},
"mix_node": {
"$ref": "#/definitions/MixNode"
},
"owner": {
"$ref": "#/definitions/Addr"
},
"total_delegation": {
"$ref": "#/definitions/Coin"
"$ref": "#/definitions/HumanAddr"
}
},
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
@@ -52,59 +37,44 @@
}
}
},
"Layer": {
"type": "string",
"enum": [
"Gateway",
"One",
"Two",
"Three"
]
"HumanAddr": {
"type": "string"
},
"MixNode": {
"type": "object",
"required": [
"host",
"http_api_port",
"identity_key",
"mix_port",
"layer",
"location",
"sphinx_key",
"verloc_port",
"version"
],
"properties": {
"host": {
"type": "string"
},
"http_api_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key.",
"type": "string"
},
"mix_port": {
"layer": {
"type": "integer",
"format": "uint16",
"format": "uint64",
"minimum": 0.0
},
"location": {
"type": "string"
},
"sphinx_key": {
"type": "string"
},
"verloc_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"version": {
"type": "string"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
+11 -262
View File
@@ -20,15 +20,18 @@
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/HumanAddr"
},
{
"type": "null"
}
]
}
}
}
},
"additionalProperties": false
}
},
{
"type": "object",
@@ -47,96 +50,10 @@
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"owns_mixnode"
],
"properties": {
"owns_mixnode": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"owns_gateway"
],
"properties": {
"owns_gateway": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"state_params"
],
"properties": {
"state_params": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_mix_delegations"
],
"properties": {
"get_mix_delegations": {
"type": "object",
"required": [
"mix_identity"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"mix_identity": {
"type": "string"
},
"start_after": {
"anyOf": [
{
"$ref": "#/definitions/Addr"
"$ref": "#/definitions/HumanAddr"
},
{
"type": "null"
@@ -145,179 +62,11 @@
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_reverse_mix_delegations"
],
"properties": {
"get_reverse_mix_delegations": {
"type": "object",
"required": [
"delegation_owner"
],
"properties": {
"delegation_owner": {
"$ref": "#/definitions/Addr"
},
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_mix_delegation"
],
"properties": {
"get_mix_delegation": {
"type": "object",
"required": [
"address",
"mix_identity"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
},
"mix_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_gateway_delegations"
],
"properties": {
"get_gateway_delegations": {
"type": "object",
"required": [
"gateway_identity"
],
"properties": {
"gateway_identity": {
"type": "string"
},
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"anyOf": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "null"
}
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_reverse_gateway_delegations"
],
"properties": {
"get_reverse_gateway_delegations": {
"type": "object",
"required": [
"delegation_owner"
],
"properties": {
"delegation_owner": {
"$ref": "#/definitions/Addr"
},
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_gateway_delegation"
],
"properties": {
"get_gateway_delegation": {
"type": "object",
"required": [
"address",
"gateway_identity"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
},
"gateway_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"layer_distribution"
],
"properties": {
"layer_distribution": {
"type": "object"
}
},
"additionalProperties": false
}
}
],
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"HumanAddr": {
"type": "string"
}
}
+3 -79
View File
@@ -3,91 +3,15 @@
"title": "State",
"type": "object",
"required": [
"gateway_epoch_bond_reward",
"gateway_epoch_delegation_reward",
"mixnode_epoch_bond_reward",
"mixnode_epoch_delegation_reward",
"network_monitor_address",
"owner",
"params"
"owner"
],
"properties": {
"gateway_epoch_bond_reward": {
"$ref": "#/definitions/Decimal"
},
"gateway_epoch_delegation_reward": {
"$ref": "#/definitions/Decimal"
},
"mixnode_epoch_bond_reward": {
"$ref": "#/definitions/Decimal"
},
"mixnode_epoch_delegation_reward": {
"$ref": "#/definitions/Decimal"
},
"network_monitor_address": {
"$ref": "#/definitions/Addr"
},
"owner": {
"$ref": "#/definitions/Addr"
},
"params": {
"$ref": "#/definitions/StateParams"
"$ref": "#/definitions/HumanAddr"
}
},
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"StateParams": {
"type": "object",
"required": [
"epoch_length",
"gateway_bond_reward_rate",
"gateway_delegation_reward_rate",
"minimum_gateway_bond",
"minimum_mixnode_bond",
"mixnode_active_set_size",
"mixnode_bond_reward_rate",
"mixnode_delegation_reward_rate"
],
"properties": {
"epoch_length": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"gateway_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"gateway_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"minimum_gateway_bond": {
"$ref": "#/definitions/Uint128"
},
"minimum_mixnode_bond": {
"$ref": "#/definitions/Uint128"
},
"mixnode_active_set_size": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"mixnode_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"mixnode_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"HumanAddr": {
"type": "string"
}
}
+12 -51
View File
@@ -10,9 +10,7 @@ use cosmwasm_std::{
entry_point, to_binary, Addr, Decimal, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Uint128,
};
use mixnet_contract::{
ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, RawDelegationData, StateParams,
};
use mixnet_contract::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, StateParams};
pub const INITIAL_DEFAULT_EPOCH_LENGTH: u32 = 2;
@@ -207,68 +205,31 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
}
#[entry_point]
pub fn migrate(mut deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
use crate::storage::{
gateway_delegations, gateway_delegations_read, gateway_delegations_read_old, gateways_read,
mix_delegations, mix_delegations_read, mix_delegations_read_old, mixnodes_read,
};
use crate::transactions::delegations;
pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
use crate::storage::{gateways, gateways_read, mixnodes, mixnodes_read};
use cosmwasm_std::{Order, StdResult};
use mixnet_contract::CURRENT_BLOCK_HEIGHT;
use mixnet_contract::{GatewayBond, MixNodeBond};
use std::sync::atomic::Ordering;
// Read existing delegations data, drop invalid values, and rewrite delegations data with valid data only
fn overwrite_mixnode_delegations_data(
identity: &str,
deps: &mut DepsMut,
) -> Result<(), ContractError> {
let delegations_bucket = mix_delegations_read(deps.storage, identity);
let old_delegations_bucket = mix_delegations_read_old(deps.storage, identity);
let mut delegations_vec = delegations(delegations_bucket)?;
let old_delegations = delegations::<Uint128>(old_delegations_bucket)?;
for delegation in old_delegations {
delegations_vec.push((delegation.0, RawDelegationData::new(delegation.1, 1)))
}
CURRENT_BLOCK_HEIGHT.store(env.block.height, Ordering::Relaxed);
for (key, delegation) in delegations_vec {
mix_delegations(deps.storage, identity).save(&key, &delegation)?;
}
Ok(())
}
fn overwrite_gateway_delegations_data(
identity: &str,
deps: &mut DepsMut,
) -> Result<(), ContractError> {
let delegations_bucket = gateway_delegations_read(deps.storage, identity);
let old_delegations_bucket = gateway_delegations_read_old(deps.storage, identity);
let mut delegations_vec = delegations(delegations_bucket)?;
let old_delegations = delegations::<Uint128>(old_delegations_bucket)?;
for delegation in old_delegations {
delegations_vec.push((delegation.0, RawDelegationData::new(delegation.1, 1)))
}
for (key, delegation) in delegations_vec {
gateway_delegations(deps.storage, identity).save(&key, &delegation)?;
}
Ok(())
}
let mixnet_bonds = mixnodes_read(deps.storage)
let bonds = mixnodes_read(deps.storage)
.range(None, None, Order::Ascending)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<MixNodeBond>>>()?;
for bond in mixnet_bonds {
overwrite_mixnode_delegations_data(bond.identity(), &mut deps)?;
for bond in bonds {
mixnodes(deps.storage).save(bond.identity().as_bytes(), &bond)?;
}
let gateway_bonds = gateways_read(deps.storage)
let bonds = gateways_read(deps.storage)
.range(None, None, Order::Ascending)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<GatewayBond>>>()?;
for bond in gateway_bonds {
overwrite_gateway_delegations_data(bond.identity(), &mut deps)?;
for bond in bonds {
gateways(deps.storage).save(bond.identity().as_bytes(), &bond)?;
}
Ok(Default::default())
-20
View File
@@ -36,8 +36,6 @@ const PREFIX_REVERSE_GATEWAY_DELEGATION: &[u8] = b"dg";
// Contract-level stuff
// TODO Unify bucket and mixnode storage functions
pub fn config(storage: &mut dyn Storage) -> Singleton<State> {
singleton(storage, CONFIG_KEY)
}
@@ -318,14 +316,6 @@ pub fn mix_delegations_read<'a>(
ReadonlyBucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_identity.as_bytes()])
}
// https://github.com/nymtech/nym/blob/122f5d9f2e5c1ced96e3b9ba0c74ef8b7dbde2c7/contracts/mixnet/src/storage.rs
pub fn mix_delegations_read_old<'a>(
storage: &'a dyn Storage,
mix_identity: IdentityKeyRef,
) -> ReadonlyBucket<'a, Uint128> {
ReadonlyBucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_identity.as_bytes()])
}
pub fn reverse_mix_delegations<'a>(storage: &'a mut dyn Storage, owner: &Addr) -> Bucket<'a, ()> {
Bucket::multilevel(storage, &[PREFIX_REVERSE_MIX_DELEGATION, owner.as_bytes()])
}
@@ -357,16 +347,6 @@ pub fn gateway_delegations_read<'a>(
)
}
pub fn gateway_delegations_read_old<'a>(
storage: &'a dyn Storage,
gateway_identity: IdentityKeyRef,
) -> ReadonlyBucket<'a, Uint128> {
ReadonlyBucket::multilevel(
storage,
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
)
}
pub fn reverse_gateway_delegations<'a>(
storage: &'a mut dyn Storage,
owner: &Addr,
+19 -49
View File
@@ -14,8 +14,6 @@ use cosmwasm_storage::ReadonlyBucket;
use mixnet_contract::{
Gateway, GatewayBond, IdentityKey, Layer, MixNode, MixNodeBond, RawDelegationData, StateParams,
};
use serde::de::DeserializeOwned;
use serde::Serialize;
const OLD_DELEGATIONS_CHUNK_SIZE: usize = 500;
pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280;
@@ -27,15 +25,12 @@ pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280;
// 3. The node unbonds
// 4. Some of the addresses that delegated in the past have not removed the delegation yet
// 5. The node rebonds with the same identity
pub fn delegations<T: DeserializeOwned + Serialize>(
delegations_bucket: ReadonlyBucket<T>,
) -> StdResult<Vec<(Vec<u8>, T)>> {
fn find_old_delegations(delegations_bucket: ReadonlyBucket<RawDelegationData>) -> StdResult<Coin> {
// I think it's incredibly unlikely to ever read more than that
// but in case we do, we should guard ourselves against possible
// out of memory errors (wasm contracts can only allocate at most 2MB
// of RAM, so we don't want to box the entire iterator)
let mut delegations = Vec::new();
// let mut total_delegation = Coin::new(0, DENOM);
let mut total_delegation = Coin::new(0, DENOM);
let mut start = None;
loop {
let iterator = delegations_bucket
@@ -44,23 +39,16 @@ pub fn delegations<T: DeserializeOwned + Serialize>(
let mut iterated = 0;
for result_tuple in iterator {
for delegation in iterator {
iterated += 1;
match result_tuple {
Ok((position, delegation)) => {
// We might skip some values due to deserializatio errors
if iterated > OLD_DELEGATIONS_CHUNK_SIZE {
// we reached start of next chunk, don't process it, mark it for the next iteration of the loop
start = Some(position);
continue;
}
delegations.push((position, delegation))
}
Err(_e) => {
// Skip errors
continue;
}
if iterated == OLD_DELEGATIONS_CHUNK_SIZE + 1 {
// we reached start of next chunk, don't process it, mark it for the next iteration of the loop
start = Some(delegation?.0);
continue;
}
let value = delegation?.1.amount;
total_delegation.amount += value;
}
if iterated <= OLD_DELEGATIONS_CHUNK_SIZE {
@@ -69,17 +57,7 @@ pub fn delegations<T: DeserializeOwned + Serialize>(
}
}
Ok(delegations)
}
fn total_delegations(delegations_bucket: ReadonlyBucket<RawDelegationData>) -> StdResult<Coin> {
match delegations(delegations_bucket) {
Ok(delegations) => Ok(Coin::new(
delegations.iter().fold(0, |acc, x| acc + x.1.amount.u128()),
"upunk",
)),
Err(e) => Err(e),
}
Ok(total_delegation)
}
fn validate_mixnode_bond(bond: &[Coin], minimum_bond: Uint128) -> Result<(), ContractError> {
@@ -160,7 +138,7 @@ pub(crate) fn try_add_mixnode(
// this might potentially require more gas if a significant number of delegations was there
let delegations_bucket = mix_delegations_read(deps.storage, &bond.mix_node.identity_key);
let existing_delegation = total_delegations(delegations_bucket)?;
let existing_delegation = find_old_delegations(delegations_bucket)?;
bond.total_delegation = existing_delegation;
let identity = bond.identity();
@@ -292,7 +270,7 @@ pub(crate) fn try_add_gateway(
// this might potentially require more gas if a significant number of delegations was there
let delegations_bucket = gateway_delegations_read(deps.storage, &bond.gateway.identity_key);
let existing_delegation = total_delegations(delegations_bucket)?;
let existing_delegation = find_old_delegations(delegations_bucket)?;
bond.total_delegation = existing_delegation;
let identity = bond.identity();
@@ -425,10 +403,6 @@ pub(crate) fn try_update_state_params(
Ok(Response::default())
}
// Note: if any changes are made to this function or anything it is calling down the stack,
// for example delegation reward distribution, the gas limits must be retested and both
// validator-api/src/rewarding/mod.rs::{MIXNODE_REWARD_OP_BASE_GAS_LIMIT, PER_MIXNODE_DELEGATION_GAS_INCREASE}
// must be updated appropriately.
pub(crate) fn try_reward_mixnode(
deps: DepsMut,
env: Env,
@@ -500,10 +474,6 @@ pub(crate) fn try_reward_mixnode(
})
}
// Note: if any changes are made to this function or anything it is calling down the stack,
// for example delegation reward distribution, the gas limits must be retested and both
// validator-api/src/rewarding/mod.rs::{GATEWAY_REWARD_OP_BASE_GAS_LIMIT, PER_GATEWAY_DELEGATION_GAS_INCREASE}
// must be updated appropriately.
pub(crate) fn try_reward_gateway(
deps: DepsMut,
env: Env,
@@ -532,7 +502,7 @@ pub(crate) fn try_reward_gateway(
}
// check if the bond even exists
let mut current_bond = match gateways_read(deps.storage).load(gateway_identity.as_bytes()) {
let mut current_bond = match gateways(deps.storage).load(gateway_identity.as_bytes()) {
Ok(bond) => bond,
Err(_) => {
return Ok(Response {
@@ -3958,7 +3928,7 @@ pub mod tests {
let node_identity: IdentityKey = "nodeidentity".into();
let read_bucket = mix_delegations_read(&deps.storage, &node_identity);
let old_delegations = total_delegations(read_bucket).unwrap();
let old_delegations = find_old_delegations(read_bucket).unwrap();
assert_eq!(Coin::new(0, DENOM), old_delegations);
}
@@ -3975,14 +3945,14 @@ pub mod tests {
OLD_DELEGATIONS_CHUNK_SIZE * 3 + 1,
];
for delegations in num_delegations {
for total_delegations in num_delegations {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "nodeidentity".into();
// delegate some stake
let mut write_bucket = mix_delegations(&mut deps.storage, &node_identity);
for i in 1..=delegations {
for i in 1..=total_delegations {
let delegator = Addr::unchecked(format!("delegator{}", i));
let delegation = raw_delegation_fixture(i as u128);
write_bucket
@@ -3991,9 +3961,9 @@ pub mod tests {
}
let read_bucket = mix_delegations_read(&deps.storage, &node_identity);
let old_delegations = total_delegations(read_bucket).unwrap();
let old_delegations = find_old_delegations(read_bucket).unwrap();
let total_delegation = (1..=delegations as u128).into_iter().sum();
let total_delegation = (1..=total_delegations as u128).into_iter().sum();
assert_eq!(Coin::new(total_delegation, DENOM), old_delegations);
}
}
+1
View File
@@ -20,6 +20,7 @@ okapi = { version = "0.6.0-alpha-1", features = ["derive_json_schema"] }
rocket_okapi = "0.7.0-alpha-1"
log = "0.4.0"
pretty_env_logger = "0.4.0"
handlebars = "4.1.3"
mixnet-contract = { path = "../common/mixnet-contract" }
network-defaults = { path = "../common/network-defaults" }
+15 -11
View File
@@ -4,6 +4,7 @@ use reqwest::Error as ReqwestError;
use crate::country_statistics::country_nodes_distribution::CountryNodesDistribution;
use crate::mix_nodes::{GeoLocation, Location};
use crate::state::ExplorerApiStateContext;
use std::env;
pub mod country_nodes_distribution;
pub mod http;
@@ -18,18 +19,21 @@ impl CountryStatistics {
}
pub(crate) fn start(mut self) {
info!("Spawning task runner...");
tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(std::time::Duration::from_secs(60 * 60));
loop {
// wait for the next interval tick
interval_timer.tick().await;
if env::var("DEV_MODE").is_err() {
info!("Spawning task runner...");
tokio::spawn(async move {
let mut interval_timer =
tokio::time::interval(std::time::Duration::from_secs(60 * 60));
loop {
// wait for the next interval tick
interval_timer.tick().await;
info!("Running task...");
self.calculate_nodes_per_country().await;
info!("Done");
}
});
info!("Running task...");
self.calculate_nodes_per_country().await;
info!("Done");
}
});
}
}
/// Retrieves the current list of mixnodes from the validators and calculates how many nodes are in each country
+4
View File
@@ -7,6 +7,7 @@ use rocket_okapi::swagger_ui::make_swagger_ui;
use crate::country_statistics::http::country_statistics_make_default_routes;
use crate::http::swagger::get_docs;
use crate::mix_node::http::mix_node_make_default_routes;
use crate::mix_node::templates::Templates;
use crate::ping::http::ping_make_default_routes;
use crate::state::ExplorerApiStateContext;
@@ -29,6 +30,8 @@ pub(crate) fn start(state: ExplorerApiStateContext) {
.to_cors()
.unwrap();
let templates = Templates::new();
let config = rocket::config::Config::release_default();
rocket::build()
.configure(config)
@@ -38,6 +41,7 @@ pub(crate) fn start(state: ExplorerApiStateContext) {
.mount("/swagger", make_swagger_ui(&get_docs()))
.register("/", catchers![not_found])
.manage(state)
.manage(templates)
.attach(cors)
.launch()
.await
+40 -3
View File
@@ -1,4 +1,5 @@
use reqwest::Error as ReqwestError;
use rocket::response::content::Html;
use rocket::serde::json::Json;
use rocket::{Route, State};
use serde::Serialize;
@@ -6,11 +7,12 @@ use serde::Serialize;
use mixnet_contract::{Addr, Coin, Layer, MixNode};
use crate::mix_node::models::{NodeDescription, NodeStats};
use crate::mix_node::templates::{PreviewTemplateData, Templates};
use crate::mix_nodes::{get_mixnode_delegations, Location};
use crate::state::ExplorerApiStateContext;
pub fn mix_node_make_default_routes() -> Vec<Route> {
routes_with_openapi![get_delegations, get_description, get_stats, list]
routes_with_openapi![get_delegations, get_description, get_stats, list, preview]
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
@@ -51,6 +53,34 @@ pub(crate) async fn list(
)
}
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/preview")]
pub(crate) async fn preview(
pubkey: &str,
templates: &State<Templates>,
state: &State<ExplorerApiStateContext>,
) -> Html<String> {
match get_mixnode_description(pubkey, state).await {
Some(node_description) => {
// use handlebars to render an HTML output for an OpenGraph / Twitter preview - this is
// used in social media apps / messenger apps that show previews for links
match templates.render_preview(PreviewTemplateData {
title: node_description.name,
description: node_description.description,
url: format!(
"https://testnet-milhon-explorer.nymtech.net/nym/mixnodes/{}",
pubkey
),
image_url: String::from("https://media2.giphy.com/media/pwyW4XDmtqjG8/200.gif?cid=dda24d50ae15e44c38783edc824618df68645c6af2592b28&amp;rid=200.gif&amp;ct=g"),
}) {
Ok(r) => Html(r),
Err(_e) => Html(String::from("Oh no, something went wrong!")),
}
}
None => Html(String::from("Sorry, mix node not found")),
}
}
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/delegations")]
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<mixnet_contract::Delegation>> {
@@ -63,6 +93,13 @@ pub(crate) async fn get_description(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Option<Json<NodeDescription>> {
get_mixnode_description(pubkey, state).await.map(Json)
}
async fn get_mixnode_description(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Option<NodeDescription> {
match state
.inner
.mix_node_cache
@@ -72,7 +109,7 @@ pub(crate) async fn get_description(
{
Some(cache_value) => {
trace!("Returning cached value for {}", pubkey);
Some(Json(cache_value))
Some(cache_value)
}
None => {
trace!("No valid cache value for {}", pubkey);
@@ -91,7 +128,7 @@ pub(crate) async fn get_description(
.mix_node_cache
.set_description(pubkey, response.clone())
.await;
Some(Json(response))
Some(response)
}
Err(e) => {
error!(
+1
View File
@@ -1,3 +1,4 @@
mod cache;
pub(crate) mod http;
pub(crate) mod models;
pub(crate) mod templates;
+73
View File
@@ -0,0 +1,73 @@
use handlebars::{Handlebars, RenderError};
use serde::Serialize;
#[derive(Clone)]
pub(crate) struct Templates {
handlebars: Handlebars<'static>,
}
impl Templates {
pub(crate) fn new() -> Self {
let mut handlebars = Handlebars::new();
assert!(handlebars
.register_template_string("preview", PREVIEW_TEMPLATE)
.is_ok());
Templates { handlebars }
}
pub(crate) fn render_preview(&self, data: PreviewTemplateData) -> Result<String, RenderError> {
self.handlebars.render("preview", &data)
}
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub(crate) struct PreviewTemplateData {
pub(crate) title: String,
pub(crate) description: String,
pub(crate) url: String,
pub(crate) image_url: String,
}
const PREVIEW_TEMPLATE: &str = r#"<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ title }}</title>
<meta name="description" content="{{ description }}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{ url }}">
<meta property="og:title" content="{{ title }}">
<meta property="og:description" content="{{ description }}">
<meta property="og:image" content="{{ image_url }}">
<meta name="twitter:card" value="summary_large_image">
<meta name="twitter:title" value="{{ title }}">
<meta name="twitter:description" value="{{ description }}">
<meta name="twitter:image" value="{{ image_url }}">
<meta name="twitter:site" value="@nymtech">
<meta http-equiv="refresh" content="0;url={{url}}" />
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
.meme {
padding-top: 20px;
}
.meme > img {
max-width: 200px;
max-height: 200px;
}
</style>
</head>
<body>
<h1>{{title}}</h1>
<div>{{description}}<div>
</body>
</html>"#;
+2
View File
@@ -28,6 +28,8 @@ tokio-tungstenite = "0.14"
url = { version = "2.2", features = [ "serde" ] }
# internal
coconut-interface = { path = "../common/coconut-interface" }
credentials = { path = "../common/credentials" }
config = { path = "../common/config" }
crypto = { path = "../common/crypto" }
gateway-requests = { path = "gateway-requests" }
+2 -2
View File
@@ -2,11 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
use crypto::generic_array::{typenum::Unsigned, GenericArray};
use crypto::symmetric::stream_cipher::{random_iv, NewCipher, IV as CryptoIV};
use crypto::symmetric::stream_cipher::{random_iv, NewStreamCipher, IV as CryptoIV};
use nymsphinx::params::GatewayEncryptionAlgorithm;
use rand::{CryptoRng, RngCore};
type NonceSize = <GatewayEncryptionAlgorithm as NewCipher>::NonceSize;
type NonceSize = <GatewayEncryptionAlgorithm as NewStreamCipher>::NonceSize;
// I think 'IV' looks better than 'Iv', feel free to change that.
#[allow(clippy::upper_case_acronyms)]
@@ -7,7 +7,7 @@ use crypto::generic_array::{
GenericArray,
};
use crypto::hmac::{compute_keyed_hmac, recompute_keyed_hmac_and_verify_tag};
use crypto::symmetric::stream_cipher::{self, CipherKey, NewCipher, IV};
use crypto::symmetric::stream_cipher::{self, Key, NewStreamCipher, IV};
use nymsphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm};
use pemstore::traits::PemStorableKey;
use std::fmt::{self, Display, Formatter};
@@ -17,14 +17,14 @@ pub type SharedKeySize = Sum<EncryptionKeySize, MacKeySize>;
// we're using 16 byte long key in sphinx, so let's use the same one here
type MacKeySize = U16;
type EncryptionKeySize = <GatewayEncryptionAlgorithm as NewCipher>::KeySize;
type EncryptionKeySize = <GatewayEncryptionAlgorithm as NewStreamCipher>::KeySize;
/// Shared key used when computing MAC for messages exchanged between client and its gateway.
pub type MacKey = GenericArray<u8, MacKeySize>;
#[derive(Clone, Debug)]
pub struct SharedKeys {
encryption_key: CipherKey<GatewayEncryptionAlgorithm>,
encryption_key: Key<GatewayEncryptionAlgorithm>,
mac_key: MacKey,
}
@@ -138,7 +138,7 @@ impl SharedKeys {
Ok(message_bytes_mut.to_vec())
}
pub fn encryption_key(&self) -> &CipherKey<GatewayEncryptionAlgorithm> {
pub fn encryption_key(&self) -> &Key<GatewayEncryptionAlgorithm> {
&self.encryption_key
}
+33
View File
@@ -5,6 +5,7 @@ use crate::authentication::encrypted_address::EncryptedAddressBytes;
use crate::iv::IV;
use crate::registration::handshake::SharedKeys;
use crate::GatewayMacSize;
use coconut_interface::Credential;
use crypto::generic_array::typenum::Unsigned;
use crypto::hmac::recompute_keyed_hmac_and_verify_tag;
use crypto::symmetric::stream_cipher;
@@ -112,6 +113,10 @@ pub enum ClientControlRequest {
},
#[serde(alias = "handshakePayload")]
RegisterHandshakeInitRequest { data: Vec<u8> },
BandwidthCredential {
enc_credential: Vec<u8>,
iv: Vec<u8>,
},
}
impl ClientControlRequest {
@@ -126,6 +131,34 @@ impl ClientControlRequest {
iv: iv.to_base58_string(),
}
}
pub fn new_enc_bandwidth_credential(
credential: &Credential,
shared_key: &SharedKeys,
iv: IV,
) -> Option<Self> {
match bincode::serialize(credential) {
Ok(serialized_credential) => {
let enc_credential =
shared_key.encrypt_and_tag(&serialized_credential, Some(iv.inner()));
Some(ClientControlRequest::BandwidthCredential {
enc_credential,
iv: iv.to_bytes(),
})
}
_ => None,
}
}
pub fn try_from_enc_bandwidth_credential(
enc_credential: Vec<u8>,
shared_key: &SharedKeys,
iv: IV,
) -> Result<Credential, GatewayRequestsError> {
let credential = shared_key.decrypt_tagged(&enc_credential, Some(iv.inner()))?;
bincode::deserialize(&credential).map_err(|_| GatewayRequestsError::MalformedEncryption)
}
}
impl From<ClientControlRequest> for Message {
@@ -0,0 +1,85 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::Arc;
use tokio::sync::RwLock;
use coconut_interface::Credential;
use credentials::error::Error;
use nymsphinx::DestinationAddressBytes;
const BANDWIDTH_INDEX: usize = 0;
pub type BandwidthDatabase = Arc<RwLock<HashMap<DestinationAddressBytes, u64>>>;
pub fn empty_bandwidth_database() -> BandwidthDatabase {
Arc::new(RwLock::new(HashMap::new()))
}
pub struct Bandwidth {
value: u64,
}
impl Bandwidth {
pub fn value(&self) -> u64 {
self.value
}
pub async fn consume_bandwidth(
bandwidths: &BandwidthDatabase,
remote_address: &DestinationAddressBytes,
consumed: u64,
) -> Result<(), Error> {
if let Some(bandwidth) = bandwidths.write().await.get_mut(remote_address) {
if let Some(res) = bandwidth.checked_sub(consumed) {
*bandwidth = res;
Ok(())
} else {
Err(Error::BandwidthOverflow(String::from(
"Allocate more bandwidth for consumption",
)))
}
} else {
Err(Error::MissingBandwidth)
}
}
pub async fn increase_bandwidth(
bandwidths: &BandwidthDatabase,
remote_address: &DestinationAddressBytes,
increase: u64,
) -> Result<(), Error> {
let mut db = bandwidths.write().await;
if let Some(bandwidth) = db.get_mut(remote_address) {
if let Some(new_bandwidth) = bandwidth.checked_add(increase) {
*bandwidth = new_bandwidth;
} else {
return Err(Error::BandwidthOverflow(String::from(
"Use some of the already allocated bandwidth",
)));
}
} else {
db.insert(*remote_address, increase);
}
Ok(())
}
}
impl TryFrom<Credential> for Bandwidth {
type Error = Error;
fn try_from(credential: Credential) -> Result<Self, Self::Error> {
match credential.public_attributes().get(BANDWIDTH_INDEX) {
None => Err(Error::NotEnoughPublicAttributes),
Some(attr) => match <[u8; 8]>::try_from(attr.as_slice()) {
Ok(bandwidth_bytes) => {
let value = u64::from_be_bytes(bandwidth_bytes);
Ok(Self { value })
}
Err(_) => Err(Error::InvalidBandwidthSize),
},
}
}
}
+1
View File
@@ -1,5 +1,6 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod bandwidth;
pub(crate) mod clients_handler;
pub(crate) mod websocket;
@@ -1,12 +1,14 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::node::client_handling::bandwidth::{Bandwidth, BandwidthDatabase};
use crate::node::client_handling::clients_handler::{
ClientsHandlerRequest, ClientsHandlerRequestSender, ClientsHandlerResponse,
};
use crate::node::client_handling::websocket::message_receiver::{
MixMessageReceiver, MixMessageSender,
};
use coconut_interface::VerificationKey;
use crypto::asymmetric::identity;
use futures::{
channel::{mpsc, oneshot},
@@ -23,6 +25,7 @@ use mixnet_client::forwarder::MixForwardingSender;
use nymsphinx::DestinationAddressBytes;
use rand::{CryptoRng, Rng};
use std::convert::TryFrom;
use std::mem;
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::{
@@ -55,8 +58,10 @@ pub(crate) struct Handle<R, S> {
clients_handler_sender: ClientsHandlerRequestSender,
outbound_mix_sender: MixForwardingSender,
socket_connection: SocketStream<S>,
local_identity: Arc<identity::KeyPair>,
aggregated_verification_key: VerificationKey,
bandwidths: BandwidthDatabase,
}
impl<R, S> Handle<R, S>
@@ -71,6 +76,8 @@ where
clients_handler_sender: ClientsHandlerRequestSender,
outbound_mix_sender: MixForwardingSender,
local_identity: Arc<identity::KeyPair>,
aggregated_verification_key: VerificationKey,
bandwidths: BandwidthDatabase,
) -> Self {
Handle {
rng,
@@ -80,6 +87,8 @@ where
outbound_mix_sender,
socket_connection: SocketStream::RawTcp(conn),
local_identity,
aggregated_verification_key,
bandwidths,
}
}
@@ -200,8 +209,19 @@ where
Ok(request) => match request {
// currently only a single type exists
BinaryRequest::ForwardSphinx(mix_packet) => {
self.outbound_mix_sender.unbounded_send(mix_packet).unwrap();
ServerResponse::Send { status: true }
let consumed_bandwidth = mem::size_of_val(&mix_packet) as u64;
if let Err(e) = Bandwidth::consume_bandwidth(
&self.bandwidths,
&self.remote_address.unwrap(),
consumed_bandwidth,
)
.await
{
ServerResponse::new_error(format!("{:?}", e))
} else {
self.outbound_mix_sender.unbounded_send(mix_packet).unwrap();
ServerResponse::Send { status: true }
}
}
},
}
@@ -337,12 +357,68 @@ where
}
}
// currently there are no valid control messages you can send after authentication
async fn handle_text(&mut self, _: String) -> Message {
trace!("Handling text message (presumably control message)");
async fn handle_bandwidth(&mut self, enc_credential: Vec<u8>, iv: Vec<u8>) -> ServerResponse {
if self.shared_key.is_none() {
return ServerResponse::new_error("No shared key has been exchanged with the gateway");
}
if self.remote_address.is_none() {
return ServerResponse::new_error("No remote address has been set");
}
let iv = match IV::try_from_bytes(&iv) {
Ok(iv) => iv,
Err(e) => {
trace!("failed to parse received IV {:?}", e);
return ServerResponse::new_error("malformed iv");
}
};
let credential = match ClientControlRequest::try_from_enc_bandwidth_credential(
enc_credential,
self.shared_key.as_ref().unwrap(),
iv,
) {
Ok(c) => c,
Err(e) => {
return ServerResponse::new_error(e.to_string());
}
};
if credential.verify(&self.aggregated_verification_key) {
match Bandwidth::try_from(credential) {
Ok(bandwidth) => {
if let Err(e) = Bandwidth::increase_bandwidth(
&self.bandwidths,
&self.remote_address.unwrap(),
bandwidth.value(),
)
.await
{
return ServerResponse::Error {
message: format!("{:?}", e),
};
}
ServerResponse::Bandwidth { status: true }
}
Err(e) => ServerResponse::Error {
message: format!("{:?}", e),
},
}
} else {
ServerResponse::Bandwidth { status: false }
}
}
error!("Currently there are no text messages besides 'Authenticate' and 'Register' and they were already dealt with!");
ServerResponse::new_error("invalid request").into()
// currently the bandwidth credential request is the only one we can receive after
// authentication
async fn handle_text(&mut self, raw_request: String) -> Message {
if let Ok(request) = ClientControlRequest::try_from(raw_request) {
match request {
ClientControlRequest::BandwidthCredential { enc_credential, iv } => {
self.handle_bandwidth(enc_credential, iv).await.into()
}
_ => ServerResponse::new_error("invalid request").into(),
}
} else {
ServerResponse::new_error("malformed request").into()
}
}
async fn handle_request(&mut self, raw_request: Message) -> Option<Message> {
@@ -379,6 +455,7 @@ where
ClientControlRequest::RegisterHandshakeInitRequest { data } => {
self.handle_register(data, mix_sender).await
}
_ => ServerResponse::new_error("invalid request"),
}
} else {
// TODO: is this a malformed request or rather a network error and
@@ -1,8 +1,10 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::node::client_handling::bandwidth::empty_bandwidth_database;
use crate::node::client_handling::clients_handler::ClientsHandlerRequestSender;
use crate::node::client_handling::websocket::connection_handler::Handle;
use coconut_interface::VerificationKey;
use crypto::asymmetric::identity;
use log::*;
use mixnet_client::forwarder::MixForwardingSender;
@@ -15,13 +17,19 @@ use tokio::task::JoinHandle;
pub(crate) struct Listener {
address: SocketAddr,
local_identity: Arc<identity::KeyPair>,
aggregated_verification_key: VerificationKey,
}
impl Listener {
pub(crate) fn new(address: SocketAddr, local_identity: Arc<identity::KeyPair>) -> Self {
pub(crate) fn new(
address: SocketAddr,
local_identity: Arc<identity::KeyPair>,
aggregated_verification_key: VerificationKey,
) -> Self {
Listener {
address,
local_identity,
aggregated_verification_key,
}
}
@@ -39,6 +47,8 @@ impl Listener {
}
};
let bandwidths = empty_bandwidth_database();
loop {
match tcp_listener.accept().await {
Ok((socket, remote_addr)) => {
@@ -51,6 +61,8 @@ impl Listener {
clients_handler_sender.clone(),
outbound_mix_sender.clone(),
Arc::clone(&self.local_identity),
self.aggregated_verification_key.clone(),
Arc::clone(&bandwidths),
);
tokio::spawn(async move { handle.start_handling().await });
}
+12 -3
View File
@@ -6,6 +6,8 @@ use crate::node::client_handling::clients_handler::{ClientsHandler, ClientsHandl
use crate::node::client_handling::websocket;
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
use crate::node::storage::{inboxes, ClientLedger};
use coconut_interface::VerificationKey;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity};
use log::*;
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
@@ -83,6 +85,7 @@ impl Gateway {
&self,
forwarding_channel: MixForwardingSender,
clients_handler_sender: ClientsHandlerRequestSender,
verification_key: VerificationKey,
) {
info!("Starting client [web]socket listener...");
@@ -91,8 +94,12 @@ impl Gateway {
self.config.get_clients_port(),
);
websocket::Listener::new(listening_address, Arc::clone(&self.identity))
.start(clients_handler_sender, forwarding_channel);
websocket::Listener::new(
listening_address,
Arc::clone(&self.identity),
verification_key,
)
.start(clients_handler_sender, forwarding_channel);
}
fn start_packet_forwarder(&self) -> MixForwardingSender {
@@ -176,11 +183,13 @@ impl Gateway {
}
}
let validators_verification_key = obtain_aggregate_verification_key(&self.config.get_validator_api_endpoints()).await.expect("failed to contact validators to obtain their verification keys");
let mix_forwarding_channel = self.start_packet_forwarder();
let clients_handler_sender = self.start_clients_handler();
self.start_mix_socket_listener(clients_handler_sender.clone(), mix_forwarding_channel.clone());
self.start_client_websocket_listener(mix_forwarding_channel, clients_handler_sender);
self.start_client_websocket_listener(mix_forwarding_channel, clients_handler_sender, validators_verification_key);
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
-15
View File
@@ -1,15 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": true
}
}
],
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": ["@babel/plugin-transform-async-to-generator"]
}
-109
View File
@@ -1,109 +0,0 @@
{
"env": {
"browser": true,
"es6": true,
"node": true,
"jest": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2019,
"sourceType": "module"
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"plugins": ["react", "react-hooks", "jsx-a11y", "prettier", "jest"],
"extends": [
"plugin:react/recommended",
"airbnb",
"prettier",
"plugin:jest/recommended",
"plugin:jest/style"
],
"rules": {
"jest/prefer-strict-equal": "error",
"jest/prefer-to-have-length": "warn",
"prettier/prettier": "error",
"import/prefer-default-export": "off",
"react/prop-types": "off",
"react/jsx-filename-extension": "off",
"react/jsx-props-no-spreading": "off",
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.[jt]s",
"**/*.spec.[jt]s",
"**/*.test.[jt]sx",
"**/*.spec.[jt]sx"
]
}
],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"tsx": "never",
"js": "never",
"jsx": "never"
}
]
},
"overrides": [
{
"files": "**/*.+(ts|tsx)",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"no-use-before-define": [0],
"@typescript-eslint/no-use-before-define": [1],
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx"
]
}
],
"quotes": "off",
"@typescript-eslint/quotes": [
2,
"single",
{
"avoidEscape": true
}
],
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
}
}
],
"settings": {
"import/resolver": {
"root-import": {
"rootPathPrefix": "@",
"rootPathSuffix": "src",
"extensions": [".js", ".ts", ".tsx", ".jsx", ".mdx"]
}
}
}
}
-1
View File
@@ -1 +0,0 @@
14
-5213
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,2 +0,0 @@
[workspace]
members = ["src-tauri"]
-19
View File
@@ -1,19 +0,0 @@
<!--
Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
SPDX-License-Identifier: Apache-2.0
-->
# Nym Tauri Wallet
A Rust and Tauri desktop wallet implementation.
## Installation prerequisites
* `Yarn`
* `NodeJS >= v16.8.0`
* `Rust & cargo >= v1.51`
## Installation & usage
* `yarn install`
* `yarn dev`
View File
-52
View File
@@ -1,52 +0,0 @@
{
"name": "tauri-app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"webpack:dev": "yarn webpack serve",
"tauri:dev": "yarn tauri dev",
"dev": "yarn run webpack:dev & yarn run tauri:dev "
},
"dependencies": {
"@babel/preset-typescript": "^7.15.0",
"@hookform/resolvers": "^2.8.0",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@material-ui/styles": "^4.11.4",
"@types/react-dom": "^17.0.9",
"bs58": "^4.0.1",
"clsx": "^1.1.1",
"qrcode.react": "^1.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-error-boundary": "^3.1.3",
"react-hook-form": "^7.14.2",
"react-router-dom": "^5.2.0",
"semver": "^6.3.0",
"yup": "^0.32.9"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-async-to-generator": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@tauri-apps/api": "^1.0.0-beta.6",
"@tauri-apps/cli": "^1.0.0-beta.9",
"@types/bs58": "^4.0.1",
"@types/qrcode.react": "^1.0.2",
"@types/react-router-dom": "^5.1.8",
"@types/semver": "^7.3.8",
"babel-loader": "^8.2.2",
"css-loader": "^6.2.0",
"favicons-webpack-plugin": "^5.0.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.2",
"style-loader": "^3.2.1",
"url-loader": "^4.1.1",
"webpack": "^5.50.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.1.0"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

-9
View File
@@ -1,9 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Nym Wallet</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
-4
View File
@@ -1,4 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
-44
View File
@@ -1,44 +0,0 @@
[package]
name = "nym_wallet"
version = "0.1.0"
description = "Nym Native Wallet"
authors = ["you"]
license = ""
repository = ""
default-run = "nym_wallet"
edition = "2018"
build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.0.0-beta.4" }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-beta.8", features = [] }
tokio = { version = "1.10", features = ["sync"] }
dirs = "3.0"
# url = "2.2"
bip39 = "1.0"
thiserror = "1.0"
tendermint-rpc = "0.21.0"
ts-rs = "3.0"
url = "2.0"
rand = "0.6.5"
cosmrs = { version = "0.1", 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 = [
"nymd-client",
] }
mixnet-contract = { path = "../../common/mixnet-contract" }
config = { path = "../../common/config" }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-13
View File
@@ -1,13 +0,0 @@
max_width = 100
hard_tabs = false
tab_spaces = 2
newline_style = "Auto"
use_small_heuristics = "Default"
reorder_imports = true
reorder_modules = true
remove_nested_parens = true
edition = "2018"
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
-3
View File
@@ -1,3 +0,0 @@
fn main() {
tauri_build::build()
}
-349
View File
@@ -1,349 +0,0 @@
// This should be moved out of the wallet, and used as a primary coin type throughout the codebase
use ::config::defaults::DENOM;
use cosmrs::Decimal;
use cosmrs::Denom as CosmosDenom;
use cosmwasm_std::Coin as CosmWasmCoin;
use cosmwasm_std::Uint128;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fmt;
use std::ops::{Add, Sub};
use std::str::FromStr;
use ts_rs::TS;
use validator_client::nymd::{CosmosCoin, GasPrice};
use crate::format_err;
#[derive(TS, Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Denom {
Major,
Minor,
}
const MINOR_IN_MAJOR: f64 = 1_000_000.;
impl fmt::Display for Denom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Denom::Major => f.write_str(&DENOM[1..]),
Denom::Minor => f.write_str(DENOM),
}
}
}
impl FromStr for Denom {
type Err = String;
fn from_str(s: &str) -> Result<Denom, String> {
let s = s.to_lowercase();
if s == DENOM.to_lowercase() || s == "minor" {
Ok(Denom::Minor)
} else if s == DENOM[1..].to_lowercase() || s == "major" {
Ok(Denom::Major)
} else {
Err(format_err!(format!(
"{} is not a valid denomination string",
s
)))
}
}
}
#[derive(TS, Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Coin {
amount: String,
denom: Denom,
}
// TODO convert to TryFrom
impl From<GasPrice> for Coin {
fn from(g: GasPrice) -> Coin {
Coin {
amount: g.amount.to_string(),
denom: Denom::from_str(&g.denom.to_string()).unwrap(),
}
}
}
impl fmt::Display for Coin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&format!("{} {}", self.amount, self.denom))
}
}
// Allows adding minor and major denominations, output will have the LHS denom.
impl Add for Coin {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let denom = self.denom.clone();
let lhs = self.to_minor();
let rhs = rhs.to_minor();
let lhs_amount = lhs.amount.parse::<u64>().unwrap();
let rhs_amount = rhs.amount.parse::<u64>().unwrap();
let amount = lhs_amount + rhs_amount;
let coin = Coin {
amount: amount.to_string(),
denom: Denom::Minor,
};
match denom {
Denom::Major => coin.to_major(),
Denom::Minor => coin,
}
}
}
// Allows adding minor and major denominations, output will have the LHS denom.
impl Sub for Coin {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
let denom = self.denom.clone();
let lhs = self.to_minor();
let rhs = rhs.to_minor();
let lhs_amount = lhs.amount.parse::<i64>().unwrap();
let rhs_amount = rhs.amount.parse::<i64>().unwrap();
let amount = lhs_amount - rhs_amount;
let coin = Coin {
amount: amount.to_string(),
denom: Denom::Minor,
};
match denom {
Denom::Major => coin.to_major(),
Denom::Minor => coin,
}
}
}
impl Coin {
pub fn major<T: ToString>(amount: T) -> Coin {
Coin {
amount: amount.to_string(),
denom: Denom::Major,
}
}
pub fn minor<T: ToString>(amount: T) -> Coin {
Coin {
amount: amount.to_string(),
denom: Denom::Minor,
}
}
pub fn new<T: ToString>(amount: T, denom: &Denom) -> Coin {
Coin {
amount: amount.to_string(),
denom: denom.clone(),
}
}
pub fn to_major(&self) -> Coin {
match self.denom {
Denom::Major => self.clone(),
Denom::Minor => Coin {
amount: (self.amount.parse::<f64>().unwrap() / MINOR_IN_MAJOR).to_string(),
denom: Denom::Major,
},
}
}
pub fn to_minor(&self) -> Coin {
match self.denom {
Denom::Minor => self.clone(),
Denom::Major => Coin {
amount: (self.amount.parse::<f64>().unwrap() * MINOR_IN_MAJOR).to_string(),
denom: Denom::Minor,
},
}
}
pub fn amount(&self) -> String {
self.amount.clone()
}
pub fn denom(&self) -> Denom {
self.denom.clone()
}
}
impl TryFrom<Coin> for CosmWasmCoin {
type Error = String;
fn try_from(coin: Coin) -> Result<CosmWasmCoin, String> {
Ok(CosmWasmCoin::new(
Uint128::try_from(coin.amount.as_str()).unwrap().u128(),
coin.denom.to_string(),
))
}
}
impl TryFrom<Coin> for CosmosCoin {
type Error = String;
fn try_from(coin: Coin) -> Result<CosmosCoin, String> {
match Decimal::from_str(&coin.amount) {
Ok(d) => Ok(CosmosCoin {
amount: d,
denom: CosmosDenom::from_str(&coin.denom.to_string()).unwrap(),
}),
Err(e) => Err(format_err!(e)),
}
}
}
impl From<CosmosCoin> for Coin {
fn from(c: CosmosCoin) -> Coin {
Coin {
amount: c.amount.to_string(),
denom: Denom::from_str(&c.denom.to_string()).unwrap(),
}
}
}
impl From<CosmWasmCoin> for Coin {
fn from(c: CosmWasmCoin) -> Coin {
Coin {
amount: c.amount.to_string(),
denom: Denom::from_str(&c.denom).unwrap(),
}
}
}
#[cfg(test)]
mod test {
use crate::coin::{Coin, Denom};
use cosmrs::Coin as CosmosCoin;
use cosmrs::Decimal;
use cosmrs::Denom as CosmosDenom;
use cosmwasm_std::Coin as CosmWasmCoin;
use serde_json::json;
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
#[test]
fn json_to_coin() {
let minor = json!({
"amount": "1",
"denom": "Minor"
});
let major = json!({
"amount": "1",
"denom": "Major"
});
let test_minor_coin = Coin::minor("1");
let test_major_coin = Coin::major("1");
let minor_coin = serde_json::from_value::<Coin>(minor).unwrap();
let major_coin = serde_json::from_value::<Coin>(major).unwrap();
assert_eq!(minor_coin, test_minor_coin);
assert_eq!(major_coin, test_major_coin);
}
#[test]
fn denom_conversions() {
let minor = Coin::minor("1");
let major = minor.to_major();
assert_eq!(major, Coin::major("0.000001"));
let minor = major.to_minor();
assert_eq!(minor, Coin::minor("1"));
}
fn amounts() -> Vec<&'static str> {
vec![
"1",
"10",
"100",
"1000",
"10000",
"100000",
"10000000",
"100000000",
"1000000000",
"10000000000",
"100000000000",
"1000000000000",
"10000000000000",
"100000000000000",
"1000000000000000",
"10000000000000000",
"100000000000000000",
"1000000000000000000",
]
}
#[test]
fn coin_to_cosmoswasm() {
for amount in amounts() {
let coin: Coin = Coin::minor(amount).into();
let cosmoswasm_coin: CosmWasmCoin = coin.try_into().unwrap();
assert_eq!(
cosmoswasm_coin,
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), Denom::Minor.to_string())
);
assert_eq!(
Coin::try_from(cosmoswasm_coin).unwrap(),
Coin::minor(amount)
);
let coin: Coin = Coin::major(amount).into();
let cosmoswasm_coin: CosmWasmCoin = coin.try_into().unwrap();
assert_eq!(
cosmoswasm_coin,
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), Denom::Major.to_string())
);
assert_eq!(
Coin::try_from(cosmoswasm_coin).unwrap(),
Coin::major(amount)
);
}
}
#[test]
fn coin_to_cosmos() {
for amount in amounts() {
let coin: Coin = Coin::minor(amount).into();
let cosmos_coin: CosmosCoin = coin.try_into().unwrap();
assert_eq!(
cosmos_coin,
CosmosCoin {
amount: Decimal::from_str(amount).unwrap(),
denom: CosmosDenom::from_str(&Denom::Minor.to_string()).unwrap()
}
);
assert_eq!(Coin::try_from(cosmos_coin).unwrap(), Coin::minor(amount));
let coin: Coin = Coin::major(amount).into();
let cosmos_coin: CosmosCoin = coin.try_into().unwrap();
assert_eq!(
cosmos_coin,
CosmosCoin {
amount: Decimal::from_str(amount).unwrap(),
denom: CosmosDenom::from_str(&Denom::Major.to_string()).unwrap()
}
);
assert_eq!(Coin::try_from(cosmos_coin).unwrap(), Coin::major(amount));
}
}
#[test]
fn test_add() {
assert_eq!(Coin::minor("1") + Coin::minor("1"), Coin::minor("2"));
assert_eq!(Coin::major("1") + Coin::major("1"), Coin::major("2"));
assert_eq!(Coin::minor("1") + Coin::major("1"), Coin::minor("1000001"));
assert_eq!(Coin::major("1") + Coin::minor("1"), Coin::major("1.000001"));
}
#[test]
fn test_sub() {
assert_eq!(Coin::minor("1") - Coin::minor("1"), Coin::minor("0"));
assert_eq!(Coin::major("1") - Coin::major("1"), Coin::major("0"));
assert_eq!(Coin::minor("1") - Coin::major("1"), Coin::minor("-999999"));
assert_eq!(Coin::major("1") - Coin::minor("1"), Coin::major("0.999999"));
}
}
-90
View File
@@ -1,90 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::{default_validators, ValidatorDetails, DEFAULT_MIXNET_CONTRACT_ADDRESS};
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::str::FromStr;
use tendermint_rpc::Url;
mod template;
use template::config_template;
use crate::error::BackendError;
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Config {
base: Base,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Base {
validators: Vec<ValidatorDetails>,
/// Address of the validator contract managing the network
mixnet_contract_address: String,
/// Mnemonic (currently of the network monitor) used for rewarding
mnemonic: String,
}
impl Default for Base {
fn default() -> Self {
Base {
validators: default_validators(),
mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(),
mnemonic: String::default(),
}
}
}
impl NymConfig for Config {
fn template() -> &'static str {
config_template()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("wallet")
}
fn root_directory(&self) -> PathBuf {
Self::default_root_directory()
}
fn config_directory(&self) -> PathBuf {
self.root_directory().join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory().join("data")
}
}
impl Config {
pub fn get_nymd_validator_url(&self) -> Result<Url, BackendError> {
// TODO make this a random choice
if let Some(validator_details) = self.base.validators.first() {
match tendermint_rpc::Url::from_str(&validator_details.nymd_url().to_string()) {
Ok(url) => Ok(url),
Err(e) => Err(e.into()),
}
} else {
panic!("No validators found in config")
}
}
pub fn get_mixnet_contract_address(&self) -> String {
self.base.mixnet_contract_address.clone()
}
// pub fn get_mnemonic(&self) -> String {
// self.base.mnemonic.clone()
// }
}
@@ -1,17 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) fn config_template() -> &'static str {
r#"
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base tauri-wallet config options #####
[base]
# Validator server to which the API will be getting information about the network.
validator_url = '{{ base.validator_url }}'
"#
}
-21
View File
@@ -1,21 +0,0 @@
use thiserror::Error;
use validator_client::nymd::error::NymdError;
#[derive(Error, Debug)]
pub enum BackendError {
#[error("Error parsing bip39 mnemonic")]
Bip39Error {
#[from]
source: bip39::Error,
},
#[error("Error parsing into tendermint Url")]
TendermintError {
#[from]
source: tendermint_rpc::Error,
},
#[error("Error getting balances")]
NymdError {
#[from]
source: NymdError,
},
}
-78
View File
@@ -1,78 +0,0 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use mixnet_contract::{Gateway, MixNode};
use std::sync::Arc;
use tokio::sync::RwLock;
use ts_rs::export;
use validator_client::nymd::fee_helpers::Operation;
mod coin;
mod config;
mod error;
mod operations;
mod state;
mod utils;
use crate::operations::account::*;
use crate::operations::admin::*;
use crate::operations::bond::*;
use crate::operations::delegate::*;
use crate::operations::send::*;
use crate::utils::*;
use crate::state::State;
#[cfg(test)]
use crate::coin::{Coin, Denom};
#[macro_export]
macro_rules! format_err {
($e:expr) => {
format!("line {}: {}", line!(), $e)
};
}
fn main() {
tauri::Builder::default()
.manage(Arc::new(RwLock::new(State::default())))
.invoke_handler(tauri::generate_handler![
connect_with_mnemonic,
get_balance,
minor_to_major,
major_to_minor,
owns_gateway,
owns_mixnode,
bond_mixnode,
unbond_mixnode,
bond_gateway,
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
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
export! {
MixNode => "../src/types/rust/mixnode.ts",
Coin => "../src/types/rust/coin.ts",
Balance => "../src/types/rust/balance.ts",
Gateway => "../src/types/rust/gateway.ts",
TauriTxResult => "../src/types/rust/tauritxresult.ts",
TransactionDetails => "../src/types/rust/transactiondetails.ts",
Operation => "../src/types/rust/operation.ts",
Denom => "../src/types/rust/denom.ts",
DelegationResult => "../src/types/rust/delegationresult.ts",
Account => "../src/types/rust/account.ts",
TauriStateParams => "../src/types/rust/stateparams.ts"
}
@@ -1,113 +0,0 @@
use crate::coin::{Coin, Denom};
use crate::config::Config;
use crate::error::BackendError;
use crate::format_err;
use crate::state::State;
use bip39::{Language, Mnemonic};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use ts_rs::TS;
use validator_client::nymd::{AccountId, NymdClient, SigningNymdClient};
#[derive(TS, Serialize, Deserialize)]
pub struct Account {
contract_address: String,
client_address: String,
denom: Denom,
mnemonmic: Option<String>,
}
#[derive(TS, Serialize, Deserialize)]
pub struct Balance {
coin: Coin,
printable_balance: String,
}
#[tauri::command]
pub async fn connect_with_mnemonic(
mnemonic: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Account, String> {
let mnemonic = match Mnemonic::from_str(&mnemonic) {
Ok(mnemonic) => mnemonic,
Err(e) => return Err(BackendError::from(e).to_string()),
};
let client;
{
let r_state = state.read().await;
client = _connect_with_mnemonic(mnemonic, &r_state.config());
}
let contract_address = match client.contract_address() {
Ok(address) => address.to_string(),
Err(e) => return Err(format_err!(e)),
};
let client_address = client.address().to_string();
let denom = match client.denom() {
Ok(denom) => denom,
Err(e) => return Err(format_err!(e)),
};
let account = Account {
contract_address,
client_address,
denom: Denom::from_str(&denom.to_string())?,
mnemonmic: None,
};
let mut w_state = state.write().await;
w_state.set_client(client);
Ok(account)
}
#[tauri::command]
pub async fn get_balance(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<Balance, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.get_balance(client.address()).await {
Ok(Some(coin)) => {
let coin = Coin::new(
&coin.amount.to_string(),
&Denom::from_str(&coin.denom.to_string())?,
);
Ok(Balance {
coin: coin.clone(),
printable_balance: coin.to_major().to_string(),
})
}
Ok(None) => Err(format!(
"No balance available for address {}",
client.address()
)),
Err(e) => Err(BackendError::from(e).to_string()),
}
}
#[tauri::command]
pub async fn create_new_account(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Account, String> {
let mnemonic = random_mnemonic();
let mut client = connect_with_mnemonic(mnemonic.to_string(), state).await?;
client.mnemonmic = Some(mnemonic.to_string());
Ok(client)
}
fn random_mnemonic() -> Mnemonic {
let mut rng = rand::thread_rng();
Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap()
}
fn _connect_with_mnemonic(mnemonic: Mnemonic, config: &Config) -> NymdClient<SigningNymdClient> {
match NymdClient::connect_with_mnemonic(
config.get_nymd_validator_url().unwrap(),
Some(AccountId::from_str(&config.get_mixnet_contract_address()).unwrap()),
mnemonic,
) {
Ok(client) => client,
Err(e) => panic!("{}", e),
}
}
@@ -1,84 +0,0 @@
use crate::format_err;
use crate::state::State;
use cosmwasm_std::Decimal;
use cosmwasm_std::Uint128;
use mixnet_contract::StateParams;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use ts_rs::TS;
#[derive(Serialize, Deserialize, TS)]
pub struct TauriStateParams {
epoch_length: u32,
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,
}
impl From<StateParams> for TauriStateParams {
fn from(p: StateParams) -> TauriStateParams {
TauriStateParams {
epoch_length: p.epoch_length,
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,
}
}
}
impl TryFrom<TauriStateParams> for StateParams {
type Error = Box<dyn std::error::Error>;
fn try_from(p: TauriStateParams) -> Result<StateParams, Self::Error> {
Ok(StateParams {
epoch_length: p.epoch_length,
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,
})
}
}
#[tauri::command]
pub async fn get_state_params(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriStateParams, String> {
let r_state = state.read().await;
let client = r_state.client()?;
match client.get_state_params().await {
Ok(params) => Ok(params.into()),
Err(e) => Err(format_err!(e)),
}
}
#[tauri::command]
pub async fn update_state_params(
params: TauriStateParams,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriStateParams, String> {
let r_state = state.read().await;
let client = r_state.client()?;
let state_params: StateParams = match params.try_into() {
Ok(state_params) => state_params,
Err(e) => return Err(format_err!(e)),
};
match client.update_state_params(state_params.clone()).await {
Ok(_) => Ok(state_params.into()),
Err(e) => Err(format_err!(e)),
}
}

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