From 4e0e081b3e9ff002e17f05c8dd006ecaf71a0020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 11 Oct 2021 09:17:58 +0100 Subject: [PATCH] Feature/coconut feature (#805) * 'Coconut' feature in gateway * Enabled coconut feature in gateway-requests * Native client coconut feature * Ibid for socks5 client * Ibid for wasm client * Coconut feature flag for validator-api * Added coconut feature flag to our CI * build.yml typo * Continue on windows errors * Missing quote * Another typo in build.yml * Reclaiming disk space when building for windows on CI --- .github/workflows/build.yml | 104 ++++++++++++++++-- .github/workflows/wasm_client_build.yml | 5 + clients/native/Cargo.toml | 7 +- clients/native/src/client/mod.rs | 20 ++-- clients/native/src/commands/init.rs | 31 +----- clients/socks5/Cargo.toml | 7 +- clients/socks5/src/client/mod.rs | 20 ++-- clients/socks5/src/commands/init.rs | 31 +----- clients/webassembly/Cargo.toml | 5 +- clients/webassembly/src/client/mod.rs | 33 ++++-- common/client-libs/gateway-client/Cargo.toml | 5 +- .../client-libs/gateway-client/src/client.rs | 85 ++++++++------ gateway/Cargo.toml | 7 +- gateway/gateway-requests/Cargo.toml | 7 +- gateway/gateway-requests/src/types.rs | 15 ++- gateway/src/node/client_handling/mod.rs | 4 +- .../connection_handler/authenticated.rs | 48 +++++--- .../websocket/connection_handler/fresh.rs | 18 ++- .../client_handling/websocket/listener.rs | 12 +- gateway/src/node/mod.rs | 15 ++- gateway/src/node/storage/bandwidth.rs | 4 + gateway/src/node/storage/mod.rs | 4 + validator-api/Cargo.toml | 6 +- validator-api/src/config/mod.rs | 21 ++-- validator-api/src/main.rs | 48 +++++--- validator-api/src/network_monitor/mod.rs | 18 ++- .../src/network_monitor/monitor/sender.rs | 22 +++- 27 files changed, 397 insertions(+), 205 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0e0c81403e..861bc63e35 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' }} + continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.os == 'windows-latest' }} strategy: matrix: rust: [stable, beta, nightly] @@ -16,33 +16,121 @@ jobs: run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools if: matrix.os == 'ubuntu-latest' - - uses: actions/checkout@v2 + - name: Check out repository code + uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install rust toolchain + uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true components: rustfmt, clippy - - uses: actions-rs/cargo@v1 + - name: Build all binaries + uses: actions-rs/cargo@v1 with: command: build args: --all - - - uses: actions-rs/cargo@v1 + - name: Run all tests + uses: actions-rs/cargo@v1 with: command: test args: --all - - uses: actions-rs/cargo@v1 + - name: Check formatting + uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - - uses: actions-rs/cargo@v1 + - name: Run clippy + uses: actions-rs/cargo@v1 if: ${{ matrix.rust != 'nightly' }} with: command: clippy args: -- -D warnings + +# COCONUT stuff + - name: Reclaim some disk space (because Windows is being annoying) + uses: actions-rs/cargo@v1 + if: ${{ matrix.os == 'windows-latest' }} + with: + command: clean + + # BUILD + - name: Build gateway with coconut feature + uses: actions-rs/cargo@v1 + with: + command: build + args: --bin nym-gateway --features=coconut + + - name: Build native client with coconut feature + uses: actions-rs/cargo@v1 + with: + command: build + args: --bin nym-client --features=coconut + + - name: Build socks5 client with coconut feature + uses: actions-rs/cargo@v1 + with: + command: build + args: --bin nym-socks5-client --features=coconut + + - name: Build validator-api with coconut feature + uses: actions-rs/cargo@v1 + with: + command: build + args: --bin nym-validator-api --features=coconut + +# TEST + - name: Test gateway with coconut feature + uses: actions-rs/cargo@v1 + with: + command: test + args: --bin nym-gateway --features=coconut + + - name: Test native client with coconut feature + uses: actions-rs/cargo@v1 + with: + command: test + args: --bin nym-client --features=coconut + + - name: Test socks5 client with coconut feature + uses: actions-rs/cargo@v1 + with: + command: test + args: --bin nym-socks5-client --features=coconut + + - name: Test validator-api with coconut feature + uses: actions-rs/cargo@v1 + with: + command: test + args: --bin nym-validator-api --features=coconut + +# CLIPPY + + - name: Run clippy on gateway with coconut feature + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --bin nym-gateway --features=coconut -- -D warnings + + - name: Run clippy on native client with coconut feature + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --bin nym-client --features=coconut -- -D warnings + + - name: Run clippy on socks5 client with coconut feature + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --bin nym-socks5-client --features=coconut -- -D warnings + + - name: Run clippy on validator-api with coconut feature + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --bin nym-validator-api --features=coconut -- -D warnings \ No newline at end of file diff --git a/.github/workflows/wasm_client_build.yml b/.github/workflows/wasm_client_build.yml index a57b76f306..e8f5d8bd30 100644 --- a/.github/workflows/wasm_client_build.yml +++ b/.github/workflows/wasm_client_build.yml @@ -21,6 +21,11 @@ jobs: command: build args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown + - uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut + # for some reason this does not seem to work correctly, leave it for later, building is good enough for now # - uses: actions-rs/cargo@v1 # with: diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 1ebea3c229..3691af376a 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -31,8 +31,8 @@ tokio-tungstenite = "0.14" # websocket ## internal client-core = { path = "../client-core" } -coconut-interface = { path = "../../common/coconut-interface" } -credentials = { path = "../../common/credentials" } +coconut-interface = { path = "../../common/coconut-interface", optional = true } +credentials = { path = "../../common/credentials", optional = true } config = { path = "../../common/config" } crypto = { path = "../../common/crypto" } gateway-client = { path = "../../common/client-libs/gateway-client" } @@ -44,5 +44,8 @@ websocket-requests = { path = "websocket-requests" } validator-client = { path = "../../common/client-libs/validator-client" } version-checker = { path = "../../common/version-checker" } +[features] +coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"] + [dev-dependencies] serde_json = "1.0" # for the "textsend" example diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index a4e57edb65..0cdde68aa3 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -22,9 +22,6 @@ use client_core::client::topology_control::{ TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, }; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; -use coconut_interface::Credential; -use credentials::bandwidth::prepare_for_spending; -use credentials::obtain_aggregate_verification_key; use crypto::asymmetric::identity; use futures::channel::mpsc; use gateway_client::{ @@ -38,6 +35,11 @@ use nymsphinx::anonymous_replies::ReplySurb; use nymsphinx::receiver::ReconstructedMessage; use tokio::runtime::Runtime; +#[cfg(feature = "coconut")] +use coconut_interface::Credential; +#[cfg(feature = "coconut")] +use credentials::{bandwidth::prepare_for_spending, obtain_aggregate_verification_key}; + pub(crate) mod config; pub struct NymClient { @@ -166,7 +168,8 @@ impl NymClient { .start(self.runtime.handle()) } - async fn prepare_credential(&self) -> Credential { + #[cfg(feature = "coconut")] + async fn prepare_coconut_credential(&self) -> Credential { let verification_key = obtain_aggregate_verification_key( &self.config.get_base().get_validator_api_endpoints(), ) @@ -208,7 +211,8 @@ impl NymClient { .expect("provided gateway id is invalid!"); self.runtime.block_on(async { - let coconut_credential = self.prepare_credential().await; + #[cfg(feature = "coconut")] + let coconut_credential = self.prepare_coconut_credential().await; let mut gateway_client = GatewayClient::new( gateway_address, @@ -218,11 +222,13 @@ impl NymClient { mixnet_message_sender, ack_sender, self.config.get_base().get_gateway_response_timeout(), - coconut_credential, ); gateway_client - .authenticate_and_start() + .authenticate_and_start( + #[cfg(feature = "coconut")] + Some(coconut_credential), + ) .await .expect("could not authenticate and start up the gateway connection"); diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index cb37d96b27..1994fe216e 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -6,10 +6,7 @@ use crate::commands::override_config; use clap::{App, Arg, ArgMatches}; use client_core::client::key_manager::KeyManager; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; -use coconut_interface::Credential; use config::NymConfig; -use credentials::bandwidth::prepare_for_spending; -use credentials::obtain_aggregate_verification_key; use crypto::asymmetric::{encryption, identity}; use gateway_client::GatewayClient; use gateway_requests::registration::handshake::SharedKeys; @@ -60,34 +57,15 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) } -// this behaviour should definitely be changed, we shouldn't -// need to get bandwidth credential for registration -async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential { - let verification_key = obtain_aggregate_verification_key(validators) - .await - .expect("could not obtain aggregate verification key of validators"); - - let bandwidth_credential = credentials::bandwidth::obtain_signature(raw_identity, validators) - .await - .expect("could not obtain bandwidth credential"); - - prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key) - .expect("could not prepare out bandwidth credential for spending") -} - async fn register_with_gateway( gateway: &gateway::Node, our_identity: Arc, - validator_urls: Vec, ) -> Arc { let timeout = Duration::from_millis(1500); - let coconut_credential = - prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await; let mut gateway_client = GatewayClient::new_init( gateway.clients_address(), gateway.identity_key, our_identity.clone(), - coconut_credential, timeout, ); gateway_client @@ -210,13 +188,8 @@ pub fn execute(matches: &ArgMatches) { config .get_base_mut() .with_gateway_id(gate_details.identity_key.to_base58_string()); - let validator_urls = config.get_base().get_validator_api_endpoints(); - let shared_keys = register_with_gateway( - &gate_details, - key_manager.identity_keypair(), - validator_urls, - ) - .await; + let shared_keys = + register_with_gateway(&gate_details, key_manager.identity_keypair()).await; (shared_keys, gate_details.clients_address()) }; diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index b494b510fb..9976db2ac7 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -24,8 +24,8 @@ url = "2.2" # internal client-core = { path = "../client-core" } -coconut-interface = { path = "../../common/coconut-interface" } -credentials = { path = "../../common/credentials" } +coconut-interface = { path = "../../common/coconut-interface", optional = true } +credentials = { path = "../../common/credentials", optional = true } config = { path = "../../common/config" } crypto = { path = "../../common/crypto" } gateway-client = { path = "../../common/client-libs/gateway-client" } @@ -38,3 +38,6 @@ pemstore = { path = "../../common/pemstore" } proxy-helpers = { path = "../../common/socks5/proxy-helpers" } validator-client = { path = "../../common/client-libs/validator-client" } version-checker = { path = "../../common/version-checker" } + +[features] +coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"] diff --git a/clients/socks5/src/client/mod.rs b/clients/socks5/src/client/mod.rs index 212787dc43..3882694f20 100644 --- a/clients/socks5/src/client/mod.rs +++ b/clients/socks5/src/client/mod.rs @@ -23,9 +23,6 @@ use client_core::client::topology_control::{ TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, }; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; -use coconut_interface::Credential; -use credentials::bandwidth::prepare_for_spending; -use credentials::obtain_aggregate_verification_key; use crypto::asymmetric::identity; use futures::channel::mpsc; use gateway_client::{ @@ -37,6 +34,11 @@ use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::nodes::NodeIdentity; use tokio::runtime::Runtime; +#[cfg(feature = "coconut")] +use coconut_interface::Credential; +#[cfg(feature = "coconut")] +use credentials::{bandwidth::prepare_for_spending, obtain_aggregate_verification_key}; + pub(crate) mod config; pub struct NymClient { @@ -154,7 +156,8 @@ impl NymClient { .start(self.runtime.handle()) } - async fn prepare_credential(&self) -> Credential { + #[cfg(feature = "coconut")] + async fn prepare_coconut_credential(&self) -> Credential { let verification_key = obtain_aggregate_verification_key( &self.config.get_base().get_validator_api_endpoints(), ) @@ -196,7 +199,8 @@ impl NymClient { .expect("provided gateway id is invalid!"); self.runtime.block_on(async { - let coconut_credential = self.prepare_credential().await; + #[cfg(feature = "coconut")] + let coconut_credential = self.prepare_coconut_credential().await; let mut gateway_client = GatewayClient::new( gateway_address, @@ -206,11 +210,13 @@ impl NymClient { mixnet_message_sender, ack_sender, self.config.get_base().get_gateway_response_timeout(), - coconut_credential, ); gateway_client - .authenticate_and_start() + .authenticate_and_start( + #[cfg(feature = "coconut")] + Some(coconut_credential), + ) .await .expect("could not authenticate and start up the gateway connection"); diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 58916dd45c..f24dc53322 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -6,10 +6,7 @@ use crate::commands::override_config; use clap::{App, Arg, ArgMatches}; use client_core::client::key_manager::KeyManager; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; -use coconut_interface::Credential; use config::NymConfig; -use credentials::bandwidth::prepare_for_spending; -use credentials::obtain_aggregate_verification_key; use crypto::asymmetric::{encryption, identity}; use gateway_client::GatewayClient; use gateway_requests::registration::handshake::SharedKeys; @@ -60,34 +57,15 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) } -// this behaviour should definitely be changed, we shouldn't -// need to get bandwidth credential for registration -async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential { - let verification_key = obtain_aggregate_verification_key(validators) - .await - .expect("could not obtain aggregate verification key of validators"); - - let bandwidth_credential = credentials::bandwidth::obtain_signature(raw_identity, validators) - .await - .expect("could not obtain bandwidth credential"); - - prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key) - .expect("could not prepare out bandwidth credential for spending") -} - async fn register_with_gateway( gateway: &gateway::Node, our_identity: Arc, - validator_urls: Vec, ) -> Arc { let timeout = Duration::from_millis(1500); - let coconut_credential = - prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await; let mut gateway_client = GatewayClient::new_init( gateway.clients_address(), gateway.identity_key, our_identity.clone(), - coconut_credential, timeout, ); gateway_client @@ -211,13 +189,8 @@ pub fn execute(matches: &ArgMatches) { config .get_base_mut() .with_gateway_id(gate_details.identity_key.to_base58_string()); - let validator_urls = config.get_base().get_validator_api_endpoints(); - let shared_keys = register_with_gateway( - &gate_details, - key_manager.identity_keypair(), - validator_urls, - ) - .await; + let shared_keys = + register_with_gateway(&gate_details, key_manager.identity_keypair()).await; (shared_keys, gate_details.clients_address()) }; diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index 41e5226798..b128e1a974 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["cdylib", "rlib"] [features] default = ["console_error_panic_hook"] offline-test = [] +coconut = ["coconut-interface", "credentials", "gateway-client/coconut"] [dependencies] futures = "0.3" @@ -25,8 +26,8 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } url = "2.2" # internal -coconut-interface = { path = "../../common/coconut-interface" } -credentials = { path = "../../common/credentials" } +coconut-interface = { path = "../../common/coconut-interface", optional = true } +credentials = { path = "../../common/credentials", optional = true } crypto = { path = "../../common/crypto" } nymsphinx = { path = "../../common/nymsphinx" } topology = { path = "../../common/topology" } diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 91c0ab3e91..1187eb0ad2 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -1,9 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use coconut_interface::Credential; -use credentials::bandwidth::prepare_for_spending; -use credentials::obtain_aggregate_verification_key; use crypto::asymmetric::{encryption, identity}; use futures::channel::mpsc; use gateway_client::GatewayClient; @@ -20,6 +17,11 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_futures::spawn_local; use wasm_utils::{console_log, console_warn}; +#[cfg(feature = "coconut")] +use coconut_interface::Credential; +#[cfg(feature = "coconut")] +use credentials::{bandwidth::prepare_for_spending, obtain_aggregate_verification_key}; + pub(crate) mod received_processor; const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(200); @@ -101,7 +103,8 @@ impl NymClient { self.self_recipient().to_string() } - async fn prepare_credential(validators: &[Url], identity_bytes: &[u8]) -> Credential { + #[cfg(feature = "coconut")] + async fn prepare_coconut_credential(validators: &[Url], identity_bytes: &[u8]) -> Credential { let verification_key = obtain_aggregate_verification_key(validators) .await .expect("could not obtain aggregate verification key of validators"); @@ -119,17 +122,23 @@ impl NymClient { // Right now it's impossible to have async exported functions to take `&self` rather than self pub async fn initial_setup(self) -> Self { - let validator_server = self.validator_server.clone(); - let identity_public_key = self.identity.public_key().clone(); + #[cfg(feature = "coconut")] + let coconut_credential = { + let validator_server = self.validator_server.clone(); + let identity_public_key = self.identity.public_key().clone(); + Self::prepare_coconut_credential( + &vec![validator_server], + &identity_public_key.to_bytes(), + ) + .await + }; + let mut client = self.get_and_update_topology().await; let gateway = client.choose_gateway(); let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded(); - let coconut_credential = - Self::prepare_credential(&vec![validator_server], &identity_public_key.to_bytes()) - .await; let mut gateway_client = GatewayClient::new( gateway.clients_address(), Arc::clone(&client.identity), @@ -138,11 +147,13 @@ impl NymClient { mixnet_messages_sender, ack_sender, DEFAULT_GATEWAY_RESPONSE_TIMEOUT, - coconut_credential, ); gateway_client - .authenticate_and_start() + .authenticate_and_start( + #[cfg(feature = "coconut")] + Some(coconut_credential), + ) .await .expect("could not authenticate and start up the gateway connection"); diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index a72f16909f..247352ac31 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -17,7 +17,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } crypto = { path = "../../crypto" } gateway-requests = { path = "../../../gateway/gateway-requests" } nymsphinx = { path = "../../nymsphinx" } -coconut-interface = { path = "../../coconut-interface" } +coconut-interface = { path = "../../coconut-interface", optional = true } [dependencies.tungstenite] version = "0.13" @@ -57,3 +57,6 @@ features = ["js"] [dev-dependencies] # for tests #url = "2.1" + +[features] +coconut = ["gateway-requests/coconut", "coconut-interface"] \ No newline at end of file diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index 14b9ee74c7..ddca41f92b 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -8,7 +8,6 @@ pub use crate::packet_router::{ AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender, }; use crate::socket_state::{PartiallyDelegated, SocketState}; -use coconut_interface::Credential; use crypto::asymmetric::identity; use futures::{FutureExt, SinkExt, StreamExt}; use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; @@ -31,6 +30,9 @@ use fluvio_wasm_timer as wasm_timer; #[cfg(target_arch = "wasm32")] use wasm_utils::websocket::JSWebsocket; +#[cfg(feature = "coconut")] +use coconut_interface::Credential; + const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10; const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5); @@ -53,7 +55,6 @@ pub struct GatewayClient { reconnection_attempts: usize, /// Delay between each subsequent reconnection attempt. reconnection_backoff: Duration, - coconut_credential: Credential, } impl GatewayClient { @@ -67,7 +68,6 @@ impl GatewayClient { mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, response_timeout_duration: Duration, - coconut_credential: Credential, ) -> Self { GatewayClient { authenticated: false, @@ -82,7 +82,6 @@ impl GatewayClient { should_reconnect_on_failure: true, reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS, reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF, - coconut_credential, } } @@ -103,7 +102,6 @@ impl GatewayClient { gateway_address: String, gateway_identity: identity::PublicKey, local_identity: Arc, - coconut_credential: Credential, response_timeout_duration: Duration, ) -> Self { use futures::channel::mpsc; @@ -127,7 +125,6 @@ impl GatewayClient { should_reconnect_on_failure: false, reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS, reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF, - coconut_credential, } } @@ -199,7 +196,14 @@ impl GatewayClient { for i in 1..self.reconnection_attempts { info!("attempt {}...", i); - if self.authenticate_and_start().await.is_ok() { + if self + .authenticate_and_start( + #[cfg(feature = "coconut")] + None, + ) + .await + .is_ok() + { info!("managed to reconnect!"); return Ok(()); } @@ -218,7 +222,13 @@ impl GatewayClient { // final attempt (done separately to be able to return a proper error) info!("attempt {}", self.reconnection_attempts); - match self.authenticate_and_start().await { + match self + .authenticate_and_start( + #[cfg(feature = "coconut")] + None, + ) + .await + { Ok(_) => { info!("managed to reconnect!"); Ok(()) @@ -455,7 +465,11 @@ impl GatewayClient { } } - pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> { + #[cfg(feature = "coconut")] + pub async fn claim_coconut_bandwidth( + &mut self, + coconut_credential: Credential, + ) -> Result<(), GatewayClientError> { if !self.authenticated { return Err(GatewayClientError::NotAuthenticated); } @@ -466,8 +480,8 @@ impl GatewayClient { let mut rng = OsRng; let iv = IV::new_random(&mut rng); - let msg = ClientControlRequest::new_enc_bandwidth_credential( - &self.coconut_credential, + let msg = ClientControlRequest::new_enc_coconut_bandwidth_credential( + &coconut_credential, self.shared_key.as_ref().unwrap(), iv, ) @@ -527,15 +541,10 @@ impl GatewayClient { } } - pub async fn send_ping_message(&mut self) -> Result<(), GatewayClientError> { - if !self.connection.is_established() { - return Err(GatewayClientError::ConnectionNotEstablished); - } - - // as per RFC6455 section 5.5.2, `Ping frame MAY include "Application data".` - // so we don't need to include any here. - let msg = Message::Ping(Vec::new()); - + async fn send_with_reconnection_on_failure( + &mut self, + msg: Message, + ) -> Result<(), GatewayClientError> { if let Err(err) = self.send_websocket_message_without_response(msg).await { if err.is_closed_connection() && self.should_reconnect_on_failure { info!("Going to attempt a reconnection"); @@ -548,6 +557,17 @@ impl GatewayClient { } } + pub async fn send_ping_message(&mut self) -> Result<(), GatewayClientError> { + if !self.connection.is_established() { + return Err(GatewayClientError::ConnectionNotEstablished); + } + + // as per RFC6455 section 5.5.2, `Ping frame MAY include "Application data".` + // so we don't need to include any here. + let msg = Message::Ping(Vec::new()); + self.send_with_reconnection_on_failure(msg).await + } + // TODO: possibly make responses optional pub async fn send_mix_packet( &mut self, @@ -569,17 +589,7 @@ impl GatewayClient { .as_ref() .expect("no shared key present even though we're authenticated!"), ); - - if let Err(err) = self.send_websocket_message_without_response(msg).await { - if err.is_closed_connection() && self.should_reconnect_on_failure { - info!("Going to attempt a reconnection"); - self.attempt_reconnection().await - } else { - Err(err) - } - } else { - Ok(()) - } + self.send_with_reconnection_on_failure(msg).await } async fn recover_socket_connection(&mut self) -> Result<(), GatewayClientError> { @@ -634,12 +644,21 @@ impl GatewayClient { Ok(()) } - pub async fn authenticate_and_start(&mut self) -> Result, GatewayClientError> { + pub async fn authenticate_and_start( + &mut self, + #[cfg(feature = "coconut")] coconut_credential: Option, + ) -> Result, GatewayClientError> { if !self.connection.is_established() { self.establish_connection().await?; } let shared_key = self.perform_initial_authentication().await?; - self.claim_bandwidth().await?; + + #[cfg(feature = "coconut")] + { + if let Some(coconut_credential) = coconut_credential { + self.claim_coconut_bandwidth(coconut_credential).await?; + } + } // this call is NON-blocking self.start_listening_for_mixnet_messages()?; diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index d6c82334af..384b28b720 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -29,8 +29,8 @@ url = { version = "2.2", features = [ "serde" ] } sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } # internal -coconut-interface = { path = "../common/coconut-interface" } -credentials = { path = "../common/credentials" } +coconut-interface = { path = "../common/coconut-interface" , optional = true} +credentials = { path = "../common/credentials" , optional = true} config = { path = "../common/config" } crypto = { path = "../common/crypto" } gateway-requests = { path = "gateway-requests" } @@ -41,6 +41,9 @@ pemstore = { path = "../common/pemstore" } validator-client = { path = "../common/client-libs/validator-client" } version-checker = { path = "../common/version-checker" } +[features] +coconut = ["coconut-interface", "credentials", "gateway-requests/coconut"] + [build-dependencies] tokio = { version = "1.4", features = ["rt-multi-thread", "macros"] } sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } diff --git a/gateway/gateway-requests/Cargo.toml b/gateway/gateway-requests/Cargo.toml index 442d9837f0..99a8b6cd15 100644 --- a/gateway/gateway-requests/Cargo.toml +++ b/gateway/gateway-requests/Cargo.toml @@ -23,8 +23,11 @@ bincode = "1.3" crypto = { path = "../../common/crypto" } pemstore = { path = "../../common/pemstore" } -coconut-interface = { path = "../../common/coconut-interface" } -credentials = { path = "../../common/credentials"} +coconut-interface = { path = "../../common/coconut-interface", optional = true } +credentials = { path = "../../common/credentials", optional = true } + +[features] +coconut = ["coconut-interface", "credentials"] [dependencies.tungstenite] version = "0.13.0" diff --git a/gateway/gateway-requests/src/types.rs b/gateway/gateway-requests/src/types.rs index 9301fef32d..015053445e 100644 --- a/gateway/gateway-requests/src/types.rs +++ b/gateway/gateway-requests/src/types.rs @@ -5,7 +5,6 @@ use crate::authentication::encrypted_address::EncryptedAddressBytes; use crate::iv::IV; use crate::registration::handshake::SharedKeys; use crate::GatewayMacSize; -use coconut_interface::Credential; use crypto::generic_array::typenum::Unsigned; use crypto::hmac::recompute_keyed_hmac_and_verify_tag; use crypto::symmetric::stream_cipher; @@ -21,6 +20,9 @@ use std::{ }; use tungstenite::protocol::Message; +#[cfg(feature = "coconut")] +use coconut_interface::Credential; + #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type", rename_all = "camelCase")] pub enum RegistrationHandshake { @@ -115,7 +117,8 @@ pub enum ClientControlRequest { }, #[serde(alias = "handshakePayload")] RegisterHandshakeInitRequest { data: Vec }, - BandwidthCredential { + #[cfg(feature = "coconut")] + CoconutBandwidthCredential { enc_credential: Vec, iv: Vec, }, @@ -134,7 +137,8 @@ impl ClientControlRequest { } } - pub fn new_enc_bandwidth_credential( + #[cfg(feature = "coconut")] + pub fn new_enc_coconut_bandwidth_credential( credential: &Credential, shared_key: &SharedKeys, iv: IV, @@ -144,7 +148,7 @@ impl ClientControlRequest { let enc_credential = shared_key.encrypt_and_tag(&serialized_credential, Some(iv.inner())); - Some(ClientControlRequest::BandwidthCredential { + Some(ClientControlRequest::CoconutBandwidthCredential { enc_credential, iv: iv.to_bytes(), }) @@ -153,7 +157,8 @@ impl ClientControlRequest { } } - pub fn try_from_enc_bandwidth_credential( + #[cfg(feature = "coconut")] + pub fn try_from_enc_coconut_bandwidth_credential( enc_credential: Vec, shared_key: &SharedKeys, iv: IV, diff --git a/gateway/src/node/client_handling/mod.rs b/gateway/src/node/client_handling/mod.rs index 9d50814da9..118a2ad4c4 100644 --- a/gateway/src/node/client_handling/mod.rs +++ b/gateway/src/node/client_handling/mod.rs @@ -2,5 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub(crate) mod active_clients; -mod bandwidth; pub(crate) mod websocket; + +#[cfg(feature = "coconut")] +mod bandwidth; diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index b7bd994461..f623503bff 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -1,12 +1,11 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::node::client_handling::bandwidth::Bandwidth; use crate::node::client_handling::websocket::connection_handler::{ClientDetails, FreshHandler}; use crate::node::client_handling::websocket::message_receiver::MixMessageReceiver; use crate::node::storage::error::StorageError; use futures::StreamExt; -use gateway_requests::iv::{IVConversionError, IV}; +use gateway_requests::iv::IVConversionError; use gateway_requests::types::{BinaryRequest, ServerResponse}; use gateway_requests::{ClientControlRequest, GatewayRequestsError}; use log::*; @@ -18,6 +17,11 @@ use thiserror::Error; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_tungstenite::tungstenite::protocol::Message; +#[cfg(feature = "coconut")] +use crate::node::client_handling::bandwidth::Bandwidth; +#[cfg(feature = "coconut")] +use gateway_requests::iv::IV; + #[derive(Debug, Error)] enum RequestHandlingError { #[error("Internal gateway storage error")] @@ -32,17 +36,20 @@ enum RequestHandlingError { #[error("Provided binary request was malformed - {0}")] InvalidTextRequest(>::Error), - #[error("Provided bandwidth credential did not verify correctly")] - InvalidBandwidthCredential, - - #[error("Provided bandwidth credential did not have expected structure - {0}")] - BandwidthCredentialError(#[from] credentials::error::Error), + #[error("The received request is not valid in the current context")] + IllegalRequest, + #[cfg(feature = "coconut")] #[error("Provided bandwidth credential asks for more bandwidth than it is supported to add at once (credential value: {0}, supported: {}). Try to split it before attempting again", i64::MAX)] UnsupportedBandwidthValue(u64), - #[error("The received request is not valid in the current context")] - IllegalRequest, + #[cfg(feature = "coconut")] + #[error("Provided bandwidth credential did not verify correctly")] + InvalidCoconutBandwidthCredential, + + #[cfg(feature = "coconut")] + #[error("Provided coconut bandwidth credential did not have expected structure - {0}")] + CoconutBandwidthCredentialError(#[from] credentials::error::Error), } impl RequestHandlingError { @@ -110,6 +117,10 @@ where Ok(bandwidth) } + // note: this is not technically a "coconut" thing, but currently we have no non-coconut + // bandwidth handling and hence clippy complains about dead and unreachable code + // so whenever we introduce another form of bandwidth claim, this feature flag should get removed + #[cfg(feature = "coconut")] /// Increases the amount of available bandwidth of the connected client by the specified value. /// /// # Arguments @@ -148,6 +159,7 @@ where } } + #[cfg(feature = "coconut")] /// Tries to handle the received bandwidth request by checking correctness of the received data /// and if successful, increases client's bandwidth by an appropriate amount. /// @@ -155,20 +167,20 @@ where /// /// * `enc_credential`: raw encrypted bandwidth credential to verify. /// * `iv`: fresh iv used for the credential. - async fn handle_bandwidth( + async fn handle_coconut_bandwidth( &mut self, enc_credential: Vec, iv: Vec, ) -> Result { let iv = IV::try_from_bytes(&iv)?; - let credential = ClientControlRequest::try_from_enc_bandwidth_credential( + let credential = ClientControlRequest::try_from_enc_coconut_bandwidth_credential( enc_credential, &self.client.shared_keys, iv, )?; if !credential.verify(&self.inner.aggregated_verification_key) { - return Err(RequestHandlingError::InvalidBandwidthCredential); + return Err(RequestHandlingError::InvalidCoconutBandwidthCredential); } let bandwidth = Bandwidth::try_from(credential)?; @@ -202,10 +214,17 @@ where &self, mix_packet: MixPacket, ) -> Result { + // currently we have no way for increasing bandwidth hence we shouldn't be performing + // any meaningful metering. Once we have another way of claiming bandwidth, this + // feature lock should go away + #[cfg(feature = "coconut")] // for now let's just use actual size of the sphinx packet. there's a tiny bit of overhead // we're not including (but it's literally like 2 bytes) when the packet is framed let consumed_bandwidth = mix_packet.sphinx_packet().len() as i64; + #[cfg(not(feature = "coconut"))] + let consumed_bandwidth = 0; + let available_bandwidth = self.get_available_bandwidth().await?; if available_bandwidth < consumed_bandwidth { @@ -254,8 +273,9 @@ where match ClientControlRequest::try_from(raw_request) { Err(e) => RequestHandlingError::InvalidTextRequest(e).into_error_message(), Ok(request) => match request { - ClientControlRequest::BandwidthCredential { enc_credential, iv } => { - match self.handle_bandwidth(enc_credential, iv).await { + #[cfg(feature = "coconut")] + ClientControlRequest::CoconutBandwidthCredential { enc_credential, iv } => { + match self.handle_coconut_bandwidth(enc_credential, iv).await { Ok(response) => response.into(), Err(err) => err.into_error_message(), } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs b/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs index 377dbebd20..5c413d7022 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs @@ -7,7 +7,6 @@ use crate::node::client_handling::websocket::connection_handler::{ }; use crate::node::storage::error::StorageError; use crate::node::storage::PersistentStorage; -use coconut_interface::VerificationKey; use crypto::asymmetric::identity; use futures::{channel::mpsc, SinkExt, StreamExt}; use gateway_requests::authentication::encrypted_address::{ @@ -28,6 +27,9 @@ use thiserror::Error; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError}; +#[cfg(feature = "coconut")] +use coconut_interface::VerificationKey; + #[derive(Debug, Error)] enum InitialAuthenticationError { #[error("Internal gateway storage error")] @@ -67,10 +69,12 @@ pub(crate) struct FreshHandler { rng: R, local_identity: Arc, pub(crate) active_clients_store: ActiveClientsStore, - pub(crate) aggregated_verification_key: VerificationKey, pub(crate) outbound_mix_sender: MixForwardingSender, pub(crate) socket_connection: SocketStream, pub(crate) storage: PersistentStorage, + + #[cfg(feature = "coconut")] + pub(crate) aggregated_verification_key: VerificationKey, } impl FreshHandler @@ -84,9 +88,9 @@ where conn: S, outbound_mix_sender: MixForwardingSender, local_identity: Arc, - aggregated_verification_key: VerificationKey, storage: PersistentStorage, active_clients_store: ActiveClientsStore, + #[cfg(feature = "coconut")] aggregated_verification_key: VerificationKey, ) -> Self { FreshHandler { rng, @@ -94,8 +98,9 @@ where outbound_mix_sender, socket_connection: SocketStream::RawTcp(conn), local_identity, - aggregated_verification_key, storage, + #[cfg(feature = "coconut")] + aggregated_verification_key, } } @@ -492,6 +497,11 @@ where ClientControlRequest::RegisterHandshakeInitRequest { data } => { self.handle_register(data).await } + + // note: this is not technically a "coconut" thing, but currently we have no non-coconut + // bandwidth handling and hence clippy complains about dead and unreachable code + // so whenever we introduce another form of bandwidth claim, this feature flag should get removed + #[cfg(feature = "coconut")] // won't accept anything else (like bandwidth) without prior authentication _ => Err(InitialAuthenticationError::InvalidRequest), } diff --git a/gateway/src/node/client_handling/websocket/listener.rs b/gateway/src/node/client_handling/websocket/listener.rs index 26e3b8b5de..c55da12c3f 100644 --- a/gateway/src/node/client_handling/websocket/listener.rs +++ b/gateway/src/node/client_handling/websocket/listener.rs @@ -4,7 +4,6 @@ use crate::node::client_handling::active_clients::ActiveClientsStore; use crate::node::client_handling::websocket::connection_handler::FreshHandler; use crate::node::storage::PersistentStorage; -use coconut_interface::VerificationKey; use crypto::asymmetric::identity; use log::*; use mixnet_client::forwarder::MixForwardingSender; @@ -14,9 +13,14 @@ use std::process; use std::sync::Arc; use tokio::task::JoinHandle; +#[cfg(feature = "coconut")] +use coconut_interface::VerificationKey; + pub(crate) struct Listener { address: SocketAddr, local_identity: Arc, + + #[cfg(feature = "coconut")] aggregated_verification_key: VerificationKey, } @@ -24,11 +28,12 @@ impl Listener { pub(crate) fn new( address: SocketAddr, local_identity: Arc, - aggregated_verification_key: VerificationKey, + #[cfg(feature = "coconut")] aggregated_verification_key: VerificationKey, ) -> Self { Listener { address, local_identity, + #[cfg(feature = "coconut")] aggregated_verification_key, } } @@ -61,9 +66,10 @@ impl Listener { socket, outbound_mix_sender.clone(), Arc::clone(&self.local_identity), - self.aggregated_verification_key.clone(), storage.clone(), active_clients_store.clone(), + #[cfg(feature = "coconut")] + self.aggregated_verification_key.clone(), ); tokio::spawn(async move { handle.start_handling().await }); } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index f746928942..201a4c8e2e 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -6,8 +6,6 @@ use crate::node::client_handling::active_clients::ActiveClientsStore; use crate::node::client_handling::websocket; use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler; use crate::node::storage::PersistentStorage; -use coconut_interface::VerificationKey; -use credentials::obtain_aggregate_verification_key; use crypto::asymmetric::{encryption, identity}; use log::*; use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder}; @@ -17,6 +15,11 @@ use std::net::SocketAddr; use std::process; use std::sync::Arc; +#[cfg(feature = "coconut")] +use coconut_interface::VerificationKey; +#[cfg(feature = "coconut")] +use credentials::obtain_aggregate_verification_key; + pub(crate) mod client_handling; pub(crate) mod mixnet_handling; pub(crate) mod storage; @@ -83,8 +86,8 @@ impl Gateway { fn start_client_websocket_listener( &self, forwarding_channel: MixForwardingSender, - verification_key: VerificationKey, active_clients_store: ActiveClientsStore, + #[cfg(feature = "coconut")] verification_key: VerificationKey, ) { info!("Starting client [web]socket listener..."); @@ -96,6 +99,7 @@ impl Gateway { websocket::Listener::new( listening_address, Arc::clone(&self.identity), + #[cfg(feature = "coconut")] verification_key, ) .start( @@ -170,6 +174,7 @@ impl Gateway { } } + #[cfg(feature = "coconut")] let validators_verification_key = obtain_aggregate_verification_key(&self.config.get_validator_api_endpoints()) .await @@ -182,10 +187,12 @@ impl Gateway { mix_forwarding_channel.clone(), active_clients_store.clone(), ); + self.start_client_websocket_listener( mix_forwarding_channel, - validators_verification_key, active_clients_store, + #[cfg(feature = "coconut")] + validators_verification_key, ); info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!"); diff --git a/gateway/src/node/storage/bandwidth.rs b/gateway/src/node/storage/bandwidth.rs index e2367b2ee1..5299f959d3 100644 --- a/gateway/src/node/storage/bandwidth.rs +++ b/gateway/src/node/storage/bandwidth.rs @@ -54,6 +54,10 @@ impl BandwidthManager { .await } + // note: this is not technically a "coconut" thing, but currently we have no non-coconut + // bandwidth handling and hence clippy complains about dead and unreachable code + // so whenever we introduce another form of bandwidth claim, this feature flag should get removed + #[cfg(feature = "coconut")] /// Increases available bandwidth of the particular client by the specified amount. /// /// # Arguments diff --git a/gateway/src/node/storage/mod.rs b/gateway/src/node/storage/mod.rs index a74d53a389..2af35291e1 100644 --- a/gateway/src/node/storage/mod.rs +++ b/gateway/src/node/storage/mod.rs @@ -211,6 +211,10 @@ impl PersistentStorage { Ok(res) } + // note: this is not technically a "coconut" thing, but currently we have no non-coconut + // bandwidth handling and hence clippy complains about dead and unreachable code + // so whenever we introduce another form of bandwidth claim, this feature flag should get removed + #[cfg(feature = "coconut")] /// Increases available bandwidth of the particular client by the specified amount. /// /// # Arguments diff --git a/validator-api/Cargo.toml b/validator-api/Cargo.toml index 099f0c0a38..c91d865fd6 100644 --- a/validator-api/Cargo.toml +++ b/validator-api/Cargo.toml @@ -48,9 +48,11 @@ nymsphinx = { path="../common/nymsphinx" } topology = { path="../common/topology" } validator-client = { path="../common/client-libs/validator-client", features = ["nymd-client"] } version-checker = { path="../common/version-checker" } -coconut-interface = { path = "../common/coconut-interface" } -credentials = { path = "../common/credentials" } +coconut-interface = { path = "../common/coconut-interface", optional = true } +credentials = { path = "../common/credentials", optional = true } +[features] +coconut = ["coconut-interface", "credentials", "gateway-client/coconut"] [build-dependencies] tokio = { version = "1.4", features = ["rt-multi-thread", "macros"] } diff --git a/validator-api/src/config/mod.rs b/validator-api/src/config/mod.rs index 285eb6b6c6..afd672f83b 100644 --- a/validator-api/src/config/mod.rs +++ b/validator-api/src/config/mod.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::config::template::config_template; -use coconut_interface::{Base58, KeyPair}; use config::defaults::{ default_api_endpoints, DEFAULT_EPOCH_LENGTH, DEFAULT_FIRST_EPOCH_START, DEFAULT_MIXNET_CONTRACT_ADDRESS, @@ -14,6 +13,9 @@ use std::time::Duration; use time::OffsetDateTime; use url::Url; +#[cfg(feature = "coconut")] +use coconut_interface::{Base58, KeyPair}; + mod template; const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657"; @@ -30,7 +32,6 @@ const DEFAULT_CACHE_INTERVAL: Duration = Duration::from_secs(60); const DEFAULT_MONITOR_THRESHOLD: u8 = 60; #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] pub struct Config { #[serde(default)] base: Base, @@ -74,7 +75,7 @@ impl NymConfig for Config { } #[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields, default)] +#[serde(default)] pub struct Base { local_validator: Url, @@ -82,6 +83,7 @@ pub struct Base { mixnet_contract_address: String, // Avoid breaking derives for now + #[cfg(feature = "coconut")] keypair_bs58: String, } @@ -92,13 +94,14 @@ impl Default for Base { .parse() .expect("default local validator is malformed!"), mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(), + #[cfg(feature = "coconut")] keypair_bs58: String::default(), } } } #[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields, default)] +#[serde(default)] pub struct NetworkMonitor { /// Specifies whether network monitoring service is enabled in this process. enabled: bool, @@ -179,7 +182,7 @@ impl Default for NetworkMonitor { } #[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields, default)] +#[serde(default)] pub struct NodeStatusAPI { /// Path to the database file containing uptime statuses for all mixnodes and gateways. database_path: PathBuf, @@ -200,7 +203,7 @@ impl Default for NodeStatusAPI { } #[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields, default)] +#[serde(default)] pub struct TopologyCacher { #[serde(with = "humantime_serde")] caching_interval: Duration, @@ -215,7 +218,7 @@ impl Default for TopologyCacher { } #[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields, default)] +#[serde(default)] pub struct Rewarding { /// Specifies whether rewarding service is enabled in this process. enabled: bool, @@ -254,6 +257,7 @@ impl Config { Config::default() } + #[cfg(feature = "coconut")] pub fn keypair(&self) -> KeyPair { KeyPair::try_from_bs58(self.base.keypair_bs58.clone()).unwrap() } @@ -298,6 +302,7 @@ impl Config { self } + #[cfg(feature = "coconut")] pub fn with_keypair>(mut self, keypair_bs58: S) -> Self { self.base.keypair_bs58 = keypair_bs58.into(); self @@ -391,6 +396,8 @@ impl Config { self.node_status_api.database_path.clone() } + // fix dead code warnings as this method is only ever used with coconut feature + #[cfg(feature = "coconut")] pub fn get_all_validator_api_endpoints(&self) -> Vec { self.network_monitor.all_validator_apis.clone() } diff --git a/validator-api/src/main.rs b/validator-api/src/main.rs index c0fce208b5..0e4fb5e9b0 100644 --- a/validator-api/src/main.rs +++ b/validator-api/src/main.rs @@ -16,7 +16,6 @@ use ::config::NymConfig; use anyhow::Result; use cache::ValidatorCache; use clap::{App, Arg, ArgMatches}; -use coconut::InternalSignRequest; use log::{info, warn}; use rocket::fairing::AdHoc; use rocket::http::Method; @@ -31,8 +30,10 @@ use tokio::sync::Notify; use url::Url; use validator_client::nymd::SigningNymdClient; +#[cfg(feature = "coconut")] +use coconut::InternalSignRequest; + pub(crate) mod cache; -mod coconut; pub(crate) mod config; mod network_monitor; mod node_status_api; @@ -40,17 +41,22 @@ pub(crate) mod nymd_client; mod rewarding; pub(crate) mod storage; +#[cfg(feature = "coconut")] +mod coconut; + const MONITORING_ENABLED: &str = "enable-monitor"; const REWARDING_ENABLED: &str = "enable-rewarding"; const V4_TOPOLOGY_ARG: &str = "v4-topology-filepath"; const V6_TOPOLOGY_ARG: &str = "v6-topology-filepath"; -const API_VALIDATORS_ARG: &str = "api-validators"; const DETAILED_REPORT_ARG: &str = "detailed-report"; const MIXNET_CONTRACT_ARG: &str = "mixnet-contract"; const MNEMONIC_ARG: &str = "mnemonic"; const WRITE_CONFIG_ARG: &str = "save-config"; -const KEYPAIR_ARG: &str = "keypair"; const NYMD_VALIDATOR_ARG: &str = "nymd-validator"; +const API_VALIDATORS_ARG: &str = "api-validators"; + +#[cfg(feature = "coconut")] +const KEYPAIR_ARG: &str = "keypair"; const EPOCH_LENGTH_ARG: &str = "epoch-length"; const FIRST_REWARDING_EPOCH_ARG: &str = "first-epoch"; @@ -70,7 +76,7 @@ fn parse_validators(raw: &str) -> Vec { } fn parse_args<'a>() -> ArgMatches<'a> { - App::new("Nym Validator API") + let base_app = App::new("Nym Validator API") .author("Nymtech") .arg( Arg::with_name(MONITORING_ENABLED) @@ -104,12 +110,6 @@ fn parse_args<'a>() -> ArgMatches<'a> { .long(NYMD_VALIDATOR_ARG) .takes_value(true) ) - .arg( - Arg::with_name(API_VALIDATORS_ARG) - .help("specifies list of all validators on the network issuing coconut credentials. Ensure they are properly ordered") - .long(API_VALIDATORS_ARG) - .takes_value(true) - ) .arg(Arg::with_name(MIXNET_CONTRACT_ARG) .long(MIXNET_CONTRACT_ARG) .help("Address of the validator contract managing the network") @@ -134,10 +134,10 @@ fn parse_args<'a>() -> ArgMatches<'a> { .short("w") ) .arg( - Arg::with_name(KEYPAIR_ARG) - .help("Path to the secret key file") + Arg::with_name(API_VALIDATORS_ARG) + .help("specifies list of all validators on the network issuing coconut credentials. Ensure they are properly ordered") + .long(API_VALIDATORS_ARG) .takes_value(true) - .long(KEYPAIR_ARG) ) .arg( Arg::with_name(FIRST_REWARDING_EPOCH_ARG) @@ -159,9 +159,17 @@ fn parse_args<'a>() -> ArgMatches<'a> { .takes_value(true) .long(REWARDING_MONITOR_THRESHOLD_ARG) .requires(REWARDING_ENABLED) - ) + ); - .get_matches() + #[cfg(feature = "coconut")] + let base_app = base_app.arg( + Arg::with_name(KEYPAIR_ARG) + .help("Path to the secret key file") + .takes_value(true) + .long(KEYPAIR_ARG), + ); + + base_app.get_matches() } async fn wait_for_interrupt() { @@ -264,6 +272,8 @@ fn override_config(mut config: Config, matches: &ArgMatches) -> Config { if matches.is_present(DETAILED_REPORT_ARG) { config = config.with_detailed_network_monitor_report(true) } + + #[cfg(feature = "coconut")] if let Some(keypair_path) = matches.value_of(KEYPAIR_ARG) { let keypair_bs58 = std::fs::read_to_string(keypair_path) .unwrap() @@ -377,8 +387,10 @@ async fn setup_rocket(config: &Config, liftoff_notify: Arc) -> Result NetworkMonitorBuilder<'a> { *encryption_keypair.public_key(), ); + #[cfg(feature = "coconut")] let bandwidth_credential = TEMPORARY_obtain_bandwidth_credential(self.config, identity_keypair.public_key()).await; @@ -97,8 +100,9 @@ impl<'a> NetworkMonitorBuilder<'a> { self.config, gateway_status_update_sender, Arc::clone(&identity_keypair), - bandwidth_credential, self.config.get_gateway_sending_rate(), + #[cfg(feature = "coconut")] + bandwidth_credential, ); let received_processor = new_received_processor( @@ -163,6 +167,7 @@ fn new_packet_preparer( // SECURITY: // this implies we are re-using the same credential for all gateways all the time (which unfortunately is true!) +#[cfg(feature = "coconut")] #[allow(non_snake_case)] async fn TEMPORARY_obtain_bandwidth_credential( config: &Config, @@ -192,17 +197,18 @@ fn new_packet_sender( config: &Config, gateways_status_updater: GatewayClientUpdateSender, local_identity: Arc, - bandwidth_credential: Credential, max_sending_rate: usize, + #[cfg(feature = "coconut")] bandwidth_credential: Credential, ) -> PacketSender { PacketSender::new( gateways_status_updater, local_identity, - bandwidth_credential, config.get_gateway_response_timeout(), config.get_gateway_connection_timeout(), config.get_max_concurrent_gateway_clients(), max_sending_rate, + #[cfg(feature = "coconut")] + bandwidth_credential, ) } diff --git a/validator-api/src/network_monitor/monitor/sender.rs b/validator-api/src/network_monitor/monitor/sender.rs index 7c36dda326..eb314d5301 100644 --- a/validator-api/src/network_monitor/monitor/sender.rs +++ b/validator-api/src/network_monitor/monitor/sender.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::network_monitor::monitor::receiver::{GatewayClientUpdate, GatewayClientUpdateSender}; -use coconut_interface::Credential; use crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH}; use futures::channel::mpsc; use futures::stream::{self, FuturesUnordered, StreamExt}; @@ -22,6 +21,9 @@ use std::task::Poll; use std::time::Duration; use tokio::time::Instant; +#[cfg(feature = "coconut")] +use coconut_interface::Credential; + const TIME_CHUNK_SIZE: Duration = Duration::from_millis(50); pub(crate) struct GatewayPackets { @@ -71,7 +73,8 @@ struct FreshGatewayClientData { // SECURITY: // since currently we have no double spending protection, just to get things running // we're re-using the same credential for all gateways all the time. THIS IS VERY BAD!! - bandwidth_credential: Credential, + #[cfg(feature = "coconut")] + coconut_bandwidth_credential: Credential, } pub(crate) struct PacketSender { @@ -93,11 +96,11 @@ impl PacketSender { pub(crate) fn new( gateways_status_updater: GatewayClientUpdateSender, local_identity: Arc, - bandwidth_credential: Credential, gateway_response_timeout: Duration, gateway_connection_timeout: Duration, max_concurrent_clients: usize, max_sending_rate: usize, + #[cfg(feature = "coconut")] coconut_bandwidth_credential: Credential, ) -> Self { PacketSender { active_gateway_clients: HashMap::new(), @@ -105,7 +108,8 @@ impl PacketSender { gateways_status_updater, local_identity, gateway_response_timeout, - bandwidth_credential, + #[cfg(feature = "coconut")] + coconut_bandwidth_credential, }), gateway_connection_timeout, max_concurrent_clients, @@ -137,7 +141,6 @@ impl PacketSender { message_sender, ack_sender, fresh_gateway_client_data.gateway_response_timeout, - fresh_gateway_client_data.bandwidth_credential.clone(), ), (message_receiver, ack_receiver), ) @@ -230,7 +233,14 @@ impl PacketSender { // (an actual bug we experienced) match tokio::time::timeout( gateway_connection_timeout, - new_client.authenticate_and_start(), + new_client.authenticate_and_start( + #[cfg(feature = "coconut")] + Some( + fresh_gateway_client_data + .coconut_bandwidth_credential + .clone(), + ), + ), ) .await {