Compare commits

..

7 Commits

Author SHA1 Message Date
Gala 9b1f4560ab Merge branch 'fix/selection-chance-buckets' of github.com:nymtech/nym into fix/selection-chance-buckets 2022-10-26 16:37:22 +02:00
Gala 84d68efdc2 Merge branch 'release/v1.1.0' into fix/selection-chance-buckets 2022-10-26 16:36:59 +02:00
Gala 4016d6b5a6 update storybook 2022-09-21 11:10:16 +02:00
Gala 8cb8297681 storybook update 2022-09-21 10:47:15 +02:00
Jon Häggblad 7998bd20a7 Lock file update 2022-09-21 08:49:25 +02:00
Jon Häggblad e9e53c8dff Fixup after rebase 2022-09-21 08:48:00 +02:00
Jon Häggblad a048cb9867 Update to latest set of selection chance buckets 2022-09-21 08:40:06 +02:00
235 changed files with 3736 additions and 5592 deletions
Vendored
BIN
View File
Binary file not shown.
+14 -27
View File
@@ -1,49 +1,36 @@
name: Daily security audit
on:
schedule:
- cron: '5 9 * * *'
on: workflow_dispatch
jobs:
cargo-deny:
security_audit:
runs-on: ubuntu-latest
steps:
- name: Checkout repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
- uses: actions/checkout@v1
- uses: actions-rs/audit-check@v1
with:
toolchain: stable
- name: Install cargo deny
run: cargo install --locked cargo-deny
- name: Run cargo deny
run: cargo deny check advisories --hide-inclusion-graph &> .github/workflows/support-files/notifications/deny.message
- uses: actions/upload-artifact@v3
with:
name: report
path: .github/workflows/support-files/notifications/deny.message
token: ${{ secrets.GITHUB_TOKEN }}
notification:
needs: cargo-deny
if: ${{ failure() }}
needs: security_audit
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Download report from previous job
uses: actions/download-artifact@v3
with:
name: report
path: .github/workflows/support-files/notifications
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: security
NYM_PROJECT_NAME: "Daily security report"
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym daily audit"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "security"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "test"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -3,7 +3,7 @@ require('dotenv').config();
const Bot = require('keybase-bot');
let context = {
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect','security'],
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect'],
};
/**
@@ -1,24 +0,0 @@
const Handlebars = require('handlebars');
const fs = require('fs');
const path = require('path');
const { Octokit, App } = require('octokit');
async function addToContextAndValidate(context) {
return
}
async function getMessageBody(context) {
try {
const source = fs
.readFileSync("deny.message").toString();
return source;
} catch (error) {
console.error(error);
}
}
module.exports = {
addToContextAndValidate,
getMessageBody,
};
-32
View File
@@ -1,32 +0,0 @@
name: Tests for validator API
on:
push:
paths:
- "validator-api/tests/**"
defaults:
run:
working-directory: validator-api/tests
jobs:
test:
name: validator api tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Node v18
uses: actions/setup-node@v3
with:
node-version: 18.1.0
- name: Install yarn
run: yarn install
- name: Run yarn
run: yarn
- name: Launch tests
run: yarn test
working-directory: validator-api/tests
-13
View File
@@ -15,15 +15,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- native-client/socks5-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671])
- wasm-client: uses updated wasm-compatible `client-core` so that it's now capable of packet retransmission, cover traffic and poisson delay (among other things!) ([#1673])
- validator-api: add `interval_operating_cost` and `profit_margin_percent` to cmpute reward estimation endpoint
- native-client/socks5-client/network-requester: improve handling error cases ([#1713])
- vesting-contract: optional locked token pledge cap per account ([#1687]), defaults to 100_000 NYM
- clients: add testing-only support for two more extended packet sizes (8kb and 16kb).
### Fixed
- validator-api, mixnode, gateway should now prefer values in config.toml over mainnet defaults ([#1645])
- validator-api should now correctly update historical uptimes for all mixnodes and gateways every 24h ([#1721])
- socks5-client: fix bug where in some cases packet reordering could trigger a connection being closed too early ([#1702],[#1724])
### Changed
@@ -31,9 +27,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
- gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669])
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
- moved `Percent` struct to to `contracts-common`, change affects explorer-api
- clients: bound the sphinx packet channel and reduce sending rate if gateway can't keep up ([#1703],[#1725])
[#1541]: https://github.com/nymtech/nym/pull/1541
[#1558]: https://github.com/nymtech/nym/pull/1558
@@ -47,14 +41,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#1669]: https://github.com/nymtech/nym/pull/1669
[#1671]: https://github.com/nymtech/nym/pull/1671
[#1673]: https://github.com/nymtech/nym/pull/1673
[#1681]: https://github.com/nymtech/nym/pull/1681
[#1687]: https://github.com/nymtech/nym/pull/1687
[#1702]: https://github.com/nymtech/nym/pull/1702
[#1703]: https://github.com/nymtech/nym/pull/1703
[#1713]: https://github.com/nymtech/nym/pull/1713
[#1721]: https://github.com/nymtech/nym/pull/1721
[#1724]: https://github.com/nymtech/nym/pull/1724
[#1725]: https://github.com/nymtech/nym/pull/1725
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
Generated
+1 -23
View File
@@ -739,7 +739,6 @@ dependencies = [
"cosmwasm-std",
"schemars",
"serde",
"serde_json",
"thiserror",
]
@@ -1586,13 +1585,12 @@ version = "1.0.1"
dependencies = [
"chrono",
"clap 3.2.8",
"contracts-common",
"dotenv",
"contracts-common",
"humantime-serde",
"isocountry",
"itertools",
"log",
"logging",
"maxminddb",
"mixnet-contract-common",
"network-defaults",
@@ -2731,14 +2729,6 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "logging"
version = "0.1.0"
dependencies = [
"log",
"pretty_env_logger",
]
[[package]]
name = "loom"
version = "0.5.4"
@@ -3081,7 +3071,6 @@ dependencies = [
"clap_complete_fig",
"dotenv",
"log",
"logging",
"network-defaults",
"nym-cli-commands",
"pretty_env_logger",
@@ -3137,7 +3126,6 @@ dependencies = [
"gateway-client",
"gateway-requests",
"log",
"logging",
"network-defaults",
"nymsphinx",
"pemstore",
@@ -3147,7 +3135,6 @@ dependencies = [
"serde_json",
"sled",
"task",
"thiserror",
"tokio",
"tokio-tungstenite 0.14.0",
"topology",
@@ -3180,7 +3167,6 @@ dependencies = [
"gateway-requests",
"humantime-serde",
"log",
"logging",
"mixnet-client",
"mixnode-common",
"network-defaults",
@@ -3223,7 +3209,6 @@ dependencies = [
"humantime-serde",
"lazy_static",
"log",
"logging",
"mixnet-client",
"mixnode-common",
"nonexhaustive-delayqueue",
@@ -3258,7 +3243,6 @@ dependencies = [
"futures",
"ipnetwork 0.20.0",
"log",
"logging",
"network-defaults",
"nymsphinx",
"ordered-buffer",
@@ -3284,7 +3268,6 @@ version = "1.0.2"
dependencies = [
"dirs",
"log",
"logging",
"pretty_env_logger",
"rocket",
"serde",
@@ -3311,7 +3294,6 @@ dependencies = [
"gateway-client",
"gateway-requests",
"log",
"logging",
"network-defaults",
"nymsphinx",
"ordered-buffer",
@@ -3324,7 +3306,6 @@ dependencies = [
"snafu 0.6.10",
"socks5-requests",
"task",
"thiserror",
"tokio",
"topology",
"url",
@@ -3386,7 +3367,6 @@ dependencies = [
"humantime-serde",
"inclusion-probability",
"log",
"logging",
"mixnet-contract-common",
"multisig-contract-common",
"nymcoconut",
@@ -6410,7 +6390,6 @@ dependencies = [
"coconut-interface",
"colored",
"config",
"contracts-common",
"cosmrs",
"cosmwasm-std",
"cw3",
@@ -6506,7 +6485,6 @@ dependencies = [
"schemars",
"serde",
"thiserror",
"vergen 5.1.17",
"vesting-contract-common",
]
-1
View File
@@ -41,7 +41,6 @@ members = [
"common/execute",
"common/inclusion-probability",
"common/ledger",
"common/logging",
"common/mixnode-common",
"common/network-defaults",
"common/nonexhaustive-delayqueue",
-3
View File
@@ -122,6 +122,3 @@ mixnet-opt: wasm
generate-typescript:
cd tools/ts-rs-cli && cargo run && cd ../..
yarn types:lint:fix
run-validator-tests:
cd validator-api/tests/functional_test && yarn test
@@ -16,7 +16,6 @@ use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc::error::TrySendError;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time;
@@ -172,18 +171,11 @@ impl LoopCoverTrafficStream<OsRng> {
)
.expect("Somehow failed to generate a loop cover message with a valid topology");
if let Err(err) = self.mix_tx.try_send(vec![cover_message]) {
match err {
TrySendError::Full(_) => {
// This isn't a problem, if the channel is full means we're already sending the
// max amount of messages downstream can handle.
log::debug!("Failed to send cover message - channel full");
}
TrySendError::Closed(_) => {
log::warn!("Failed to send cover message - channel closed");
}
}
}
// if this one fails, there's no retrying because it means that either:
// - we run out of memory
// - the receiver channel is closed
// in either case there's no recovery and we can only panic
self.mix_tx.unbounded_send(vec![cover_message]).unwrap();
// TODO: I'm not entirely sure whether this is really required, because I'm not 100%
// sure how `yield_now()` works - whether it just notifies the scheduler or whether it
+15 -17
View File
@@ -2,15 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
use futures::channel::mpsc;
use futures::StreamExt;
use gateway_client::GatewayClient;
use log::*;
use nymsphinx::forwarding::packet::MixPacket;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = mpsc::UnboundedReceiver<Vec<MixPacket>>;
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
const MAX_FAILURE_COUNT: usize = 100;
pub struct MixTrafficController {
@@ -25,17 +25,15 @@ pub struct MixTrafficController {
}
impl MixTrafficController {
pub fn new(gateway_client: GatewayClient) -> (MixTrafficController, BatchMixMessageSender) {
let (sphinx_message_sender, sphinx_message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
MixTrafficController {
gateway_client,
mix_rx: sphinx_message_receiver,
consecutive_gateway_failure_count: 0,
},
sphinx_message_sender,
)
pub fn new(
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
) -> MixTrafficController {
MixTrafficController {
gateway_client,
mix_rx,
consecutive_gateway_failure_count: 0,
}
}
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
@@ -74,7 +72,7 @@ impl MixTrafficController {
while !shutdown.is_shutdown() {
tokio::select! {
mix_packets = self.mix_rx.recv() => match mix_packets {
mix_packets = self.mix_rx.next() => match mix_packets {
Some(mix_packets) => {
self.on_messages(mix_packets).await;
},
@@ -98,7 +96,7 @@ impl MixTrafficController {
spawn_future(async move {
debug!("Started MixTrafficController without graceful shutdown support");
while let Some(mix_packets) = self.mix_rx.recv().await {
while let Some(mix_packets) = self.mix_rx.next().await {
self.on_messages(mix_packets).await;
}
})
@@ -27,23 +27,6 @@ use tokio::time;
#[cfg(target_arch = "wasm32")]
use wasm_timer;
// The minimum time between increasing the average delay between packets. If we hit the ceiling in
// the available buffer space we want to take somewhat swift action, but we still need to give a
// short time to give the channel a chance reduce pressure.
const INCREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 1;
// The minimum time between decreasing the average delay between packets. We don't want to change
// to quickly to keep things somewhat stable. Also there are buffers downstreams meaning we need to
// wait a little to see the effect before we decrease further.
const DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 30;
// If we enough time passes without any sign of backpressure in the channel, we can consider
// lowering the average delay. The goal is to keep somewhat stable, rather than maxing out
// bandwidth at all times.
const ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS: u64 = 30;
// The maximum multiplier we apply to the base average Poisson delay.
const MAX_DELAY_MULTIPLIER: u32 = 6;
// The minium multiplier we apply to the base average Poisson delay.
const MIN_DELAY_MULTIPLIER: u32 = 1;
/// Configurable parameters of the `OutQueueControl`
pub(crate) struct Config {
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
@@ -85,101 +68,6 @@ impl Config {
}
}
struct SendingDelayController {
/// Multiply the average sending delay.
/// This is normally set to unity, but if we detect backpressure we increase this
/// multiplier. We use discrete steps.
current_multiplier: u32,
/// Maximum delay multiplier
upper_bound: u32,
/// Minimum delay multiplier
lower_bound: u32,
/// To make sure we don't change the multiplier to fast, we limit a change to some duration
#[cfg(not(target_arch = "wasm32"))]
time_when_changed: time::Instant,
#[cfg(target_arch = "wasm32")]
time_when_changed: wasm_timer::Instant,
/// If we have a long enough time without any backpressure detected we try reducing the sending
/// delay multiplier
#[cfg(not(target_arch = "wasm32"))]
time_when_backpressure_detected: time::Instant,
#[cfg(target_arch = "wasm32")]
time_when_backpressure_detected: wasm_timer::Instant,
}
#[cfg(not(target_arch = "wasm32"))]
fn get_time_now() -> time::Instant {
time::Instant::now()
}
#[cfg(target_arch = "wasm32")]
fn get_time_now() -> wasm_timer::Instant {
wasm_timer::Instant::now()
}
impl SendingDelayController {
fn new(lower_bound: u32, upper_bound: u32) -> Self {
assert!(lower_bound <= upper_bound);
let now = get_time_now();
SendingDelayController {
current_multiplier: MIN_DELAY_MULTIPLIER,
upper_bound,
lower_bound,
time_when_changed: now,
time_when_backpressure_detected: now,
}
}
fn current_multiplier(&self) -> u32 {
self.current_multiplier
}
fn increase_delay_multiplier(&mut self) {
self.current_multiplier =
(self.current_multiplier + 1).clamp(self.lower_bound, self.upper_bound);
self.time_when_changed = get_time_now();
log::debug!(
"Increasing sending delay multiplier to: {}",
self.current_multiplier
);
}
fn decrease_delay_multiplier(&mut self) {
self.current_multiplier =
(self.current_multiplier - 1).clamp(self.lower_bound, self.upper_bound);
self.time_when_changed = get_time_now();
log::debug!(
"Decreasing sending delay multiplier to: {}",
self.current_multiplier
);
}
fn record_backpressure_detected(&mut self) {
self.time_when_backpressure_detected = get_time_now();
}
fn not_increased_delay_recently(&self) -> bool {
get_time_now()
> self.time_when_changed + Duration::from_secs(INCREASE_DELAY_MIN_CHANGE_INTERVAL_SECS)
}
fn is_sending_reliable(&self) -> bool {
let now = get_time_now();
let delay_change_interval = Duration::from_secs(DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS);
let acceptable_time_without_backpressure =
Duration::from_secs(ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS);
now > self.time_when_backpressure_detected + acceptable_time_without_backpressure
&& now > self.time_when_changed + delay_change_interval
}
}
pub(crate) struct OutQueueControl<R>
where
R: CryptoRng + Rng,
@@ -201,10 +89,6 @@ where
#[cfg(target_arch = "wasm32")]
next_delay: Option<Pin<Box<wasm_timer::Delay>>>,
// To make sure we don't overload the mix_tx channel, we limit the rate we are pushing
// messages.
sending_rate_controller: SendingDelayController,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// out to the network without any further delays.
mix_tx: BatchMixMessageSender,
@@ -272,10 +156,6 @@ where
ack_key,
sent_notifier,
next_delay: None,
sending_rate_controller: SendingDelayController::new(
MIN_DELAY_MULTIPLIER,
MAX_DELAY_MULTIPLIER,
),
mix_tx,
real_receiver,
our_full_destination,
@@ -296,7 +176,7 @@ where
async fn on_message(&mut self, next_message: StreamMessage) {
trace!("created new message");
let (next_message, fragment_id) = match next_message {
let next_message = match next_message {
StreamMessage::Cover => {
// TODO for way down the line: in very rare cases (during topology update) we might have
// to wait a really tiny bit before actually obtaining the permit hence messing with our
@@ -315,35 +195,32 @@ where
}
let topology_ref = topology_ref_option.unwrap();
(
generate_loop_cover_packet(
&mut self.rng,
topology_ref,
&self.ack_key,
&self.our_full_destination,
self.config.average_ack_delay,
self.config.average_packet_delay,
self.config.cover_packet_size,
)
.expect(
"Somehow failed to generate a loop cover message with a valid topology",
),
None,
generate_loop_cover_packet(
&mut self.rng,
topology_ref,
&self.ack_key,
&self.our_full_destination,
self.config.average_ack_delay,
self.config.average_packet_delay,
self.config.cover_packet_size,
)
.expect("Somehow failed to generate a loop cover message with a valid topology")
}
StreamMessage::Real(real_message) => {
(real_message.mix_packet, Some(real_message.fragment_id))
self.sent_notify(real_message.fragment_id);
real_message.mix_packet
}
};
if let Err(err) = self.mix_tx.send(vec![next_message]).await {
log::error!("Failed to send - channel closed: {}", err);
}
// notify ack controller about sending our message only after we actually managed to push it
// through the channel
if let Some(fragment_id) = fragment_id {
self.sent_notify(fragment_id);
// if this one fails, there's no retrying because it means that either:
// - we run out of memory
// - the receiver channel is closed
// in either case there's no recovery and we can only panic
if let Err(err) = self.mix_tx.unbounded_send(vec![next_message]) {
log::warn!(
"Failed to send {} packets (possible process shutdown?)",
err.into_inner().len()
);
}
// JS: Not entirely sure why or how it fixes stuff, but without the yield call,
@@ -357,44 +234,7 @@ where
tokio::task::yield_now().await;
}
fn current_average_message_sending_delay(&self) -> Duration {
self.config.average_message_sending_delay
* self.sending_rate_controller.current_multiplier()
}
fn adjust_current_average_message_sending_delay(&mut self) {
let used_slots = self.mix_tx.max_capacity() - self.mix_tx.capacity();
log::trace!(
"used_slots: {used_slots}, current_multiplier: {}",
self.sending_rate_controller.current_multiplier()
);
// Even just a single used slot is enough to signal backpressure
if used_slots > 0 {
log::trace!("Backpressure detected");
self.sending_rate_controller.record_backpressure_detected();
}
// If the buffer is running out, slow down the sending rate
if self.mix_tx.capacity() == 0
&& self.sending_rate_controller.not_increased_delay_recently()
{
self.sending_rate_controller.increase_delay_multiplier();
}
// Very carefully step up the sending rate in case it seems like we can solidly handle the
// current rate.
if self.sending_rate_controller.is_sending_reliable() {
self.sending_rate_controller.decrease_delay_multiplier();
}
}
fn poll_poisson(&mut self, cx: &mut Context<'_>) -> Poll<Option<StreamMessage>> {
// The average delay could change depending on if backpressure in the downstream channel
// (mix_tx) was detected.
self.adjust_current_average_message_sending_delay();
let avg_delay = self.current_average_message_sending_delay();
if let Some(ref mut next_delay) = &mut self.next_delay {
// it is not yet time to return a message
if next_delay.as_mut().poll(cx).is_pending() {
@@ -403,6 +243,7 @@ where
// we know it's time to send a message, so let's prepare delay for the next one
// Get the `now` by looking at the current `delay` deadline
let avg_delay = self.config.average_message_sending_delay;
let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay);
// The next interval value is `next_poisson_delay` after the one that just
@@ -57,15 +57,24 @@ impl<'a> TopologyReadPermit<'a> {
) -> Option<&'a NymTopology> {
// Note: implicit deref with Deref for TopologyReadPermit is happening here
let topology_ref_option = self.permit.as_ref();
topology_ref_option.as_ref().filter(|topology_ref| {
!(!topology_ref.can_construct_path_through(DEFAULT_NUM_MIX_HOPS)
|| !topology_ref.gateway_exists(ack_recipient.gateway())
|| if let Some(packet_recipient) = packet_recipient {
!topology_ref.gateway_exists(packet_recipient.gateway())
match topology_ref_option {
None => None,
Some(topology_ref) => {
// see if it's possible to route the packet to both gateways
if !topology_ref.can_construct_path_through(DEFAULT_NUM_MIX_HOPS)
|| !topology_ref.gateway_exists(ack_recipient.gateway())
|| if let Some(packet_recipient) = packet_recipient {
!topology_ref.gateway_exists(packet_recipient.gateway())
} else {
false
}
{
None
} else {
false
})
})
Some(topology_ref)
}
}
}
}
}
+5 -24
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use config::NymConfig;
use nymsphinx::params::PacketSize;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
@@ -248,8 +247,8 @@ impl<T: NymConfig> Config<T> {
self.debug.disable_main_poisson_packet_distribution
}
pub fn get_use_extended_packet_size(&self) -> Option<ExtendedPacketSize> {
self.debug.use_extended_packet_size.clone()
pub fn get_use_extended_packet_size(&self) -> bool {
self.debug.use_extended_packet_size
}
pub fn get_version(&self) -> &str {
@@ -471,16 +470,8 @@ pub struct Debug {
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
pub use_extended_packet_size: Option<ExtendedPacketSize>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
Extended8,
Extended16,
Extended32,
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool,
}
impl Default for Debug {
@@ -497,17 +488,7 @@ impl Default for Debug {
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_loop_cover_traffic_stream: false,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
}
}
}
impl From<ExtendedPacketSize> for PacketSize {
fn from(size: ExtendedPacketSize) -> PacketSize {
match size {
ExtendedPacketSize::Extended8 => PacketSize::ExtendedPacket8,
ExtendedPacketSize::Extended16 => PacketSize::ExtendedPacket16,
ExtendedPacketSize::Extended32 => PacketSize::ExtendedPacket32,
use_extended_packet_size: false,
}
}
}
-2
View File
@@ -24,6 +24,4 @@ pub enum ClientCoreError {
ListOfValidatorApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
#[error("The current network topology seem to be insufficient to route any packets through")]
InsufficientNetworkTopology,
}
-2
View File
@@ -27,7 +27,6 @@ pretty_env_logger = "0.4" # for formatting log messages
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
sled = "0.34" # for storage of replySURB decryption keys
thiserror = "1.0.34"
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.14" # websocket
@@ -39,7 +38,6 @@ completions = { path = "../../common/completions" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials", optional = true }
crypto = { path = "../../common/crypto" }
logging = { path = "../../common/logging"}
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
network-defaults = { path = "../../common/network-defaults" }
+30 -31
View File
@@ -6,7 +6,9 @@ use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
};
use client_core::client::key_manager::KeyManager;
use client_core::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use client_core::client::mix_traffic::{
BatchMixMessageReceiver, BatchMixMessageSender, MixTrafficController,
};
use client_core::client::real_messages_control;
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{
@@ -18,7 +20,6 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
@@ -30,11 +31,11 @@ use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySurb;
use nymsphinx::params::PacketSize;
use nymsphinx::receiver::ReconstructedMessage;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
use crate::client::config::{Config, SocketType};
use crate::error::ClientError;
use crate::websocket;
pub(crate) mod config;
@@ -102,9 +103,8 @@ impl NymClient {
topology_accessor,
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
if self.config.get_base().get_use_extended_packet_size() {
stream.set_custom_packet_size(PacketSize::ExtendedPacket)
}
stream.start_with_shutdown(shutdown);
@@ -132,9 +132,8 @@ impl NymClient {
self.as_mix_recipient(),
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
if self.config.get_base().get_use_extended_packet_size() {
controller_config.set_custom_packet_size(PacketSize::ExtendedPacket)
}
info!("Starting real traffic stream...");
@@ -234,7 +233,7 @@ impl NymClient {
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) -> Result<(), ClientError> {
) {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(),
@@ -249,16 +248,14 @@ impl NymClient {
// TODO: a slightly more graceful termination here
if !topology_refresher.is_topology_routable().await {
log::error!(
"The current network topology seem to be insufficient to route any packets through \
panic!(
"The current network topology seem to be insufficient to route any packets through\
- check if enough nodes and a gateway are online"
);
return Err(ClientCoreError::InsufficientNetworkTopology.into());
}
info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown);
Ok(())
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
@@ -266,13 +263,13 @@ impl NymClient {
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
&mut self,
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) -> BatchMixMessageSender {
) {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
MixTrafficController::new(mix_rx, gateway_client).start_with_shutdown(shutdown);
}
fn start_websocket_listener(
@@ -332,8 +329,8 @@ impl NymClient {
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) -> Result<(), ClientError> {
let shutdown = self.start().await?;
pub async fn run_forever(&mut self) {
let shutdown = self.start().await;
wait_for_signal().await;
println!(
@@ -350,16 +347,20 @@ impl NymClient {
//shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-client");
Ok(())
}
pub async fn start(&mut self) -> Result<ShutdownNotifier, ClientError> {
pub async fn start(&mut self) -> ShutdownNotifier {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
// sphinx_message_sender is the transmitter for any component generating sphinx packets that are to be sent to the mixnet
// they are used by cover traffic stream and real traffic stream
// sphinx_message_receiver is the receiver used by MixTrafficController that sends the actual traffic
let (sphinx_message_sender, sphinx_message_receiver) = mpsc::unbounded();
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
@@ -384,7 +385,7 @@ impl NymClient {
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
.await?;
.await;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
@@ -396,13 +397,11 @@ impl NymClient {
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await;
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
self.start_mix_traffic_controller(
sphinx_message_receiver,
gateway_client,
shutdown.subscribe(),
);
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
reply_key_storage,
@@ -448,6 +447,6 @@ impl NymClient {
info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient());
Ok(shutdown)
shutdown
}
}
+2 -4
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, SocketType};
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use completions::{fig_generate, ArgShell};
@@ -84,17 +83,16 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
pub(crate) async fn execute(args: &Cli) {
let bin_name = "nym-native-client";
match &args.command {
Commands::Init(m) => init::execute(m).await,
Commands::Run(m) => run::execute(m).await?,
Commands::Run(m) => run::execute(m).await,
Commands::Upgrade(m) => upgrade::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
}
Ok(())
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
+4 -5
View File
@@ -4,7 +4,6 @@
use crate::{
client::{config::Config, NymClient},
commands::{override_config, OverrideConfig},
error::ClientError,
};
use clap::Args;
@@ -74,14 +73,14 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
pub(crate) async fn execute(args: &Run) {
let id = &args.id;
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
return Err(ClientError::FailedToLoadConfig(id.to_string()));
return;
}
};
@@ -90,8 +89,8 @@ pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
if !version_check(&config) {
error!("failed the local version check");
return Err(ClientError::FailedLocalVersionCheck);
return;
}
NymClient::new(config).run_forever().await
NymClient::new(config).run_forever().await;
}
-23
View File
@@ -1,23 +0,0 @@
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
#[error("Failed to load config for: {0}")]
FailedToLoadConfig(String),
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
}
-1
View File
@@ -2,5 +2,4 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client;
pub mod error;
pub mod websocket;
+24 -5
View File
@@ -2,23 +2,20 @@
// SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, Parser};
use error::ClientError;
use logging::setup_logging;
use network_defaults::setup_env;
pub mod client;
pub mod commands;
pub mod error;
pub mod websocket;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
async fn main() {
setup_logging();
println!("{}", banner());
let args = commands::Cli::parse();
setup_env(args.config_env_file.clone());
commands::execute(&args).await
commands::execute(&args).await;
}
fn banner() -> String {
@@ -37,3 +34,25 @@ fn banner() -> String {
crate_version!()
)
}
fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") {
log_builder.parse_filters(&s);
} else {
// default to 'Info'
log_builder.filter(None, log::LevelFilter::Info);
}
log_builder
.filter_module("hyper", log::LevelFilter::Warn)
.filter_module("tokio_reactor", log::LevelFilter::Warn)
.filter_module("reqwest", log::LevelFilter::Warn)
.filter_module("mio", log::LevelFilter::Warn)
.filter_module("want", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.filter_module("handlebars", log::LevelFilter::Warn)
.filter_module("sled", log::LevelFilter::Warn)
.init();
}
-2
View File
@@ -20,7 +20,6 @@ pretty_env_logger = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
snafu = "0.6"
thiserror = "1.0.34"
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
url = "2.2"
@@ -32,7 +31,6 @@ completions = { path = "../../common/completions" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials", optional = true }
crypto = { path = "../../common/crypto" }
logging = { path = "../../common/logging"}
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
network-defaults = { path = "../../common/network-defaults" }
+32 -37
View File
@@ -4,7 +4,6 @@
use std::sync::atomic::Ordering;
use crate::client::config::Config;
use crate::error::Socks5ClientError;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::SphinxSocksServer,
@@ -14,7 +13,9 @@ use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
};
use client_core::client::key_manager::KeyManager;
use client_core::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use client_core::client::mix_traffic::{
BatchMixMessageReceiver, BatchMixMessageSender, MixTrafficController,
};
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
@@ -24,7 +25,6 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use futures::StreamExt;
@@ -36,6 +36,7 @@ use gateway_client::{
use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::params::PacketSize;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
pub mod config;
@@ -102,9 +103,8 @@ impl NymClient {
topology_accessor,
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
if self.config.get_base().get_use_extended_packet_size() {
stream.set_custom_packet_size(PacketSize::ExtendedPacket)
}
stream.start_with_shutdown(shutdown);
@@ -132,9 +132,8 @@ impl NymClient {
self.as_mix_recipient(),
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
if self.config.get_base().get_use_extended_packet_size() {
controller_config.set_custom_packet_size(PacketSize::ExtendedPacket)
}
info!("Starting real traffic stream...");
@@ -234,7 +233,7 @@ impl NymClient {
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) -> Result<(), Socks5ClientError> {
) {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(),
@@ -249,16 +248,14 @@ impl NymClient {
// TODO: a slightly more graceful termination here
if !topology_refresher.is_topology_routable().await {
log::error!(
"The current network topology seem to be insufficient to route any packets through \
panic!(
"The current network topology seem to be insufficient to route any packets through\
- check if enough nodes and a gateway are online"
);
return Err(ClientCoreError::InsufficientNetworkTopology.into());
}
info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown);
Ok(())
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
@@ -266,13 +263,13 @@ impl NymClient {
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
&mut self,
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) -> BatchMixMessageSender {
) {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
MixTrafficController::new(mix_rx, gateway_client).start_with_shutdown(shutdown);
}
fn start_socks5_listener(
@@ -297,8 +294,8 @@ impl NymClient {
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) -> Result<(), Socks5ClientError> {
let mut shutdown = self.start().await?;
pub async fn run_forever(&mut self) {
let mut shutdown = self.start().await;
wait_for_signal().await;
log::info!("Sending shutdown");
@@ -309,15 +306,11 @@ impl NymClient {
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
Ok(())
}
// Variant of `run_forever` that listends for remote control messages
pub async fn run_and_listen(
&mut self,
mut receiver: Socks5ControlMessageReceiver,
) -> Result<(), Socks5ClientError> {
let mut shutdown = self.start().await?;
pub async fn run_and_listen(&mut self, mut receiver: Socks5ControlMessageReceiver) {
let mut shutdown = self.start().await;
tokio::select! {
message = receiver.next() => {
log::debug!("Received message: {:?}", message);
@@ -343,16 +336,20 @@ impl NymClient {
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
Ok(())
}
pub async fn start(&mut self) -> Result<ShutdownNotifier, Socks5ClientError> {
pub async fn start(&mut self) -> ShutdownNotifier {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
// sphinx_message_sender is the transmitter for any component generating sphinx packets that are to be sent to the mixnet
// they are used by cover traffic stream and real traffic stream
// sphinx_message_receiver is the receiver used by MixTrafficController that sends the actual traffic
let (sphinx_message_sender, sphinx_message_receiver) = mpsc::unbounded();
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
@@ -377,7 +374,7 @@ impl NymClient {
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
.await?;
.await;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
@@ -389,13 +386,11 @@ impl NymClient {
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await;
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
self.start_mix_traffic_controller(
sphinx_message_receiver,
gateway_client,
shutdown.subscribe(),
);
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
reply_key_storage,
@@ -426,6 +421,6 @@ impl NymClient {
info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient());
Ok(shutdown)
shutdown
}
}
+2 -4
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use completions::{fig_generate, ArgShell};
@@ -84,17 +83,16 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), Socks5ClientError> {
pub(crate) async fn execute(args: &Cli) {
let bin_name = "nym-socks5-client";
match &args.command {
Commands::Init(m) => init::execute(m).await,
Commands::Run(m) => run::execute(m).await?,
Commands::Run(m) => run::execute(m).await,
Commands::Upgrade(m) => upgrade::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
}
Ok(())
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
+4 -5
View File
@@ -4,7 +4,6 @@
use crate::{
client::{config::Config, NymClient},
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
use clap::Args;
@@ -81,14 +80,14 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: &Run) -> Result<(), Socks5ClientError> {
pub(crate) async fn execute(args: &Run) {
let id = &args.id;
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
return Err(Socks5ClientError::FailedToLoadConfig(id.to_string()));
return;
}
};
@@ -97,8 +96,8 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Socks5ClientError> {
if !version_check(&config) {
error!("failed the local version check");
return Err(Socks5ClientError::FailedLocalVersionCheck);
return;
}
NymClient::new(config).run_forever().await
NymClient::new(config).run_forever().await;
}
-23
View File
@@ -1,23 +0,0 @@
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
#[error("Failed to load config for: {0}")]
FailedToLoadConfig(String),
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
}
-1
View File
@@ -2,5 +2,4 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client;
pub mod error;
pub mod socks;
+22 -5
View File
@@ -2,23 +2,20 @@
// SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, Parser};
use error::Socks5ClientError;
use logging::setup_logging;
use network_defaults::setup_env;
pub mod client;
mod commands;
pub mod error;
pub mod socks;
#[tokio::main]
async fn main() -> Result<(), Socks5ClientError> {
async fn main() {
setup_logging();
println!("{}", banner());
let args = commands::Cli::parse();
setup_env(args.config_env_file.clone());
commands::execute(&args).await
commands::execute(&args).await;
}
fn banner() -> String {
@@ -37,3 +34,23 @@ fn banner() -> String {
crate_version!()
)
}
fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") {
log_builder.parse_filters(&s);
} else {
// default to 'Info'
log_builder.filter(None, log::LevelFilter::Info);
}
log_builder
.filter_module("hyper", log::LevelFilter::Warn)
.filter_module("tokio_reactor", log::LevelFilter::Warn)
.filter_module("reqwest", log::LevelFilter::Warn)
.filter_module("mio", log::LevelFilter::Warn)
.filter_module("want", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.init();
}
+3 -5
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.0.1"
version = "1.0.0"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
@@ -55,8 +55,6 @@ wee_alloc = { version = "0.4", optional = true }
wasm-bindgen-test = "0.3"
[package.metadata.wasm-pack.profile.release]
wasm-opt = true
wasm-opt = false
[profile.release]
lto = true
opt-level = 'z'
@@ -2,11 +2,6 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
entry: {
bootstrap: './bootstrap.js',
worker: './worker.js',
@@ -27,7 +22,6 @@ module.exports = {
},
],
}),
],
experiments: { syncWebAssembly: true },
};
-1
View File
@@ -73,7 +73,6 @@ async function main() {
// const preferredGateway = 'CgQrYP8etksSBf4nALNqp93SHPpgFwEUyTsjBNNLj5WM';
const gatewayEndpoint = await get_gateway(validator, preferredGateway);
gatewayEndpoint.gateway_listener = "wss://gateway1.nymtech.net:443"; // this is needed if we want it to work on the web. However this gateway is a v1 gateway, we will need to change for v2 once we get there
// only really useful if you want to adjust some settings like traffic rate
// (if not needed you can just pass a null)
File diff suppressed because it is too large Load Diff
+3 -8
View File
@@ -4,7 +4,7 @@
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
#![allow(clippy::drop_non_drop)]
use client_core::config::{Debug as ConfigDebug, ExtendedPacketSize, GatewayEndpoint};
use client_core::config::{Debug as ConfigDebug, GatewayEndpoint};
use std::time::Duration;
use url::Url;
use wasm_bindgen::prelude::*;
@@ -107,11 +107,6 @@ pub struct Debug {
impl From<Debug> for ConfigDebug {
fn from(debug: Debug) -> Self {
// For now we just always use the (older) 32kb extended size
let use_extended_packet_size = debug
.use_extended_packet_size
.then(|| ExtendedPacketSize::Extended32);
ConfigDebug {
average_packet_delay: Duration::from_millis(debug.average_packet_delay_ms),
average_ack_delay: Duration::from_millis(debug.average_ack_delay_ms),
@@ -131,7 +126,7 @@ impl From<Debug> for ConfigDebug {
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
disable_main_poisson_packet_distribution: debug
.disable_main_poisson_packet_distribution,
use_extended_packet_size,
use_extended_packet_size: debug.use_extended_packet_size,
}
}
}
@@ -153,7 +148,7 @@ impl From<ConfigDebug> for Debug {
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
disable_main_poisson_packet_distribution: debug
.disable_main_poisson_packet_distribution,
use_extended_packet_size: debug.use_extended_packet_size.is_some(),
use_extended_packet_size: debug.use_extended_packet_size,
}
}
}
+18 -15
View File
@@ -6,7 +6,7 @@ use client_core::client::{
cover_traffic_stream::LoopCoverTrafficStream,
inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender},
key_manager::KeyManager,
mix_traffic::{BatchMixMessageSender, MixTrafficController},
mix_traffic::{BatchMixMessageReceiver, BatchMixMessageSender, MixTrafficController},
real_messages_control::{self, RealMessagesController},
received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestReceiver, ReceivedBufferRequestSender,
@@ -22,6 +22,7 @@ use gateway_client::{
MixnetMessageSender,
};
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::params::PacketSize;
use rand::rngs::OsRng;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::spawn_local;
@@ -109,8 +110,8 @@ impl NymClient {
topology_accessor,
);
if let Some(size) = &self.config.debug.use_extended_packet_size {
stream.set_custom_packet_size(size.clone().into());
if self.config.debug.use_extended_packet_size {
stream.set_custom_packet_size(PacketSize::ExtendedPacket)
}
stream.start();
@@ -134,8 +135,8 @@ impl NymClient {
self.as_mix_recipient(),
);
if let Some(size) = &self.config.debug.use_extended_packet_size {
controller_config.set_custom_packet_size(size.clone().into());
if self.config.debug.use_extended_packet_size {
controller_config.set_custom_packet_size(PacketSize::ExtendedPacket)
}
console_log!("Starting real traffic stream...");
@@ -252,11 +253,13 @@ impl NymClient {
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(gateway_client: GatewayClient) -> BatchMixMessageSender {
fn start_mix_traffic_controller(
&mut self,
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
) {
console_log!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start();
mix_tx
MixTrafficController::new(mix_rx, gateway_client).start();
}
// TODO: this procedure is extremely overcomplicated, because it's based off native client's behaviour
@@ -304,6 +307,11 @@ impl NymClient {
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
// sphinx_message_sender is the transmitter for any component generating sphinx packets that are to be sent to the mixnet
// they are used by cover traffic stream and real traffic stream
// sphinx_message_receiver is the receiver used by MixTrafficController that sends the actual traffic
let (sphinx_message_sender, sphinx_message_receiver) = mpsc::unbounded();
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
@@ -331,12 +339,7 @@ impl NymClient {
.start_gateway_client(mixnet_messages_sender, ack_sender)
.await;
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender = Self::start_mix_traffic_controller(gateway_client);
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
ack_receiver,
-2
View File
@@ -1,2 +0,0 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
@@ -62,19 +62,9 @@ impl PacketRouter {
trace!("routing regular packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket8.plaintext_size() - ack_overhead
== PacketSize::ExtendedPacket.plaintext_size() - ack_overhead
{
trace!("routing extended8 packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket16.plaintext_size() - ack_overhead
{
trace!("routing extended16 packet");
received_messages.push(received_packet);
} else if received_packet.len()
== PacketSize::ExtendedPacket32.plaintext_size() - ack_overhead
{
trace!("routing extended32 packet");
trace!("routing extended packet");
received_messages.push(received_packet);
} else {
// this can happen if other clients are not padding their messages
@@ -23,7 +23,6 @@ pub struct Config {
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
}
impl Config {
@@ -32,14 +31,12 @@ impl Config {
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
) -> Self {
Config {
initial_reconnection_backoff,
maximum_reconnection_backoff,
initial_connection_timeout,
maximum_connection_buffer_size,
use_legacy_version,
}
}
}
@@ -204,8 +201,7 @@ impl SendWithoutResponse for Client {
packet_mode: PacketMode,
) -> io::Result<()> {
trace!("Sending packet to {:?}", address);
let framed_packet =
FramedSphinxPacket::new(packet, packet_mode, self.config.use_legacy_version);
let framed_packet = FramedSphinxPacket::new(packet, packet_mode);
if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) {
@@ -263,7 +259,6 @@ mod tests {
maximum_reconnection_backoff: Duration::from_millis(300_000),
initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128,
use_legacy_version: false,
})
}
@@ -24,14 +24,12 @@ impl PacketForwarder {
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
) -> (PacketForwarder, MixForwardingSender) {
let client_config = Config::new(
initial_reconnection_backoff,
maximum_reconnection_backoff,
initial_connection_timeout,
maximum_connection_buffer_size,
use_legacy_version,
);
let (packet_sender, packet_receiver) = mpsc::unbounded();
@@ -13,7 +13,6 @@ colored = "2.0"
cw3 = "0.13.1"
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
vesting-contract = { path = "../../../contracts/vesting" }
@@ -6,10 +6,8 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
use crate::nymd::error::NymdError;
use crate::nymd::NymdClient;
use async_trait::async_trait;
use contracts_common::ContractBuildInformation;
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
use mixnet_contract_common::MixId;
use serde::Deserialize;
use vesting_contract::vesting::Account;
use vesting_contract_common::{
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
@@ -18,15 +16,6 @@ use vesting_contract_common::{
#[async_trait]
pub trait VestingQueryClient {
async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NymdError>
where
for<'a> T: Deserialize<'a>;
async fn get_vesting_contract_version(&self) -> Result<ContractBuildInformation, NymdError> {
self.query_vesting_contract(VestingQueryMsg::GetContractVersion {})
.await
}
async fn locked_coins(
&self,
address: &str,
@@ -118,15 +107,6 @@ pub trait VestingQueryClient {
#[async_trait]
impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NymdError>
where
for<'a> T: Deserialize<'a>,
{
self.client
.query_contract_smart(self.vesting_contract_address(), &query)
.await
}
async fn locked_coins(
&self,
vesting_account_address: &str,
@@ -1,5 +1,4 @@
use thiserror::Error;
use validator_api_requests::models::RequestError;
#[derive(Error, Debug)]
pub enum ValidatorAPIError {
@@ -11,7 +10,4 @@ pub enum ValidatorAPIError {
#[error("Request failed with error message - {0}")]
GenericRequestFailure(String),
#[error("The validator API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
ApiRequestFailure { status: u16, error: RequestError },
}
@@ -5,7 +5,6 @@ use crate::validator_api::error::ValidatorAPIError;
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use mixnet_contract_common::mixnode::MixNodeDetails;
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use reqwest::Response;
use serde::{Deserialize, Serialize};
use url::Url;
use validator_api_requests::coconut::{
@@ -13,10 +12,9 @@ use validator_api_requests::coconut::{
VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
GatewayCoreStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
};
pub mod error;
@@ -49,19 +47,6 @@ impl Client {
&self.url
}
async fn send_get_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, ValidatorAPIError>
where
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path, params);
Ok(self.reqwest_client.get(url).send().await?)
}
async fn query_validator_api<T, K, V>(
&self,
path: PathSegments<'_>,
@@ -72,36 +57,8 @@ impl Client {
K: AsRef<str>,
V: AsRef<str>,
{
let res = self.send_get_request(path, params).await?;
if res.status().is_success() {
Ok(res.json().await?)
} else {
Err(ValidatorAPIError::GenericRequestFailure(res.text().await?))
}
}
// This works for endpoints returning Result<Json<T>, ErrorResponse>
async fn query_validator_api_fallible<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, ValidatorAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let res = self.send_get_request(path, params).await?;
let status = res.status();
if res.status().is_success() {
Ok(res.json().await?)
} else {
let request_error: RequestError = res.json().await?;
Err(ValidatorAPIError::ApiRequestFailure {
status: status.as_u16(),
error: request_error,
})
}
let url = create_api_url(&self.url, path, params);
Ok(self.reqwest_client.get(url).send().await?.json().await?)
}
async fn post_validator_api<B, T, K, V>(
@@ -178,74 +135,6 @@ impl Client {
.await
}
pub async fn get_mixnode_report(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusReportResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS,
routes::MIXNODE,
&mix_id.to_string(),
routes::REPORT,
],
NO_PARAMS,
)
.await
}
pub async fn get_gateway_report(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayStatusReportResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS,
routes::GATEWAY,
identity,
routes::REPORT,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_history(
&self,
mix_id: MixId,
) -> Result<MixnodeUptimeHistoryResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS,
routes::MIXNODE,
&mix_id.to_string(),
routes::HISTORY,
],
NO_PARAMS,
)
.await
}
pub async fn get_gateway_history(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayUptimeHistoryResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS,
routes::GATEWAY,
identity,
routes::HISTORY,
],
NO_PARAMS,
)
.await
}
pub async fn get_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
@@ -345,7 +234,7 @@ impl Client {
&self,
mix_id: MixId,
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -362,7 +251,7 @@ impl Client {
&self,
mix_id: MixId,
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -379,7 +268,7 @@ impl Client {
&self,
mix_id: MixId,
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -396,7 +285,7 @@ impl Client {
&self,
mix_id: MixId,
) -> Result<UptimeResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -10,6 +10,7 @@ pub const GATEWAYS: &str = "gateways";
pub const DETAILED: &str = "detailed";
pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded";
pub const COCONUT_ROUTES: &str = "coconut";
pub const BANDWIDTH: &str = "bandwidth";
@@ -27,8 +28,6 @@ pub const CORE_STATUS_COUNT: &str = "core-status-count";
pub const SINCE_ARG: &str = "since";
pub const STATUS: &str = "status";
pub const REPORT: &str = "report";
pub const HISTORY: &str = "history";
pub const REWARD_ESTIMATION: &str = "reward-estimation";
pub const AVG_UPTIME: &str = "avg_uptime";
pub const STAKE_SATURATION: &str = "stake-saturation";
@@ -11,6 +11,3 @@ cosmwasm-std = "1.0.0"
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
thiserror = "1"
[dev-dependencies]
serde_json = "1.0.0"
@@ -11,8 +11,6 @@ use cosmwasm_std::Event;
/// * `event`: event to search through.
/// * `key`: key associated with the particular attribute
pub fn must_find_attribute(event: &Event, key: &str) -> String {
// due to how the function is supposed to work, the unwrap is fine in this instance
#[allow(clippy::unwrap_used)]
may_find_attribute(event, key).unwrap()
}
@@ -1,9 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub mod events;
pub mod types;
@@ -47,14 +47,6 @@ impl Percent {
self.0 == Decimal::zero()
}
pub fn zero() -> Self {
Self(Decimal::zero())
}
pub fn hundred() -> Self {
Self(Decimal::one())
}
pub fn from_percentage_value(value: u64) -> Result<Self, ContractsCommonError> {
Percent::new(Decimal::percent(value))
}
@@ -72,7 +64,7 @@ impl Percent {
impl Display for Percent {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let adjusted = Decimal::from_ratio(100u32, 1u32) * self.0;
let adjusted = Decimal::from_atomics(100u32, 0).unwrap() * self.0;
write!(f, "{}%", adjusted)
}
}
@@ -152,47 +144,3 @@ pub struct ContractBuildInformation {
/// Provides the rustc version that was used for the build, for example `1.52.0-nightly`.
pub rustc_version: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn percent_serde() {
let valid_value = Percent::from_percentage_value(80).unwrap();
let serialized = serde_json::to_string(&valid_value).unwrap();
let deserialized: Percent = serde_json::from_str(&serialized).unwrap();
assert_eq!(valid_value, deserialized);
let invalid_values = vec!["\"42\"", "\"1.1\"", "\"1.00000001\"", "\"foomp\"", "\"1a\""];
for invalid_value in invalid_values {
assert!(serde_json::from_str::<'_, Percent>(invalid_value).is_err())
}
assert_eq!(
serde_json::from_str::<'_, Percent>("\"0.95\"").unwrap(),
Percent::from_percentage_value(95).unwrap()
)
}
#[test]
fn percent_to_absolute_integer() {
let p = serde_json::from_str::<'_, Percent>("\"0.0001\"").unwrap();
assert_eq!(p.round_to_integer(), 0);
let p = serde_json::from_str::<'_, Percent>("\"0.0099\"").unwrap();
assert_eq!(p.round_to_integer(), 0);
let p = serde_json::from_str::<'_, Percent>("\"0.0199\"").unwrap();
assert_eq!(p.round_to_integer(), 1);
let p = serde_json::from_str::<'_, Percent>("\"0.45123\"").unwrap();
assert_eq!(p.round_to_integer(), 45);
let p = serde_json::from_str::<'_, Percent>("\"0.999999999\"").unwrap();
assert_eq!(p.round_to_integer(), 99);
let p = serde_json::from_str::<'_, Percent>("\"1.00\"").unwrap();
assert_eq!(p.round_to_integer(), 100);
}
}
@@ -1,9 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Decimal, Uint128};
pub const TOKEN_SUPPLY: Uint128 = Uint128::new(1_000_000_000_000_000);
use cosmwasm_std::Decimal;
// I'm still not 100% sure how to feel about existence of this file
// This is equivalent of representing our display coin with 6 decimal places.
@@ -4,10 +4,8 @@
// due to code generated by JsonSchema
#![allow(clippy::field_reassign_with_default)]
use crate::constants::TOKEN_SUPPLY;
use crate::helpers::IntoBaseDecimal;
use crate::{Addr, MixId};
use cosmwasm_std::{Coin, Decimal, StdResult};
use cosmwasm_std::{Coin, Decimal};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -62,11 +60,6 @@ impl Delegation {
height: u64,
proxy: Option<Addr>,
) -> Self {
assert!(
amount.amount <= TOKEN_SUPPLY,
"delegation cannot be larger than the token supply"
);
Delegation {
owner,
mix_id,
@@ -94,8 +87,10 @@ impl Delegation {
(mix_id, owner_proxy_subkey)
}
pub fn dec_amount(&self) -> StdResult<Decimal> {
self.amount.amount.into_base_decimal()
pub fn dec_amount(&self) -> Decimal {
// the unwrap here is fine as we're guaranteed our base coin amount is going to fit in a Decimal
// with 0 decimal places
Decimal::from_atomics(self.amount.amount, 0).unwrap()
}
pub fn proxy_storage_key(&self) -> OwnerProxySubKey {
@@ -1,7 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Decimal, StdError, StdResult, Uint128};
use cosmwasm_std::Decimal;
pub fn compare_decimals(a: Decimal, b: Decimal, epsilon: Option<Decimal>) {
let epsilon = epsilon.unwrap_or_else(|| Decimal::from_ratio(1u128, 100_000_000u128));
@@ -11,23 +11,3 @@ pub fn compare_decimals(a: Decimal, b: Decimal, epsilon: Option<Decimal>) {
assert!(b - a < epsilon, "{} != {}", a, b)
}
}
pub fn into_base_decimal(val: impl Into<Uint128>) -> StdResult<Decimal> {
val.into_base_decimal()
}
pub trait IntoBaseDecimal {
fn into_base_decimal(self) -> StdResult<Decimal>;
}
impl<T> IntoBaseDecimal for T
where
T: Into<Uint128>,
{
fn into_base_decimal(self) -> StdResult<Decimal> {
let atomics = self.into();
Decimal::from_atomics(atomics, 0).map_err(|_| StdError::GenericErr {
msg: format!("Decimal range exceeded for {atomics} with 0 decimal places."),
})
}
}
@@ -73,9 +73,6 @@ pub struct Interval {
// TODO add a better TS type generation
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
#[serde(with = "string_rfc3339_offset_date_time")]
// note: the `ts-rs failed to parse this attribute. It will be ignored.` warning emitted during
// compilation is fine (I guess). `ts-rs` can't handle `with` serde attribute, but that's okay
// since we explicitly specified this field should correspond to typescript's string
current_epoch_start: OffsetDateTime,
current_epoch_id: EpochId,
#[cfg_attr(feature = "generate-ts", ts(type = "{ secs: number; nanos: number; }"))]
@@ -145,17 +142,14 @@ impl JsonSchema for Interval {
impl Interval {
/// Initialize epoch in the contract with default values.
pub fn init_interval(epochs_in_interval: u32, epoch_length: Duration, env: &Env) -> Self {
// if this fails it means the value provided from the chain itself (via cosmwasm) is invalid,
// so we really have to panic here as anything beyond that point would be invalid anyway
#[allow(clippy::expect_used)]
let current_epoch_start =
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64)
.expect("The timestamp provided via env.block.time is invalid");
Interval {
id: 0,
epochs_in_interval,
current_epoch_start,
// I really don't see a way for this to fail, unless the blockchain is lying to us
current_epoch_start: OffsetDateTime::from_unix_timestamp(
env.block.time.seconds() as i64
)
.expect("Invalid timestamp from env.block.time"),
current_epoch_id: 0,
epoch_length,
total_elapsed_epochs: 0,
@@ -1,9 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
mod constants;
pub mod delegation;
pub mod error;
@@ -4,14 +4,13 @@
// due to code generated by JsonSchema
#![allow(clippy::field_reassign_with_default)]
use crate::constants::{TOKEN_SUPPLY, UNIT_DELEGATION_BASE};
use crate::constants::UNIT_DELEGATION_BASE;
use crate::error::MixnetContractError;
use crate::helpers::IntoBaseDecimal;
use crate::reward_params::{NodeRewardParams, RewardingParams};
use crate::rewarding::helpers::truncate_reward;
use crate::rewarding::RewardDistribution;
use crate::{Delegation, EpochId, IdentityKey, MixId, Percent, SphinxKey};
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -65,7 +64,7 @@ impl MixNodeDetails {
self.rewarding_details.pending_operator_reward(pledge)
}
pub fn pending_detailed_operator_reward(&self) -> StdResult<Decimal> {
pub fn pending_detailed_operator_reward(&self) -> Decimal {
let pledge = self.original_pledge();
self.rewarding_details
.pending_detailed_operator_reward(pledge)
@@ -108,21 +107,16 @@ impl MixNodeRewarding {
cost_params: MixNodeCostParams,
initial_pledge: &Coin,
current_epoch: EpochId,
) -> Result<Self, MixnetContractError> {
assert!(
initial_pledge.amount <= TOKEN_SUPPLY,
"pledge cannot be larger than the token supply"
);
Ok(MixNodeRewarding {
) -> Self {
MixNodeRewarding {
cost_params,
operator: initial_pledge.amount.into_base_decimal()?,
operator: Decimal::from_atomics(initial_pledge.amount, 0).unwrap(),
delegates: Decimal::zero(),
total_unit_reward: Decimal::zero(),
unit_delegation: UNIT_DELEGATION_BASE,
last_rewarded_epoch: current_epoch,
unique_delegations: 0,
})
}
}
/// Determines whether this node is still bonded. This is performed via a simple check,
@@ -141,30 +135,27 @@ impl MixNodeRewarding {
}
}
pub fn pending_detailed_operator_reward(&self, original_pledge: &Coin) -> StdResult<Decimal> {
let initial_dec = original_pledge.amount.into_base_decimal()?;
pub fn pending_detailed_operator_reward(&self, original_pledge: &Coin) -> Decimal {
let initial_dec = Decimal::from_atomics(original_pledge.amount, 0).unwrap();
if initial_dec > self.operator {
panic!(
"seems slashing has occurred while it has not been implemented nor accounted for!"
)
}
Ok(self.operator - initial_dec)
self.operator - initial_dec
}
pub fn operator_pledge_with_reward(&self, denom: impl Into<String>) -> Coin {
truncate_reward(self.operator, denom)
}
pub fn pending_delegator_reward(&self, delegation: &Delegation) -> StdResult<Coin> {
let delegator_reward = self.determine_delegation_reward(delegation)?;
Ok(truncate_reward(delegator_reward, &delegation.amount.denom))
pub fn pending_delegator_reward(&self, delegation: &Delegation) -> Coin {
let delegator_reward = self.determine_delegation_reward(delegation);
truncate_reward(delegator_reward, &delegation.amount.denom)
}
pub fn withdraw_operator_reward(
&mut self,
original_pledge: &Coin,
) -> Result<Coin, MixnetContractError> {
let initial_dec = original_pledge.amount.into_base_decimal()?;
pub fn withdraw_operator_reward(&mut self, original_pledge: &Coin) -> Coin {
let initial_dec = Decimal::from_atomics(original_pledge.amount, 0).unwrap();
if initial_dec > self.operator {
panic!(
"seems slashing has occurred while it has not been implemented nor accounted for!"
@@ -173,14 +164,14 @@ impl MixNodeRewarding {
let diff = self.operator - initial_dec;
self.operator = initial_dec;
Ok(truncate_reward(diff, &original_pledge.denom))
truncate_reward(diff, &original_pledge.denom)
}
pub fn withdraw_delegator_reward(
&mut self,
delegation: &mut Delegation,
) -> Result<Coin, MixnetContractError> {
let reward = self.determine_delegation_reward(delegation)?;
let reward = self.determine_delegation_reward(delegation);
self.decrease_delegates_decimal(reward)?;
delegation.cumulative_reward_ratio = self.full_reward_ratio();
@@ -310,27 +301,23 @@ impl MixNodeRewarding {
self.distribute_rewards(reward_distribution, absolute_epoch_id)
}
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> StdResult<Decimal> {
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> Decimal {
let starting_ratio = delegation.cumulative_reward_ratio;
let ending_ratio = self.full_reward_ratio();
let adjust = starting_ratio + self.unit_delegation;
Ok((ending_ratio - starting_ratio) * delegation.dec_amount()? / adjust)
(ending_ratio - starting_ratio) * delegation.dec_amount() / adjust
}
// this updates `unique_delegations` field
pub fn add_base_delegation(&mut self, amount: Uint128) -> Result<(), MixnetContractError> {
self.increase_delegates_uint128(amount)?;
pub fn add_base_delegation(&mut self, amount: Uint128) {
self.increase_delegates_uint128(amount);
self.unique_delegations += 1;
Ok(())
}
pub fn increase_delegates_uint128(
&mut self,
amount: Uint128,
) -> Result<(), MixnetContractError> {
self.delegates += amount.into_base_decimal()?;
Ok(())
pub fn increase_delegates_uint128(&mut self, amount: Uint128) {
// the unwrap here is fine as the value is guaranteed to fit under provided constraints
self.delegates += Decimal::from_atomics(amount, 0).unwrap()
}
// this updates `unique_delegations` field
@@ -348,7 +335,7 @@ impl MixNodeRewarding {
&mut self,
amount: Uint128,
) -> Result<(), MixnetContractError> {
let amount_dec = amount.into_base_decimal()?;
let amount_dec = Decimal::from_atomics(amount, 0).unwrap();
self.decrease_delegates_decimal(amount_dec)
}
@@ -381,8 +368,8 @@ impl MixNodeRewarding {
}
pub fn undelegate(&mut self, delegation: &Delegation) -> Result<Coin, MixnetContractError> {
let reward = self.determine_delegation_reward(delegation)?;
let full_amount = reward + delegation.dec_amount()?;
let reward = self.determine_delegation_reward(delegation);
let full_amount = reward + delegation.dec_amount();
self.remove_delegation_decimal(full_amount)?;
Ok(truncate_reward(full_amount, &delegation.amount.denom))
}
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::delegation::OwnerProxySubKey;
use crate::error::MixnetContractError;
use crate::helpers::IntoBaseDecimal;
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use crate::reward_params::{
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
@@ -33,7 +31,6 @@ pub struct InitialRewardingParams {
pub initial_reward_pool: Decimal,
pub initial_staking_supply: Decimal,
pub staking_supply_scale_factor: Percent,
pub sybil_resistance: Percent,
pub active_set_work_factor: Decimal,
pub interval_pool_emission: Percent,
@@ -43,21 +40,17 @@ pub struct InitialRewardingParams {
}
impl InitialRewardingParams {
pub fn into_rewarding_params(
self,
epochs_in_interval: u32,
) -> Result<RewardingParams, MixnetContractError> {
pub fn into_rewarding_params(self, epochs_in_interval: u32) -> RewardingParams {
let epoch_reward_budget = self.initial_reward_pool
/ epochs_in_interval.into_base_decimal()?
/ Decimal::from_atomics(epochs_in_interval, 0).unwrap()
* self.interval_pool_emission;
let stake_saturation_point =
self.initial_staking_supply / self.rewarded_set_size.into_base_decimal()?;
self.initial_staking_supply / Decimal::from_atomics(self.rewarded_set_size, 0).unwrap();
Ok(RewardingParams {
RewardingParams {
interval: IntervalRewardParams {
reward_pool: self.initial_reward_pool,
staking_supply: self.initial_staking_supply,
staking_supply_scale_factor: self.staking_supply_scale_factor,
epoch_reward_budget,
stake_saturation_point,
sybil_resistance: self.sybil_resistance,
@@ -66,7 +59,7 @@ impl InitialRewardingParams {
},
rewarded_set_size: self.rewarded_set_size,
active_set_size: self.active_set_size,
})
}
}
}
@@ -1,7 +1,6 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::IntoBaseDecimal;
use crate::{error::MixnetContractError, Percent};
use cosmwasm_std::Decimal;
use schemars::JsonSchema;
@@ -27,11 +26,6 @@ pub struct IntervalRewardParams {
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
pub staking_supply: Decimal,
/// Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set.
/// Also known as `beta`.
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
pub staking_supply_scale_factor: Percent,
// computed values
/// Current value of the computed reward budget per epoch, per node.
/// It is expected to be constant throughout the interval.
@@ -111,33 +105,24 @@ impl RewardingParams {
pub fn dec_rewarded_set_size(&self) -> Decimal {
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
// with 0 decimal places
#[allow(clippy::unwrap_used)]
self.rewarded_set_size.into_base_decimal().unwrap()
Decimal::from_atomics(self.rewarded_set_size, 0).unwrap()
}
pub fn dec_active_set_size(&self) -> Decimal {
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
// with 0 decimal places
#[allow(clippy::unwrap_used)]
self.active_set_size.into_base_decimal().unwrap()
Decimal::from_atomics(self.active_set_size, 0).unwrap()
}
fn dec_standby_set_size(&self) -> Decimal {
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
// with 0 decimal places
#[allow(clippy::unwrap_used)]
(self.rewarded_set_size - self.active_set_size)
.into_base_decimal()
.unwrap()
Decimal::from_atomics(self.rewarded_set_size - self.active_set_size, 0).unwrap()
}
pub fn apply_epochs_in_interval_change(&mut self, new_epochs_in_interval: u32) {
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
// with 0 decimal places
#[allow(clippy::unwrap_used)]
let new_epochs_in_interval = new_epochs_in_interval.into_base_decimal().unwrap();
self.interval.epoch_reward_budget = self.interval.reward_pool / new_epochs_in_interval
self.interval.epoch_reward_budget = self.interval.reward_pool
/ Decimal::from_atomics(new_epochs_in_interval, 0).unwrap()
* self.interval.interval_pool_emission;
}
@@ -179,10 +164,6 @@ impl RewardingParams {
self.interval.staking_supply = staking_supply;
}
if let Some(staking_supply_scale_factor) = updates.staking_supply_scale_factor {
self.interval.staking_supply_scale_factor = staking_supply_scale_factor
}
if let Some(sybil_resistance_percent) = updates.sybil_resistance_percent {
self.interval.sybil_resistance = sybil_resistance_percent;
}
@@ -210,13 +191,13 @@ impl RewardingParams {
if recompute_epoch_budget {
self.interval.epoch_reward_budget = self.interval.reward_pool
/ epochs_in_interval.into_base_decimal()?
/ Decimal::from_atomics(epochs_in_interval, 0).unwrap()
* self.interval.interval_pool_emission;
}
if recompute_saturation_point {
self.interval.stake_saturation_point =
self.interval.staking_supply / self.rewarded_set_size.into_base_decimal()?
self.interval.stake_saturation_point = self.interval.staking_supply
/ Decimal::from_atomics(self.rewarded_set_size, 0).unwrap();
}
Ok(())
@@ -254,9 +235,6 @@ pub struct IntervalRewardingParamsUpdate {
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
pub staking_supply: Option<Decimal>,
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
pub staking_supply_scale_factor: Option<Percent>,
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
pub sybil_resistance_percent: Option<Percent>,
@@ -274,7 +252,6 @@ impl IntervalRewardingParamsUpdate {
// essentially at least a single field has to be a `Some`
self.reward_pool.is_some()
|| self.staking_supply.is_some()
|| self.staking_supply_scale_factor.is_some()
|| self.sybil_resistance_percent.is_some()
|| self.active_set_work_factor.is_some()
|| self.interval_pool_emission.is_some()
@@ -29,7 +29,7 @@ pub struct RewardEstimate {
pub operating_cost: Decimal,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
pub struct RewardDistribution {
pub operator: Decimal,
pub delegates: Decimal,
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::MixnetContractError;
use crate::helpers::IntoBaseDecimal;
use crate::reward_params::NodeRewardParams;
use crate::rewarding::simulator::simulated_node::SimulatedNode;
use crate::rewarding::RewardDistribution;
@@ -34,33 +33,24 @@ impl Simulator {
}
}
fn advance_epoch(&mut self) -> Result<(), MixnetContractError> {
fn advance_epoch(&mut self) {
let updated = self.interval.advance_epoch();
// we rolled over an interval
if self.interval.current_interval_id() + 1 == updated.current_interval_id() {
let old = self.system_rewarding_params.interval;
let reward_pool = old.reward_pool - self.pending_reward_pool_emission;
let staking_supply = old.staking_supply
+ self
.system_rewarding_params
.interval
.staking_supply_scale_factor
* self.pending_reward_pool_emission;
let staking_supply = old.staking_supply + self.pending_reward_pool_emission;
let epoch_reward_budget = reward_pool
/ self.interval.epochs_in_interval().into_base_decimal()?
/ Decimal::from_atomics(self.interval.epochs_in_interval(), 0).unwrap()
* old.interval_pool_emission.value();
let stake_saturation_point = staking_supply
/ self
.system_rewarding_params
.rewarded_set_size
.into_base_decimal()?;
/ Decimal::from_atomics(self.system_rewarding_params.rewarded_set_size, 0).unwrap();
let updated_params = RewardingParams {
interval: IntervalRewardParams {
reward_pool,
staking_supply,
staking_supply_scale_factor: old.staking_supply_scale_factor,
epoch_reward_budget,
stake_saturation_point,
sybil_resistance: old.sybil_resistance,
@@ -75,15 +65,9 @@ impl Simulator {
self.pending_reward_pool_emission = Decimal::zero();
}
self.interval = updated;
Ok(())
}
pub fn bond(
&mut self,
pledge: Coin,
cost_params: MixNodeCostParams,
) -> Result<MixId, MixnetContractError> {
pub fn bond(&mut self, pledge: Coin, cost_params: MixNodeCostParams) -> MixId {
let mix_id = self.next_mix_id;
self.nodes.insert(
@@ -93,24 +77,16 @@ impl Simulator {
cost_params,
&pledge,
self.interval.current_epoch_absolute_id(),
)?,
),
);
self.next_mix_id += 1;
Ok(mix_id)
mix_id
}
pub fn delegate<S: Into<String>>(
&mut self,
delegator: S,
delegation: Coin,
mix_id: MixId,
) -> Result<(), MixnetContractError> {
let node = self
.nodes
.get_mut(&mix_id)
.ok_or(MixnetContractError::MixNodeBondNotFound { mix_id })?;
pub fn delegate<S: Into<String>>(&mut self, delegator: S, delegation: Coin, mix_id: MixId) {
let node = self.nodes.get_mut(&mix_id).expect("node doesn't exist");
node.delegate(delegator, delegation)
}
@@ -121,35 +97,23 @@ impl Simulator {
delegator: S,
mix_id: MixId,
) -> Result<(Coin, Coin), MixnetContractError> {
let node = self
.nodes
.get_mut(&mix_id)
.ok_or(MixnetContractError::MixNodeBondNotFound { mix_id })?;
let node = self.nodes.get_mut(&mix_id).expect("node not found");
node.undelegate(delegator)
}
pub fn simulate_epoch_single_node(
&mut self,
params: NodeRewardParams,
) -> Result<RewardDistribution, MixnetContractError> {
pub fn simulate_epoch_single_node(&mut self, params: NodeRewardParams) -> RewardDistribution {
assert_eq!(self.nodes.len(), 1);
if let Some(&id) = self.nodes.keys().next() {
let mut params_map = BTreeMap::new();
params_map.insert(id, params);
Ok(self
.simulate_epoch(&params_map)?
.remove(&id)
.unwrap_or_default())
} else {
Ok(RewardDistribution::default())
}
let id = *self.nodes.keys().next().unwrap();
let mut params_map = BTreeMap::new();
params_map.insert(id, params);
self.simulate_epoch(&params_map).remove(&id).unwrap()
}
pub fn simulate_epoch(
&mut self,
node_params: &BTreeMap<MixId, NodeRewardParams>,
) -> Result<BTreeMap<MixId, RewardDistribution>, MixnetContractError> {
) -> BTreeMap<MixId, RewardDistribution> {
let mut params_keys = node_params.keys().copied().collect::<Vec<_>>();
params_keys.sort_unstable();
let mut node_keys = self.nodes.keys().copied().collect::<Vec<_>>();
@@ -177,41 +141,34 @@ impl Simulator {
dist.insert(*mix_id, reward_distribution);
}
self.advance_epoch()?;
Ok(dist)
self.advance_epoch();
dist
}
pub fn determine_delegation_reward(
&self,
delegation: &Delegation,
) -> Result<Decimal, MixnetContractError> {
Ok(self.nodes[&delegation.mix_id]
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> Decimal {
self.nodes[&delegation.mix_id]
.rewarding_details
.determine_delegation_reward(delegation)?)
.determine_delegation_reward(delegation)
}
pub fn determine_total_delegation_reward(&self) -> Result<Decimal, MixnetContractError> {
pub fn determine_total_delegation_reward(&self) -> Decimal {
let mut total = Decimal::zero();
for node in self.nodes.values() {
for delegation in node.delegations.values() {
total += node
.rewarding_details
.determine_delegation_reward(delegation)?
.determine_delegation_reward(delegation)
}
}
Ok(total)
total
}
// assume node state doesn't change in the interval (kinda unrealistic)
pub fn simulate_full_interval(
&mut self,
node_params: &BTreeMap<MixId, NodeRewardParams>,
) -> Result<(), MixnetContractError> {
pub fn simulate_full_interval(&mut self, node_params: &BTreeMap<MixId, NodeRewardParams>) {
for _ in 0..self.interval.epochs_in_interval() {
self.simulate_epoch(node_params)?;
self.simulate_epoch(node_params);
}
Ok(())
}
}
@@ -234,10 +191,6 @@ mod tests {
use cosmwasm_std::testing::mock_env;
use std::time::Duration;
// explicitly marking this as part of #[allow(clippy::unwrap_used)] until
// https://github.com/rust-lang/rust-clippy/pull/9686
// is merged into a release
#[allow(clippy::unwrap_used)]
fn base_simulator(initial_pledge: u128) -> Simulator {
let profit_margin = Percent::from_percentage_value(10).unwrap();
let interval_operating_cost = Coin::new(40_000_000, "unym");
@@ -256,7 +209,6 @@ mod tests {
interval: IntervalRewardParams {
reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(), // 250M * 1M (we're expressing it all in base tokens)
staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(), // 100M * 1M
staking_supply_scale_factor: Percent::hundred(),
epoch_reward_budget,
stake_saturation_point,
sybil_resistance: Percent::from_percentage_value(30).unwrap(),
@@ -279,32 +231,20 @@ mod tests {
profit_margin_percent: profit_margin,
interval_operating_cost,
};
simulator.bond(initial_pledge, cost_params).unwrap();
simulator.bond(initial_pledge, cost_params);
simulator
}
// essentially our delegations + estimated rewards HAVE TO equal to what we actually determined
//
// explicitly marking this as part of #[allow(clippy::unwrap_used)] until
// https://github.com/rust-lang/rust-clippy/pull/9686
// is merged into a release
#[allow(clippy::unwrap_used)]
fn check_rewarding_invariant(simulator: &Simulator) {
for node in simulator.nodes.values() {
let delegation_sum: Decimal = node
.delegations
.values()
.map(|d| d.dec_amount().unwrap())
.sum();
let delegation_sum: Decimal =
node.delegations.values().map(|d| d.dec_amount()).sum();
let reward_sum: Decimal = node
.delegations
.values()
.map(|d| {
node.rewarding_details
.determine_delegation_reward(d)
.unwrap()
})
.map(|d| node.rewarding_details.determine_delegation_reward(d))
.sum();
// let reward_sum = simulator.determine_total_delegation_reward();
@@ -322,7 +262,7 @@ mod tests {
let epoch_params =
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
let rewards = simulator.simulate_epoch_single_node(epoch_params).unwrap();
let rewards = simulator.simulate_epoch_single_node(epoch_params);
assert_eq!(rewards.delegates, Decimal::zero());
compare_decimals(
@@ -335,13 +275,11 @@ mod tests {
#[test]
fn single_delegation_at_genesis() {
let mut simulator = base_simulator(10000_000000);
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.unwrap();
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
let node_params =
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
let rewards = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards = simulator.simulate_epoch_single_node(node_params);
compare_decimals(
rewards.delegates,
@@ -352,7 +290,7 @@ mod tests {
compare_decimals(
rewards.delegates,
simulator.determine_total_delegation_reward().unwrap(),
simulator.determine_total_delegation_reward(),
None,
);
let node = &simulator.nodes[&0];
@@ -372,22 +310,20 @@ mod tests {
let node_params =
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards1 = simulator.simulate_epoch_single_node(node_params);
let expected_operator1 = "1128452.5416104363".parse().unwrap();
assert_eq!(rewards1.delegates, Decimal::zero());
compare_decimals(rewards1.operator, expected_operator1, None);
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.unwrap();
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards2 = simulator.simulate_epoch_single_node(node_params);
let expected_operator2 = "1363843.413584609".parse().unwrap();
let expected_delegator_reward1 = "1795952.25874404".parse().unwrap();
compare_decimals(rewards2.delegates, expected_delegator_reward1, None);
compare_decimals(rewards2.operator, expected_operator2, None);
let rewards3 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards3 = simulator.simulate_epoch_single_node(node_params);
let expected_operator3 = "1364017.7824440491".parse().unwrap();
let expected_delegator_reward2 = "1796135.9269468693".parse().unwrap();
compare_decimals(rewards3.delegates, expected_delegator_reward2, None);
@@ -421,15 +357,11 @@ mod tests {
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
// delegating at different times still work)
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.unwrap();
simulator
.delegate("bob", Coin::new(4000_000000, "unym"), 0)
.unwrap();
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
simulator.delegate("bob", Coin::new(4000_000000, "unym"), 0);
// "normal", sanity check rewarding
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards1 = simulator.simulate_epoch_single_node(node_params);
let expected_operator1 = "1411087.1007647323".parse().unwrap();
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
compare_decimals(rewards1.delegates, expected_delegator_reward1, None);
@@ -439,15 +371,14 @@ mod tests {
let node = simulator.nodes.get_mut(&0).unwrap();
let reward = node
.rewarding_details
.withdraw_operator_reward(&original_pledge)
.unwrap();
.withdraw_operator_reward(&original_pledge);
assert_eq!(reward.amount, truncate_reward_amount(expected_operator1));
assert_eq!(
node.rewarding_details.operator,
Decimal::from_atomics(original_pledge.amount, 0).unwrap()
);
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards2 = simulator.simulate_epoch_single_node(node_params);
let expected_operator2 = "1411113.0004067947".parse().unwrap();
let expected_delegator_reward2 = "2200183.3879084454".parse().unwrap();
compare_decimals(rewards2.delegates, expected_delegator_reward2, None);
@@ -464,15 +395,11 @@ mod tests {
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
// delegating at different times still work)
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.unwrap();
simulator
.delegate("bob", Coin::new(4000_000000, "unym"), 0)
.unwrap();
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
simulator.delegate("bob", Coin::new(4000_000000, "unym"), 0);
// "normal", sanity check rewarding
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards1 = simulator.simulate_epoch_single_node(node_params);
let expected_operator1 = "1411087.1007647323".parse().unwrap();
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
compare_decimals(rewards1.delegates, expected_delegator_reward1, None);
@@ -490,7 +417,7 @@ mod tests {
assert_eq!(reward.amount, truncate_reward_amount(expected_del1_reward));
// new reward after withdrawal
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
let rewards2 = simulator.simulate_epoch_single_node(node_params);
let expected_operator2 = "1411250.1907492676".parse().unwrap();
let expected_delegator_reward2 = "2200004.051009689".parse().unwrap();
compare_decimals(rewards2.delegates, expected_delegator_reward2, None);
@@ -533,30 +460,22 @@ mod tests {
let mut performance = Percent::from_percentage_value(100).unwrap();
for epoch in 0..720 {
if epoch == 0 {
simulator
.delegate("a", Coin::new(18000_000000, "unym"), 0)
.unwrap()
simulator.delegate("a", Coin::new(18000_000000, "unym"), 0)
}
if epoch == 42 {
simulator
.delegate("b", Coin::new(2000_000000, "unym"), 0)
.unwrap()
simulator.delegate("b", Coin::new(2000_000000, "unym"), 0)
}
if epoch == 89 {
is_active = false;
}
if epoch == 123 {
simulator
.delegate("c", Coin::new(6666_000000, "unym"), 0)
.unwrap()
simulator.delegate("c", Coin::new(6666_000000, "unym"), 0)
}
if epoch == 167 {
performance = Percent::from_percentage_value(90).unwrap();
}
if epoch == 245 {
simulator
.delegate("d", Coin::new(2050_000000, "unym"), 0)
.unwrap()
simulator.delegate("d", Coin::new(2050_000000, "unym"), 0)
}
if epoch == 264 {
let (delegation, _reward) = simulator.undelegate("b", 0).unwrap();
@@ -577,15 +496,13 @@ mod tests {
// TODO: figure out if there's a good way to verify whether `reward` is what we expect it to be
}
if epoch == 545 {
simulator
.delegate("e", Coin::new(5000_000000, "unym"), 0)
.unwrap()
simulator.delegate("e", Coin::new(5000_000000, "unym"), 0)
}
// this has to always hold
check_rewarding_invariant(&simulator);
let node_params = NodeRewardParams::new(performance, is_active);
simulator.simulate_epoch_single_node(node_params).unwrap();
simulator.simulate_epoch_single_node(node_params);
}
// after everyone undelegates, there should be nothing left in the delegates pool
@@ -620,7 +537,6 @@ mod tests {
interval: IntervalRewardParams {
reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(),
staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(),
staking_supply_scale_factor: Percent::hundred(),
epoch_reward_budget,
stake_saturation_point,
sybil_resistance: Percent::from_percentage_value(30).unwrap(),
@@ -639,135 +555,95 @@ mod tests {
let mut simulator = Simulator::new(rewarding_params, interval);
let n0 = simulator
.bond(
Coin::new(11_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n0)
.unwrap();
let n0 = simulator.bond(
Coin::new(11_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n0);
let n1 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(11_000_000_000000, "unym"), n1)
.unwrap();
let n1 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(11_000_000_000000, "unym"), n1);
let n2 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n2)
.unwrap();
let n2 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n2);
let n3 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
interval_operating_cost: Coin::new(500_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n3)
.unwrap();
let n3 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
interval_operating_cost: Coin::new(500_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n3);
let n4 = simulator
.bond(
Coin::new(1000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_999_000_000000, "unym"), n4)
.unwrap();
let n4 = simulator.bond(
Coin::new(1000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(7_999_000_000000, "unym"), n4);
let n5 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n5)
.unwrap();
let n5 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n5);
let n6 = simulator
.bond(
Coin::new(11_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n6)
.unwrap();
let n6 = simulator.bond(
Coin::new(11_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n6);
let n7 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n7)
.unwrap();
let n7 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n7);
let n8 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
interval_operating_cost: Coin::new(500_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n8)
.unwrap();
let n8 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
interval_operating_cost: Coin::new(500_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n8);
let n9 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n9)
.unwrap();
let n9 = simulator.bond(
Coin::new(1_000_000_000000, "unym"),
MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
},
);
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n9);
let uptime_1 = Percent::from_percentage_value(100).unwrap();
let uptime_09 = Percent::from_percentage_value(90).unwrap();
@@ -789,7 +665,7 @@ mod tests {
.collect::<BTreeMap<_, _>>();
for _ in 0..23 {
simulator.simulate_full_interval(&node_params).unwrap();
simulator.simulate_full_interval(&node_params);
}
// we allow the delta to be within 0.1unym,
@@ -20,25 +20,21 @@ impl SimulatedNode {
cost_params: MixNodeCostParams,
initial_pledge: &Coin,
current_epoch: EpochId,
) -> Result<Self, MixnetContractError> {
Ok(SimulatedNode {
) -> Self {
SimulatedNode {
mix_id,
rewarding_details: MixNodeRewarding::initialise_new(
cost_params,
initial_pledge,
current_epoch,
)?,
),
delegations: HashMap::new(),
})
}
}
pub fn delegate<S: Into<String>>(
&mut self,
delegator: S,
delegation: Coin,
) -> Result<(), MixnetContractError> {
pub fn delegate<S: Into<String>>(&mut self, delegator: S, delegation: Coin) {
self.rewarding_details
.add_base_delegation(delegation.amount)?;
.add_base_delegation(delegation.amount);
let delegator = delegator.into();
let delegation = Delegation::new(
@@ -51,7 +47,6 @@ impl SimulatedNode {
);
self.delegations.insert(delegator, delegation);
Ok(())
}
pub fn undelegate<S: Into<String>>(
@@ -59,19 +54,16 @@ impl SimulatedNode {
delegator: S,
) -> Result<(Coin, Coin), MixnetContractError> {
let delegator = delegator.into();
let delegation = self.delegations.remove(&delegator).ok_or(
MixnetContractError::NoMixnodeDelegationFound {
mix_id: MixId::MAX,
address: delegator,
proxy: None,
},
)?;
let delegation = self
.delegations
.remove(&delegator)
.expect("delegation not found");
let reward = self
.rewarding_details
.determine_delegation_reward(&delegation)?;
.determine_delegation_reward(&delegation);
self.rewarding_details
.remove_delegation_decimal(delegation.dec_amount()? + reward)?;
.remove_delegation_decimal(delegation.dec_amount() + reward)?;
let reward_denom = &delegation.amount.denom;
let truncated_reward = truncate_reward(reward, reward_denom);
@@ -35,10 +35,6 @@ impl LayerDistribution {
(Layer::Two, self.layer2),
(Layer::Three, self.layer3),
];
// we explicitly put 3 elements into the iterator, so the iterator is DEFINITELY
// not empty and thus the unwrap cannot fail
#[allow(clippy::unwrap_used)]
layers.iter().min_by_key(|x| x.1).unwrap().0
}
@@ -126,3 +122,48 @@ pub struct PagedRewardedSetResponse {
pub nodes: Vec<(MixId, RewardedSetNodeStatus)>,
pub start_next_after: Option<MixId>,
}
#[cfg(test)]
mod tests {
use contracts_common::Percent;
#[test]
fn percent_serde() {
let valid_value = Percent::from_percentage_value(80).unwrap();
let serialized = serde_json::to_string(&valid_value).unwrap();
println!("{}", serialized);
let deserialized: Percent = serde_json::from_str(&serialized).unwrap();
assert_eq!(valid_value, deserialized);
let invalid_values = vec!["\"42\"", "\"1.1\"", "\"1.00000001\"", "\"foomp\"", "\"1a\""];
for invalid_value in invalid_values {
assert!(serde_json::from_str::<'_, Percent>(invalid_value).is_err())
}
assert_eq!(
serde_json::from_str::<'_, Percent>("\"0.95\"").unwrap(),
Percent::from_percentage_value(95).unwrap()
)
}
#[test]
fn percent_to_absolute_integer() {
let p = serde_json::from_str::<'_, Percent>("\"0.0001\"").unwrap();
assert_eq!(p.round_to_integer(), 0);
let p = serde_json::from_str::<'_, Percent>("\"0.0099\"").unwrap();
assert_eq!(p.round_to_integer(), 0);
let p = serde_json::from_str::<'_, Percent>("\"0.0199\"").unwrap();
assert_eq!(p.round_to_integer(), 1);
let p = serde_json::from_str::<'_, Percent>("\"0.45123\"").unwrap();
assert_eq!(p.round_to_integer(), 45);
let p = serde_json::from_str::<'_, Percent>("\"0.999999999\"").unwrap();
assert_eq!(p.round_to_integer(), 99);
let p = serde_json::from_str::<'_, Percent>("\"1.00\"").unwrap();
assert_eq!(p.round_to_integer(), 100);
}
}
@@ -1,16 +1,13 @@
use std::str::FromStr;
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use contracts_common::Percent;
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
use log::warn;
use mixnet_contract_common::MixId;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
@@ -85,7 +82,7 @@ impl FromStr for PledgeCap {
impl Default for PledgeCap {
fn default() -> Self {
PledgeCap::Percent(Percent::from_percentage_value(10).expect("This can never fail!"))
PledgeCap::Absolute(Uint128::from(100_000_000_000u128))
}
}
@@ -160,7 +160,6 @@ impl ExecuteMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetContractVersion {},
LockedCoins {
vesting_account_address: String,
block_time: Option<Timestamp>,
-10
View File
@@ -1,10 +0,0 @@
[package]
name = "logging"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.0"
pretty_env_logger = "0.4.0"
-25
View File
@@ -1,25 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// I'd argue we should start transitioning from `log` to `tracing`
pub fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") {
log_builder.parse_filters(&s);
} else {
// default to 'Info'
log_builder.filter(None, log::LevelFilter::Info);
}
log_builder
.filter_module("hyper", log::LevelFilter::Warn)
.filter_module("tokio_reactor", log::LevelFilter::Warn)
.filter_module("reqwest", log::LevelFilter::Warn)
.filter_module("mio", log::LevelFilter::Warn)
.filter_module("want", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.filter_module("handlebars", log::LevelFilter::Warn)
.filter_module("sled", log::LevelFilter::Warn)
.init();
}
@@ -116,10 +116,7 @@ impl SphinxPacketProcessor {
trace!("received an ack packet!");
Ok((None, data))
}
PacketSize::RegularPacket
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => {
PacketSize::RegularPacket | PacketSize::ExtendedPacket => {
trace!("received a normal packet!");
let (ack_data, message) = self.split_hop_data_into_ack_and_message(data)?;
let (ack_first_hop, ack_packet) = SurbAck::try_recover_first_hop_packet(&ack_data)?;
+3 -3
View File
@@ -85,7 +85,7 @@ impl SecretKey {
// x || ys.len() || ys
pub fn to_bytes(&self) -> Vec<u8> {
let ys_len = self.ys.len();
let mut bytes = Vec::with_capacity(8 + (ys_len + 1) * 32);
let mut bytes = Vec::with_capacity(8 + (ys_len + 1) as usize * 32);
bytes.extend_from_slice(&self.x.to_bytes());
bytes.extend_from_slice(&ys_len.to_le_bytes());
@@ -162,7 +162,7 @@ impl TryFrom<&[u8]> for VerificationKey {
let mut beta_g1_end: u64 = 0;
for i in 0..betas_len {
let start = (104 + i * 48) as usize;
let end = start + 48;
let end = (start + 48) as usize;
let beta_i_bytes = bytes[start..end].try_into().unwrap();
let beta_i = try_deserialize_g1_projective(
&beta_i_bytes,
@@ -178,7 +178,7 @@ impl TryFrom<&[u8]> for VerificationKey {
let mut beta_g2 = Vec::with_capacity(betas_len as usize);
for i in 0..betas_len {
let start = (beta_g1_end + i * 96) as usize;
let end = start + 96;
let end = (start + 96) as usize;
let beta_i_bytes = bytes[start..end].try_into().unwrap();
let beta_i = try_deserialize_g2_projective(
&beta_i_bytes,
+2 -2
View File
@@ -94,10 +94,10 @@ fn prepare_unlinked_fragmented_set(
for i in 1..(pre_casted_frags + 1) {
// we can't use u8 directly here as upper (NON-INCLUSIVE, so it would always fit) bound could be u8::max_value() + 1
let lb = (i - 1) * unlinked_fragment_payload_max_len(max_plaintext_size);
let lb = (i as usize - 1) * unlinked_fragment_payload_max_len(max_plaintext_size);
let ub = usize::min(
message.len(),
i * unlinked_fragment_payload_max_len(max_plaintext_size),
i as usize * unlinked_fragment_payload_max_len(max_plaintext_size),
);
fragments.push(
Fragment::try_new(
+2 -4
View File
@@ -25,10 +25,8 @@ impl Display for MixPacketFormattingError {
InvalidPacketSize(actual) =>
write!(
f,
"received request had invalid size. (actual: {}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))",
actual, PacketSize::AckPacket.size(), PacketSize::RegularPacket.size(),
PacketSize::ExtendedPacket8.size(), PacketSize::ExtendedPacket16.size(),
PacketSize::ExtendedPacket32.size()
"received request had invalid size. (actual: {}, but expected one of: {} (ACK), {} (REGULAR), {} (EXTENDED))",
actual, PacketSize::AckPacket.size(), PacketSize::RegularPacket.size(), PacketSize::ExtendedPacket.size()
),
MalformedSphinxPacket => write!(f, "received sphinx packet was malformed"),
InvalidPacketMode => write!(f, "provided packet mode is invalid")
+21 -114
View File
@@ -6,6 +6,7 @@ use bytes::{Buf, BufMut, BytesMut};
use nymsphinx_params::packet_modes::InvalidPacketMode;
use nymsphinx_params::packet_sizes::{InvalidPacketSize, PacketSize};
use nymsphinx_types::SphinxPacket;
use std::convert::TryFrom;
use std::io;
use tokio_util::codec::{Decoder, Encoder};
@@ -74,7 +75,7 @@ impl Decoder for SphinxCodec {
if src.is_empty() {
// can't do anything if we have no bytes, but let's reserve enough for the most
// conservative case, i.e. receiving an ack packet
src.reserve(Header::LEGACY_SIZE + PacketSize::AckPacket.size());
src.reserve(Header::SIZE + PacketSize::AckPacket.size());
return Ok(None);
}
@@ -86,7 +87,7 @@ impl Decoder for SphinxCodec {
};
let sphinx_packet_size = header.packet_size.size();
let frame_len = header.size() + sphinx_packet_size;
let frame_len = Header::SIZE + sphinx_packet_size;
if src.len() < frame_len {
// we don't have enough bytes to read the rest of frame
@@ -95,7 +96,7 @@ impl Decoder for SphinxCodec {
}
// advance buffer past the header - at this point we have enough bytes
src.advance(header.size());
src.advance(Header::SIZE);
let sphinx_packet_bytes = src.split_to(sphinx_packet_size);
let sphinx_packet = match SphinxPacket::from_bytes(&sphinx_packet_bytes) {
Ok(sphinx_packet) => sphinx_packet,
@@ -114,27 +115,21 @@ impl Decoder for SphinxCodec {
// has appropriate capacity in anticipation of future calls to decode.
// Failing to do so leads to inefficiency.
// if we have enough bytes to decode the header of the next packet, we can reserve enough bytes for
// if we have at least one more byte available, we can reserve enough bytes for
// the entire next frame, if not, we assume the next frame is an ack packet and
// reserve for that.
// we also assume the next packet coming from the same client will use exactly the same versioning
// as the current packet
let mut allocate_for_next_packet = header.size() + PacketSize::AckPacket.size();
if !src.is_empty() {
match Header::decode(src) {
Ok(Some(next_header)) => {
allocate_for_next_packet = next_header.size() + next_header.packet_size.size();
}
Ok(None) => {
// we don't have enough information to know how much to reserve, fallback to the ack case
}
let next_packet_len = match PacketSize::try_from(src[0]) {
Ok(next_packet_len) => next_packet_len,
// the next frame will be malformed but let's leave handling the error to the next
// call to 'decode', as presumably, the current sphinx packet is still valid
Err(_) => return Ok(Some(nymsphinx_packet)),
};
let next_frame_len = next_packet_len.size() + Header::SIZE;
src.reserve(next_frame_len - 1);
} else {
src.reserve(Header::SIZE + PacketSize::AckPacket.size());
}
src.reserve(allocate_for_next_packet);
Ok(Some(nymsphinx_packet))
}
@@ -204,8 +199,6 @@ mod packet_encoding {
#[cfg(test)]
mod decode_will_allocate_enough_bytes_for_next_call {
use super::*;
use nymsphinx_params::packet_version::PacketVersion;
use nymsphinx_params::PacketMode;
#[test]
fn for_empty_bytes() {
@@ -214,23 +207,20 @@ mod packet_encoding {
assert!(SphinxCodec.decode(&mut empty_bytes).unwrap().is_none());
assert_eq!(
empty_bytes.capacity(),
Header::LEGACY_SIZE + PacketSize::AckPacket.size()
Header::SIZE + PacketSize::AckPacket.size()
);
}
#[test]
fn for_bytes_with_legacy_header() {
fn for_bytes_with_header() {
// if header gets decoded there should be enough bytes for the entire frame
let packet_sizes = vec![
PacketSize::AckPacket,
PacketSize::RegularPacket,
PacketSize::ExtendedPacket8,
PacketSize::ExtendedPacket16,
PacketSize::ExtendedPacket32,
PacketSize::ExtendedPacket,
];
for packet_size in packet_sizes {
let header = Header {
packet_version: PacketVersion::Legacy,
packet_size,
packet_mode: Default::default(),
};
@@ -238,60 +228,12 @@ mod packet_encoding {
header.encode(&mut bytes);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert_eq!(bytes.capacity(), Header::LEGACY_SIZE + packet_size.size())
assert_eq!(bytes.capacity(), Header::SIZE + packet_size.size())
}
}
#[test]
fn for_bytes_with_versioned_header() {
// if header gets decoded there should be enough bytes for the entire frame
let packet_sizes = vec![
PacketSize::AckPacket,
PacketSize::RegularPacket,
PacketSize::ExtendedPacket8,
PacketSize::ExtendedPacket16,
PacketSize::ExtendedPacket32,
];
for packet_size in packet_sizes {
let header = Header {
packet_version: PacketVersion::Versioned(123),
packet_size,
packet_mode: Default::default(),
};
let mut bytes = BytesMut::new();
header.encode(&mut bytes);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert_eq!(
bytes.capacity(),
Header::VERSIONED_SIZE + packet_size.size()
)
}
}
#[test]
fn for_full_frame_with_legacy_header() {
// if full frame is used exactly, there should be enough space for header + ack packet
let packet = FramedSphinxPacket {
header: Header {
packet_version: PacketVersion::Legacy,
packet_size: Default::default(),
packet_mode: Default::default(),
},
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(packet, &mut bytes).unwrap();
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert_eq!(
bytes.capacity(),
Header::LEGACY_SIZE + PacketSize::AckPacket.size()
);
}
#[test]
fn for_full_frame_with_versioned_header() {
fn for_full_frame() {
// if full frame is used exactly, there should be enough space for header + ack packet
let packet = FramedSphinxPacket {
header: Header::default(),
@@ -303,50 +245,17 @@ mod packet_encoding {
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert_eq!(
bytes.capacity(),
Header::VERSIONED_SIZE + PacketSize::AckPacket.size()
Header::SIZE + PacketSize::AckPacket.size()
);
}
#[test]
fn for_full_frame_with_extra_bytes_with_legacy_header() {
// if there was at least 2 byte left, there should be enough space for entire next frame
fn for_full_frame_with_extra_byte() {
// if there was at least 1 byte left, there should be enough space for entire next frame
let packet_sizes = vec![
PacketSize::AckPacket,
PacketSize::RegularPacket,
PacketSize::ExtendedPacket8,
PacketSize::ExtendedPacket16,
PacketSize::ExtendedPacket32,
];
for packet_size in packet_sizes {
let first_packet = FramedSphinxPacket {
header: Header {
packet_version: PacketVersion::Legacy,
packet_size: Default::default(),
packet_mode: Default::default(),
},
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(first_packet, &mut bytes).unwrap();
bytes.put_u8(packet_size as u8);
bytes.put_u8(PacketMode::default() as u8);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert!(bytes.capacity() >= Header::LEGACY_SIZE + packet_size.size())
}
}
#[test]
fn for_full_frame_with_extra_bytes_with_versioned_header() {
// if there was at least 3 byte left, there should be enough space for entire next frame
let packet_sizes = vec![
PacketSize::AckPacket,
PacketSize::RegularPacket,
PacketSize::ExtendedPacket8,
PacketSize::ExtendedPacket16,
PacketSize::ExtendedPacket32,
PacketSize::ExtendedPacket,
];
for packet_size in packet_sizes {
@@ -357,12 +266,10 @@ mod packet_encoding {
let mut bytes = BytesMut::new();
SphinxCodec.encode(first_packet, &mut bytes).unwrap();
bytes.put_u8(PacketVersion::new_versioned(123).as_u8().unwrap());
bytes.put_u8(packet_size as u8);
bytes.put_u8(PacketMode::default() as u8);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert!(bytes.capacity() >= Header::VERSIONED_SIZE + packet_size.size())
assert!(bytes.capacity() >= Header::SIZE + packet_size.size())
}
}
}
+14 -81
View File
@@ -4,7 +4,6 @@
use crate::codec::SphinxCodecError;
use bytes::{BufMut, BytesMut};
use nymsphinx_params::packet_sizes::PacketSize;
use nymsphinx_params::packet_version::PacketVersion;
use nymsphinx_params::PacketMode;
use nymsphinx_types::SphinxPacket;
use std::convert::TryFrom;
@@ -18,14 +17,12 @@ pub struct FramedSphinxPacket {
}
impl FramedSphinxPacket {
pub fn new(packet: SphinxPacket, packet_mode: PacketMode, use_legacy_version: bool) -> Self {
pub fn new(packet: SphinxPacket, packet_mode: PacketMode) -> Self {
// If this fails somebody is using the library in a super incorrect way, because they
// already managed to somehow create a sphinx packet
let packet_size = PacketSize::get_type(packet.len()).unwrap();
FramedSphinxPacket {
header: Header {
packet_version: PacketVersion::new(use_legacy_version),
packet_size,
packet_mode,
},
@@ -51,9 +48,6 @@ impl FramedSphinxPacket {
// but would that really be worth it?
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
pub struct Header {
/// Represents the wire format version used to construct this packet.
pub(crate) packet_version: PacketVersion,
/// Represents type and consequently size of the included SphinxPacket.
pub(crate) packet_size: PacketSize,
@@ -70,25 +64,11 @@ pub struct Header {
}
impl Header {
pub(crate) const LEGACY_SIZE: usize = 2;
pub(crate) const VERSIONED_SIZE: usize = 3;
pub(crate) fn size(&self) -> usize {
if self.packet_version.is_legacy() {
Self::LEGACY_SIZE
} else {
Self::VERSIONED_SIZE
}
}
pub(crate) const SIZE: usize = 2;
pub(crate) fn encode(&self, dst: &mut BytesMut) {
// we reserve one byte for `packet_size` and the other for `mode`
dst.reserve(Self::LEGACY_SIZE);
if let Some(version) = self.packet_version.as_u8() {
dst.reserve(Self::VERSIONED_SIZE);
dst.put_u8(version)
}
dst.reserve(Self::SIZE);
dst.put_u8(self.packet_size as u8);
dst.put_u8(self.packet_mode as u8);
// reserve bytes for the actual packet
@@ -96,30 +76,16 @@ impl Header {
}
pub(crate) fn decode(src: &mut BytesMut) -> Result<Option<Self>, SphinxCodecError> {
if src.len() < Self::LEGACY_SIZE {
if src.len() < Self::SIZE {
// can't do anything if we don't have enough bytes - but reserve enough for the next call
src.reserve(Self::LEGACY_SIZE);
src.reserve(Self::SIZE);
return Ok(None);
}
let packet_version = PacketVersion::from(src[0]);
if packet_version.is_legacy() {
Ok(Some(Header {
packet_version,
packet_size: PacketSize::try_from(src[0])?,
packet_mode: PacketMode::try_from(src[1])?,
}))
} else if src.len() < Self::VERSIONED_SIZE {
// we're missing that 1 byte to read the full header...
src.reserve(Self::VERSIONED_SIZE);
Ok(None)
} else {
Ok(Some(Header {
packet_version,
packet_size: PacketSize::try_from(src[1])?,
packet_mode: PacketMode::try_from(src[2])?,
}))
}
Ok(Some(Header {
packet_size: PacketSize::try_from(src[0])?,
packet_mode: PacketMode::try_from(src[1])?,
}))
}
}
@@ -142,16 +108,7 @@ mod header_encoding {
// make sure this is still 'unknown' for if we make changes in the future
assert!(PacketSize::try_from(unknown_packet_size).is_err());
// unfortunately this will only work for the 'versioned' variant
// due to the hack used to get legacy mode compatibility
let mut bytes = BytesMut::from(
[
PacketVersion::new_versioned(123).as_u8().unwrap(),
unknown_packet_size,
PacketMode::default() as u8,
]
.as_ref(),
);
let mut bytes = BytesMut::from([unknown_packet_size, PacketMode::default() as u8].as_ref());
assert!(Header::decode(&mut bytes).is_err())
}
@@ -170,47 +127,23 @@ mod header_encoding {
let mut empty_bytes = BytesMut::new();
let decode_attempt_1 = Header::decode(&mut empty_bytes).unwrap();
assert!(decode_attempt_1.is_none());
assert!(empty_bytes.capacity() > Header::LEGACY_SIZE);
assert!(empty_bytes.capacity() > Header::SIZE);
let mut empty_bytes = BytesMut::with_capacity(1);
let decode_attempt_2 = Header::decode(&mut empty_bytes).unwrap();
assert!(decode_attempt_2.is_none());
assert!(empty_bytes.capacity() > Header::LEGACY_SIZE);
assert!(empty_bytes.capacity() > Header::SIZE);
}
#[test]
fn header_encoding_reserves_enough_bytes_for_full_sphinx_packet_in_legacy_mode() {
fn header_encoding_reserves_enough_bytes_for_full_sphinx_packet() {
let packet_sizes = vec![
PacketSize::AckPacket,
PacketSize::RegularPacket,
PacketSize::ExtendedPacket8,
PacketSize::ExtendedPacket16,
PacketSize::ExtendedPacket32,
PacketSize::ExtendedPacket,
];
for packet_size in packet_sizes {
let header = Header {
packet_version: PacketVersion::Legacy,
packet_size,
packet_mode: Default::default(),
};
let mut bytes = BytesMut::new();
header.encode(&mut bytes);
assert_eq!(bytes.capacity(), bytes.len() + packet_size.size())
}
}
#[test]
fn header_encoding_reserves_enough_bytes_for_full_sphinx_packet_in_versioned_mode() {
let packet_sizes = vec![
PacketSize::AckPacket,
PacketSize::RegularPacket,
PacketSize::ExtendedPacket8,
PacketSize::ExtendedPacket16,
PacketSize::ExtendedPacket32,
];
for packet_size in packet_sizes {
let header = Header {
packet_version: PacketVersion::Versioned(123),
packet_size,
packet_mode: Default::default(),
};
-12
View File
@@ -13,7 +13,6 @@ pub use packet_sizes::PacketSize;
pub mod packet_modes;
pub mod packet_sizes;
pub mod packet_version;
// If somebody can provide an argument why it might be reasonable to have more than 255 mix hops,
// I will change this to [`usize`]
@@ -25,17 +24,6 @@ pub const DEFAULT_NUM_MIX_HOPS: u8 = 3;
pub const FRAG_ID_LEN: usize = 5;
pub type SerializedFragmentIdentifier = [u8; FRAG_ID_LEN];
// wait, wait, but why are we starting with version 7?
// when packet header gets serialized, the following bytes (in that order) are put onto the wire:
// - packet_version (starting with v1.1.0)
// - packet_size indicator
// - packet_mode
// it also just so happens that the only valid values for packet_size indicator include values 1-6
// therefore if we receive byte `7` (or larger than that) we'll know we received a versioned packet,
// otherwise we should treat it as legacy
/// Increment it whenever we perform any breaking change in the wire format!
const CURRENT_PACKET_VERSION_NUMBER: u8 = 7;
// TODO: ask @AP about the choice of below algorithms
/// Hashing algorithm used during hkdf for ephemeral shared key generation per sphinx packet payload.
+6 -58
View File
@@ -5,7 +5,6 @@ use crate::FRAG_ID_LEN;
use nymsphinx_types::header::HEADER_SIZE;
use nymsphinx_types::PAYLOAD_OVERHEAD_SIZE;
use std::convert::TryFrom;
use std::str::FromStr;
// it's up to the smart people to figure those values out : )
const REGULAR_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 2 * 1024;
@@ -16,16 +15,11 @@ const REGULAR_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 2 * 102
const ACK_IV_SIZE: usize = 16;
const ACK_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + ACK_IV_SIZE + FRAG_ID_LEN;
const EXTENDED_PACKET_SIZE_8: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 8 * 1024;
const EXTENDED_PACKET_SIZE_16: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 16 * 1024;
const EXTENDED_PACKET_SIZE_32: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
const EXTENDED_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
#[derive(Debug)]
pub struct InvalidPacketSize;
#[derive(Debug)]
pub struct InvalidExtendedPacketSize;
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PacketSize {
@@ -36,28 +30,7 @@ pub enum PacketSize {
AckPacket = 2,
// for example for streaming fast and furious in uncompressed 10bit 4K HDR quality
ExtendedPacket32 = 3,
// for example for streaming fast and furious in heavily compressed lossy RealPlayer quality
ExtendedPacket8 = 4,
// for example for streaming fast and furious in compressed XviD quality
ExtendedPacket16 = 5,
}
impl FromStr for PacketSize {
type Err = InvalidPacketSize;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"regular" => Ok(Self::RegularPacket),
"ack" => Ok(Self::AckPacket),
"extended8" => Ok(Self::ExtendedPacket8),
"extended16" => Ok(Self::ExtendedPacket16),
"extended32" => Ok(Self::ExtendedPacket32),
_ => Err(InvalidPacketSize),
}
}
ExtendedPacket = 3,
}
impl TryFrom<u8> for PacketSize {
@@ -67,9 +40,7 @@ impl TryFrom<u8> for PacketSize {
match value {
_ if value == (PacketSize::RegularPacket as u8) => Ok(Self::RegularPacket),
_ if value == (PacketSize::AckPacket as u8) => Ok(Self::AckPacket),
_ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8),
_ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16),
_ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32),
_ if value == (PacketSize::ExtendedPacket as u8) => Ok(Self::ExtendedPacket),
_ => Err(InvalidPacketSize),
}
}
@@ -80,9 +51,7 @@ impl PacketSize {
match self {
PacketSize::RegularPacket => REGULAR_PACKET_SIZE,
PacketSize::AckPacket => ACK_PACKET_SIZE,
PacketSize::ExtendedPacket8 => EXTENDED_PACKET_SIZE_8,
PacketSize::ExtendedPacket16 => EXTENDED_PACKET_SIZE_16,
PacketSize::ExtendedPacket32 => EXTENDED_PACKET_SIZE_32,
PacketSize::ExtendedPacket => EXTENDED_PACKET_SIZE,
}
}
@@ -99,33 +68,12 @@ impl PacketSize {
Ok(PacketSize::RegularPacket)
} else if PacketSize::AckPacket.size() == size {
Ok(PacketSize::AckPacket)
} else if PacketSize::ExtendedPacket8.size() == size {
Ok(PacketSize::ExtendedPacket8)
} else if PacketSize::ExtendedPacket16.size() == size {
Ok(PacketSize::ExtendedPacket16)
} else if PacketSize::ExtendedPacket32.size() == size {
Ok(PacketSize::ExtendedPacket32)
} else if PacketSize::ExtendedPacket.size() == size {
Ok(PacketSize::ExtendedPacket)
} else {
Err(InvalidPacketSize)
}
}
pub fn is_extended_size(&self) -> bool {
match self {
PacketSize::RegularPacket | PacketSize::AckPacket => false,
PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => true,
}
}
pub fn as_extended_size(self) -> Option<Self> {
if self.is_extended_size() {
Some(self)
} else {
None
}
}
}
impl Default for PacketSize {
@@ -1,59 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{PacketSize, CURRENT_PACKET_VERSION_NUMBER};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PacketVersion {
// this will allow updated mixnodes to still understand packets from before the update
Legacy,
Versioned(u8),
}
impl PacketVersion {
pub fn new(use_legacy: bool) -> Self {
if use_legacy {
Self::new_legacy()
} else {
Self::new_versioned(CURRENT_PACKET_VERSION_NUMBER)
}
}
pub fn new_legacy() -> Self {
PacketVersion::Legacy
}
pub fn new_versioned(version: u8) -> Self {
PacketVersion::Versioned(version)
}
pub fn is_legacy(&self) -> bool {
matches!(self, PacketVersion::Legacy)
}
pub fn as_u8(&self) -> Option<u8> {
match self {
PacketVersion::Legacy => None,
PacketVersion::Versioned(version) => Some(*version),
}
}
}
impl From<u8> for PacketVersion {
fn from(v: u8) -> Self {
match v {
n if n == PacketSize::RegularPacket as u8 => PacketVersion::Legacy,
n if n == PacketSize::AckPacket as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket8 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket16 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket32 as u8 => PacketVersion::Legacy,
n => PacketVersion::Versioned(n),
}
}
}
impl Default for PacketVersion {
fn default() -> Self {
PacketVersion::Versioned(CURRENT_PACKET_VERSION_NUMBER)
}
}
+10 -23
View File
@@ -13,13 +13,6 @@ pub struct OrderedMessageBuffer {
messages: HashMap<u64, OrderedMessage>,
}
/// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data.
#[derive(Debug, PartialEq, Eq)]
pub struct ReadContiguousData {
pub data: Vec<u8>,
pub last_index: u64,
}
impl OrderedMessageBuffer {
pub fn new() -> OrderedMessageBuffer {
OrderedMessageBuffer {
@@ -49,7 +42,7 @@ impl OrderedMessageBuffer {
/// a read will return the bytes of messages 0, 1, 2. Subsequent reads will
/// return `None` until message 3 comes in, at which point 3, 4, and any
/// further contiguous messages which have arrived will be returned.
pub fn read(&mut self) -> Option<ReadContiguousData> {
pub fn read(&mut self) -> Option<Vec<u8>> {
if !self.messages.contains_key(&self.next_index) {
return None;
}
@@ -73,10 +66,7 @@ impl OrderedMessageBuffer {
.collect();
trace!("Returning {} bytes from ordered message buffer", data.len());
Some(ReadContiguousData {
data,
last_index: index,
})
Some(data)
}
}
@@ -112,11 +102,11 @@ mod test_chunking_and_reassembling {
};
buffer.write(first_message);
let first_read = buffer.read().unwrap().data;
let first_read = buffer.read().unwrap();
assert_eq!(vec![1, 2, 3, 4], first_read);
buffer.write(second_message);
let second_read = buffer.read().unwrap().data;
let second_read = buffer.read().unwrap();
assert_eq!(vec![5, 6, 7, 8], second_read);
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
@@ -138,7 +128,7 @@ mod test_chunking_and_reassembling {
buffer.write(first_message);
buffer.write(second_message);
let second_read = buffer.read();
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap().data);
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap());
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
}
@@ -157,8 +147,8 @@ mod test_chunking_and_reassembling {
buffer.write(second_message);
buffer.write(first_message);
let read = buffer.read().unwrap().data;
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read);
let read = buffer.read();
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read.unwrap());
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
}
}
@@ -192,7 +182,7 @@ mod test_chunking_and_reassembling {
#[test]
fn everything_up_to_the_indexing_gap_is_returned() {
let mut buffer = setup();
let ordered_bytes = buffer.read().unwrap().data;
let ordered_bytes = buffer.read().unwrap();
assert_eq!([0, 0, 0, 0, 1, 1, 1, 1].to_vec(), ordered_bytes);
// we shouldn't get any more from a second attempt if nothing is added
@@ -218,7 +208,7 @@ mod test_chunking_and_reassembling {
};
buffer.write(two_message);
let more_ordered_bytes = buffer.read().unwrap().data;
let more_ordered_bytes = buffer.read().unwrap();
assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes);
// let's add another message
@@ -237,10 +227,7 @@ mod test_chunking_and_reassembling {
};
buffer.write(four_message);
assert_eq!(
[4, 4, 4, 4, 5, 5, 5, 5].to_vec(),
buffer.read().unwrap().data
);
assert_eq!([4, 4, 4, 4, 5, 5, 5, 5].to_vec(), buffer.read().unwrap());
// at this point we should again get back nothing if we try a read
assert_eq!(None, buffer.read());
+1 -1
View File
@@ -2,7 +2,7 @@ mod buffer;
mod message;
mod sender;
pub use buffer::{OrderedMessageBuffer, ReadContiguousData};
pub use buffer::OrderedMessageBuffer;
pub use message::MessageError;
pub use message::OrderedMessage;
pub use sender::OrderedMessageSender;
@@ -4,7 +4,7 @@
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData};
use ordered_buffer::{OrderedMessage, OrderedMessageBuffer};
use socks5_requests::ConnectionId;
use std::collections::{HashMap, HashSet};
use task::ShutdownListener;
@@ -38,13 +38,12 @@ pub enum ControllerCommand {
struct ActiveConnection {
is_closed: bool,
closed_at_index: Option<u64>,
connection_sender: Option<ConnectionSender>,
ordered_buffer: OrderedMessageBuffer,
}
impl ActiveConnection {
fn write_to_buf(&mut self, payload: Vec<u8>, is_closed: bool) {
fn write_to_buf(&mut self, payload: Vec<u8>) {
let ordered_message = match OrderedMessage::try_from_bytes(payload) {
Ok(msg) => msg,
Err(err) => {
@@ -52,13 +51,10 @@ impl ActiveConnection {
return;
}
};
if is_closed {
self.closed_at_index = Some(ordered_message.index);
}
self.ordered_buffer.write(ordered_message);
}
fn read_from_buf(&mut self) -> Option<ReadContiguousData> {
fn read_from_buf(&mut self) -> Option<Vec<u8>> {
self.ordered_buffer.read()
}
}
@@ -103,7 +99,6 @@ impl Controller {
is_closed: false,
connection_sender: Some(connection_sender),
ordered_buffer: OrderedMessageBuffer::new(),
closed_at_index: None,
};
if let Some(_active_conn) = self.active_connections.insert(conn_id, active_connection) {
error!("Received a duplicate 'Connect'!")
@@ -132,23 +127,21 @@ impl Controller {
fn send_to_connection(&mut self, conn_id: ConnectionId, payload: Vec<u8>, is_closed: bool) {
if let Some(active_connection) = self.active_connections.get_mut(&conn_id) {
if !payload.is_empty() {
active_connection.write_to_buf(payload, is_closed);
active_connection.write_to_buf(payload);
} else if !is_closed {
error!("Tried to write an empty message to a not-closing connection. Please let us know if you see this message");
}
// if messages get unordered, make sure we don't lose information about
// remote socket getting closed!
active_connection.is_closed |= is_closed;
if let Some(payload) = active_connection.read_from_buf() {
if let Some(closed_at_index) = active_connection.closed_at_index {
if payload.last_index > closed_at_index {
active_connection.is_closed = true;
}
}
if let Err(err) = active_connection
.connection_sender
.as_mut()
.unwrap()
.unbounded_send(ConnectionMessage {
payload: payload.data,
payload,
socket_closed: active_connection.is_closed,
})
{
-1
View File
@@ -62,7 +62,6 @@ pub struct DelegationWithEverything {
// DEPRECATED, IF POSSIBLE TRY TO DISCONTINUE USE OF IT!
pub pending_events: Vec<DelegationEvent>,
pub mixnode_is_unbonding: Option<bool>,
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
-8
View File
@@ -1,11 +1,3 @@
## Unreleased
### Added
- vesting-contract: added query for obtaining contract build information ([#1726])
[#1726]: https://github.com/nymtech/nym/pull/1726
## [nym-contracts-v1.0.2](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.2) (2022-09-13)
### Added
-1
View File
@@ -1615,7 +1615,6 @@ dependencies = [
"schemars",
"serde",
"thiserror",
"vergen",
"vesting-contract-common",
]
-2
View File
@@ -1,2 +0,0 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
+1 -3
View File
@@ -63,7 +63,7 @@ pub fn instantiate(
Interval::init_interval(msg.epochs_in_interval, msg.epoch_duration, &env);
let reward_params = msg
.initial_rewarding_params
.into_rewarding_params(msg.epochs_in_interval)?;
.into_rewarding_params(msg.epochs_in_interval);
interval_storage::initialise_storage(deps.storage, starting_interval)?;
mixnet_params_storage::initialise_storage(deps.storage, state)?;
@@ -497,7 +497,6 @@ mod tests {
initial_rewarding_params: InitialRewardingParams {
initial_reward_pool: Decimal::from_atomics(100_000_000_000_000u128, 0).unwrap(),
initial_staking_supply: Decimal::from_atomics(123_456_000_000_000u128, 0).unwrap(),
staking_supply_scale_factor: Percent::hundred(),
sybil_resistance: Percent::from_percentage_value(23).unwrap(),
active_set_work_factor: Decimal::from_atomics(10u32, 0).unwrap(),
interval_pool_emission: Percent::from_percentage_value(1).unwrap(),
@@ -536,7 +535,6 @@ mod tests {
interval: IntervalRewardParams {
reward_pool: Decimal::from_atomics(100_000_000_000_000u128, 0).unwrap(),
staking_supply: Decimal::from_atomics(123_456_000_000_000u128, 0).unwrap(),
staking_supply_scale_factor: Percent::hundred(),
epoch_reward_budget: expected_epoch_reward_budget,
stake_saturation_point: expected_stake_saturation_point,
sybil_resistance: Percent::from_percentage_value(23).unwrap(),
@@ -107,7 +107,7 @@ pub(crate) fn delegate(
};
// add the amount we're intending to delegate (whether it's fresh or we're adding to the existing one)
mix_rewarding.add_base_delegation(stored_delegation_amount.amount)?;
mix_rewarding.add_base_delegation(stored_delegation_amount.amount);
let cosmos_event = new_delegation_event(
created_at,
@@ -1220,7 +1220,6 @@ mod tests {
let update = IntervalRewardingParamsUpdate {
reward_pool: Some(before.interval.reward_pool / two),
staking_supply: Some(before.interval.staking_supply * four),
staking_supply_scale_factor: None,
sybil_resistance_percent: Some(Percent::from_percentage_value(42).unwrap()),
active_set_work_factor: None,
interval_pool_emission: None,
-3
View File
@@ -1,9 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
mod constants;
pub mod contract;
mod delegations;
+1 -1
View File
@@ -78,7 +78,7 @@ pub(crate) fn save_new_mixnode(
let mix_id = next_mixnode_id_counter(storage)?;
let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id();
let mixnode_rewarding = MixNodeRewarding::initialise_new(cost_params, &pledge, current_epoch)?;
let mixnode_rewarding = MixNodeRewarding::initialise_new(cost_params, &pledge, current_epoch);
let mixnode_bond = MixNodeBond::new(
mix_id,
owner,
+13 -20
View File
@@ -4,9 +4,8 @@
use super::storage;
use crate::delegations::storage as delegations_storage;
use crate::interval::storage as interval_storage;
use cosmwasm_std::{Coin, Storage};
use cosmwasm_std::{Coin, Decimal, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::helpers::IntoBaseDecimal;
use mixnet_contract_common::mixnode::{MixNodeDetails, MixNodeRewarding};
use mixnet_contract_common::Delegation;
@@ -21,12 +20,12 @@ pub(crate) fn apply_reward_pool_changes(
let reward_pool = rewarding_params.interval.reward_pool - pending_pool_change.removed
+ pending_pool_change.added;
let staking_supply = rewarding_params.interval.staking_supply
+ rewarding_params.interval.staking_supply_scale_factor * pending_pool_change.removed;
let epoch_reward_budget = reward_pool / interval.epochs_in_interval().into_base_decimal()?
let staking_supply = rewarding_params.interval.staking_supply + pending_pool_change.removed;
let epoch_reward_budget = reward_pool
/ Decimal::from_atomics(interval.epochs_in_interval(), 0).unwrap()
* rewarding_params.interval.interval_pool_emission;
let stake_saturation_point =
staking_supply / rewarding_params.rewarded_set_size.into_base_decimal()?;
staking_supply / Decimal::from_atomics(rewarding_params.rewarded_set_size, 0).unwrap();
rewarding_params.interval.reward_pool = reward_pool;
rewarding_params.interval.staking_supply = staking_supply;
@@ -46,7 +45,7 @@ pub(crate) fn withdraw_operator_reward(
let mix_id = mix_details.mix_id();
let mut mix_rewarding = mix_details.rewarding_details;
let original_pledge = mix_details.bond_information.original_pledge;
let reward = mix_rewarding.withdraw_operator_reward(&original_pledge)?;
let reward = mix_rewarding.withdraw_operator_reward(&original_pledge);
// save updated rewarding info
storage::MIXNODE_REWARDING.save(store, mix_id, &mix_rewarding)?;
@@ -87,7 +86,7 @@ mod tests {
let mut test = TestSetup::new();
let epochs_in_interval = test.current_interval().epochs_in_interval();
let epochs_in_interval_dec = epochs_in_interval.into_base_decimal().unwrap();
let epochs_in_interval_dec = Decimal::from_atomics(epochs_in_interval, 0).unwrap();
let start_rewarding_params = test.rewarding_params();
// nothing changes if pending changes are empty
@@ -95,7 +94,7 @@ mod tests {
assert_eq!(start_rewarding_params, test.rewarding_params());
// normal case of having distributed some rewards
let distributed_rewards = 100_000_000u32.into_base_decimal().unwrap();
let distributed_rewards = Decimal::from_atomics(100_000_000u32, 0).unwrap();
storage::PENDING_REWARD_POOL_CHANGE
.save(
test.deps_mut().storage,
@@ -132,10 +131,7 @@ mod tests {
assert_eq!(
updated_rewarding_params.interval.stake_saturation_point,
updated_rewarding_params.interval.staking_supply
/ updated_rewarding_params
.rewarded_set_size
.into_base_decimal()
.unwrap()
/ Decimal::from_atomics(updated_rewarding_params.rewarded_set_size, 0).unwrap()
);
// resets changes back to 0
@@ -147,7 +143,7 @@ mod tests {
);
// future case of having to also increase the reward pool
let added_credentials = 50_000_000u32.into_base_decimal().unwrap();
let added_credentials = Decimal::from_atomics(50_000_000u32, 0).unwrap();
storage::PENDING_REWARD_POOL_CHANGE
.save(
test.deps_mut().storage,
@@ -184,10 +180,7 @@ mod tests {
assert_eq!(
updated_rewarding_params2.interval.stake_saturation_point,
updated_rewarding_params2.interval.staking_supply
/ updated_rewarding_params2
.rewarded_set_size
.into_base_decimal()
.unwrap()
/ Decimal::from_atomics(updated_rewarding_params2.rewarded_set_size, 0).unwrap()
);
// resets changes back to 0
@@ -204,7 +197,7 @@ mod tests {
let mut test = TestSetup::new();
let pledge = Uint128::new(250_000_000);
let pledge_dec = 250_000_000u32.into_base_decimal().unwrap();
let pledge_dec = Decimal::from_atomics(250_000_000u32, 0).unwrap();
let mix_id = test.add_dummy_mixnode("mix-owner", Some(pledge));
// no rewards
@@ -241,7 +234,7 @@ mod tests {
let mut test = TestSetup::new();
let delegation_amount = Uint128::new(2500_000_000);
let delegation_dec = 2500_000_000u32.into_base_decimal().unwrap();
let delegation_dec = Decimal::from_atomics(2500_000_000u32, 0).unwrap();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegator = "delegator";
test.add_immediate_delegation(delegator, delegation_amount, mix_id);
+11 -17
View File
@@ -7,7 +7,6 @@ use crate::interval::storage as interval_storage;
use crate::mixnodes;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{coin, Coin, Decimal, Deps, StdResult};
use mixnet_contract_common::helpers::into_base_decimal;
use mixnet_contract_common::mixnode::MixNodeDetails;
use mixnet_contract_common::reward_params::{NodeRewardParams, Performance, RewardingParams};
use mixnet_contract_common::rewarding::helpers::truncate_reward;
@@ -20,18 +19,16 @@ pub(crate) fn query_rewarding_params(deps: Deps<'_>) -> StdResult<RewardingParam
storage::REWARDING_PARAMS.load(deps.storage)
}
fn pending_operator_reward(
mix_details: Option<MixNodeDetails>,
) -> StdResult<PendingRewardResponse> {
Ok(match mix_details {
fn pending_operator_reward(mix_details: Option<MixNodeDetails>) -> PendingRewardResponse {
match mix_details {
Some(mix_details) => PendingRewardResponse {
amount_staked: Some(mix_details.original_pledge().clone()),
amount_earned: Some(mix_details.pending_operator_reward()),
amount_earned_detailed: Some(mix_details.pending_detailed_operator_reward()?),
amount_earned_detailed: Some(mix_details.pending_detailed_operator_reward()),
mixnode_still_fully_bonded: !mix_details.is_unbonding(),
},
None => PendingRewardResponse::default(),
})
}
}
pub fn query_pending_operator_reward(
@@ -42,7 +39,7 @@ pub fn query_pending_operator_reward(
// in order to determine operator's reward we need to know its original pledge and thus
// we have to load the entire thing
let mix_details = mixnodes::helpers::get_mixnode_details_by_owner(deps.storage, owner_address)?;
pending_operator_reward(mix_details)
Ok(pending_operator_reward(mix_details))
}
pub fn query_pending_mixnode_operator_reward(
@@ -52,7 +49,7 @@ pub fn query_pending_mixnode_operator_reward(
// in order to determine operator's reward we need to know its original pledge and thus
// we have to load the entire thing
let mix_details = mixnodes::helpers::get_mixnode_details_by_id(deps.storage, mix_id)?;
pending_operator_reward(mix_details)
Ok(pending_operator_reward(mix_details))
}
pub fn query_pending_delegator_reward(
@@ -77,8 +74,8 @@ pub fn query_pending_delegator_reward(
None => return Ok(PendingRewardResponse::default()),
};
let detailed_reward = mix_rewarding.determine_delegation_reward(&delegation)?;
let delegator_reward = mix_rewarding.pending_delegator_reward(&delegation)?;
let detailed_reward = mix_rewarding.determine_delegation_reward(&delegation);
let delegator_reward = mix_rewarding.pending_delegator_reward(&delegation);
// check if the mixnode isnt in the process of unbonding (or has already unbonded)
let is_bonded = matches!(mixnodes_storage::mixnode_bonds().may_load(deps.storage, mix_id)?, Some(mix_bond) if !mix_bond.is_unbonding);
@@ -180,8 +177,8 @@ pub(crate) fn query_estimated_current_epoch_delegator_reward(
None => return Ok(EstimatedCurrentEpochRewardResponse::empty_response()),
};
let staked_dec = into_base_decimal(delegation.amount.amount)?;
let current_value = staked_dec + mix_rewarding.determine_delegation_reward(&delegation)?;
let staked_dec = Decimal::from_atomics(delegation.amount.amount, 0).unwrap();
let current_value = staked_dec + mix_rewarding.determine_delegation_reward(&delegation);
let amount_staked = delegation.amount;
// check if the mixnode isnt in the process of unbonding (or has already unbonded)
@@ -795,10 +792,7 @@ mod tests {
let delegation = test.delegation(mix_id, owner, &None);
let staked_dec = Decimal::from_atomics(delegation.amount.amount, 0).unwrap();
let current_value = staked_dec
+ mix_rewarding
.determine_delegation_reward(&delegation)
.unwrap();
let current_value = staked_dec + mix_rewarding.determine_delegation_reward(&delegation);
let amount_staked = delegation.amount;
EstimatedCurrentEpochRewardResponse {
+22 -36
View File
@@ -744,7 +744,7 @@ pub mod tests {
performance,
in_active_set: true,
};
let sim_res = sim.simulate_epoch_single_node(node_params).unwrap();
let sim_res = sim.simulate_epoch_single_node(node_params);
assert_eq!(sim_res, dist);
}
test.skip_to_next_epoch_end();
@@ -768,7 +768,7 @@ pub mod tests {
performance,
in_active_set: true,
};
let sim_res = sim.simulate_epoch_single_node(node_params).unwrap();
let sim_res = sim.simulate_epoch_single_node(node_params);
assert_eq!(sim_res, dist);
}
test.skip_to_next_epoch_end();
@@ -809,8 +809,8 @@ pub mod tests {
in_active_set: true,
};
let dist1 = sim1.simulate_epoch_single_node(node_params).unwrap();
let dist2 = sim2.simulate_epoch_single_node(node_params).unwrap();
let dist1 = sim1.simulate_epoch_single_node(node_params);
let dist2 = sim2.simulate_epoch_single_node(node_params);
let env = test.env();
@@ -858,17 +858,15 @@ pub mod tests {
let unit_delegation_base = actual_prior1.unit_delegation;
// recompute the state of fully compounded delegation from before this rewarding was distributed
let pre_rewarding_del11 = del11.dec_amount().unwrap()
+ (prior_unit_reward - del11.cumulative_reward_ratio)
* del11.dec_amount().unwrap()
let pre_rewarding_del11 = del11.dec_amount()
+ (prior_unit_reward - del11.cumulative_reward_ratio) * del11.dec_amount()
/ (del11.cumulative_reward_ratio + unit_delegation_base);
let computed_del11_reward =
pre_rewarding_del11 / prior_delegates1 * delegates_reward1;
let pre_rewarding_del12 = del12.dec_amount().unwrap()
+ (prior_unit_reward - del12.cumulative_reward_ratio)
* del12.dec_amount().unwrap()
let pre_rewarding_del12 = del12.dec_amount()
+ (prior_unit_reward - del12.cumulative_reward_ratio) * del12.dec_amount()
/ (del12.cumulative_reward_ratio + unit_delegation_base);
let computed_del12_reward =
@@ -922,9 +920,8 @@ pub mod tests {
let unit_delegation_base = actual_prior2.unit_delegation;
// recompute the state of fully compounded delegation from before this rewarding was distributed
let pre_rewarding_del21 = del21.dec_amount().unwrap()
+ (prior_unit_reward - del21.cumulative_reward_ratio)
* del21.dec_amount().unwrap()
let pre_rewarding_del21 = del21.dec_amount()
+ (prior_unit_reward - del21.cumulative_reward_ratio) * del21.dec_amount()
/ (del21.cumulative_reward_ratio + unit_delegation_base);
let computed_del21_reward =
@@ -952,8 +949,8 @@ pub mod tests {
in_active_set: true,
};
let dist1 = sim1.simulate_epoch_single_node(node_params).unwrap();
let dist2 = sim2.simulate_epoch_single_node(node_params).unwrap();
let dist1 = sim1.simulate_epoch_single_node(node_params);
let dist2 = sim2.simulate_epoch_single_node(node_params);
let env = test.env();
@@ -1001,25 +998,22 @@ pub mod tests {
let unit_delegation_base = actual_prior1.unit_delegation;
// recompute the state of fully compounded delegation from before this rewarding was distributed
let pre_rewarding_del11 = del11.dec_amount().unwrap()
+ (prior_unit_reward - del11.cumulative_reward_ratio)
* del11.dec_amount().unwrap()
let pre_rewarding_del11 = del11.dec_amount()
+ (prior_unit_reward - del11.cumulative_reward_ratio) * del11.dec_amount()
/ (del11.cumulative_reward_ratio + unit_delegation_base);
let computed_del11_reward =
pre_rewarding_del11 / prior_delegates1 * delegates_reward1;
let pre_rewarding_del12 = del12.dec_amount().unwrap()
+ (prior_unit_reward - del12.cumulative_reward_ratio)
* del12.dec_amount().unwrap()
let pre_rewarding_del12 = del12.dec_amount()
+ (prior_unit_reward - del12.cumulative_reward_ratio) * del12.dec_amount()
/ (del12.cumulative_reward_ratio + unit_delegation_base);
let computed_del12_reward =
pre_rewarding_del12 / prior_delegates1 * delegates_reward1;
let pre_rewarding_del13 = del13.dec_amount().unwrap()
+ (prior_unit_reward - del13.cumulative_reward_ratio)
* del13.dec_amount().unwrap()
let pre_rewarding_del13 = del13.dec_amount()
+ (prior_unit_reward - del13.cumulative_reward_ratio) * del13.dec_amount()
/ (del13.cumulative_reward_ratio + unit_delegation_base);
let computed_del13_reward =
@@ -1073,17 +1067,15 @@ pub mod tests {
let unit_delegation_base = actual_prior2.unit_delegation;
// recompute the state of fully compounded delegation from before this rewarding was distributed
let pre_rewarding_del21 = del21.dec_amount().unwrap()
+ (prior_unit_reward - del21.cumulative_reward_ratio)
* del21.dec_amount().unwrap()
let pre_rewarding_del21 = del21.dec_amount()
+ (prior_unit_reward - del21.cumulative_reward_ratio) * del21.dec_amount()
/ (del21.cumulative_reward_ratio + unit_delegation_base);
let computed_del21_reward =
pre_rewarding_del21 / prior_delegates2 * delegates_reward2;
let pre_rewarding_del23 = del23.dec_amount().unwrap()
+ (prior_unit_reward - del23.cumulative_reward_ratio)
* del23.dec_amount().unwrap()
let pre_rewarding_del23 = del23.dec_amount()
+ (prior_unit_reward - del23.cumulative_reward_ratio) * del23.dec_amount()
/ (del23.cumulative_reward_ratio + unit_delegation_base);
let computed_del23_reward =
@@ -1718,7 +1710,6 @@ pub mod tests {
let update = IntervalRewardingParamsUpdate {
reward_pool: None,
staking_supply: None,
staking_supply_scale_factor: None,
sybil_resistance_percent: None,
active_set_work_factor: None,
interval_pool_emission: None,
@@ -1751,7 +1742,6 @@ pub mod tests {
let empty_update = IntervalRewardingParamsUpdate {
reward_pool: None,
staking_supply: None,
staking_supply_scale_factor: None,
sybil_resistance_percent: None,
active_set_work_factor: None,
interval_pool_emission: None,
@@ -1771,7 +1761,6 @@ pub mod tests {
let update = IntervalRewardingParamsUpdate {
reward_pool: None,
staking_supply: None,
staking_supply_scale_factor: None,
sybil_resistance_percent: None,
active_set_work_factor: None,
interval_pool_emission: None,
@@ -1806,7 +1795,6 @@ pub mod tests {
let update = IntervalRewardingParamsUpdate {
reward_pool: None,
staking_supply: None,
staking_supply_scale_factor: None,
sybil_resistance_percent: None,
active_set_work_factor: None,
interval_pool_emission: None,
@@ -1831,7 +1819,6 @@ pub mod tests {
let update = IntervalRewardingParamsUpdate {
reward_pool: None,
staking_supply: None,
staking_supply_scale_factor: None,
sybil_resistance_percent: None,
active_set_work_factor: None,
interval_pool_emission: None,
@@ -1872,7 +1859,6 @@ pub mod tests {
let update = IntervalRewardingParamsUpdate {
reward_pool: Some(old.interval.reward_pool / two),
staking_supply: Some(old.interval.staking_supply * four),
staking_supply_scale_factor: None,
sybil_resistance_percent: None,
active_set_work_factor: None,
interval_pool_emission: None,
-8
View File
@@ -69,10 +69,6 @@ pub(crate) fn validate_pledge(
});
}
// throughout this function we've been using the value at `pledge[0]` without problems
// (plus we have even validated that the vec is not empty), so the unwrap here is absolutely fine,
// since it cannot possibly fail without UB
#[allow(clippy::unwrap_used)]
Ok(pledge.pop().unwrap())
}
@@ -110,10 +106,6 @@ pub(crate) fn validate_delegation_stake(
return Err(MixnetContractError::EmptyDelegation);
}
// throughout this function we've been using the value at `delegation[0]` without problems
// (plus we have even validated that the vec is not empty), so the unwrap here is absolutely fine,
// since it cannot possibly fail without UB
#[allow(clippy::unwrap_used)]
Ok(delegation.pop().unwrap())
}
@@ -768,7 +768,6 @@ pub mod test_helpers {
InitialRewardingParams {
initial_reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(), // 250M * 1M (we're expressing it all in base tokens)
initial_staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(), // 100M * 1M
staking_supply_scale_factor: Percent::hundred(),
sybil_resistance: Percent::from_percentage_value(30).unwrap(),
active_set_work_factor: Decimal::from_atomics(10u32, 0).unwrap(),
interval_pool_emission: Percent::from_percentage_value(2).unwrap(),
+1 -4
View File
@@ -23,7 +23,4 @@ cw-storage-plus = { version = "0.13.4", features = ["iterator"] }
schemars = "0.8"
serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = { version = "1.0" }
[build-dependencies]
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc"] }
thiserror = { version = "1.0" }
-8
View File
@@ -1,8 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use vergen::{vergen, Config};
fn main() {
vergen(Config::default()).expect("failed to extract build metadata")
}
+2 -19
View File
@@ -8,7 +8,6 @@ use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use contracts_common::ContractBuildInformation;
use cosmwasm_std::{
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
QueryResponse, Response, StdResult, Timestamp, Uint128,
@@ -501,7 +500,6 @@ fn try_create_periodic_vesting_account(
#[entry_point]
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let query_res = match msg {
QueryMsg::GetContractVersion {} => to_binary(&get_contract_version()),
QueryMsg::LockedCoins {
vesting_account_address,
block_time,
@@ -589,7 +587,7 @@ pub fn try_get_current_vesting_period(
env: Env,
) -> Result<Period, ContractError> {
let account = account_from_address(address, deps.storage, deps.api)?;
account.get_current_vesting_period(env.block.time)
Ok(account.get_current_vesting_period(env.block.time))
}
/// Loads mixnode bond from vesting contract storage.
@@ -608,21 +606,6 @@ pub fn try_get_account(address: &str, deps: Deps<'_>) -> Result<Account, Contrac
account_from_address(address, deps.storage, deps.api)
}
/// Gets build information of this contract.
pub fn get_contract_version() -> ContractBuildInformation {
// as per docs
// env! macro will expand to the value of the named environment variable at
// compile time, yielding an expression of type `&'static str`
ContractBuildInformation {
build_timestamp: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
build_version: env!("VERGEN_BUILD_SEMVER").to_string(),
commit_sha: env!("VERGEN_GIT_SHA").to_string(),
commit_timestamp: env!("VERGEN_GIT_COMMIT_TIMESTAMP").to_string(),
commit_branch: env!("VERGEN_GIT_BRANCH").to_string(),
rustc_version: env!("VERGEN_RUSTC_SEMVER").to_string(),
}
}
/// Gets currently locked coins, see [crate::traits::VestingAccount::locked_coins]
pub fn try_get_locked_coins(
vesting_account_address: &str,
@@ -691,7 +674,7 @@ pub fn try_get_original_vesting(
deps: Deps<'_>,
) -> Result<OriginalVestingResponse, ContractError> {
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
account.get_original_vesting()
Ok(account.get_original_vesting())
}
/// See [crate::traits::VestingAccount::get_delegated_free]
-2
View File
@@ -50,6 +50,4 @@ pub enum ContractError {
MinVestingFunds { sent: u128, need: u128 },
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
#[error("VESTING: ({}: Account owned by {owner} has unpopulated vesting periods!", line!())]
UnpopulatedVestingPeriods { owner: Addr },
}
-3
View File
@@ -1,9 +1,6 @@
#![allow(rustdoc::private_intra_doc_links)]
//! Nym vesting contract, providing vesting accounts with ability to stake unvested tokens
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub mod contract;
mod errors;
mod queued_migrations;
@@ -55,7 +55,7 @@ pub trait VestingAccount {
/// Returns amount of coins set at account creation
/// See [/vesting-contract/struct.Account.html/method.get_original_vesting] for impl
fn get_original_vesting(&self) -> Result<OriginalVestingResponse, ContractError>;
fn get_original_vesting(&self) -> OriginalVestingResponse;
/// See [/vesting-contract/struct.Account.html/method.get_delegated_free] for impl
fn get_delegated_free(
+9 -31
View File
@@ -69,7 +69,7 @@ impl Account {
pub fn absolute_pledge_cap(&self) -> Result<Uint128, ContractError> {
match self.pledge_cap() {
PledgeCap::Absolute(cap) => Ok(cap),
PledgeCap::Percent(p) => Ok(p * self.get_original_vesting()?.amount.amount),
PledgeCap::Percent(p) => Ok(p * self.get_original_vesting().amount.amount),
}
}
@@ -81,13 +81,8 @@ impl Account {
self.periods.len()
}
pub fn period_duration(&self) -> Result<u64, ContractError> {
self.periods
.get(0)
.ok_or(ContractError::UnpopulatedVestingPeriods {
owner: self.owner_address.clone(),
})
.map(|p| p.period_seconds)
pub fn period_duration(&self) -> u64 {
self.periods.get(0).unwrap().period_seconds
}
pub fn storage_key(&self) -> u32 {
@@ -124,28 +119,11 @@ impl Account {
/// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
/// In case vesting is over it will always return NUM_VESTING_PERIODS.
pub fn get_current_vesting_period(
&self,
block_time: Timestamp,
) -> Result<Period, ContractError> {
let first_period =
self.periods
.first()
.ok_or(ContractError::UnpopulatedVestingPeriods {
owner: self.owner_address.clone(),
})?;
let last_period = self
.periods
.last()
.ok_or(ContractError::UnpopulatedVestingPeriods {
owner: self.owner_address.clone(),
})?;
if block_time.seconds() < first_period.start_time {
Ok(Period::Before)
} else if last_period.end_time() < block_time {
Ok(Period::After)
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
if block_time.seconds() < self.periods.first().unwrap().start_time {
Period::Before
} else if self.periods.last().unwrap().end_time() < block_time {
Period::After
} else {
let mut index = 0;
for period in &self.periods {
@@ -154,7 +132,7 @@ impl Account {
}
index += 1;
}
Ok(Period::In(index))
Period::In(index)
}
}
@@ -67,7 +67,7 @@ impl VestingAccount for Account {
storage: &dyn Storage,
) -> Result<Coin, ContractError> {
let block_time = block_time.unwrap_or(env.block.time);
let period = self.get_current_vesting_period(block_time)?;
let period = self.get_current_vesting_period(block_time);
let denom = MIX_DENOM.load(storage)?;
let amount = match period {
@@ -94,7 +94,7 @@ impl VestingAccount for Account {
storage: &dyn Storage,
) -> Result<Coin, ContractError> {
Ok(Coin {
amount: self.get_original_vesting()?.amount().amount
amount: self.get_original_vesting().amount().amount
- self.get_vested_coins(block_time, env, storage)?.amount,
denom: MIX_DENOM.load(storage)?,
})
@@ -105,15 +105,15 @@ impl VestingAccount for Account {
}
fn get_end_time(&self) -> Timestamp {
self.periods[(self.num_vesting_periods() - 1)].end_time()
self.periods[(self.num_vesting_periods() - 1) as usize].end_time()
}
fn get_original_vesting(&self) -> Result<OriginalVestingResponse, ContractError> {
Ok(OriginalVestingResponse::new(
fn get_original_vesting(&self) -> OriginalVestingResponse {
OriginalVestingResponse::new(
self.coin.clone(),
self.num_vesting_periods(),
self.period_duration()?,
))
self.period_duration(),
)
}
fn get_delegated_free(
@@ -123,16 +123,17 @@ impl VestingAccount for Account {
storage: &dyn Storage,
) -> Result<Coin, ContractError> {
let block_time = block_time.unwrap_or(env.block.time);
let period = self.get_current_vesting_period(block_time)?;
let withdrawn = self.load_withdrawn(storage)?;
let max_available = self
.get_vested_coins(Some(block_time), env, storage)?
.amount
.saturating_sub(withdrawn);
let period = self.get_current_vesting_period(block_time);
let start_time = match period {
Period::Before => 0,
Period::After => u64::MAX,
Period::In(idx) => self.periods[idx].start_time,
Period::In(idx) => self.periods[idx as usize].start_time,
};
let coin = self.total_delegations_at_timestamp(storage, start_time)?;
@@ -173,12 +174,12 @@ impl VestingAccount for Account {
storage: &dyn Storage,
) -> Result<Coin, ContractError> {
let block_time = block_time.unwrap_or(env.block.time);
let period = self.get_current_vesting_period(block_time)?;
let period = self.get_current_vesting_period(block_time);
let max_vested = self.get_vested_coins(Some(block_time), env, storage)?;
let start_time = match period {
Period::Before => 0,
Period::After => u64::MAX,
Period::In(idx) => self.periods[idx].start_time,
Period::In(idx) => self.periods[idx as usize].start_time,
};
let amount = if let Some(bond) = self
+13 -53
View File
@@ -15,7 +15,7 @@ pub struct VestingPeriod {
impl VestingPeriod {
pub fn end_time(&self) -> Timestamp {
Timestamp::from_seconds(self.start_time + self.period_seconds)
Timestamp::from_seconds(self.start_time + self.period_seconds as u64)
}
}
@@ -26,7 +26,7 @@ pub fn populate_vesting_periods(
let mut periods = Vec::with_capacity(vesting_spec.num_periods() as usize);
for i in 0..vesting_spec.num_periods() {
let period = VestingPeriod {
start_time: start_time + i * vesting_spec.period_seconds(),
start_time: start_time + i as u64 * vesting_spec.period_seconds(),
period_seconds: vesting_spec.period_seconds(),
};
periods.push(period);
@@ -183,14 +183,12 @@ mod tests {
assert_eq!(account.periods().len(), num_vesting_periods as usize);
let current_period = account
.get_current_vesting_period(Timestamp::from_seconds(0))
.unwrap();
let current_period = account.get_current_vesting_period(Timestamp::from_seconds(0));
assert_eq!(Period::Before, current_period);
let block_time =
Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1);
let current_period = account.get_current_vesting_period(block_time).unwrap();
let current_period = account.get_current_vesting_period(block_time);
assert_eq!(current_period, Period::In(1));
let vested_coins = account
.get_vested_coins(Some(block_time), &env, &deps.storage)
@@ -201,37 +199,21 @@ mod tests {
assert_eq!(
vested_coins.amount,
Uint128::new(
account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
/ num_vesting_periods as u128
account.get_original_vesting().amount().amount.u128() / num_vesting_periods as u128
)
);
assert_eq!(
vesting_coins.amount,
Uint128::new(
account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
- account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
account.get_original_vesting().amount().amount.u128()
- account.get_original_vesting().amount().amount.u128()
/ num_vesting_periods as u128
)
);
let block_time =
Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1);
let current_period = account.get_current_vesting_period(block_time).unwrap();
let current_period = account.get_current_vesting_period(block_time);
assert_eq!(current_period, Period::In(5));
let vested_coins = account
.get_vested_coins(Some(block_time), &env, &deps.storage)
@@ -242,30 +224,15 @@ mod tests {
assert_eq!(
vested_coins.amount,
Uint128::new(
5 * account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
5 * account.get_original_vesting().amount().amount.u128()
/ num_vesting_periods as u128
)
);
assert_eq!(
vesting_coins.amount,
Uint128::new(
account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
- 5 * account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
account.get_original_vesting().amount().amount.u128()
- 5 * account.get_original_vesting().amount().amount.u128()
/ num_vesting_periods as u128
)
);
@@ -273,7 +240,7 @@ mod tests {
let block_time = Timestamp::from_seconds(
account.start_time().seconds() + vesting_over_period * vesting_period + 1,
);
let current_period = account.get_current_vesting_period(block_time).unwrap();
let current_period = account.get_current_vesting_period(block_time);
assert_eq!(current_period, Period::After);
let vested_coins = account
.get_vested_coins(Some(block_time), &env, &deps.storage)
@@ -283,14 +250,7 @@ mod tests {
.unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(
account
.get_original_vesting()
.unwrap()
.amount()
.amount
.u128()
)
Uint128::new(account.get_original_vesting().amount().amount.u128())
);
assert_eq!(vesting_coins.amount, Uint128::zero());
}
-269
View File
@@ -1,269 +0,0 @@
# This template contains all of the possible sections and their default values
# Note that all fields that take a lint level have these possible values:
# * deny - An error will be produced and the check will fail
# * warn - A warning will be produced, but the check will not fail
# * allow - No warning or error will be produced, though in some cases a note
# will be
# The values provided in this template are the default values that will be used
# when any section or field is not specified in your own configuration
# Root options
# If 1 or more target triples (and optionally, target_features) are specified,
# only the specified targets will be checked when running `cargo deny check`.
# This means, if a particular package is only ever used as a target specific
# dependency, such as, for example, the `nix` crate only being used via the
# `target_family = "unix"` configuration, that only having windows targets in
# this list would mean the nix crate, as well as any of its exclusive
# dependencies not shared by any other crates, would be ignored, as the target
# list here is effectively saying which targets you are building for.
targets = [
# The triple can be any string, but only the target triples built in to
# rustc (as of 1.40) can be checked against actual config expressions
#{ triple = "x86_64-unknown-linux-musl" },
# You can also specify which target_features you promise are enabled for a
# particular target. target_features are currently not validated against
# the actual valid features supported by the target architecture.
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
]
# When creating the dependency graph used as the source of truth when checks are
# executed, this field can be used to prune crates from the graph, removing them
# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate
# is pruned from the graph, all of its dependencies will also be pruned unless
# they are connected to another crate in the graph that hasn't been pruned,
# so it should be used with care. The identifiers are [Package ID Specifications]
# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html)
#exclude = []
# If true, metadata will be collected with `--all-features`. Note that this can't
# be toggled off if true, if you want to conditionally enable `--all-features` it
# is recommended to pass `--all-features` on the cmd line instead
all-features = false
# If true, metadata will be collected with `--no-default-features`. The same
# caveat with `all-features` applies
no-default-features = false
# If set, these feature will be enabled when collecting metadata. If `--features`
# is specified on the cmd line they will take precedence over this option.
#features = []
# When outputting inclusion graphs in diagnostics that include features, this
# option can be used to specify the depth at which feature edges will be added.
# This option is included since the graphs can be quite large and the addition
# of features from the crate(s) to all of the graph roots can be far too verbose.
# This option can be overridden via `--feature-depth` on the cmd line
feature-depth = 1
# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
# The path where the advisory database is cloned/fetched into
db-path = "~/.cargo/advisory-db"
# The url(s) of the advisory databases to use
db-urls = ["https://github.com/rustsec/advisory-db"]
# The lint level for security vulnerabilities
vulnerability = "deny"
# The lint level for unmaintained crates
unmaintained = "allow"
# The lint level for crates that have been yanked from their source registry
yanked = "warn"
# The lint level for crates with security notices. Note that as of
# 2019-12-17 there are no security notice advisories in
# https://github.com/rustsec/advisory-db
notice = "warn"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
"RUSTSEC-2020-0159",
"RUSTSEC-2020-0071",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
# will still output a note when they are encountered.
# * None - CVSS Score 0.0
# * Low - CVSS Score 0.1 - 3.9
# * Medium - CVSS Score 4.0 - 6.9
# * High - CVSS Score 7.0 - 8.9
# * Critical - CVSS Score 9.0 - 10.0
#severity-threshold =
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
# See Git Authentication for more information about setting up git authentication.
#git-fetch-with-cli = true
# This section is considered when running `cargo deny check licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
# The lint level for crates which do not have a detectable license
unlicensed = "deny"
# List of explicitly allowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
#"MIT",
#"Apache-2.0",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
deny = [
#"Nokia",
]
# Lint level for licenses considered copyleft
copyleft = "warn"
# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
# * both - The license will be approved if it is both OSI-approved *AND* FSF
# * either - The license will be approved if it is either OSI-approved *OR* FSF
# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
# * neither - This predicate is ignored and the default lint level is used
allow-osi-fsf-free = "neither"
# Lint level used when no other predicates are matched
# 1. License isn't in the allow or deny lists
# 2. License isn't copyleft
# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
default = "deny"
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
# canonical license text of a valid SPDX license file.
# [possible values: any between 0.0 and 1.0].
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = [
# Each entry is the crate and version constraint, and its specific allow
# list
#{ allow = ["Zlib"], name = "adler32", version = "*" },
]
# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information
#[[licenses.clarify]]
# The name of the crate the clarification applies to
#name = "ring"
# The optional version constraint for the crate
#version = "*"
# The SPDX expression for the license requirements of the crate
#expression = "MIT AND ISC AND OpenSSL"
# One or more files in the crate's source used as the "source of truth" for
# the license expression. If the contents match, the clarification will be used
# when running the license check, otherwise the clarification will be ignored
# and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration
#license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 }
#]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
# published to private registries.
# To see how to mark a crate as unpublished (to the official registry),
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
ignore = false
# One or more private registries that you might publish crates to, if a crate
# is only published to private registries, and ignore is true, the crate will
# not have its license(s) checked
registries = [
#"https://sekretz.com/registry
]
# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
# Lint level for when multiple versions of the same crate are detected
multiple-versions = "warn"
# Lint level for when a crate version requirement is `*`
wildcards = "allow"
# The graph highlighting used when creating dotgraphs for crates
# with multiple versions
# * lowest-version - The path to the lowest versioned duplicate is highlighted
# * simplest-path - The path to the version with the fewest edges is highlighted
# * all - Both lowest-version and simplest-path are used
highlight = "all"
# The default lint level for `default` features for crates that are members of
# the workspace that is being checked. This can be overriden by allowing/denying
# `default` on a crate-by-crate basis if desired.
workspace-default-features = "allow"
# The default lint level for `default` features for external crates that are not
# members of the workspace. This can be overriden by allowing/denying `default`
# on a crate-by-crate basis if desired.
external-default-features = "allow"
# List of crates that are allowed. Use with care!
allow = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# List of crates to deny
deny = [
# Each entry the name of a crate and a version range. If version is
# not specified, all versions will be matched.
#{ name = "ansi_term", version = "=0.11.0" },
#
# Wrapper crates can optionally be specified to allow the crate when it
# is a direct dependency of the otherwise banned crate
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
]
# List of features to allow/deny
# Each entry the name of a crate and a version range. If version is
# not specified, all versions will be matched.
#[[bans.features]]
#name = "reqwest"
# Features to not allow
#deny = ["json"]
# Features to allow
#allow = [
# "rustls",
# "__rustls",
# "__tls",
# "hyper-rustls",
# "rustls",
# "rustls-pemfile",
# "rustls-tls-webpki-roots",
# "tokio-rustls",
# "webpki-roots",
#]
# If true, the allowed features must exactly match the enabled feature set. If
# this is set there is no point setting `deny`
#exact = true
# Certain crates/versions that will be skipped when doing duplicate detection.
skip = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# Similarly to `skip` allows you to skip certain crates during duplicate
# detection. Unlike skip, it also includes the entire tree of transitive
# dependencies starting at the specified crate, up to a certain depth, which is
# by default infinite.
skip-tree = [
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
]
# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
# Lint level for what to happen when a crate from a crate registry that is not
# in the allow list is encountered
unknown-registry = "warn"
# Lint level for what to happen when a crate from a git repository that is not
# in the allow list is encountered
unknown-git = "warn"
# List of URLs for allowed crate registries. Defaults to the crates.io index
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
allow-git = []
[sources.allow-org]
# 1 or more github.com organizations to allow git sources for
github = [""]
# 1 or more gitlab.com organizations to allow git sources for
gitlab = [""]
# 1 or more bitbucket.org organizations to allow git sources for
bitbucket = [""]
-1
View File
@@ -29,6 +29,5 @@ dotenv = "0.15.0"
mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" }
contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
network-defaults = { path = "../common/network-defaults" }
logging = { path = "../common/logging"}
task = { path = "../common/task" }
validator-client = { path = "../common/client-libs/validator-client", features=["nymd-client"] }

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