Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cc63bac1c | |||
| 6519bfa533 | |||
| dc9823334a |
Generated
+1467
-96
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -33,7 +33,7 @@ members = [
|
||||
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||
"common/cosmwasm-smart-contracts/coconut-dkg",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
# "common/cosmwasm-smart-contracts/ephemera",
|
||||
"common/cosmwasm-smart-contracts/ephemera",
|
||||
"common/cosmwasm-smart-contracts/group-contract",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||
@@ -104,7 +104,7 @@ members = [
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/internal/ssl-inject",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/sdk-version-bump",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-nr-query",
|
||||
"tools/nymvisor",
|
||||
@@ -158,7 +158,7 @@ log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
parking_lot = "0.12.1"
|
||||
rand = "0.8.5"
|
||||
reqwest = { version = "0.11.22", default_features = false, features = ["rustls-tls"] }
|
||||
reqwest = "0.11.22"
|
||||
schemars = "0.8.1"
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
@@ -168,9 +168,9 @@ time = "0.3.30"
|
||||
thiserror = "1.0.48"
|
||||
tokio = "1.33.0"
|
||||
tokio-util = "0.7.10"
|
||||
tokio-tungstenite = { version = "0.20.1", features = ["rustls"] }
|
||||
tokio-tungstenite = "0.20.1"
|
||||
tracing = "0.1.37"
|
||||
tungstenite = { version = "0.20.1", default-features = false, features = ["rustls"] }
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
ts-rs = "7.0.0"
|
||||
utoipa = "3.5.0"
|
||||
utoipa-swagger-ui = "3.1.5"
|
||||
|
||||
@@ -52,6 +52,7 @@ use nym_topology::provider_trait::TopologyProvider;
|
||||
use nym_topology::HardcodedTopologyProvider;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use std::fmt::Debug;
|
||||
use std::os::fd::RawFd;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
@@ -683,6 +684,7 @@ where
|
||||
packet_stats_reporter.clone(),
|
||||
);
|
||||
|
||||
let gateway_fd = gateway_transceiver.ws_fd();
|
||||
// The 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.
|
||||
@@ -755,6 +757,7 @@ where
|
||||
received_buffer_request_sender,
|
||||
},
|
||||
},
|
||||
gateway_fd,
|
||||
client_state: ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
@@ -770,6 +773,7 @@ pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
pub gateway_fd: Option<RawFd>,
|
||||
|
||||
pub task_handle: TaskHandle,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use nym_gateway_client::GatewayClient;
|
||||
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use std::fmt::Debug;
|
||||
use std::os::fd::RawFd;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -25,6 +26,7 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
|
||||
/// This combines combines the functionalities of being able to send and receive mix packets.
|
||||
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
|
||||
fn gateway_identity(&self) -> identity::PublicKey;
|
||||
fn ws_fd(&self) -> Option<RawFd>;
|
||||
}
|
||||
|
||||
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
|
||||
@@ -66,6 +68,9 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
(**self).gateway_identity()
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
(**self).ws_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -112,6 +117,9 @@ where
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.gateway_client.gateway_identity()
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
self.gateway_client.ws_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -187,6 +195,9 @@ mod nonwasm_sealed {
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.local_identity
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -259,4 +270,7 @@ impl GatewayTransceiver for MockGateway {
|
||||
fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.dummy_identity
|
||||
}
|
||||
fn ws_fd(&self) -> Option<RawFd> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ features = ["net", "sync", "time"]
|
||||
workspace = true
|
||||
# the choice of this particular tls feature was arbitrary;
|
||||
# if you reckon a different one would be more appropriate, feel free to change it
|
||||
# features = ["native-tls"]
|
||||
features = ["native-tls"]
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
|
||||
@@ -26,6 +26,8 @@ use nym_task::TaskClient;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::convert::TryFrom;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tungstenite::protocol::Message;
|
||||
@@ -34,6 +36,7 @@ use tungstenite::protocol::Message;
|
||||
use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
use tokio_tungstenite::MaybeTlsStream;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
@@ -146,6 +149,21 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.gateway_identity
|
||||
}
|
||||
|
||||
pub fn ws_fd(&self) -> Option<RawFd> {
|
||||
match &self.connection {
|
||||
SocketState::Available(conn) => match conn.get_ref() {
|
||||
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
|
||||
MaybeTlsStream::NativeTls(stream) => Some(stream.as_raw_fd()),
|
||||
&_ => None,
|
||||
},
|
||||
SocketState::PartiallyDelegated(conn) => Some(conn.ws_fd()),
|
||||
_ => {
|
||||
log::warn!("No fd yet");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remaining_bandwidth(&self) -> i64 {
|
||||
self.bandwidth_remaining
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use futures::{SinkExt, StreamExt};
|
||||
use log::*;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_task::TaskClient;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
use tungstenite::Message;
|
||||
|
||||
@@ -38,11 +39,15 @@ type WsConn = JSWebsocket;
|
||||
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
|
||||
|
||||
pub(crate) struct PartiallyDelegated {
|
||||
ws_fd: RawFd,
|
||||
sink_half: SplitSink<WsConn, Message>,
|
||||
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
|
||||
}
|
||||
|
||||
impl PartiallyDelegated {
|
||||
pub fn ws_fd(&self) -> RawFd {
|
||||
self.ws_fd
|
||||
}
|
||||
fn recover_received_plaintexts(ws_msgs: Vec<Message>, shared_key: &SharedKeys) -> Vec<Vec<u8>> {
|
||||
let mut plaintexts = Vec::with_capacity(ws_msgs.len());
|
||||
for ws_msg in ws_msgs {
|
||||
@@ -92,6 +97,11 @@ impl PartiallyDelegated {
|
||||
let (notify_sender, notify_receiver) = oneshot::channel();
|
||||
let (stream_sender, stream_receiver) = oneshot::channel();
|
||||
|
||||
let ws_fd = match conn.get_ref() {
|
||||
MaybeTlsStream::Plain(stream) => stream.as_raw_fd(),
|
||||
MaybeTlsStream::NativeTls(stream) => stream.as_raw_fd(),
|
||||
_ => 0.into(),
|
||||
};
|
||||
let (sink, mut stream) = conn.split();
|
||||
|
||||
let mixnet_receiver_future = async move {
|
||||
@@ -141,6 +151,7 @@ impl PartiallyDelegated {
|
||||
tokio::spawn(mixnet_receiver_future);
|
||||
|
||||
PartiallyDelegated {
|
||||
ws_fd,
|
||||
sink_half: sink,
|
||||
delegated_stream: (stream_receiver, notify_sender),
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ log = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tokio = { workspace = true, features = ["sync", "time"] }
|
||||
futures = { workspace = true }
|
||||
openssl = { version = "^0.10.55", features = ["vendored"], optional = true }
|
||||
|
||||
nym-coconut-interface = { path = "../../coconut-interface" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
@@ -89,7 +90,7 @@ required-features = ["http-client"]
|
||||
|
||||
[features]
|
||||
default = ["http-client"]
|
||||
http-client = ["cosmrs/rpc"]
|
||||
http-client = ["cosmrs/rpc", "openssl"]
|
||||
generate-ts = []
|
||||
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ tokio = { version = "1.24.1", features = [
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
url = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
fastbloom-rs = { git = "https://github.com/simonwicky/fastbloom"}
|
||||
|
||||
## tracing
|
||||
tracing = { version = "0.1.37", optional = true }
|
||||
|
||||
@@ -28,7 +28,4 @@ pub enum MixProcessingError {
|
||||
|
||||
#[error("failed to process received outfox packet: {0}")]
|
||||
OutfoxProcessingError(#[from] OutfoxError),
|
||||
|
||||
#[error("this packet was already processed, it's a replay")]
|
||||
ReplayedPacketDetected,
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@
|
||||
|
||||
pub mod error;
|
||||
pub mod processor;
|
||||
pub mod replay_detection;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::measure;
|
||||
use crate::packet_processor::error::MixProcessingError;
|
||||
use crate::packet_processor::replay_detection::ReplayDetector;
|
||||
use log::*;
|
||||
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
|
||||
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
|
||||
@@ -41,9 +40,6 @@ pub enum MixProcessingResult {
|
||||
pub struct SphinxPacketProcessor {
|
||||
/// Private sphinx key of this node required to unwrap received sphinx packet.
|
||||
sphinx_key: Arc<PrivateKey>,
|
||||
|
||||
/// Detector of replay attack
|
||||
replay_detector: ReplayDetector,
|
||||
}
|
||||
|
||||
impl SphinxPacketProcessor {
|
||||
@@ -51,7 +47,6 @@ impl SphinxPacketProcessor {
|
||||
pub fn new(sphinx_key: PrivateKey) -> Self {
|
||||
SphinxPacketProcessor {
|
||||
sphinx_key: Arc::new(sphinx_key),
|
||||
replay_detector: ReplayDetector::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +184,7 @@ impl SphinxPacketProcessor {
|
||||
match packet {
|
||||
NymProcessedPacket::Sphinx(packet) => {
|
||||
match packet {
|
||||
ProcessedPacket::ForwardHop(packet, address, delay, _) => self
|
||||
ProcessedPacket::ForwardHop(packet, address, delay) => self
|
||||
.process_forward_hop(
|
||||
NymPacket::Sphinx(*packet),
|
||||
address,
|
||||
@@ -198,13 +193,12 @@ impl SphinxPacketProcessor {
|
||||
),
|
||||
// right now there's no use for the surb_id included in the header - probably it should get removed from the
|
||||
// sphinx all together?
|
||||
ProcessedPacket::FinalHop(destination, _, payload, _) => self
|
||||
.process_final_hop(
|
||||
destination,
|
||||
payload.recover_plaintext()?,
|
||||
packet_size,
|
||||
packet_type,
|
||||
),
|
||||
ProcessedPacket::FinalHop(destination, _, payload) => self.process_final_hop(
|
||||
destination,
|
||||
payload.recover_plaintext()?,
|
||||
packet_size,
|
||||
packet_type,
|
||||
),
|
||||
}
|
||||
}
|
||||
NymProcessedPacket::Outfox(packet) => {
|
||||
@@ -245,10 +239,6 @@ impl SphinxPacketProcessor {
|
||||
// unwrap the sphinx packet and if possible and appropriate, cache keys
|
||||
let processed_packet = self.perform_initial_unwrapping(received)?;
|
||||
|
||||
//check for replay attack
|
||||
self.replay_detector
|
||||
.handle_replay_tag(&processed_packet.replay_tag())?;
|
||||
|
||||
// for forward packets, extract next hop and set delay (but do NOT delay here)
|
||||
// for final packets, extract SURBAck
|
||||
let final_processing_result =
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::packet_processor::error::MixProcessingError;
|
||||
use fastbloom_rs::{BloomFilter, FilterBuilder, Membership};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
const BLOOM_FILTER_SIZE: u64 = 10_000_000;
|
||||
const FP_RATE: f64 = 1e-4;
|
||||
|
||||
//alias for convenience
|
||||
type ReplayTag = [u8];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ReplayDetector(Arc<Mutex<ReplayDetectorInner>>);
|
||||
|
||||
impl ReplayDetector {
|
||||
pub fn new() -> Self {
|
||||
ReplayDetector(Arc::new(Mutex::new(ReplayDetectorInner::new())))
|
||||
}
|
||||
|
||||
//check if secret has been seen already
|
||||
//if no, return Ok
|
||||
//if yes, add the secret to the list, then return an error
|
||||
pub fn handle_replay_tag(&self, replay_tag: &ReplayTag) -> Result<(), MixProcessingError> {
|
||||
match self.0.lock() {
|
||||
Ok(mut inner) => {
|
||||
if !inner.lookup_then_insert(replay_tag) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixProcessingError::ReplayedPacketDetected)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("Failed to handle replay_tag : {err}");
|
||||
Ok(()) //what is the sensible thing to do, if the lock is poisoned? Reset the filter ?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReplayDetector {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReplayDetectorInner {
|
||||
filter: BloomFilter,
|
||||
}
|
||||
|
||||
impl ReplayDetectorInner {
|
||||
pub fn new() -> Self {
|
||||
ReplayDetectorInner {
|
||||
filter: FilterBuilder::new(BLOOM_FILTER_SIZE, FP_RATE).build_bloom_filter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_then_insert(&mut self, replay_tag: &ReplayTag) -> bool {
|
||||
self.filter.contains_then_add(replay_tag)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod replay_detector_test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn handle_replay_tag_correctly_detects_replay() {
|
||||
let replay_detector = ReplayDetector::new();
|
||||
let replay_tag = b"Hello World!";
|
||||
assert!(replay_detector.handle_replay_tag(replay_tag).is_ok()); //first insert is fine
|
||||
assert!(replay_detector.handle_replay_tag(replay_tag).is_err()); //second is not
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,9 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
#sphinx-packet = { version = "0.1.0", optional = true }
|
||||
sphinx-packet = { version = "0.1.0", optional = true }
|
||||
nym-outfox = { path = "../../../nym-outfox", optional = true }
|
||||
thiserror = { workspace = true }
|
||||
sphinx-packet = { git = "https://github.com/nymtech/sphinx.git", branch = "simon/replay_tag", optional = true}
|
||||
|
||||
[features]
|
||||
default = ["sphinx"]
|
||||
|
||||
@@ -9,7 +9,6 @@ pub use nym_outfox::{
|
||||
// re-exporting types and constants available in sphinx
|
||||
#[cfg(feature = "outfox")]
|
||||
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
|
||||
use sphinx_packet::header::keys::ReplayTag;
|
||||
#[cfg(feature = "sphinx")]
|
||||
pub use sphinx_packet::{
|
||||
constants::{
|
||||
@@ -58,15 +57,6 @@ pub enum NymProcessedPacket {
|
||||
Outfox(OutfoxProcessedPacket),
|
||||
}
|
||||
|
||||
impl NymProcessedPacket {
|
||||
pub fn replay_tag(&self) -> ReplayTag {
|
||||
match self {
|
||||
NymProcessedPacket::Sphinx(sphinx) => sphinx.replay_tag(),
|
||||
NymProcessedPacket::Outfox(_) => todo!(), //SW temporary while I add a replay tag to outfox
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NymPacket {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[allow(unreachable_patterns)]
|
||||
|
||||
@@ -217,7 +217,6 @@ deny = [
|
||||
# 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 = [] },
|
||||
{ name = "openssl"},
|
||||
]
|
||||
|
||||
# List of features to allow/deny
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ nym-config = { path = "../common/config" }
|
||||
nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
|
||||
pretty_env_logger = "0.4"
|
||||
refinery = { version = "0.8.7", features = ["rusqlite"], optional = true }
|
||||
reqwest = { version = "0.11.22", default_features = false, features = ["rustls-tls", "json"] }
|
||||
reqwest = { version = "0.11.22", features = ["json"] }
|
||||
# Rocksdb kills compilation times and we're not currently using it. The reason
|
||||
# we comment it out is that rust-analyzer runs with --all-features
|
||||
#rocksdb = { version = "0.21.0", optional = true }
|
||||
|
||||
+9
-9
@@ -66,20 +66,21 @@ schemars = { workspace = true, features = ["preserve_order"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
## ephemera-specific
|
||||
#actix-web = "4"
|
||||
#array-bytes = "6.0.0"
|
||||
#chrono = { version = "0.4.24", default-features = false, features = ["clock"] }
|
||||
#futures-util = "0.3.25"
|
||||
#serde_derive = "1.0.149"
|
||||
#uuid = { version = "1.3.0", features = ["serde", "v4"] }
|
||||
actix-web = "4"
|
||||
array-bytes = "6.0.0"
|
||||
chrono = { version = "0.4.24", default-features = false, features = ["clock"] }
|
||||
futures-util = "0.3.25"
|
||||
serde_derive = "1.0.149"
|
||||
tempfile = "3.3.0"
|
||||
uuid = { version = "1.3.0", features = ["serde", "v4"] }
|
||||
|
||||
## internal
|
||||
#ephemera = { path = "../ephemera" }
|
||||
ephemera = { path = "../ephemera" }
|
||||
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
|
||||
nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-coconut-interface = { path = "../common/coconut-interface" }
|
||||
#nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
|
||||
nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
|
||||
nym-config = { path = "../common/config" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
nym-credential-storage = { path = "../common/credential-storage" }
|
||||
@@ -122,7 +123,6 @@ sqlx = { workspace = true, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3.0"
|
||||
cw3 = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
rand_chacha = "0.3"
|
||||
|
||||
@@ -5,7 +5,7 @@ use super::serde_helpers::generated_dealings;
|
||||
use crate::coconut::dkg::state::DkgParticipant;
|
||||
use nym_coconut_dkg_common::types::DealingIndex;
|
||||
use nym_dkg::{Dealing, NodeIndex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
|
||||
@@ -5,7 +5,7 @@ use super::serde_helpers::recovered_keys;
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
|
||||
use nym_dkg::{G2Projective, NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
type ProposalId = u64;
|
||||
|
||||
@@ -7,7 +7,7 @@ use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
|
||||
use nym_dkg::bte::PublicKeyWithProof;
|
||||
use nym_dkg::{bte, NodeIndex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
// while those files are completely unused now, I'm going to leave them here for future reference once we decide to revive the project to have a starting point
|
||||
// but whoever picks it up: you are forbidden from trying to use actix
|
||||
|
||||
extern crate core;
|
||||
|
||||
use clap::Parser;
|
||||
use ephemera::cli::init::Cmd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) mod application;
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::epoch_operations::RewardedSetUpdater;
|
||||
use cosmwasm_std::{Decimal, Fraction};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::{ExecuteMsg, Interval, MixId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub(crate) struct MixnodeWithPerformance {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// 3. Eventually this whole procedure is going to get expanded to allow for distribution of rewarded set generation
|
||||
// and hence this might be a good place for it.
|
||||
|
||||
use crate::ephemera::reward::{EpochOperations, RewardManager};
|
||||
use crate::node_status_api::ONE_DAY;
|
||||
use crate::nym_contract_cache::cache::NymContractCache;
|
||||
use crate::support::nyxd::Client;
|
||||
@@ -32,6 +33,7 @@ mod rewarding;
|
||||
mod transition_beginning;
|
||||
|
||||
pub struct RewardedSetUpdater {
|
||||
ephemera_reward_manager: Option<RewardManager>,
|
||||
nyxd_client: Client,
|
||||
nym_contract_cache: NymContractCache,
|
||||
storage: NymApiStorage,
|
||||
@@ -45,11 +47,13 @@ impl RewardedSetUpdater {
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
ephemera_reward_manager: Option<RewardManager>,
|
||||
nyxd_client: Client,
|
||||
nym_contract_cache: NymContractCache,
|
||||
storage: NymApiStorage,
|
||||
) -> Self {
|
||||
RewardedSetUpdater {
|
||||
ephemera_reward_manager,
|
||||
nyxd_client,
|
||||
nym_contract_cache,
|
||||
storage,
|
||||
@@ -88,6 +92,11 @@ impl RewardedSetUpdater {
|
||||
/// 8. the whole process repeats once the new epoch finishes
|
||||
async fn perform_epoch_operations(&mut self, interval: Interval) -> Result<(), RewardingError> {
|
||||
let mut rewards = self.nodes_to_reward(interval).await;
|
||||
if let Some(ephemera_reward_manager) = self.ephemera_reward_manager.as_mut() {
|
||||
rewards = ephemera_reward_manager
|
||||
.perform_epoch_operations(rewards)
|
||||
.await?;
|
||||
}
|
||||
rewards.sort_by_key(|a| a.mix_id);
|
||||
|
||||
log::info!("The current epoch has finished.");
|
||||
@@ -265,12 +274,14 @@ impl RewardedSetUpdater {
|
||||
}
|
||||
|
||||
pub(crate) fn start(
|
||||
ephemera_reward_manager: Option<RewardManager>,
|
||||
nyxd_client: Client,
|
||||
nym_contract_cache: &NymContractCache,
|
||||
storage: &NymApiStorage,
|
||||
shutdown: &TaskManager,
|
||||
) {
|
||||
let mut rewarded_set_updater = RewardedSetUpdater::new(
|
||||
ephemera_reward_manager,
|
||||
nyxd_client,
|
||||
nym_contract_cache.to_owned(),
|
||||
storage.to_owned(),
|
||||
|
||||
+36
-1
@@ -16,6 +16,7 @@ use crate::support::cli;
|
||||
use crate::support::config::Config;
|
||||
use crate::support::storage;
|
||||
use crate::support::storage::NymApiStorage;
|
||||
use ::ephemera::configuration::Configuration as EphemeraConfiguration;
|
||||
use ::nym_config::defaults::setup_env;
|
||||
use circulating_supply_api::cache::CirculatingSupplyCache;
|
||||
use clap::Parser;
|
||||
@@ -31,6 +32,7 @@ use support::{http, nyxd};
|
||||
|
||||
mod circulating_supply_api;
|
||||
mod coconut;
|
||||
mod ephemera;
|
||||
mod epoch_operations;
|
||||
pub(crate) mod network;
|
||||
mod network_monitor;
|
||||
@@ -161,6 +163,33 @@ async fn start_nym_api_tasks(config: Config) -> anyhow::Result<ShutdownHandles>
|
||||
// and then only start the uptime updater (and the monitor itself, duh)
|
||||
// if the monitoring if it's enabled
|
||||
if config.network_monitor.enabled {
|
||||
let ephemera_config =
|
||||
match EphemeraConfiguration::try_load(config.get_ephemera_config_path()) {
|
||||
Ok(c) => c,
|
||||
Err(_) => {
|
||||
config
|
||||
.get_ephemera_args()
|
||||
.cmd
|
||||
.clone()
|
||||
.execute(Some(&config.get_id()));
|
||||
EphemeraConfiguration::try_load(config.get_ephemera_config_path())
|
||||
.expect("Config file should be created now")
|
||||
}
|
||||
};
|
||||
let ephemera_reward_manager = if config.ephemera.enabled {
|
||||
Some(
|
||||
ephemera::application::NymApi::run(
|
||||
config.get_ephemera_args().clone(),
|
||||
ephemera_config,
|
||||
nyxd_client.clone(),
|
||||
&shutdown,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// if network monitor is enabled, the storage MUST BE available
|
||||
let storage = maybe_storage.unwrap();
|
||||
|
||||
@@ -178,7 +207,13 @@ async fn start_nym_api_tasks(config: Config) -> anyhow::Result<ShutdownHandles>
|
||||
// start 'rewarding' if its enabled
|
||||
if config.rewarding.enabled {
|
||||
epoch_operations::ensure_rewarding_permission(&nyxd_client).await?;
|
||||
RewardedSetUpdater::start(nyxd_client, nym_contract_cache_state, storage, &shutdown);
|
||||
RewardedSetUpdater::start(
|
||||
ephemera_reward_manager,
|
||||
nyxd_client,
|
||||
nym_contract_cache_state,
|
||||
storage,
|
||||
&shutdown,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::support::caching::cache::{SharedCache, UninitialisedCache};
|
||||
use crate::support::caching::refresher::{CacheItemProvider, CacheRefresher};
|
||||
use crate::support::config;
|
||||
use crate::support::config::DEFAULT_NODE_DESCRIBE_BATCH_SIZE;
|
||||
use futures::{stream, StreamExt};
|
||||
use futures_util::{stream, StreamExt};
|
||||
use nym_api_requests::models::{
|
||||
IpPacketRouterDetails, NetworkRequesterDetails, NymNodeDescription,
|
||||
};
|
||||
|
||||
@@ -106,6 +106,8 @@ pub struct Config {
|
||||
pub rewarding: Rewarding,
|
||||
|
||||
pub coconut_signer: CoconutSigner,
|
||||
|
||||
pub ephemera: Ephemera,
|
||||
}
|
||||
|
||||
impl NymConfigTemplate for Config {
|
||||
@@ -125,6 +127,7 @@ impl Config {
|
||||
circulating_supply_cacher: Default::default(),
|
||||
rewarding: Default::default(),
|
||||
coconut_signer: CoconutSigner::new_default(id.as_ref()),
|
||||
ephemera: Ephemera::new_default(id.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +142,10 @@ impl Config {
|
||||
bail!("can't enable coconut signer without providing a mnemonic")
|
||||
}
|
||||
|
||||
if !can_sign && self.ephemera.enabled {
|
||||
bail!("can't enable ephemera without providing a mnemonic")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -216,6 +223,10 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> String {
|
||||
self.base.id.clone()
|
||||
}
|
||||
|
||||
pub fn get_nyxd_url(&self) -> Url {
|
||||
self.base.local_validator.clone()
|
||||
}
|
||||
@@ -223,6 +234,14 @@ impl Config {
|
||||
pub fn get_mnemonic(&self) -> Option<&bip39::Mnemonic> {
|
||||
self.base.mnemonic.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_ephemera_args(&self) -> &crate::ephemera::Args {
|
||||
&self.ephemera.args
|
||||
}
|
||||
|
||||
pub fn get_ephemera_config_path(&self) -> PathBuf {
|
||||
self.ephemera.args.ephemera_config.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// we only really care about the mnemonic being zeroized
|
||||
@@ -539,3 +558,25 @@ impl Default for CoconutSignerDebug {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Ephemera {
|
||||
pub enabled: bool,
|
||||
args: crate::ephemera::Args,
|
||||
}
|
||||
|
||||
impl Ephemera {
|
||||
fn new_default(id: &str) -> Self {
|
||||
Ephemera {
|
||||
enabled: false,
|
||||
args: crate::ephemera::Args {
|
||||
ephemera_config: ephemera::configuration::Configuration::ephemera_config_file_home(
|
||||
Some(id),
|
||||
)
|
||||
.unwrap(),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,4 +127,12 @@ decryption_key_path = '{{ coconut_signer.storage_paths.decryption_key_path }}'
|
||||
# Path to the dkg dealer public key with proof
|
||||
public_key_with_proof_path = '{{ coconut_signer.storage_paths.public_key_with_proof_path }}'
|
||||
|
||||
[ephemera]
|
||||
|
||||
enabled = {{ ephemera.enabled }}
|
||||
|
||||
[ephemera.args]
|
||||
|
||||
ephemera_config = '{{ ephemera.args.ephemera_config }}'
|
||||
|
||||
"#;
|
||||
|
||||
@@ -23,7 +23,8 @@ use nym_coconut_dkg_common::{
|
||||
verification_key::{ContractVKShare, VerificationKeyShare},
|
||||
};
|
||||
use nym_config::defaults::{ChainDetails, NymNetworkDetails};
|
||||
|
||||
use nym_ephemera_common::msg::QueryMsg as EphemeraQueryMsg;
|
||||
use nym_ephemera_common::types::JsonPeerInfo;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::reward_params::RewardingParams;
|
||||
@@ -37,10 +38,11 @@ use nym_validator_client::nyxd::contract_traits::{NameServiceQueryClient, PagedD
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::nyxd::{
|
||||
contract_traits::{
|
||||
CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, GroupQueryClient,
|
||||
MixnetQueryClient, MixnetSigningClient, MultisigQueryClient, MultisigSigningClient,
|
||||
NymContractsProvider, PagedMixnetQueryClient, PagedMultisigQueryClient,
|
||||
PagedVestingQueryClient, SpDirectoryQueryClient,
|
||||
CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, EphemeraQueryClient,
|
||||
EphemeraSigningClient, GroupQueryClient, MixnetQueryClient, MixnetSigningClient,
|
||||
MultisigQueryClient, MultisigSigningClient, NymContractsProvider, PagedEphemeraQueryClient,
|
||||
PagedMixnetQueryClient, PagedMultisigQueryClient, PagedVestingQueryClient,
|
||||
SpDirectoryQueryClient,
|
||||
},
|
||||
cosmwasm_client::types::ExecuteResult,
|
||||
CosmWasmClient, Fee,
|
||||
@@ -567,6 +569,23 @@ impl crate::coconut::client::Client for Client {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ephemera::client::Client for Client {
|
||||
async fn get_ephemera_peers(&self) -> crate::ephemera::error::Result<Vec<JsonPeerInfo>> {
|
||||
Ok(nyxd_query!(self, get_all_ephemera_peers().await?))
|
||||
}
|
||||
|
||||
async fn register_ephemera_peer(
|
||||
&self,
|
||||
peer_info: JsonPeerInfo,
|
||||
) -> crate::ephemera::error::Result<ExecuteResult> {
|
||||
Ok(nyxd_signing!(
|
||||
self,
|
||||
register_as_peer(peer_info, None).await?
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DkgQueryClient for Client {
|
||||
async fn query_dkg_contract<T>(&self, query: DkgQueryMsg) -> std::result::Result<T, NyxdError>
|
||||
@@ -577,6 +596,19 @@ impl DkgQueryClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EphemeraQueryClient for Client {
|
||||
async fn query_ephemera_contract<T>(
|
||||
&self,
|
||||
query: EphemeraQueryMsg,
|
||||
) -> std::result::Result<T, NyxdError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
{
|
||||
nyxd_query!(self, query_ephemera_contract(query).await)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SpDirectoryQueryClient for Client {
|
||||
async fn query_service_provider_contract<T>(
|
||||
|
||||
+6
-1
@@ -37,7 +37,12 @@ mime = "0.3.17"
|
||||
hyper = { workspace = true }
|
||||
tower = { version = "0.4.13" }
|
||||
tower-http = { version = "0.4.4", features = ["fs"] }
|
||||
utoipa = { workspace = true, features = ["axum_extras"] }
|
||||
|
||||
# `actix_extras`? what the hell?
|
||||
# hear me out first!
|
||||
# we can't use `axum_extras` because of freaking ephemera that depends on `actix_extras`.
|
||||
# however, it seems that pulling in `actix_extras` pulls in just enough shared features to improve `IntoParams` for our Query attributes
|
||||
utoipa = { workspace = true, features = ["actix_extras"] } # can't use `"axum_extras"` feature because ephemera uses `"actix_extras"` -.-'
|
||||
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
|
||||
|
||||
# if we ever wanted redoc/rapidoc bridges:
|
||||
|
||||
@@ -18,8 +18,7 @@ curve25519-dalek = "3.2"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
getrandom = { workspace = true, features = ["js"] }
|
||||
thiserror = { workspace = true }
|
||||
#sphinx-packet = "0.1.0"
|
||||
sphinx-packet = { git = "https://github.com/nymtech/sphinx.git", branch = "simon/replay_tag"}
|
||||
sphinx-packet = "0.1.0"
|
||||
rand = "0.7.3"
|
||||
log = "0.4"
|
||||
|
||||
|
||||
Generated
-8
@@ -750,12 +750,6 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
|
||||
|
||||
[[package]]
|
||||
name = "const-str"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
@@ -3115,7 +3109,6 @@ dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"const-str",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"schemars",
|
||||
@@ -3324,7 +3317,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"dotenvy",
|
||||
"hex-literal",
|
||||
"log",
|
||||
"once_cell",
|
||||
"schemars",
|
||||
"serde",
|
||||
|
||||
@@ -23,6 +23,7 @@ nym-config-common = { path = "../../../common/config", package = "nym-config" }
|
||||
nym-credential-storage = { path = "../../../common/credential-storage" }
|
||||
nym-crypto = { path = "../../../common/crypto" }
|
||||
nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = false }
|
||||
openssl = { version = "^0.10.55", features = ["vendored"] }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "time"] }
|
||||
log = "0.4.17"
|
||||
|
||||
@@ -714,6 +714,7 @@ where
|
||||
client_state,
|
||||
reconstructed_receiver,
|
||||
started_client.task_handle,
|
||||
started_client.gateway_fd,
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use nym_task::{
|
||||
TaskHandle,
|
||||
};
|
||||
use nym_topology::NymTopology;
|
||||
use std::os::fd::RawFd;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -44,6 +45,8 @@ pub struct MixnetClient {
|
||||
pub(crate) task_handle: TaskHandle,
|
||||
pub(crate) packet_type: Option<PacketType>,
|
||||
|
||||
pub(crate) gateway_fd: Option<RawFd>,
|
||||
|
||||
// internal state used for the `Stream` implementation
|
||||
_buffered: Vec<ReconstructedMessage>,
|
||||
}
|
||||
@@ -56,6 +59,7 @@ impl MixnetClient {
|
||||
client_state: ClientState,
|
||||
reconstructed_receiver: ReconstructedMessagesReceiver,
|
||||
task_handle: TaskHandle,
|
||||
gateway_fd: Option<RawFd>,
|
||||
packet_type: Option<PacketType>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -65,6 +69,7 @@ impl MixnetClient {
|
||||
client_state,
|
||||
reconstructed_receiver,
|
||||
task_handle,
|
||||
gateway_fd,
|
||||
packet_type,
|
||||
_buffered: Vec::new(),
|
||||
}
|
||||
@@ -97,6 +102,10 @@ impl MixnetClient {
|
||||
&self.nym_address
|
||||
}
|
||||
|
||||
pub fn gateway_fd(&self) -> Option<RawFd> {
|
||||
self.gateway_fd
|
||||
}
|
||||
|
||||
/// Get a shallow clone of [`MixnetClientSender`]. Useful if you want split the send and
|
||||
/// receive logic in different locations.
|
||||
pub fn split_sender(&self) -> MixnetClientSender {
|
||||
|
||||
Reference in New Issue
Block a user