a7d8613c9d
* Feature/multi surbs (#1796) * bunch of wip with focus on serialization * Being able to send normal data (NO SURBS yet) to yourself again * Fixed RepliableMessage deserialization * Recovering data from surb messages * Extracted common code in sphinx payload construction * Cleanup within received buffer * requesting, sending and using additional reply surbs * Following discussion with @simonwicky, removing sender proof and decreasing size of sender tag * Made sender tag more easily configurable * Refactoring of message creation * Propagating reply surb acks but not retransmitting them yet * Surb retransmissions * requesting additional surbs from the retransmission flow * correctly determining the point of requesting additional surbs * Ability to use socks5 (and network requester) with surbs * Improved surbs retranmsission reliability * naive way of not over-requesting surbs * wip on tag storage * Improved error propagation for message construction * Requesting more surbs for stale entries * Better controlling the point of having to request additional surbs * Using pseudorandom sender tag instead of a hardcoded one * First cleanup round in MessageHandler * Error cleanup and if simplification * Assigned a more permanent name to the ReplyController * Removed PendingReply redundant type * Made socks5 client less eager to over-send reply surbs * 'anonymous' field on socks5 client to decide whether to use surbs or attach address * Dead code and import removal in client-core * Updating ClientRequest variants * Adjusted decision threshold for requesting more surbs * Native client cleanup * Made socks5 client usage of surbs configurable * Restored statistics in network requester * Validator-api compiles once again * Further improved surb request logic * boxing the recipient in controller requests * Removal of hardcoded values in favour of propagating them from the config * more validation during surb requests * Fixed ClientRequest::Send deserialization * Added length checks for request deserialization * post-merge formatting * Unit tests once again compile and pass * controlling retransmission_reply_surb_request_size from config * More Recipient boxing action * Requesting additional reply surbs for retransmission BEFORE dipping below the threshold * Making clippy generally happier * Wasm client compiles (but might not yet work correctly) * Feature/use expect instead of panicking (#1797) * Implementation of 'Debug' on 'RealMessage' * expect with failed channel name instead of throwing empty panics * Introduced Debug trait constraint in ProxyRunner * Derive Debug for socks5_requests::Message * Fix decrypting stored received msg (#1786) * Fix decrypting stored received msg * rustfmt * Moving binary message recovery to separate function Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> * real_traffic_stream: reduce frequency of status print (#1794) * Properly defined unnamed errors * Dealing with previously ignored errors * logging improvements * Removed old example code Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com> * Missing changelog entry for multi-surbs (#1802) * Making anonymous sender tag human readable (#1801) * Created wrapper with string serialization for AnonymousSenderTag * Using Display implementation of AnonymousSenderTag for logs * Using Display implementation of MessageRecoveryError when logging (#1803) * Using Display implementation of MessageRecoveryError when logging * Updated changelog * Defined socks5 client startup flag to enable reply-surb communication (#1804) * Feature/persistent surbs data (#1835) * prototyping wip * Implemented ReplyStorageBackend trait for the sql-backed storage * Storing correct surb threshold * using correct database path * Starting surb persistent storage in native and socks5 clients * loading or creating fresh surb storage in socks5 and native clients * making clippy happier + fixing config templates * Creating status table on database rotation * Completed the 'Empty' ReplyStorageBackend * feature locking wasm-incompatible bits and pieces * Feature/develop resync (#1844) * Network-requester: throttle inbound connections (#1789) * Return and handle ClientRequest::LaneQueueLenghts * Pass lane queue lengths to inbound future * Remove unused self reference * Request lane queue lengths periodically for all open connections * Add timeouts * Rename to ConnectionCommandSender and Receiver * Rename to client_connection_tx/rx * Fix wasm build * Replace bool with enum * rust: bump required version to 1.65 in some crates that need it * Add step to release GH actions (#1792) * feat: add a release step to nym contracts GH action * feat: add shrinking the size of wasm * Possibilty to change gateway ws listener (#1779) * add: set gatewayListener * Update types.ts * Update worker.ts * Update contracts-build.yml * real_traffic_stream: reduce frequency of status print (#1794) * Update wallet and connect lock files (#1793) * client-core: add warning when delay multiplier is larger than 1 * Fix decrypting stored received msg (#1786) * Fix decrypting stored received msg * rustfmt * Moving binary message recovery to separate function Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> * Feature/use expect instead of panicking (#1797) * Implementation of 'Debug' on 'RealMessage' * expect with failed channel name instead of throwing empty panics * Introduced Debug trait constraint in ProxyRunner * Derive Debug for socks5_requests::Message * Make connection_id optional in ClientRequest::Send (#1798) * changelog: add missing entry for fixing message decrypt in gateway-client * websocket-requests: fix length check before deserialize (#1799) * Fix export dkg contract addr (#1800) * Export dkg contract for mainnet when no config file present * Remove redundant env files * nym-cli: improve error reporting/handling and changed `vesting-schedule` queries to use query client instead of signing client * Feature/gateway client protocol version (#1795) * Introducing concept of gateway protocol version * Remove version-based gateway filtering * Fixed the unit test * grammar * Set build on latest release on schedule event * Added nightly build workflow on second latest release * socks5: if any task panics, signal all other tasks to shutdown (#1805) * socks5: signal shutdown on error * Mark as success * Tidy * Reduce wait to 5 sec * Replace unwrap with expect * Two more unwraps * Update changelog * client-core: less frequent status logging (#1806) * Feature/nym connect UI updates (#1784) * create custom titlebar * create help page * create generic modal component * create separate connection time component * link to shipyard docs * move timer to separate component and update connection status component usage * use separate component for copying ip and port details * only show infomodal once after connection * set service provider on tauri side * Emit events when stopped * listen and unlisten for tauri events * connect: add trace log to get_services * Add back CI notifications * Update README Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com> Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> * Use default serde value for upgrade (#1807) * fix ui overflow bug (#1808) * update nym connect error text (#1809) * set flag to false * Fix wait_for_signal_and_error on win (#1811) * Add socks5-client changes to nym-connect changelog * Fix links in nym-connect changelog * More entries in nym-connect CHANGELOG * Fix typo in changelog * Update CHANGELOG.md * Experiment/client refactoring (#1814) * experimenting with extracting more common client code * drying up the wasm client * allowing some dead code for the time being * fixed formatting in nym-connect * made socks5 client inside nym-connect immutable * made clippy a bit happier * hidden away target locking for recv timeout * New transactions for increasing amount of pledged tokens * unit tests * Added an option to pledge extra tokens through the vesting contract * Introduced wallet endpoints for new operations * Using updated pledge cap in the vesting contract * Bumping version numbers * Changelog for v1.1.1 * Bumping final version numbers for 1.1.1 * Bumping nym-cli version, missed it last time * socks5-client: SOCKS4a support (#1822) * socks5-client: SOCKS4a support * Tidy * Fix a few errors in socks5 client and network-requester (#1823) * Fix two unwraps in socks5 and network-requester * Make sure client task never sends shutdown signal * Fix panic on getting socks version * wip * connecting to the back and making the requests work * display details modal * logs removal * Feature/pledge more (#1679) * New transactions for increasing amount of pledged tokens * unit tests * Added an option to pledge extra tokens through the vesting contract * Introduced wallet endpoints for new operations * Using updated pledge cap in the vesting contract * Changelog update * nym-connect: update lock file * avoid mix tokens pools * amount error * envs/mainnet: update to latest mixnet contract and nymd validator url * validator-api: add missing shortform for --config-env-file (#1830) * gateway-client: handle shutdown listener (#1829) * WIP * WIP: try another approach * WIP * Reworked * Tidy * fix * validator-api: remove storage dependency in contract cache (#1685) * validator-api: remove storage dependency in contract cache * validator-client: update detailed routes * contract_cache: forward to new endpoints for compat * Move reward_estimate * client: add --no-cover and update --fastmode (#1831) * adding a oversaturaded bonding more modal * common/task: extract out spawn_with_report_error (#1837) * stop panic on failed buffer request * Compilable wasm client * Enabled hard error on lack of gateway-client protocol version * Missing generic parameter for ClientCoreError in BackendError * Removed unused imports * Additional wasm feature locking Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com> Co-authored-by: Fran Arbanas <arbanasfran@gmail.com> Co-authored-by: cgi-bin/ <6095048+sven-hash@users.noreply.github.com> Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> Co-authored-by: Bogdan-Ștefan Neacşu <bogdan@nymtech.net> Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com> Co-authored-by: Raphaël Walther <raphael@nymtech.net> Co-authored-by: Fouad <fmtabbara@hotmail.co.uk> Co-authored-by: Gala <calero.vg@gmail.com> Co-authored-by: Dave Hrycyszyn <futurechimp@users.noreply.github.com> * Making native client wait for shutdown * Marking dead test code * Feature/multi surbs invalidation (#1858) * Cleaned up RealMessagesController constructor * introduced config field for maximum_reply_surb_age * Handling edge-case reply-surb failures * invalidating old reply surbs * Removing old reply keys from cache * Invalidating old reply keys * missing config changes * logging created tag details * Fixed clippy warning in test code * Saving reply key timestamp on data flush (#1867) * Remove panic if ReconstructedMessagesReceiver is closed (#1868) Instead log error and return because presumably the shutdown procedure has started * Feature/multi surbs basic wasm interface (#1846) * Added builder to wasm client * missing wasm_bindgen macros * Added constructor macro on GatewayEndpointConfig * Attempting to use updated wasm client api * Removing dead code * Exposed other messages types in wasm client * cleanup in js-example * Changed 'self_address' to be a method call * Removed needless borrow when cloning an Arc * Improving arguments in 'on_message' callback * fixed wasm-client dependency/features * Reverted hard requirement for gateway protocol presence (#1875) * Feature/prioritise surb retransmission (#1883) * Improved error messages + removed redundant variants * Improved estimation of 'expected_forward_delay' * Removed old wasm-specific startup code * Removed old unused reply-related code * hacky and temporary way of buffering retransmission data * offloading retransmission reply handling to ReplyController * fixed linter errors + rebuffering retransmission data on failure * Removed unused fields from wasm client debug config * Chore/v1.2.0 update (#2666) * Network-requester: throttle inbound connections (#1789) * Return and handle ClientRequest::LaneQueueLenghts * Pass lane queue lengths to inbound future * Remove unused self reference * Request lane queue lengths periodically for all open connections * Add timeouts * Rename to ConnectionCommandSender and Receiver * Rename to client_connection_tx/rx * Fix wasm build * Replace bool with enum * rust: bump required version to 1.65 in some crates that need it * Add step to release GH actions (#1792) * feat: add a release step to nym contracts GH action * feat: add shrinking the size of wasm * Possibilty to change gateway ws listener (#1779) * add: set gatewayListener * Update types.ts * Update worker.ts * Update contracts-build.yml * real_traffic_stream: reduce frequency of status print (#1794) * Update wallet and connect lock files (#1793) * client-core: add warning when delay multiplier is larger than 1 * Fix decrypting stored received msg (#1786) * Fix decrypting stored received msg * rustfmt * Moving binary message recovery to separate function Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> * Feature/use expect instead of panicking (#1797) * Implementation of 'Debug' on 'RealMessage' * expect with failed channel name instead of throwing empty panics * Introduced Debug trait constraint in ProxyRunner * Derive Debug for socks5_requests::Message * Make connection_id optional in ClientRequest::Send (#1798) * changelog: add missing entry for fixing message decrypt in gateway-client * websocket-requests: fix length check before deserialize (#1799) * Fix export dkg contract addr (#1800) * Export dkg contract for mainnet when no config file present * Remove redundant env files * nym-cli: improve error reporting/handling and changed `vesting-schedule` queries to use query client instead of signing client * Feature/gateway client protocol version (#1795) * Introducing concept of gateway protocol version * Remove version-based gateway filtering * Fixed the unit test * grammar * Set build on latest release on schedule event * feat(wallet): buy page bootstrap * feat(wallet-buy): tutorial * feat(explorer-api): add route to fetch nym terms&cdts * Revert "feat(explorer-api): add route to fetch nym terms&cdts" This reverts commit 876f752697d89061b1904e1ddd1d5bcb7045dc5c. * feat(wallet-buy-nym): buy page new ui * fix(wallet-buy-nym): signature output * feat(wallet-buy-nym): update signature modal ui * Added nightly build workflow on second latest release * socks5: if any task panics, signal all other tasks to shutdown (#1805) * socks5: signal shutdown on error * Mark as success * Tidy * Reduce wait to 5 sec * Replace unwrap with expect * Two more unwraps * Update changelog * client-core: less frequent status logging (#1806) * Feature/nym connect UI updates (#1784) * create custom titlebar * create help page * create generic modal component * create separate connection time component * link to shipyard docs * move timer to separate component and update connection status component usage * use separate component for copying ip and port details * only show infomodal once after connection * set service provider on tauri side * Emit events when stopped * listen and unlisten for tauri events * connect: add trace log to get_services * Add back CI notifications * Update README Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com> Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> * Use default serde value for upgrade (#1807) * fix ui overflow bug (#1808) * feat(wallet): add link to nym exchange interface * update nym connect error text (#1809) * refactor(wallet): clean code * set flag to false * Fix wait_for_signal_and_error on win (#1811) * Use config URLs in clients before the env values (#1813) * Add socks5-client changes to nym-connect changelog * Fix links in nym-connect changelog * More entries in nym-connect CHANGELOG * Fix typo in changelog * Update CHANGELOG.md * Experiment/client refactoring (#1814) * experimenting with extracting more common client code * drying up the wasm client * allowing some dead code for the time being * fixed formatting in nym-connect * made socks5 client inside nym-connect immutable * made clippy a bit happier * hidden away target locking for recv timeout * New transactions for increasing amount of pledged tokens * unit tests * Added an option to pledge extra tokens through the vesting contract * Introduced wallet endpoints for new operations * Using updated pledge cap in the vesting contract * Feature/dkg integration tests (#1815) * DKG contract e2e test * Refactor to the same format as other contracts * Vk share tests * State tests * Dealings tests * Dealer tests * Api dkg tests * Fix path to contract after refactor * Fix test target clippy * Bumping version numbers * Changelog for v1.1.1 * Bumping final version numbers for 1.1.1 * Bumping nym-cli version, missed it last time * socks5-client: SOCKS4a support (#1822) * socks5-client: SOCKS4a support * Tidy * Fix a few errors in socks5 client and network-requester (#1823) * Fix two unwraps in socks5 and network-requester * Make sure client task never sends shutdown signal * Fix panic on getting socks version * wip * connecting to the back and making the requests work * display details modal * logs removal * Feature/pledge more (#1679) * New transactions for increasing amount of pledged tokens * unit tests * Added an option to pledge extra tokens through the vesting contract * Introduced wallet endpoints for new operations * Using updated pledge cap in the vesting contract * Changelog update * Feature/pledge more (#1679) * New transactions for increasing amount of pledged tokens * unit tests * Added an option to pledge extra tokens through the vesting contract * Introduced wallet endpoints for new operations * Using updated pledge cap in the vesting contract * Changelog update * Fix a few errors in socks5 client and network-requester (backport) (#1824) * Fix two unwraps in socks5 and network-requester * Make sure client task never sends shutdown signal * nym-connect: update lock file * fix(wallet): typo * avoid mix tokens pools * fix(wallet): typo * fix(wallet): buy tutorial ui responsivness * amount error * envs/mainnet: update to latest mixnet contract and nymd validator url * validator-api: add missing shortform for --config-env-file (#1830) * gateway-client: handle shutdown listener (#1829) * WIP * WIP: try another approach * WIP * Reworked * Tidy * fix * validator-api: remove storage dependency in contract cache (#1685) * validator-api: remove storage dependency in contract cache * validator-client: update detailed routes * contract_cache: forward to new endpoints for compat * Move reward_estimate * Node family management (#1670) * Family management messages * Add family queries * Add queries to client * Layer assignment message * Paged family queries, annotate mixnodes with family * Add layer assignments to epoch operations * Remove family layer peristence * Add NotImplemented error for kick Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> * Fixed layer distribution skewness check (#1766) * client: add --no-cover and update --fastmode (#1831) * adding a oversaturaded bonding more modal * Use better naming on gateway credential handling (#1834) * Fix comment in configuration file (#1836) * common/task: extract out spawn_with_report_error (#1837) * nym-connect/changelog: add note about disconnect fix * Feature/simplify credential binary (#1841) * Expose name of standard directories * Use one command instead of two * nym-connect: append error to failed message (#1839) * nym-connect: append error to failed message * changelog: add note * Fix clippy * remove extra checks to display vesting schedule(#1826) * Set explorer to use rpc.nymtech.net * update versions for platfrom, nym-connect and nym-wallet to v1.1.2 * changed nym-connect version to 1.1.1 * Modifying changelog for v1.1.2 * changed nym-connect version to 1.1.2 * update nym-connect CHANGELOG * Updated changelog for wallet * Feature/wallet content updates (#1825) * fix up balance screen * fix up app bar and nym logo alignment * fix up delegation action icon font weight * fix up bond page * Corrected env variable name in workflows * Use config URLs in clients before the env values (#1813) * Feature/dkg integration tests (#1815) * DKG contract e2e test * Refactor to the same format as other contracts * Vk share tests * State tests * Dealings tests * Dealer tests * Api dkg tests * Fix path to contract after refactor * Fix test target clippy * Node family management (#1670) * Family management messages * Add family queries * Add queries to client * Layer assignment message * Paged family queries, annotate mixnodes with family * Add layer assignments to epoch operations * Remove family layer peristence * Add NotImplemented error for kick Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> * Fixed layer distribution skewness check (#1766) * Use better naming on gateway credential handling (#1834) * Fix comment in configuration file (#1836) * nym-connect/changelog: add note about disconnect fix * Feature/simplify credential binary (#1841) * Expose name of standard directories * Use one command instead of two * Fix clippy * feat(wallet): buy page bootstrap * feat(wallet-buy): tutorial * feat(explorer-api): add route to fetch nym terms&cdts * Revert "feat(explorer-api): add route to fetch nym terms&cdts" This reverts commit 876f752697d89061b1904e1ddd1d5bcb7045dc5c. * feat(wallet-buy-nym): buy page new ui * fix(wallet-buy-nym): signature output * feat(wallet-buy-nym): update signature modal ui * feat(wallet): add link to nym exchange interface * refactor(wallet): clean code * fix(wallet): typo * fix(wallet): typo * fix(wallet): buy tutorial ui responsivness * update versions for platfrom, nym-connect and nym-wallet to v1.1.2 * changed nym-connect version to 1.1.1 * Modifying changelog for v1.1.2 * changed nym-connect version to 1.1.2 * update nym-connect CHANGELOG * Updated changelog for wallet * Resolve merge conflicts * Update qa-qwerty.env * Fixed URL to branch * changed ubuntu-latest on GH actions to ubuntu-20.04 * docs: updated changelog for contracts release v1.1.2 and updated versions of mixnet and vesting contracts as well * Add ignore to dkg expensive tests (#1856) * introduce minimize button in custom title bar (#1843) * refresh balance after sending tokens (#1857) * Feature/fix client multi cred consume (#1859) * Mark consumed credentials in the db * Add signature log * Fix wasm mock Storage trait * Fix clippy * Feature/verify bte proof (#1866) * Update lock file * Include bte public key verification * Wallet - Buy, copy changes (#1855) * use mix_id for account to get correct pending cost event (#1869) * use mix_id for account to get correct pending cost event * Properly add consumed to table (#1870) * nym-connect: update Cargo.lock to 1.1.2 * Clients: save init results to JSON (#1865) * clients: output results of init to json * Remove leftover dbg * Tidy * Fix nym-connect * Client: dedup setup gateway during init (#1871) * clients: dedup gateway setup logic * nym-connect: extract out print_save_config * Feature/dkg state to disk (#1872) * Add PersistentState * Save and load state to/from disk * If in progress, don't continually write the same state * Fix tests and add serde one * Update changelog * Fix clippy * network-requester: return error on socket close (#1876) * network-requester: return error when the socket closes * changelog: add note * clients: further deduplicate init code (#1873) * client-core: move init helpers to module * WIP * socks5: return error instead of terminate in init * Extract out reuse_existing_gateway_config * rustfmt * Remove comment out code * nym-connect: use setup_gateway * Linebreak * changelog: update * Tweak log * rustfmt * client: pick from old lanes probabilisticlly (#1877) * Pick from old lanes probabilisticly * changelog: update * clients: dont panic in base client gateway client handling (#1878) * client-core: fix some panics related to gateway-client * changelog: update * fix * changelog: fix wording * Use default mainnet values when nothing is specified (#1884) Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com> Co-authored-by: Fran Arbanas <arbanasfran@gmail.com> Co-authored-by: cgi-bin/ <6095048+sven-hash@users.noreply.github.com> Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> Co-authored-by: Bogdan-Ștefan Neacşu <bogdan@nymtech.net> Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com> Co-authored-by: Raphaël Walther <raphael@nymtech.net> Co-authored-by: pierre <dommerc.pierre@gmail.com> Co-authored-by: Fouad <fmtabbara@hotmail.co.uk> Co-authored-by: Gala <calero.vg@gmail.com> Co-authored-by: Dave Hrycyszyn <futurechimp@users.noreply.github.com> Co-authored-by: Drazen Urch <drazen@urch.eu> Co-authored-by: durch <durch@users.noreply.github.com> Co-authored-by: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com> Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com> Co-authored-by: Fran Arbanas <arbanasfran@gmail.com> Co-authored-by: cgi-bin/ <6095048+sven-hash@users.noreply.github.com> Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> Co-authored-by: Bogdan-Ștefan Neacşu <bogdan@nymtech.net> Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com> Co-authored-by: Raphaël Walther <raphael@nymtech.net> Co-authored-by: Fouad <fmtabbara@hotmail.co.uk> Co-authored-by: Gala <calero.vg@gmail.com> Co-authored-by: Dave Hrycyszyn <futurechimp@users.noreply.github.com> Co-authored-by: pierre <dommerc.pierre@gmail.com> Co-authored-by: Drazen Urch <drazen@urch.eu> Co-authored-by: durch <durch@users.noreply.github.com> Co-authored-by: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com>
508 lines
19 KiB
Rust
508 lines
19 KiB
Rust
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::client::replies::reply_controller::ReplyControllerSender;
|
|
use crate::client::replies::reply_storage::SentReplyKeys;
|
|
use crate::spawn_future;
|
|
use crypto::asymmetric::encryption;
|
|
use crypto::Digest;
|
|
use futures::channel::mpsc;
|
|
use futures::lock::Mutex;
|
|
use futures::StreamExt;
|
|
use gateway_client::MixnetMessageReceiver;
|
|
use log::*;
|
|
use nymsphinx::anonymous_replies::requests::{
|
|
RepliableMessage, RepliableMessageContent, ReplyMessage, ReplyMessageContent,
|
|
};
|
|
use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEncryptionKey};
|
|
use nymsphinx::message::{NymMessage, PlainMessage};
|
|
use nymsphinx::params::ReplySurbKeyDigestAlgorithm;
|
|
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
|
|
use std::collections::HashSet;
|
|
use std::sync::Arc;
|
|
|
|
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
|
|
// or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead"
|
|
pub type ReceivedBufferRequestSender = mpsc::UnboundedSender<ReceivedBufferMessage>;
|
|
pub type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferMessage>;
|
|
|
|
// The channel set for the above
|
|
pub type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
|
|
pub type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
|
|
|
|
struct ReceivedMessagesBufferInner {
|
|
messages: Vec<ReconstructedMessage>,
|
|
local_encryption_keypair: Arc<encryption::KeyPair>,
|
|
|
|
// TODO: looking how it 'looks' here, perhaps `MessageReceiver` should be renamed to something
|
|
// else instead.
|
|
message_receiver: MessageReceiver,
|
|
message_sender: Option<ReconstructedMessagesSender>,
|
|
|
|
// TODO: this will get cleared upon re-running the client
|
|
// but perhaps it should be changed to include timestamps of when the message was reconstructed
|
|
// and every now and then remove ids older than X
|
|
recently_reconstructed: HashSet<i32>,
|
|
}
|
|
|
|
impl ReceivedMessagesBufferInner {
|
|
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
|
|
if nymsphinx::cover::is_cover(fragment_data) {
|
|
trace!("The message was a loop cover message! Skipping it");
|
|
return None;
|
|
}
|
|
|
|
let fragment = match self.message_receiver.recover_fragment(fragment_data) {
|
|
Err(err) => {
|
|
warn!("failed to recover fragment from raw data: {err}. The whole underlying message might be corrupted and unrecoverable!");
|
|
return None;
|
|
}
|
|
Ok(frag) => frag,
|
|
};
|
|
|
|
if self.recently_reconstructed.contains(&fragment.id()) {
|
|
debug!("Received a chunk of already re-assembled message ({:?})! It probably got here because the ack got lost", fragment.id());
|
|
return None;
|
|
}
|
|
|
|
// if we returned an error the underlying message is malformed in some way
|
|
match self.message_receiver.insert_new_fragment(fragment) {
|
|
Err(err) => match err {
|
|
MessageRecoveryError::MalformedReconstructedMessage { source, used_sets } => {
|
|
error!("message reconstruction failed - {source}. Attempting to re-use the message sets...");
|
|
// TODO: should we really insert reconstructed sets? could this be abused for some attack?
|
|
for set_id in used_sets {
|
|
if !self.recently_reconstructed.insert(set_id) {
|
|
// or perhaps we should even panic at this point?
|
|
error!("Reconstructed another message containing already used set id!")
|
|
}
|
|
}
|
|
None
|
|
}
|
|
_ => unreachable!(
|
|
"no other error kind should have been returned here! If so, it's a bug!"
|
|
),
|
|
},
|
|
Ok(reconstruction_result) => match reconstruction_result {
|
|
Some((reconstructed_message, used_sets)) => {
|
|
for set_id in used_sets {
|
|
if !self.recently_reconstructed.insert(set_id) {
|
|
// or perhaps we should even panic at this point?
|
|
error!("Reconstructed another message containing already used set id!")
|
|
}
|
|
}
|
|
Some(reconstructed_message)
|
|
}
|
|
None => None,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn process_received_reply(
|
|
&mut self,
|
|
reply_ciphertext: &mut [u8],
|
|
reply_key: SurbEncryptionKey,
|
|
) -> Option<NymMessage> {
|
|
// note: this performs decryption IN PLACE without extra allocation
|
|
self.message_receiver
|
|
.recover_plaintext_from_reply(reply_ciphertext, reply_key);
|
|
let fragment_data = reply_ciphertext;
|
|
|
|
self.recover_from_fragment(fragment_data)
|
|
}
|
|
|
|
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
|
|
let fragment_data = match self.message_receiver.recover_plaintext_from_regular_packet(
|
|
self.local_encryption_keypair.private_key(),
|
|
&mut raw_fragment,
|
|
) {
|
|
Err(err) => {
|
|
warn!("failed to recover fragment data: {err}. The whole underlying message might be corrupted and unrecoverable!");
|
|
return None;
|
|
}
|
|
Ok(frag_data) => frag_data,
|
|
};
|
|
|
|
self.recover_from_fragment(fragment_data)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
// Note: you should NEVER create more than a single instance of this using 'new()'.
|
|
// You should always use .clone() to create additional instances
|
|
struct ReceivedMessagesBuffer {
|
|
inner: Arc<Mutex<ReceivedMessagesBufferInner>>,
|
|
reply_key_storage: SentReplyKeys,
|
|
reply_controller_sender: ReplyControllerSender,
|
|
}
|
|
|
|
impl ReceivedMessagesBuffer {
|
|
fn new(
|
|
local_encryption_keypair: Arc<encryption::KeyPair>,
|
|
reply_key_storage: SentReplyKeys,
|
|
reply_controller_sender: ReplyControllerSender,
|
|
) -> Self {
|
|
ReceivedMessagesBuffer {
|
|
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
|
messages: Vec::new(),
|
|
local_encryption_keypair,
|
|
message_receiver: MessageReceiver::new(),
|
|
message_sender: None,
|
|
recently_reconstructed: HashSet::new(),
|
|
})),
|
|
reply_key_storage,
|
|
reply_controller_sender,
|
|
}
|
|
}
|
|
|
|
async fn disconnect_sender(&mut self) {
|
|
let mut guard = self.inner.lock().await;
|
|
if guard.message_sender.is_none() {
|
|
// in theory we could just ignore it, but that situation should have never happened
|
|
// in the first place, so this way we at least know we have an important bug to fix
|
|
panic!("trying to disconnect non-existent sender!")
|
|
}
|
|
guard.message_sender = None;
|
|
}
|
|
|
|
async fn connect_sender(&mut self, sender: ReconstructedMessagesSender) {
|
|
let mut guard = self.inner.lock().await;
|
|
if guard.message_sender.is_some() {
|
|
// in theory we could just ignore it, but that situation should have never happened
|
|
// in the first place, so this way we at least know we have an important bug to fix
|
|
panic!("trying overwrite an existing sender!")
|
|
}
|
|
|
|
// while we're at it, also empty the buffer if we happened to receive anything while
|
|
// no sender was connected
|
|
let stored_messages = std::mem::take(&mut guard.messages);
|
|
if !stored_messages.is_empty() {
|
|
if let Err(err) = sender.unbounded_send(stored_messages) {
|
|
error!(
|
|
"The sender channel we just received is already invalidated - {:?}",
|
|
err
|
|
);
|
|
// put the values back to the buffer
|
|
// the returned error has two fields: err: SendError and val: T,
|
|
// where val is the value that was failed to get sent;
|
|
// it's returned by the `into_inner` call
|
|
guard.messages = err.into_inner();
|
|
return;
|
|
}
|
|
}
|
|
guard.message_sender = Some(sender);
|
|
}
|
|
|
|
fn handle_reconstructed_plain_messages(
|
|
&mut self,
|
|
msgs: Vec<PlainMessage>,
|
|
) -> Vec<ReconstructedMessage> {
|
|
msgs.into_iter().map(Into::into).collect()
|
|
}
|
|
|
|
fn handle_reconstructed_repliable_messages(
|
|
&mut self,
|
|
msgs: Vec<RepliableMessage>,
|
|
) -> Vec<ReconstructedMessage> {
|
|
let mut reconstructed = Vec::new();
|
|
for msg in msgs {
|
|
let (reply_surbs, from_surb_request) = match msg.content {
|
|
RepliableMessageContent::Data {
|
|
message,
|
|
reply_surbs,
|
|
} => {
|
|
trace!(
|
|
"received message that also contained additional {} reply surbs from {:?}!",
|
|
reply_surbs.len(),
|
|
msg.sender_tag
|
|
);
|
|
|
|
reconstructed.push(ReconstructedMessage::new(message, msg.sender_tag));
|
|
|
|
(reply_surbs, false)
|
|
}
|
|
RepliableMessageContent::AdditionalSurbs { reply_surbs } => {
|
|
trace!(
|
|
"received additional {} reply surbs from {:?}!",
|
|
reply_surbs.len(),
|
|
msg.sender_tag
|
|
);
|
|
(reply_surbs, true)
|
|
}
|
|
RepliableMessageContent::Heartbeat {
|
|
additional_reply_surbs,
|
|
} => {
|
|
error!("received a repliable heartbeat message - we don't know how to handle it yet (and we won't know until future PRs)");
|
|
(additional_reply_surbs, false)
|
|
}
|
|
};
|
|
|
|
self.reply_controller_sender.send_additional_surbs(
|
|
msg.sender_tag,
|
|
reply_surbs,
|
|
from_surb_request,
|
|
)
|
|
}
|
|
reconstructed
|
|
}
|
|
|
|
fn handle_reconstructed_reply_messages(
|
|
&mut self,
|
|
msgs: Vec<ReplyMessage>,
|
|
) -> Vec<ReconstructedMessage> {
|
|
let mut reconstructed = Vec::new();
|
|
for msg in msgs {
|
|
match msg.content {
|
|
ReplyMessageContent::Data { message } => reconstructed.push(message.into()),
|
|
ReplyMessageContent::SurbRequest { recipient, amount } => {
|
|
debug!("received request for {amount} additional reply SURBs from {recipient}");
|
|
self.reply_controller_sender
|
|
.send_additional_surbs_request(*recipient, amount);
|
|
}
|
|
}
|
|
}
|
|
reconstructed
|
|
}
|
|
|
|
async fn handle_reconstructed_messages(&mut self, msgs: Vec<NymMessage>) {
|
|
if msgs.is_empty() {
|
|
return;
|
|
}
|
|
|
|
let mut plain_messages = Vec::new();
|
|
let mut repliable_messages = Vec::new();
|
|
let mut reply_messages = Vec::new();
|
|
|
|
for msg in msgs {
|
|
match msg {
|
|
NymMessage::Plain(plain) => plain_messages.push(plain),
|
|
NymMessage::Repliable(repliable) => repliable_messages.push(repliable),
|
|
NymMessage::Reply(reply) => reply_messages.push(reply),
|
|
}
|
|
}
|
|
|
|
let mut reconstructed_messages = self.handle_reconstructed_plain_messages(plain_messages);
|
|
reconstructed_messages
|
|
.append(&mut self.handle_reconstructed_repliable_messages(repliable_messages));
|
|
reconstructed_messages
|
|
.append(&mut self.handle_reconstructed_reply_messages(reply_messages));
|
|
|
|
let mut inner_guard = self.inner.lock().await;
|
|
debug!(
|
|
"Adding {:?} new messages to the buffer!",
|
|
reconstructed_messages.len()
|
|
);
|
|
|
|
if let Some(sender) = &inner_guard.message_sender {
|
|
trace!("Sending reconstructed messages to announced sender");
|
|
if let Err(err) = sender.unbounded_send(reconstructed_messages) {
|
|
warn!("The reconstructed message receiver went offline without explicit notification (relevant error: - {err})");
|
|
inner_guard.message_sender = None;
|
|
inner_guard.messages.extend(err.into_inner());
|
|
}
|
|
} else {
|
|
trace!("No sender available - buffering reconstructed messages");
|
|
inner_guard.messages.extend(reconstructed_messages)
|
|
}
|
|
}
|
|
|
|
// this function doesn't really belong here...
|
|
fn get_reply_key<'a>(
|
|
&self,
|
|
raw_message: &'a mut [u8],
|
|
) -> Option<(SurbEncryptionKey, &'a mut [u8])> {
|
|
let reply_surb_digest_size = ReplySurbKeyDigestAlgorithm::output_size();
|
|
if raw_message.len() < reply_surb_digest_size {
|
|
return None;
|
|
}
|
|
|
|
let possible_key_digest =
|
|
EncryptionKeyDigest::clone_from_slice(&raw_message[..reply_surb_digest_size]);
|
|
self.reply_key_storage
|
|
.try_pop(possible_key_digest)
|
|
.map(|reply_encryption_key| {
|
|
(
|
|
*reply_encryption_key,
|
|
&mut raw_message[reply_surb_digest_size..],
|
|
)
|
|
})
|
|
}
|
|
|
|
async fn handle_new_received(&mut self, msgs: Vec<Vec<u8>>) {
|
|
trace!(
|
|
"Processing {:?} new message that might get added to the buffer!",
|
|
msgs.len()
|
|
);
|
|
|
|
let mut completed_messages = Vec::new();
|
|
let mut inner_guard = self.inner.lock().await;
|
|
|
|
// first check if this is a reply or a chunked message
|
|
// note: there's a possible information leakage associated with this check https://github.com/nymtech/nym/issues/296
|
|
for mut msg in msgs {
|
|
// check first `HasherOutputSize` bytes if they correspond to known encryption key
|
|
// if yes - this is a reply message
|
|
let completed_message =
|
|
if let Some((reply_key, reply_message)) = self.get_reply_key(&mut msg) {
|
|
inner_guard.process_received_reply(reply_message, reply_key)
|
|
} else {
|
|
inner_guard.process_received_regular_packet(msg)
|
|
};
|
|
|
|
if let Some(completed) = completed_message {
|
|
info!("received {completed}");
|
|
completed_messages.push(completed)
|
|
}
|
|
}
|
|
|
|
drop(inner_guard);
|
|
|
|
if !completed_messages.is_empty() {
|
|
self.handle_reconstructed_messages(completed_messages).await
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum ReceivedBufferMessage {
|
|
// Signals a websocket connection (or a native implementation) was established and we should stop buffering messages,
|
|
// and instead send them directly to the received channel
|
|
ReceiverAnnounce(ReconstructedMessagesSender),
|
|
|
|
// Explicit signal that Receiver connection will no longer accept messages
|
|
ReceiverDisconnect,
|
|
}
|
|
|
|
struct RequestReceiver {
|
|
received_buffer: ReceivedMessagesBuffer,
|
|
query_receiver: ReceivedBufferRequestReceiver,
|
|
}
|
|
|
|
impl RequestReceiver {
|
|
fn new(
|
|
received_buffer: ReceivedMessagesBuffer,
|
|
query_receiver: ReceivedBufferRequestReceiver,
|
|
) -> Self {
|
|
RequestReceiver {
|
|
received_buffer,
|
|
query_receiver,
|
|
}
|
|
}
|
|
|
|
async fn handle_message(&mut self, message: ReceivedBufferMessage) {
|
|
match message {
|
|
ReceivedBufferMessage::ReceiverAnnounce(sender) => {
|
|
self.received_buffer.connect_sender(sender).await;
|
|
}
|
|
ReceivedBufferMessage::ReceiverDisconnect => {
|
|
self.received_buffer.disconnect_sender().await
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
|
|
debug!("Started RequestReceiver with graceful shutdown support");
|
|
while !shutdown.is_shutdown() {
|
|
tokio::select! {
|
|
biased;
|
|
_ = shutdown.recv() => {
|
|
log::trace!("RequestReceiver: Received shutdown");
|
|
}
|
|
request = self.query_receiver.next() => {
|
|
match request {
|
|
Some(message) => self.handle_message(message).await,
|
|
None => {
|
|
log::trace!("RequestReceiver: Stopping since channel closed");
|
|
break;
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
shutdown.recv_timeout().await;
|
|
log::debug!("RequestReceiver: Exiting");
|
|
}
|
|
}
|
|
|
|
struct FragmentedMessageReceiver {
|
|
received_buffer: ReceivedMessagesBuffer,
|
|
mixnet_packet_receiver: MixnetMessageReceiver,
|
|
}
|
|
|
|
impl FragmentedMessageReceiver {
|
|
fn new(
|
|
received_buffer: ReceivedMessagesBuffer,
|
|
mixnet_packet_receiver: MixnetMessageReceiver,
|
|
) -> Self {
|
|
FragmentedMessageReceiver {
|
|
received_buffer,
|
|
mixnet_packet_receiver,
|
|
}
|
|
}
|
|
|
|
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
|
|
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
|
|
while !shutdown.is_shutdown() {
|
|
tokio::select! {
|
|
new_messages = self.mixnet_packet_receiver.next() => match new_messages {
|
|
Some(new_messages) => {
|
|
self.received_buffer.handle_new_received(new_messages).await;
|
|
}
|
|
None => {
|
|
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
|
|
break;
|
|
}
|
|
},
|
|
_ = shutdown.recv() => {
|
|
log::trace!("FragmentedMessageReceiver: Received shutdown");
|
|
}
|
|
}
|
|
}
|
|
shutdown.recv_timeout().await;
|
|
log::debug!("FragmentedMessageReceiver: Exiting");
|
|
}
|
|
}
|
|
|
|
pub(crate) struct ReceivedMessagesBufferController {
|
|
fragmented_message_receiver: FragmentedMessageReceiver,
|
|
request_receiver: RequestReceiver,
|
|
}
|
|
|
|
impl ReceivedMessagesBufferController {
|
|
pub(crate) fn new(
|
|
local_encryption_keypair: Arc<encryption::KeyPair>,
|
|
query_receiver: ReceivedBufferRequestReceiver,
|
|
mixnet_packet_receiver: MixnetMessageReceiver,
|
|
reply_key_storage: SentReplyKeys,
|
|
reply_controller_sender: ReplyControllerSender,
|
|
) -> Self {
|
|
let received_buffer = ReceivedMessagesBuffer::new(
|
|
local_encryption_keypair,
|
|
reply_key_storage,
|
|
reply_controller_sender,
|
|
);
|
|
|
|
ReceivedMessagesBufferController {
|
|
fragmented_message_receiver: FragmentedMessageReceiver::new(
|
|
received_buffer.clone(),
|
|
mixnet_packet_receiver,
|
|
),
|
|
request_receiver: RequestReceiver::new(received_buffer, query_receiver),
|
|
}
|
|
}
|
|
|
|
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
|
|
let mut fragmented_message_receiver = self.fragmented_message_receiver;
|
|
let mut request_receiver = self.request_receiver;
|
|
|
|
let shutdown_handle = shutdown.clone();
|
|
spawn_future(async move {
|
|
fragmented_message_receiver
|
|
.run_with_shutdown(shutdown_handle)
|
|
.await;
|
|
});
|
|
spawn_future(async move {
|
|
request_receiver.run_with_shutdown(shutdown).await;
|
|
});
|
|
}
|
|
}
|