Compare commits

..

8 Commits

Author SHA1 Message Date
durch e5bed9d3e2 Remove cpucycles 2023-05-08 16:39:20 +02:00
durch 1119505990 Client tweaks 2023-05-01 16:04:09 +02:00
durch f0de459c58 Rebase on develop
commit 342883fcbe
Author: durch <durch@users.noreply.github.com>
Date:   Thu Apr 27 09:17:18 2023 +0200

    Put back PacketType 1

commit 61a0ee5a19
Author: Tommy Verrall <tommyvez@protonmail.com>
Date:   Wed Apr 26 16:37:29 2023 +0100

    change output for cpu-cycle management logs

commit 3956109c7e
Author: Tommy Verrall <tommy@nymtech.net>
Date:   Wed Apr 26 12:13:22 2023 +0100

    change the workflow file to build with cpucycles

commit 8d725b13c5
Author: durch <durch@users.noreply.github.com>
Date:   Mon Apr 24 13:14:58 2023 +0200

    Outfox client compat

commit 4d166c389b
Author: durch <durch@users.noreply.github.com>
Date:   Fri Apr 21 00:30:46 2023 +0200

    Address PR comments

commit 145c3c1223
Author: durch <durch@users.noreply.github.com>
Date:   Fri Apr 21 00:12:35 2023 +0200

    Rename PacketMode

commit cbd654d6fd
Author: Drazen Urch <drazen@urch.eu>
Date:   Thu Apr 20 23:59:40 2023 +0200

    Outfox rest compat (#3333)

    * Outfox forwarding compat

    * Tidy up interface

    * PacketSize compat

commit e7be91a94c
Author: durch <durch@users.noreply.github.com>
Date:   Wed Apr 19 16:36:48 2023 +0200

    Remove serde cruft

commit 582e7d566a
Author: durch <durch@users.noreply.github.com>
Date:   Wed Apr 19 16:24:09 2023 +0200

    Outfox framing

commit 6464da5f01
Author: durch <durch@users.noreply.github.com>
Date:   Tue Apr 18 22:23:02 2023 +0200

    Framing compat

commit d5e77e499b
Author: durch <durch@users.noreply.github.com>
Date:   Tue Apr 18 18:18:54 2023 +0200

    Framed encoding serde POC

commit f086f9c35a
Author: durch <durch@users.noreply.github.com>
Date:   Tue Apr 18 16:54:21 2023 +0200

    Experiment with serde
2023-05-01 15:50:59 +02:00
durch b4c525b664 Address PR comments 2023-05-01 15:48:36 +02:00
Drazen Urch 07291fd0ad Outfox rest compat (#3333)
* Outfox forwarding compat

* Tidy up interface

* PacketSize compat
2023-05-01 15:48:36 +02:00
durch 36c3604bb6 Outfox framing 2023-05-01 15:48:36 +02:00
durch 5a32f5da68 Framed encoding serde POC 2023-05-01 15:48:36 +02:00
durch 66f77d481c Experiment with serde 2023-05-01 15:48:36 +02:00
251 changed files with 7509 additions and 12290 deletions
-14
View File
@@ -1,14 +0,0 @@
---
name: 'Documentation'
about: Suggest a fix or enhancement to the documentation or developer portal content
title: "[DOCS]"
labels: documentation
assignees: mfahampshire
---
Is your issue either:
- [ ] a fix to existing documentation (e.g. fixing a broken link or incorrect command)
- [ ] an enhancement (e.g. adding a description for an undocumented feature)
Please briefly describe your issue:
-79
View File
@@ -1,79 +0,0 @@
name: Upload nyxd to CI
on:
workflow_dispatch:
jobs:
publish-nyxd:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Prepare build output directory
shell: bash
env:
OUTPUT_DIR: ci-builds/nyxd
run: |
rm -rf ci-builds || true
mkdir -p $OUTPUT_DIR
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools git
continue-on-error: true
- name: Update env variables to include go
run: |
sudo rm -rf /usr/local/go
curl https://dl.google.com/go/go1.19.2.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf -
cat <<'EOF' >>$HOME/.profile
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export GO111MODULE=on
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
EOF
source $HOME/.profile
- name: Verify Go is installed
run: go version
- name: Clone nyxd repo
run: |
git clone https://github.com/tommyv1987/nyxd
cd nyxd
git checkout release/v0.30.2
- name: Run nyxd
run: |
pwd
cd nyxd && make build
sleep 10
ls /home/runner/work/nym/nym/nyxd/build
- name: Prepare build output
shell: bash
env:
OUTPUT_DIR: ci-builds/nyxd
run: |
cp /home/runner/work/nym/nym/nyxd/build/nyxd $OUTPUT_DIR
WASMVM_SO=$(ldd /home/runner/work/nym/nym/nyxd/build/nyxd | grep "libwasm*" | awk '{ print $3 }')
ls $WASMVM_SO
sleep 3
cp $(echo $WASMVM_SO) $OUTPUT_DIR
- name: Deploy nyxd to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "ci-builds/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
EXCLUDE: "/dist/, /node_modules/"
+2 -1
View File
@@ -41,4 +41,5 @@ storybook-static
envs/qwerty.env
.parcel-cache
**/.DS_Store
cpu-cycles/libcpucycles/build
cpu-cycles/libcpucycles/build
foxyfox.env
-40
View File
@@ -4,46 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.19] (2023-05-16)
- nym-name-service endpoint in nym-api ([#3403])
- Implement key storage for WASM client using IndexedDB (for browser) ([#3329])
- Initial version of nym-name-service contract providing name aliases for nym-addresses ([#3274])
- Update Cargo.lock ([#3410])
[#3403]: https://github.com/nymtech/nym/issues/3403
[#3329]: https://github.com/nymtech/nym/issues/3329
[#3274]: https://github.com/nymtech/nym/issues/3274
[#3410]: https://github.com/nymtech/nym/pull/3410
## [v1.1.18] (2023-05-09)
- Implement heartbeat messages between socks5 proxy and network requester ([#3215])
[#3215]: https://github.com/nymtech/nym/issues/3215
## [v1.1.17] (2023-05-02)
- Add service-provider-directory-contract support to nym-cli ([#3334])
- Start using the node-testing-utils (implemented in #3270) in nym-api Network monitor to simplify the logic there ([#3312])
- Add service-provider-directory support to validator-client ([#3296])
- Allow topology injection in our WASM client ('test my node' feature) ([#3270])
- Expose service-provider-directory contract data in nym-api endpoints ([#3242])
- Cache service provider contract in nym-api ([#3241])
- Feature/1 1 17 docs ([#3370])
- adding a test for SP endpoint ([#3367])
- Feature/store cipher ([#3350])
[#3334]: https://github.com/nymtech/nym/issues/3334
[#3312]: https://github.com/nymtech/nym/issues/3312
[#3296]: https://github.com/nymtech/nym/issues/3296
[#3270]: https://github.com/nymtech/nym/issues/3270
[#3242]: https://github.com/nymtech/nym/issues/3242
[#3241]: https://github.com/nymtech/nym/issues/3241
[#3370]: https://github.com/nymtech/nym/pull/3370
[#3367]: https://github.com/nymtech/nym/pull/3367
[#3350]: https://github.com/nymtech/nym/pull/3350
## [v1.1.16] (2023-04-25)
- Explorer - Fix sorting function on Stake Saturation. It is currently working per page and not globally ([#3320])
Generated
+579 -718
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -37,7 +37,6 @@ members = [
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/name-service",
"common/cosmwasm-smart-contracts/service-provider-directory",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
@@ -61,14 +60,12 @@ members = [
"common/nymsphinx/forwarding",
"common/nymsphinx/framing",
"common/nymsphinx/params",
"common/nymsphinx/routing",
"common/nymsphinx/types",
"common/pemstore",
"common/socks5-client-core",
"common/socks5/proxy-helpers",
"common/socks5/requests",
"common/statistics",
"common/store-cipher",
"common/task",
"common/topology",
"common/types",
-2
View File
@@ -99,7 +99,6 @@ CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
VESTING_CONTRACT=$(CONTRACTS_OUT_DIR)/vesting_contract.wasm
MIXNET_CONTRACT=$(CONTRACTS_OUT_DIR)/mixnet_contract.wasm
SERVICE_PROVIDER_DIRECTORY_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_service_provider_directory.wasm
NAME_SERVICE_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_name_service.wasm
wasm: wasm-build wasm-opt
@@ -110,7 +109,6 @@ wasm-opt:
wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT)
wasm-opt --disable-sign-ext -Os $(MIXNET_CONTRACT) -o $(MIXNET_CONTRACT)
wasm-opt --disable-sign-ext -Os $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT)
wasm-opt --disable-sign-ext -Os $(NAME_SERVICE_CONTRACT) -o $(NAME_SERVICE_CONTRACT)
# -----------------------------------------------------------------------------
# Misc
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.19"
version = "1.1.16"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+12 -4
View File
@@ -16,6 +16,7 @@ use nym_client_core::client::received_buffer::{
};
use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
use nym_task::TaskManager;
use nym_validator_client::nyxd::QueryNyxdClient;
@@ -119,6 +120,7 @@ impl SocketClient {
self_address,
shared_lane_queue_lengths,
reply_controller_sender,
None,
);
websocket::Listener::new(config.get_listening_ip(), config.get_listening_port())
@@ -178,7 +180,10 @@ impl SocketClient {
Ok(started_client.task_manager)
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
pub async fn start_direct(
self,
packet_type: Option<PacketType>,
) -> Result<DirectClient, ClientError> {
if self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
@@ -224,6 +229,7 @@ impl SocketClient {
reconstructed_receiver,
address,
shutdown_notifier: started_client.task_manager,
packet_type,
})
}
}
@@ -237,6 +243,7 @@ pub struct DirectClient {
// we need to keep reference to this guy otherwise things will start dropping
shutdown_notifier: TaskManager,
packet_type: Option<PacketType>,
}
impl DirectClient {
@@ -257,7 +264,7 @@ impl DirectClient {
/// well enough in local tests)
pub async fn send_regular_message(&mut self, recipient: Recipient, message: Vec<u8>) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_regular(recipient, message, lane);
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
self.client_input
.input_sender
@@ -276,7 +283,8 @@ impl DirectClient {
reply_surbs: u32,
) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane);
let input_msg =
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
self.client_input
.input_sender
@@ -290,7 +298,7 @@ impl DirectClient {
/// well enough in local tests)
pub async fn send_reply(&mut self, recipient_tag: AnonymousSenderTag, message: Vec<u8>) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_reply(recipient_tag, message, lane);
let input_msg = InputMessage::new_reply(recipient_tag, message, lane, self.packet_type);
self.client_input
.input_sender
+10 -3
View File
@@ -14,6 +14,7 @@ use nym_client_core::client::{
use nym_client_websocket_requests::{requests::ClientRequest, responses::ServerResponse};
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType;
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::connections::{
ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane,
@@ -41,6 +42,7 @@ pub(crate) struct HandlerBuilder {
self_full_address: Recipient,
lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>,
}
impl HandlerBuilder {
@@ -51,6 +53,7 @@ impl HandlerBuilder {
self_full_address: &Recipient,
lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>,
) -> Self {
Self {
msg_input,
@@ -59,6 +62,7 @@ impl HandlerBuilder {
self_full_address: *self_full_address,
lane_queue_lengths,
reply_controller_sender,
packet_type,
}
}
@@ -73,6 +77,7 @@ impl HandlerBuilder {
received_response_type: Default::default(),
lane_queue_lengths: self.lane_queue_lengths.clone(),
reply_controller_sender: self.reply_controller_sender.clone(),
packet_type: self.packet_type,
}
}
}
@@ -86,6 +91,7 @@ pub(crate) struct Handler {
received_response_type: ReceivedResponseType,
lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>,
}
impl Drop for Handler {
@@ -160,7 +166,7 @@ impl Handler {
});
// the ack control is now responsible for chunking, etc.
let input_msg = InputMessage::new_regular(recipient, message, lane);
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
self.msg_input
.send(input_msg)
.await
@@ -191,7 +197,8 @@ impl Handler {
TransmissionLane::ConnectionId(id)
});
let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane);
let input_msg =
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
self.msg_input
.send(input_msg)
.await
@@ -218,7 +225,7 @@ impl Handler {
TransmissionLane::ConnectionId(id)
});
let input_msg = InputMessage::new_reply(recipient_tag, message, lane);
let input_msg = InputMessage::new_reply(recipient_tag, message, lane, self.packet_type);
self.msg_input
.send(input_msg)
.await
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.19"
version = "1.1.16"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+1
View File
@@ -91,6 +91,7 @@ impl From<Init> for OverrideConfig {
no_cover: init_config.no_cover,
nyxd_urls: init_config.nyxd_urls,
enabled_credentials_mode: init_config.enabled_credentials_mode,
outfox: false,
}
}
}
+8
View File
@@ -10,6 +10,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::{NymConfig, OptionalSet};
use nym_socks5_client_core::config::old_config_v1_1_13::OldConfigV1_1_13;
use nym_socks5_client_core::config::{BaseConfig, Config};
use nym_sphinx::params::PacketType;
use std::error::Error;
pub mod init;
@@ -64,6 +65,7 @@ pub(crate) struct OverrideConfig {
no_cover: bool,
nyxd_urls: Option<Vec<url::Url>>,
enabled_credentials_mode: Option<bool>,
outfox: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
@@ -80,9 +82,15 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
}
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
let packet_type = if args.outfox {
PacketType::Outfox
} else {
PacketType::Mix
};
config
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
.with_base(BaseConfig::with_packet_type, packet_type)
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port)
.with_optional_custom_env_ext(
+4
View File
@@ -67,6 +67,9 @@ pub(crate) struct Run {
/// with bandwidth credential requirement.
#[clap(long, hide = true)]
enabled_credentials_mode: Option<bool>,
#[clap(long, hide = true, action)]
outfox: bool,
}
impl From<Run> for OverrideConfig {
@@ -79,6 +82,7 @@ impl From<Run> for OverrideConfig {
no_cover: run_config.no_cover,
nyxd_urls: run_config.nyxd_urls,
enabled_credentials_mode: run_config.enabled_credentials_mode,
outfox: run_config.outfox,
}
}
}
+12 -23
View File
@@ -34,14 +34,7 @@ import {
StakeSaturationResponse,
UnbondedMixnodeResponse,
VestingAccountInfo,
ContractState,
VestingAccountsCoinPaged,
VestingAccountsPaged,
DelegationTimes,
Delegations,
Period,
VestingAccountNode,
DelegationBlock,
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode, DelegationBlock
} from '@nymproject/types';
import QueryClient from './query-client';
import SigningClient, { ISigningClient } from './signing-client';
@@ -214,7 +207,7 @@ export default class ValidatorClient implements INymClient {
let mixNodes: UnbondedMixnodeResponse[] = [];
const limit = 50;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
this.mixnetContract,
@@ -237,7 +230,7 @@ export default class ValidatorClient implements INymClient {
let mixNodes: MixNodeBond[] = [];
const limit = 50;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
this.mixnetContract,
@@ -259,7 +252,7 @@ export default class ValidatorClient implements INymClient {
let mixNodes: MixNodeDetails[] = [];
const limit = 50;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
this.mixnetContract,
@@ -291,7 +284,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
this.mixnetContract,
@@ -314,7 +307,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
this.mixnetContract,
@@ -337,7 +330,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
this.mixnetContract,
@@ -525,9 +518,11 @@ export default class ValidatorClient implements INymClient {
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
}
// VESTING
// VESTING
// TODO - MOVE TO A DIFFERENT FILE
public async getVestingAccountsPaged(): Promise<VestingAccountsPaged> {
return this.client.getVestingAccountsPaged(this.vestingContract);
}
@@ -613,9 +608,9 @@ export default class ValidatorClient implements INymClient {
}
public async getDelegation(address: string, mix_id: number): Promise<DelegationBlock> {
return this.client.getDelegation(this.vestingContract, address, mix_id);
return this.client.getDelegation(this.vestingContract, address, mix_id );
}
public async getTotalDelegationAmount(address: string, mix_id: number, block_timestamp_sec: number): Promise<Coin> {
return this.client.getTotalDelegationAmount(this.vestingContract, address, mix_id, block_timestamp_sec);
}
@@ -623,10 +618,4 @@ export default class ValidatorClient implements INymClient {
public async getCurrentVestingPeriod(address: string): Promise<Period> {
return this.client.getCurrentVestingPeriod(this.vestingContract, address);
}
// SIMULATE
public async simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]) {
return (this.client as SigningClient).simulateSend(signingAddress, from, to, amount);
}
}
+9 -48
View File
@@ -40,18 +40,9 @@ import {
RewardingParams,
UnbondedMixnodeResponse,
VestingAccountInfo,
ContractState,
VestingAccountsCoinPaged,
VestingAccountsPaged,
DelegationTimes,
Delegations,
Period,
VestingAccountNode,
DelegationBlock,
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode, DelegationBlock
} from '@nymproject/types';
import NymApiQuerier from './nym-api-querier';
import { makeBankMsgSend } from './utils';
import { ISimulateClient } from './types/simulate';
// methods exposed by `SigningCosmWasmClient`
export interface ICosmWasmSigning {
@@ -157,7 +148,7 @@ export interface INymSigning {
clientAddress: string;
}
export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSigning, ISimulateClient {
export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSigning {
bondMixNode(
mixnetContractAddress: string,
mixNode: MixNode,
@@ -520,11 +511,11 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
);
}
// vesting related
// vesting related
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
}
};
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
@@ -578,10 +569,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
}
getOriginalVestingDetails(
vestingContractAddress: string,
vestingAccountAddress: string,
): Promise<OriginalVestingResponse> {
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
}
@@ -601,11 +589,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
}
getDelegationTimes(
vestingContractAddress: string,
mix_id: number,
delegatorAddress: string,
): Promise<DelegationTimes> {
getDelegationTimes(vestingContractAddress: string, mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
}
@@ -613,38 +597,15 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
}
getDelegation(
vestingContractAddress: string,
vestingAccountAddress: string,
mix_id: number,
): Promise<DelegationBlock> {
getDelegation(vestingContractAddress: string, vestingAccountAddress: string, mix_id: number): Promise<DelegationBlock> {
return this.nyxdQuerier.getDelegation(vestingContractAddress, vestingAccountAddress, mix_id);
}
getTotalDelegationAmount(
vestingContractAddress: string,
vestingAccountAddress: string,
mix_id: number,
block_timestamp_sec: number,
): Promise<Coin> {
return this.nyxdQuerier.getTotalDelegationAmount(
vestingContractAddress,
vestingAccountAddress,
mix_id,
block_timestamp_sec,
);
getTotalDelegationAmount(vestingContractAddress: string, vestingAccountAddress: string, mix_id: number, block_timestamp_sec: number): Promise<Coin> {
return this.nyxdQuerier.getTotalDelegationAmount(vestingContractAddress, vestingAccountAddress, mix_id, block_timestamp_sec);
}
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
}
// simulation
// TODO consider adding multipling factor
simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]) {
const sendMsg = makeBankMsgSend(from, to, amount);
return this.simulate(signingAddress, [sendMsg], 'simulate send tx');
}
}
@@ -1,31 +0,0 @@
import expect from 'expect';
import ValidatorClient from '../..';
const dotenv = require('dotenv');
dotenv.config();
// TODO: implement for QA with .env for mnemonics
describe('Simualtions', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connect(
process.env.mnemonic || '',
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
it('can simulate sending tokens', async () => {
const res = await client.simulateSend(client.address, client.address, client.address, [
{ amount: '400000', denom: 'unym' },
]);
expect(typeof res).toBe('number');
}).timeout(10000);
});
-5
View File
@@ -1,5 +0,0 @@
import { Coin } from '@cosmjs/proto-signing';
export interface ISimulateClient {
simulateSend(signingAddress: string, from: string, to: string, amount: Coin[]): Promise<number>;
}
+1 -1
View File
@@ -64,7 +64,7 @@ wee_alloc = { version = "0.4", optional = true }
wasm-bindgen-test = "0.3"
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
wasm-opt = true
[profile.release]
lto = true
+15 -4
View File
@@ -13,7 +13,7 @@ use std::sync::Arc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, simple_js_error};
use wasm_utils::{console_log, js_error, simple_js_error};
#[wasm_bindgen]
pub struct NymClientTestRequest {
@@ -142,9 +142,20 @@ impl WasmTopologyExt for Arc<ClientState> {
return Err(WasmClientError::NonExistentMixnode { mixnode_identity }.into());
};
let ext = WasmTestMessageExt::new(test_id);
let test_msgs = NodeTestMessage::mix_plaintexts(mix, num_test_packets, ext)
.map_err(WasmClientError::from)?;
let mut test_msgs = Vec::with_capacity(num_test_packets as usize);
for i in 1..=num_test_packets {
let msg = NodeTestMessage::new_mix(
mix,
i,
num_test_packets,
WasmTestMessageExt::new(test_id),
);
let serialized = match msg.as_bytes() {
Ok(bytes) => bytes,
Err(err) => return Err(js_error!("failed to serialize test message: {err}")),
};
test_msgs.push(serialized);
}
let mut updated = current_topology.clone();
updated.set_mixes_in_layer(mix.layer.into(), vec![mix.to_owned()]);
+11 -4
View File
@@ -21,6 +21,7 @@ use nym_client_core::config::{
CoverTraffic, DebugConfig, GatewayEndpointConfig, Topology, Traffic,
};
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
use nym_task::TaskManager;
use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
@@ -49,6 +50,7 @@ pub struct NymClient {
// even though we don't use graceful shutdowns, other components rely on existence of this struct
// and if it's dropped, everything will start going offline
_task_manager: TaskManager,
packet_type: Option<PacketType>,
}
#[wasm_bindgen]
@@ -67,6 +69,7 @@ pub struct NymClientBuilder {
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
packet_type: Option<PacketType>,
}
#[wasm_bindgen]
@@ -82,6 +85,7 @@ impl NymClientBuilder {
on_message,
bandwidth_controller: None,
disabled_credentials: true,
packet_type: None,
}
}
@@ -132,6 +136,7 @@ impl NymClientBuilder {
on_message,
bandwidth_controller: None,
disabled_credentials: true,
packet_type: None,
}
}
@@ -189,6 +194,7 @@ impl NymClientBuilder {
client_state: Arc::new(started_client.client_state),
_full_topology: None,
_task_manager: started_client.task_manager,
packet_type: self.packet_type,
})
}
@@ -255,7 +261,7 @@ impl NymClient {
let input_msgs = request
.test_msgs
.into_iter()
.map(|p| InputMessage::new_regular(recipient, p, lane))
.map(|p| InputMessage::new_regular(recipient, p, lane, None))
.collect();
self.client_input.send_messages(input_msgs)
@@ -275,7 +281,7 @@ impl NymClient {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_regular(recipient, message, lane);
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
self.client_input.send_message(input_msg)
}
@@ -302,7 +308,8 @@ impl NymClient {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane);
let input_msg =
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
self.client_input.send_message(input_msg)
}
@@ -320,7 +327,7 @@ impl NymClient {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_reply(sender_tag, message, lane);
let input_msg = InputMessage::new_reply(sender_tag, message, lane, self.packet_type);
self.client_input.send_message(input_msg)
}
}
@@ -1,10 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::tester::helpers::{NodeTestResult, WasmTestMessageExt};
use crate::tester::helpers::NodeTestResult;
use crate::tester::NodeTestMessage;
use futures::StreamExt;
use nym_node_tester_utils::processor::Received;
use nym_node_tester_utils::receiver::ReceivedReceiver;
use nym_node_tester_utils::receiver::{Received, ReceivedReceiver};
use nym_sphinx::chunking::fragment::FragmentIdentifier;
use std::collections::HashSet;
use std::time::Duration;
@@ -21,7 +21,7 @@ pub(crate) struct EphemeralTestReceiver<'a> {
duplicate_acks: u32,
timeout_duration: Duration,
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver<WasmTestMessageExt>>,
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver>,
}
impl<'a> EphemeralTestReceiver<'a> {
@@ -38,7 +38,7 @@ impl<'a> EphemeralTestReceiver<'a> {
pub(crate) fn new(
sent_packets: u32,
expected_acks: HashSet<FragmentIdentifier>,
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver<WasmTestMessageExt>>,
receiver_permit: AsyncMutexGuard<'a, ReceivedReceiver>,
timeout: Duration,
) -> Self {
EphemeralTestReceiver {
@@ -53,18 +53,23 @@ impl<'a> EphemeralTestReceiver<'a> {
}
}
fn on_next_received_packet(&mut self, packet: Option<Received<WasmTestMessageExt>>) -> bool {
fn on_next_received_packet(&mut self, packet: Option<Received>) -> bool {
let Some(received_packet) = packet else {
// can't do anything more...
console_error!("packet receiver has stopped processing results!");
return true
};
match received_packet {
Received::Message(msg) => {
if !self.received_valid_messages.insert(msg.msg_id) {
self.duplicate_packets += 1;
Received::Message(msg) => match NodeTestMessage::try_recover(msg) {
Ok(test_msg) => {
if !self.received_valid_messages.insert(test_msg.msg_id) {
self.duplicate_packets += 1;
}
}
}
Err(err) => {
console_warn!("failed to recover test message from received packet: {err}")
}
},
Received::Ack(frag_id) => {
if self.expected_acks.contains(&frag_id) {
if !self.received_valid_acks.insert(frag_id) {
+4 -5
View File
@@ -4,8 +4,7 @@
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
#![allow(clippy::drop_non_drop)]
use nym_node_tester_utils::processor::Received;
use nym_node_tester_utils::receiver::ReceivedReceiver;
use nym_node_tester_utils::receiver::{Received, ReceivedReceiver};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::sync::atomic::{AtomicBool, Ordering};
@@ -15,10 +14,10 @@ use wasm_bindgen::prelude::*;
use wasm_utils::{console_log, console_warn};
#[derive(Clone)]
pub(super) struct ReceivedReceiverWrapper(Arc<AsyncMutex<ReceivedReceiver<WasmTestMessageExt>>>);
pub(super) struct ReceivedReceiverWrapper(Arc<AsyncMutex<ReceivedReceiver>>);
impl ReceivedReceiverWrapper {
pub(super) fn new(inner: ReceivedReceiver<WasmTestMessageExt>) -> Self {
pub(super) fn new(inner: ReceivedReceiver) -> Self {
ReceivedReceiverWrapper(Arc::new(AsyncMutex::new(inner)))
}
@@ -37,7 +36,7 @@ impl ReceivedReceiverWrapper {
}
}
pub(super) async fn lock(&self) -> AsyncMutexGuard<'_, ReceivedReceiver<WasmTestMessageExt>> {
pub(super) async fn lock(&self) -> AsyncMutexGuard<'_, ReceivedReceiver> {
self.0.lock().await
}
}
+2 -7
View File
@@ -162,7 +162,7 @@ impl NymNodeTesterBuilder {
let tester = NodeTester::new(
rng,
self.base_topology,
Some(address(&self.key_manager, gateway_identity)),
address(&self.key_manager, gateway_identity),
PacketSize::default(),
Duration::from_millis(5),
Duration::from_millis(5),
@@ -266,12 +266,7 @@ impl NymNodeTester {
let test_ext = WasmTestMessageExt::new(test_nonce);
let mut tester_permit = self.tester.lock().expect("mutex got poisoned");
tester_permit
.existing_identity_mixnode_test_packets(
mixnode_identity,
test_ext,
num_test_packets,
None,
)
.existing_identity_mixnode_test_packets(mixnode_identity, test_ext, num_test_packets)
.map_err(Into::into)
}
@@ -409,7 +409,7 @@ where
Ok(())
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
// controller for sending packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
@@ -524,11 +524,11 @@ where
task_manager.subscribe(),
);
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// 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.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
let message_sender =
Self::start_mix_traffic_controller(gateway_client, task_manager.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
@@ -550,7 +550,7 @@ where
shared_topology_accessor.clone(),
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
message_sender.clone(),
reply_storage,
reply_controller_sender.clone(),
reply_controller_receiver,
@@ -569,7 +569,7 @@ where
self.key_manager.ack_key(),
self_address,
shared_topology_accessor.clone(),
sphinx_message_sender,
message_sender,
task_manager.subscribe(),
);
}
@@ -45,7 +45,7 @@ where
#[cfg(target_arch = "wasm32")]
next_delay: Pin<Box<wasm_timer::Delay>>,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// Channel used for sending prepared nym packets to `MixTrafficController` that sends them
/// out to the network without any further delays.
mix_tx: BatchMixMessageSender,
@@ -4,6 +4,7 @@
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
pub type InputMessageSender = tokio::sync::mpsc::Sender<InputMessage>;
@@ -53,18 +54,49 @@ pub enum InputMessage {
data: Vec<u8>,
lane: TransmissionLane,
},
MessageWrapper {
message: Box<InputMessage>,
packet_type: PacketType,
},
}
impl InputMessage {
pub fn new_premade(msgs: Vec<MixPacket>, lane: TransmissionLane) -> Self {
InputMessage::Premade { msgs, lane }
pub fn new_premade(
msgs: Vec<MixPacket>,
lane: TransmissionLane,
packet_type: PacketType,
) -> Self {
let message = InputMessage::Premade { msgs, lane };
if packet_type == PacketType::Mix {
message
} else {
InputMessage::new_wrapper(message, packet_type)
}
}
pub fn new_regular(recipient: Recipient, data: Vec<u8>, lane: TransmissionLane) -> Self {
InputMessage::Regular {
pub fn new_wrapper(message: InputMessage, packet_type: PacketType) -> Self {
InputMessage::MessageWrapper {
message: Box::new(message),
packet_type,
}
}
pub fn new_regular(
recipient: Recipient,
data: Vec<u8>,
lane: TransmissionLane,
packet_type: Option<PacketType>,
) -> Self {
let message = InputMessage::Regular {
recipient,
data,
lane,
};
if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type)
} else {
message
}
}
@@ -73,12 +105,18 @@ impl InputMessage {
data: Vec<u8>,
reply_surbs: u32,
lane: TransmissionLane,
packet_type: Option<PacketType>,
) -> Self {
InputMessage::Anonymous {
let message = InputMessage::Anonymous {
recipient,
data,
reply_surbs,
lane,
};
if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type)
} else {
message
}
}
@@ -86,11 +124,17 @@ impl InputMessage {
recipient_tag: AnonymousSenderTag,
data: Vec<u8>,
lane: TransmissionLane,
packet_type: Option<PacketType>,
) -> Self {
InputMessage::Reply {
let message = InputMessage::Reply {
recipient_tag,
data,
lane,
};
if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type)
} else {
message
}
}
@@ -100,6 +144,7 @@ impl InputMessage {
| InputMessage::Anonymous { lane, .. }
| InputMessage::Reply { lane, .. }
| InputMessage::Premade { lane, .. } => lane,
InputMessage::MessageWrapper { message, .. } => message.lane(),
}
}
}
+3 -3
View File
@@ -39,15 +39,15 @@ where
pub fn new(
gateway_client: GatewayClient<C, St>,
) -> (MixTrafficController<C, St>, BatchMixMessageSender) {
let (sphinx_message_sender, sphinx_message_receiver) =
let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
(
MixTrafficController {
gateway_client,
mix_rx: sphinx_message_receiver,
mix_rx: message_receiver,
consecutive_gateway_failure_count: 0,
},
sphinx_message_sender,
message_sender,
)
}
@@ -9,6 +9,7 @@ use log::*;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
use rand::{CryptoRng, Rng};
@@ -71,10 +72,11 @@ where
recipient: Recipient,
content: Vec<u8>,
lane: TransmissionLane,
packet_type: PacketType,
) {
if let Err(err) = self
.message_handler
.try_send_plain_message(recipient, content, lane)
.try_send_plain_message(recipient, content, lane, packet_type)
.await
{
warn!("failed to send a plain message - {err}")
@@ -87,10 +89,11 @@ where
content: Vec<u8>,
reply_surbs: u32,
lane: TransmissionLane,
packet_type: PacketType,
) {
if let Err(err) = self
.message_handler
.try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane)
.try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane, packet_type)
.await
{
warn!("failed to send a repliable message - {err}")
@@ -103,14 +106,17 @@ where
recipient,
data,
lane,
} => self.handle_plain_message(recipient, data, lane).await,
} => {
self.handle_plain_message(recipient, data, lane, PacketType::Mix)
.await
}
InputMessage::Anonymous {
recipient,
data,
reply_surbs,
lane,
} => {
self.handle_repliable_message(recipient, data, reply_surbs, lane)
self.handle_repliable_message(recipient, data, reply_surbs, lane, PacketType::Mix)
.await
}
InputMessage::Reply {
@@ -121,6 +127,40 @@ where
self.handle_reply(recipient_tag, data, lane).await;
}
InputMessage::Premade { msgs, lane } => self.handle_premade_packets(msgs, lane).await,
InputMessage::MessageWrapper {
message,
packet_type,
} => match *message {
InputMessage::Regular {
recipient,
data,
lane,
} => {
self.handle_plain_message(recipient, data, lane, packet_type)
.await
}
InputMessage::Anonymous {
recipient,
data,
reply_surbs,
lane,
} => {
self.handle_repliable_message(recipient, data, reply_surbs, lane, packet_type)
.await
}
InputMessage::Reply {
recipient_tag,
data,
lane,
} => {
self.handle_reply(recipient_tag, data, lane).await;
}
InputMessage::Premade { msgs, lane } => {
self.handle_premade_packets(msgs, lane).await
}
// MessageWrappers can't be nested
InputMessage::MessageWrapper { .. } => unimplemented!(),
},
};
}
@@ -11,9 +11,9 @@ use crate::client::real_messages_control::real_traffic_stream::RealMessage;
use crate::client::replies::reply_controller::ReplyControllerSender;
use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::chunking::fragment::Fragment;
use nym_sphinx::preparer::PreparedFragment;
use nym_sphinx::{addressing::clients::Recipient, params::PacketType};
use nym_task::connections::TransmissionLane;
use rand::{CryptoRng, Rng};
use std::sync::{Arc, Weak};
@@ -51,8 +51,10 @@ where
) -> Result<PreparedFragment, PreparationError> {
debug!("retransmitting normal packet...");
// TODO: Figure out retransmission packet type signaling
self.message_handler
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data)
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, PacketType::Mix)
.await
}
@@ -15,7 +15,7 @@ use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessa
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
use nym_sphinx::message::NymMessage;
use nym_sphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx::params::{PacketSize, PacketType, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
use nym_sphinx::Delay;
use nym_task::connections::TransmissionLane;
@@ -417,9 +417,10 @@ where
recipient: Recipient,
message: Vec<u8>,
lane: TransmissionLane,
packet_type: PacketType,
) -> Result<(), PreparationError> {
let message = NymMessage::new_plain(message);
self.try_split_and_send_non_reply_message(message, recipient, lane)
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type)
.await
}
@@ -428,6 +429,7 @@ where
message: NymMessage,
recipient: Recipient,
lane: TransmissionLane,
packet_type: PacketType,
) -> Result<(), PreparationError> {
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised
debug_assert!(!matches!(message, NymMessage::Reply(_)));
@@ -453,6 +455,7 @@ where
topology,
&self.config.ack_key,
&recipient,
&packet_type,
)?;
let real_message = RealMessage::new(
@@ -476,6 +479,7 @@ where
&mut self,
recipient: Recipient,
amount: u32,
packet_type: PacketType,
) -> Result<(), PreparationError> {
let sender_tag = self.get_or_create_sender_tag(&recipient);
let (reply_surbs, reply_keys) =
@@ -490,6 +494,7 @@ where
message,
recipient,
TransmissionLane::AdditionalReplySurbs,
packet_type,
)
.await?;
@@ -505,6 +510,7 @@ where
message: Vec<u8>,
num_reply_surbs: u32,
lane: TransmissionLane,
packet_type: PacketType,
) -> Result<(), SurbWrappedPreparationError> {
let sender_tag = self.get_or_create_sender_tag(&recipient);
let (reply_surbs, reply_keys) = self
@@ -514,7 +520,7 @@ where
let message =
NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs));
self.try_split_and_send_non_reply_message(message, recipient, lane)
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type)
.await?;
log::trace!("storing {} reply keys", reply_keys.len());
@@ -527,13 +533,20 @@ where
&mut self,
recipient: Recipient,
chunk: Fragment,
packet_type: PacketType,
) -> Result<PreparedFragment, PreparationError> {
let topology_permit = self.topology_access.get_read_permit().await;
let topology = self.get_topology(&topology_permit)?;
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk, topology, &self.config.ack_key, &recipient)
.prepare_chunk_for_sending(
chunk,
topology,
&self.config.ack_key,
&recipient,
&packet_type,
)
.unwrap();
Ok(prepared_fragment)
@@ -569,6 +582,7 @@ where
topology,
&self.config.ack_key,
reply_surb,
PacketType::Mix,
)
.unwrap()
})
@@ -588,7 +602,13 @@ where
let prepared_fragment = self
.message_preparer
.prepare_reply_chunk_for_sending(chunk, topology, &self.config.ack_key, reply_surb)
.prepare_reply_chunk_for_sending(
chunk,
topology,
&self.config.ack_key,
reply_surb,
PacketType::Mix,
)
.unwrap();
Ok(prepared_fragment)
@@ -92,7 +92,7 @@ where
// messages.
sending_delay_controller: SendingDelayController,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// Channel used for sending prepared packets to `MixTrafficController` that sends them
/// out to the network without any further delays.
mix_tx: BatchMixMessageSender,
@@ -136,7 +136,7 @@ impl From<PreparedFragment> for RealMessage {
impl RealMessage {
pub(crate) fn packet_size(&self) -> usize {
self.mix_packet.sphinx_packet().len()
self.mix_packet.packet().len()
}
pub(crate) fn new(mix_packet: MixPacket, fragment_id: Option<FragmentIdentifier>) -> Self {
@@ -386,7 +386,7 @@ where
// On every iteration we get new messages from upstream. Given that these come bunched
// in `Vec`, this ensures that on average we will fetch messages faster than we can
// send, which is a condition for being able to multiplex sphinx packets from multiple
// send, which is a condition for being able to multiplex packets from multiple
// data streams.
match Pin::new(&mut self.real_receiver).poll_recv(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
@@ -512,7 +512,11 @@ where
let to_send = min(remaining, 100);
if let Err(err) = self
.message_handler
.try_send_additional_reply_surbs(recipient, to_send)
.try_send_additional_reply_surbs(
recipient,
to_send,
nym_sphinx::params::PacketType::Mix,
)
.await
{
warn!("failed to send additional surbs to {recipient} - {err}");
@@ -28,7 +28,7 @@ impl SizedData for RealMessage {
impl SizedData for Fragment {
fn data_size(&self) -> usize {
// note that raw `Fragment` is smaller than sphinx packet payload
// note that raw `Fragment` is smaller than packet payload
// as it doesn't include surb-ack or the [shared] key materials
self.payload_size()
}
+13 -1
View File
@@ -3,7 +3,7 @@
use nym_config::defaults::NymNetworkDetails;
use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
use nym_sphinx::params::PacketSize;
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
@@ -217,6 +217,11 @@ impl<T> Config<T> {
self
}
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
self.client.packet_type = Some(packet_type);
self
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages
@@ -404,6 +409,10 @@ impl<T> Config<T> {
pub fn get_maximum_reply_key_age(&self) -> Duration {
self.debug.reply_surbs.maximum_reply_key_age
}
pub fn get_packet_type(&self) -> PacketType {
self.client.packet_type.unwrap_or(PacketType::Mix)
}
}
impl<T: NymConfig> Default for Config<T> {
@@ -518,6 +527,8 @@ pub struct Client<T> {
#[serde(skip)]
pub super_struct: PhantomData<T>,
pub packet_type: Option<PacketType>,
}
impl<T: NymConfig> Default for Client<T> {
@@ -556,6 +567,7 @@ impl<T: NymConfig> Default for Client<T> {
reply_surb_database_path: Default::default(),
nym_root_directory: T::default_root_directory(),
super_struct: Default::default(),
packet_type: Default::default(),
}
}
}
@@ -210,8 +210,8 @@ impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
database_path: value.client.database_path,
reply_surb_database_path: value.client.reply_surb_database_path,
nym_root_directory: value.client.nym_root_directory,
super_struct: PhantomData,
packet_type: Some(nym_sphinx::params::PacketType::Mix),
},
logging: value.logging,
debug: value.debug.into(),
@@ -605,7 +605,7 @@ where
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
packets
.iter()
.map(|packet| packet.sphinx_packet().len())
.map(|packet| packet.packet().len())
.sum::<usize>() as i64
}
@@ -686,9 +686,9 @@ where
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
if (mix_packet.packet().len() as i64) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth(
mix_packet.sphinx_packet().len() as i64,
mix_packet.packet().len() as i64,
self.bandwidth_remaining,
));
}
+15 -18
View File
@@ -4,10 +4,11 @@
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_sphinx::framing::codec::SphinxCodec;
use nym_sphinx::framing::packet::FramedSphinxPacket;
use nym_sphinx::params::PacketMode;
use nym_sphinx::{addressing::nodes::NymNodeRoutingAddress, SphinxPacket};
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket;
use nym_sphinx::params::PacketType;
use nym_sphinx::NymPacket;
use std::collections::HashMap;
use std::io;
use std::net::SocketAddr;
@@ -50,8 +51,8 @@ pub trait SendWithoutResponse {
fn send_without_response(
&mut self,
address: NymNodeRoutingAddress,
packet: SphinxPacket,
packet_mode: PacketMode,
packet: NymPacket,
packet_type: PacketType,
) -> io::Result<()>;
}
@@ -61,12 +62,12 @@ pub struct Client {
}
struct ConnectionSender {
channel: mpsc::Sender<FramedSphinxPacket>,
channel: mpsc::Sender<FramedNymPacket>,
current_reconnection_attempt: Arc<AtomicU32>,
}
impl ConnectionSender {
fn new(channel: mpsc::Sender<FramedSphinxPacket>) -> Self {
fn new(channel: mpsc::Sender<FramedNymPacket>) -> Self {
ConnectionSender {
channel,
current_reconnection_attempt: Arc::new(AtomicU32::new(0)),
@@ -84,7 +85,7 @@ impl Client {
async fn manage_connection(
address: SocketAddr,
receiver: mpsc::Receiver<FramedSphinxPacket>,
receiver: mpsc::Receiver<FramedNymPacket>,
connection_timeout: Duration,
current_reconnection: &AtomicU32,
) {
@@ -96,7 +97,7 @@ impl Client {
debug!("Managed to establish connection to {}", address);
// if we managed to connect, reset the reconnection count (whatever it might have been)
current_reconnection.store(0, Ordering::Release);
Framed::new(stream, SphinxCodec)
Framed::new(stream, NymCodec)
}
Err(err) => {
debug!(
@@ -148,11 +149,7 @@ impl Client {
}
}
fn make_connection(
&mut self,
address: NymNodeRoutingAddress,
pending_packet: FramedSphinxPacket,
) {
fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
let (mut sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
// this CAN'T fail because we just created the channel which has a non-zero capacity
@@ -200,12 +197,12 @@ impl SendWithoutResponse for Client {
fn send_without_response(
&mut self,
address: NymNodeRoutingAddress,
packet: SphinxPacket,
packet_mode: PacketMode,
packet: NymPacket,
packet_type: PacketType,
) -> io::Result<()> {
trace!("Sending packet to {:?}", address);
let framed_packet =
FramedSphinxPacket::new(packet, packet_mode, self.config.use_legacy_version);
FramedNymPacket::new(packet, packet_type, self.config.use_legacy_version);
if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) {
@@ -59,14 +59,14 @@ impl PacketForwarder {
trace!("Going to forward packet to {:?}", mix_packet.next_hop());
let next_hop = mix_packet.next_hop();
let packet_mode = mix_packet.packet_mode();
let sphinx_packet = mix_packet.into_sphinx_packet();
let packet_type = mix_packet.packet_type();
let packet = mix_packet.into_packet();
// we don't care about responses, we just want to fire packets
// as quickly as possible
if let Err(err) =
self.mixnet_client
.send_without_response(next_hop, sphinx_packet, packet_mode)
.send_without_response(next_hop, packet, packet_type)
{
debug!("failed to forward the packet - {err}")
}
@@ -17,9 +17,7 @@ nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-con
nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-name-service-common = { path = "../../cosmwasm-smart-contracts/name-service" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
nym-vesting-contract = { path = "../../../contracts/vesting" }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
@@ -65,14 +63,6 @@ name = "offline_signing"
# (traits would need to be moved around and refactored themselves)
required-features = ["nyxd-client"]
[[example]]
name = "query_service_provider_directory"
required-features = ["nyxd-client"]
[[example]]
name = "query_name_service"
required-features = ["nyxd-client"]
[features]
nyxd-client = [
"async-trait",
@@ -1,35 +0,0 @@
use std::str::FromStr;
use cosmrs::AccountId;
use nym_name_service_common::Address;
use nym_network_defaults::{setup_env, NymNetworkDetails};
use nym_validator_client::nyxd::traits::NameServiceQueryClient;
#[tokio::main]
async fn main() {
setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap()));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
let client = nym_validator_client::Client::new_query(config).unwrap();
let config = client.nyxd.get_name_service_config().await.unwrap();
println!("config: {config:?}");
let names_paged = client.nyxd.get_names_paged(None, None).await.unwrap();
println!("names (paged): {names_paged:#?}");
let names = client.nyxd.get_all_names().await.unwrap();
println!("names: {names:#?}");
let owner = AccountId::from_str("n1hmf957kc7arcd39rl7xq8l0a4zyg7kxnv7su87").unwrap();
let names_by_owner = client.nyxd.get_names_by_owner(owner).await.unwrap();
println!("names (by owner): {names_by_owner:#?}");
let nym_address = Address::new("client_id.client_key@gateway_id");
let names_by_address = client.nyxd.get_names_by_address(nym_address).await.unwrap();
println!("names (by address): {names_by_address:#?}");
let service_info = client.nyxd.get_name_entry(1).await;
println!("service info: {service_info:#?}");
}
@@ -1,43 +0,0 @@
use std::str::FromStr;
use cosmrs::AccountId;
use nym_network_defaults::{setup_env, NymNetworkDetails};
use nym_service_provider_directory_common::NymAddress;
use nym_validator_client::nyxd::traits::SpDirectoryQueryClient;
#[tokio::main]
async fn main() {
setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap()));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
let client = nym_validator_client::Client::new_query(config).unwrap();
let config = client.nyxd.get_service_config().await.unwrap();
println!("config: {config:?}");
let services_paged = client.nyxd.get_services_paged(None, None).await.unwrap();
println!("services (paged): {services_paged:#?}");
let services = client.nyxd.get_all_services().await.unwrap();
println!("services: {services:#?}");
let announcer = AccountId::from_str("n1hmf957kc7arcd39rl7xq8l0a4zyg7kxnv7su87").unwrap();
let services_by_announcer = client
.nyxd
.get_services_by_announcer(announcer)
.await
.unwrap();
println!("services (by announcer): {services_by_announcer:#?}");
let nym_address = NymAddress::new("foo.bar@gateway");
let services_by_nym_address = client
.nyxd
.get_services_by_nym_address(nym_address)
.await
.unwrap();
assert_eq!(services_by_announcer, services_by_nym_address);
let service_info = client.nyxd.get_service_info(1).await;
println!("service info: {service_info:#?}");
}
@@ -1,6 +1,6 @@
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient};
use crate::{NymApiClient, ValidatorClientError};
use crate::NymApiClient;
use crate::nyxd::traits::MixnetQueryClient;
use colored::Colorize;
@@ -45,23 +45,6 @@ pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
)
}
pub async fn test_nyxd_url_connection(
network: NymNetworkDetails,
nyxd_url: Url,
address: cosmrs::AccountId,
) -> Result<bool, ValidatorClientError> {
let config = ClientConfig::try_from_nym_network_details(&network)
.expect("failed to create valid nyxd client config");
let mut nyxd_client = NyxdClient::<QueryNyxdClient>::connect(config, nyxd_url.as_str())?;
// possibly redundant, but lets just leave it here
nyxd_client.set_mixnet_contract_address(address);
match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await {
ConnectionResult::Nyxd(_, _, res) => Ok(res),
_ => Ok(false), // ✶ not possible to happens
}
}
fn setup_connection_tests<H: BuildHasher + 'static>(
nyxd_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
api_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
@@ -122,7 +105,7 @@ async fn test_nyxd_connection(
{
Ok(Err(NyxdError::TendermintError(e))) => {
// If we get a tendermint-rpc error, we classify the node as not contactable
log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e);
log::debug!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e);
false
}
Ok(Err(NyxdError::AbciError { code, log, .. })) => {
@@ -134,13 +117,13 @@ async fn test_nyxd_connection(
);
code == 18
}
Ok(Err(error @ NyxdError::NoContractAddressAvailable(_))) => {
log::warn!("Checking: nyxd url: {url}: {}: {error}", "failed".red());
Ok(Err(error @ NyxdError::NoContractAddressAvailable)) => {
log::debug!("Checking: nyxd url: {url}: {}: {error}", "failed".red());
false
}
Ok(Err(e)) => {
// For any other error, we're optimistic and just try anyway.
log::warn!(
log::debug!(
"Checking: nyxd_url: {url}: {}, but with error: {e}",
"success".green()
);
@@ -151,7 +134,7 @@ async fn test_nyxd_connection(
true
}
Err(e) => {
log::warn!("Checking: nyxd_url: {url}: {}: {e}", "failed".red());
log::debug!("Checking: nyxd_url: {url}: {}: {e}", "failed".red());
false
}
};
@@ -9,9 +9,6 @@ pub enum NymAPIError {
source: reqwest::Error,
},
#[error("Not found")]
NotFound,
#[error("Request failed with error message - {0}")]
GenericRequestFailure(String),
@@ -15,8 +15,7 @@ use nym_api_requests::models::{
};
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_service_provider_directory_common::ServiceInfo;
use reqwest::{Response, StatusCode};
use reqwest::Response;
use serde::{Deserialize, Serialize};
use url::Url;
@@ -77,8 +76,6 @@ impl Client {
let res = self.send_get_request(path, params).await?;
if res.status().is_success() {
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(NymAPIError::NotFound)
} else {
Err(NymAPIError::GenericRequestFailure(res.text().await?))
}
@@ -169,7 +166,7 @@ impl Client {
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
&[routes::API_VERSION, routes::MIXNODES],
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
NO_PARAMS,
)
.await
@@ -483,11 +480,6 @@ impl Client {
)
.await
}
pub async fn get_service_providers(&self) -> Result<Vec<ServiceInfo>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
@@ -32,5 +32,3 @@ pub const COMPUTE_REWARD_ESTIMATION: &str = "compute-reward-estimation";
pub const AVG_UPTIME: &str = "avg_uptime";
pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
pub const SERVICE_PROVIDERS: &str = "service-providers";
@@ -21,8 +21,8 @@ use std::{io, time::Duration};
#[derive(Debug, Error)]
pub enum NyxdError {
#[error("No contract address is available to perform the call: {0}")]
NoContractAddressAvailable(String),
#[error("No contract address is available to perform the call")]
NoContractAddressAvailable,
#[error(transparent)]
WalletError(#[from] DirectSecp256k1HdWalletError),
@@ -162,7 +162,7 @@ fn try_parse_abci_log(log: &abci::Log) -> Option<String> {
.value()
.contains("Maximum amount of locked coins has already been pledged")
{
Some("Maximum amount of locked tokens has already been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string())
Some("Maximum amount of locked tokens has alredy been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string())
} else {
None
}
@@ -67,8 +67,6 @@ pub struct Config {
pub(crate) group_contract_address: Option<AccountId>,
pub(crate) multisig_contract_address: Option<AccountId>,
pub(crate) coconut_dkg_contract_address: Option<AccountId>,
pub(crate) service_provider_contract_address: Option<AccountId>,
pub(crate) name_service_contract_address: Option<AccountId>,
// TODO: add this in later commits
// pub(crate) gas_price: GasPrice,
}
@@ -133,17 +131,6 @@ impl Config {
details.contracts.coconut_dkg_contract_address.as_ref(),
prefix,
)?,
service_provider_contract_address: Self::parse_optional_account(
details
.contracts
.service_provider_directory_contract_address
.as_ref(),
prefix,
)?,
name_service_contract_address: Self::parse_optional_account(
details.contracts.name_service_contract_address.as_ref(),
prefix,
)?,
})
}
}
@@ -259,10 +246,6 @@ impl<C> NyxdClient<C> {
self.config.multisig_contract_address = Some(address);
}
pub fn set_service_provider_contract_address(&mut self, address: AccountId) {
self.config.service_provider_contract_address = Some(address);
}
// TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits
// note: what unwrap is doing here is just moving a failure that would have normally
// occurred in `connect` when attempting to parse an empty address,
@@ -321,16 +304,6 @@ impl<C> NyxdClient<C> {
self.config.coconut_dkg_contract_address.as_ref().unwrap()
}
// The service provider directory contract is optional, so we return an Option not a Result
pub fn service_provider_contract_address(&self) -> Option<&AccountId> {
self.config.service_provider_contract_address.as_ref()
}
// The name service contract is optional, so we return an Option not a Result
pub fn name_service_contract_address(&self) -> Option<&AccountId> {
self.config.name_service_contract_address.as_ref()
}
pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) {
self.simulated_gas_multiplier = multiplier;
}
@@ -16,25 +16,15 @@ mod mixnet_signing_client;
mod multisig_signing_client;
mod vesting_signing_client;
mod sp_directory_query_client;
mod sp_directory_signing_client;
mod name_service_query_client;
mod name_service_signing_client;
pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient;
pub use dkg_query_client::DkgQueryClient;
pub use group_query_client::GroupQueryClient;
pub use mixnet_query_client::MixnetQueryClient;
pub use multisig_query_client::MultisigQueryClient;
pub use name_service_query_client::NameServiceQueryClient;
pub use sp_directory_query_client::SpDirectoryQueryClient;
pub use vesting_query_client::VestingQueryClient;
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
pub use dkg_signing_client::DkgSigningClient;
pub use mixnet_signing_client::MixnetSigningClient;
pub use multisig_signing_client::MultisigSigningClient;
pub use name_service_signing_client::NameServiceSigningClient;
pub use sp_directory_signing_client::SpDirectorySigningClient;
pub use vesting_signing_client::VestingSigningClient;
@@ -1,109 +0,0 @@
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::ContractBuildInformation;
use nym_name_service_common::{
msg::QueryMsg as NameQueryMsg,
response::{ConfigResponse, NamesListResponse, PagedNamesListResponse},
Address, NameEntry, NameId,
};
use serde::Deserialize;
use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient};
#[async_trait]
pub trait NameServiceQueryClient {
async fn query_name_service_contract<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_name_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::Config {})
.await
}
async fn get_name_entry(&self, name_id: NameId) -> Result<NameEntry, NyxdError> {
self.query_name_service_contract(NameQueryMsg::NameId { name_id })
.await
}
async fn get_names_paged(
&self,
start_after: Option<NameId>,
limit: Option<u32>,
) -> Result<PagedNamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::All { limit, start_after })
.await
}
async fn get_names_by_owner(&self, owner: AccountId) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByOwner {
owner: owner.to_string(),
})
.await
}
async fn get_names_by_address(&self, address: Address) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByAddress { address })
.await
}
async fn get_name_service_contract_version(
&self,
) -> Result<ContractBuildInformation, NyxdError> {
self.query_name_service_contract(NameQueryMsg::GetContractVersion {})
.await
}
async fn get_all_names(&self) -> Result<Vec<NameEntry>, NyxdError> {
let mut services = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self.get_names_paged(start_after.take(), None).await?;
let last_id = paged_response.names.last().map(|serv| serv.name_id);
services.append(&mut paged_response.names);
if let Some(start_after_res) = last_id {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(services)
}
}
#[async_trait]
impl<C> NameServiceQueryClient for NyxdClient<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn query_name_service_contract<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
self.client
.query_contract_smart(
self.name_service_contract_address().ok_or(
NyxdError::NoContractAddressAvailable("name service contract".to_string()),
)?,
&query,
)
.await
}
}
#[async_trait]
impl<C> NameServiceQueryClient for crate::Client<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn query_name_service_contract<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
self.nyxd.query_name_service_contract(query).await
}
}
@@ -1,96 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, Address, NameId, NymName};
use crate::nyxd::{
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient,
SigningCosmWasmClient,
};
#[async_trait]
pub trait NameServiceSigningClient {
async fn execute_name_service_contract(
&self,
fee: Option<Fee>,
msg: NameExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn register_name(
&self,
name: NymName,
address: Address,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(
fee,
NameExecuteMsg::Register { name, address },
vec![deposit],
)
.await
}
async fn delete_name_by_id(
&self,
name_id: NameId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(fee, NameExecuteMsg::DeleteId { name_id }, vec![])
.await
}
async fn delete_service_provider_by_name(
&self,
name: NymName,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(fee, NameExecuteMsg::DeleteName { name }, vec![])
.await
}
async fn update_deposit_required(
&self,
deposit_required: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(
fee,
NameExecuteMsg::UpdateDepositRequired {
deposit_required: deposit_required.into(),
},
vec![],
)
.await
}
}
#[async_trait]
impl<C> NameServiceSigningClient for NyxdClient<C>
where
C: SigningCosmWasmClient + Sync + Send,
{
async fn execute_name_service_contract(
&self,
fee: Option<Fee>,
msg: NameExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let memo = msg.default_memo();
self.client
.execute(
self.address(),
self.name_service_contract_address().ok_or(
NyxdError::NoContractAddressAvailable("name service contract".to_string()),
)?,
&msg,
fee,
memo,
funds,
)
.await
}
}
@@ -1,120 +0,0 @@
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::ContractBuildInformation;
use nym_service_provider_directory_common::{
msg::QueryMsg as SpQueryMsg,
response::{
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
},
NymAddress, ServiceId, ServiceInfo,
};
use serde::Deserialize;
use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient};
#[async_trait]
pub trait SpDirectoryQueryClient {
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::Config {})
.await
}
async fn get_service_info(
&self,
service_id: ServiceId,
) -> Result<ServiceInfoResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id })
.await
}
async fn get_services_paged(
&self,
start_after: Option<ServiceId>,
limit: Option<u32>,
) -> Result<PagedServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::All { limit, start_after })
.await
}
async fn get_services_by_announcer(
&self,
announcer: AccountId,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ByAnnouncer {
announcer: announcer.to_string(),
})
.await
}
async fn get_services_by_nym_address(
&self,
nym_address: NymAddress,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address })
.await
}
async fn get_sp_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::GetContractVersion {})
.await
}
async fn get_all_services(&self) -> Result<Vec<ServiceInfo>, NyxdError> {
let mut services = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self.get_services_paged(start_after.take(), None).await?;
let last_id = paged_response.services.last().map(|serv| serv.service_id);
services.append(&mut paged_response.services);
if let Some(start_after_res) = last_id {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(services)
}
}
#[async_trait]
impl<C> SpDirectoryQueryClient for NyxdClient<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
self.client
.query_contract_smart(
self.service_provider_contract_address().ok_or(
NyxdError::NoContractAddressAvailable(
"service provider directory contract".to_string(),
),
)?,
&query,
)
.await
}
}
#[async_trait]
impl<C> SpDirectoryQueryClient for crate::Client<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
self.nyxd.query_service_provider_contract(query).await
}
}
@@ -1,111 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_service_provider_directory_common::{
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceId, ServiceType,
};
use crate::nyxd::{
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient,
SigningCosmWasmClient,
};
#[async_trait]
pub trait SpDirectorySigningClient {
async fn execute_service_provider_directory_contract(
&self,
fee: Option<Fee>,
msg: SpExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn announce_service_provider(
&self,
nym_address: NymAddress,
service_type: ServiceType,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::Announce {
nym_address,
service_type,
},
vec![deposit],
)
.await
}
async fn delete_service_provider_by_id(
&self,
service_id: ServiceId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::DeleteId { service_id },
vec![],
)
.await
}
async fn delete_service_provider_by_nym_address(
&self,
nym_address: NymAddress,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::DeleteNymAddress { nym_address },
vec![],
)
.await
}
async fn update_deposit_required(
&self,
deposit_required: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::UpdateDepositRequired {
deposit_required: deposit_required.into(),
},
vec![],
)
.await
}
}
#[async_trait]
impl<C> SpDirectorySigningClient for NyxdClient<C>
where
C: SigningCosmWasmClient + Sync + Send,
{
async fn execute_service_provider_directory_contract(
&self,
fee: Option<Fee>,
msg: SpExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let memo = msg.default_memo();
self.client
.execute(
self.address(),
self.service_provider_contract_address().ok_or(
NyxdError::NoContractAddressAvailable(
"service provider directory contract".to_string(),
),
)?,
&msg,
fee,
memo,
funds,
)
.await
}
}
-1
View File
@@ -38,4 +38,3 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
@@ -5,7 +5,6 @@ use clap::{Args, Subcommand};
pub mod gateway;
pub mod mixnode;
pub mod service;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
@@ -20,6 +19,4 @@ pub enum MixnetOperatorsCommands {
Mixnode(mixnode::MixnetOperatorsMixnode),
/// Manage your gateway
Gateway(gateway::MixnetOperatorsGateway),
/// Manage your service
ServiceProvider(service::MixnetOperatorsService),
}
@@ -1,33 +0,0 @@
use clap::Parser;
use log::info;
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceType};
use nym_validator_client::nyxd::traits::SpDirectorySigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub nym_address: String,
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
#[clap(long)]
pub deposit: u128,
}
pub async fn announce(args: Args, client: SigningClient) {
info!("Annoucing service provider");
let nym_address = NymAddress::Address(args.nym_address);
let service_type = ServiceType::NetworkRequester;
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.deposit, denom);
let res = client
.announce_service_provider(nym_address, service_type, deposit.into(), None)
.await
.expect("Failed to announce service provider");
info!("Announced service provider: {res:?}");
}
@@ -1,23 +0,0 @@
use clap::Parser;
use log::info;
use nym_service_provider_directory_common::ServiceId;
use nym_validator_client::nyxd::traits::SpDirectorySigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub id: ServiceId,
}
pub async fn delete(args: Args, client: SigningClient) {
info!("Deleting service provider with id {}", args.id);
let res = client
.delete_service_provider_by_id(args.id, None)
.await
.expect("Failed to delete service provider");
info!("Deleted: {res:?}");
}
@@ -1,19 +0,0 @@
use clap::{Args, Subcommand};
pub mod announce;
pub mod delete;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsService {
#[clap(subcommand)]
pub command: MixnetOperatorsServiceCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsServiceCommands {
/// Announce service provider to the world
Announce(announce::Args),
/// Delete entry for service provider from the directory
Delete(delete::Args),
}
@@ -5,7 +5,6 @@ use clap::{Args, Subcommand};
pub mod query_all_gateways;
pub mod query_all_mixnodes;
pub mod query_all_service_providers;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
@@ -20,6 +19,4 @@ pub enum MixnetQueryCommands {
Mixnodes(query_all_mixnodes::Args),
/// Query gateways
Gateways(query_all_gateways::Args),
/// Query announced service-providers
ServiceProviders(query_all_service_providers::Args),
}
@@ -1,55 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::nym_api::error::NymAPIError;
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the service provider to display")]
pub nym_address: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
match client.nym_api.get_service_providers().await {
Ok(res) => {
if let Some(nym_address) = args.nym_address {
let service = res.iter().find(|service| {
service
.service
.nym_address
.to_string()
.eq_ignore_ascii_case(&nym_address)
});
println!(
"{}",
::serde_json::to_string_pretty(&service).expect("json formatting error")
);
} else {
let mut table = Table::new();
table.set_header(vec!["Service Id", "Announcer", "Nym Address"]);
for service in res {
table.add_row(vec![
service.service_id.to_string(),
service.service.announcer.to_string(),
service.service.service_type.to_string(),
service.service.nym_address.to_string(),
]);
}
println!("The service providers in the directory are:");
println!("{table}");
}
}
Err(NymAPIError::NotFound) => {
println!("nym-api reports no service provider endpoint available");
}
Err(e) => show_error(e),
}
}
@@ -1,11 +0,0 @@
[package]
name = "nym-name-service-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = { workspace = true }
schemars = "0.8"
serde = { workspace = true, features = ["derive"] }
@@ -1,66 +0,0 @@
use cosmwasm_std::{Coin, Event};
use crate::{NameId, RegisteredName};
pub enum NameEventType {
Register,
DeleteId,
DeleteName,
UpdateDepositRequired,
}
impl std::fmt::Display for NameEventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NameEventType::Register => write!(f, "register"),
NameEventType::DeleteId => write!(f, "delete_id"),
NameEventType::DeleteName => write!(f, "delete_name"),
NameEventType::UpdateDepositRequired => write!(f, "update_deposit_required"),
}
}
}
impl From<NameEventType> for String {
fn from(event_type: NameEventType) -> Self {
event_type.to_string()
}
}
pub const ACTION: &str = "action";
pub const NAME_ID: &str = "name_id";
pub const NAME: &str = "name";
pub const OWNER: &str = "owner";
pub const DEPOSIT_REQUIRED: &str = "deposit_required";
pub fn new_register_event(name_id: NameId, name: RegisteredName) -> Event {
Event::new(NameEventType::Register)
.add_attribute(ACTION, NameEventType::Register)
.add_attribute(NAME_ID, name_id.to_string())
.add_attribute(NAME, name.name.to_string())
.add_attribute(name.address.event_tag(), name.address.to_string())
.add_attribute(OWNER, name.owner.to_string())
}
pub fn new_delete_id_event(name_id: NameId, name: RegisteredName) -> Event {
Event::new(NameEventType::DeleteId)
.add_attribute(ACTION, NameEventType::DeleteId)
.add_attribute(NAME_ID, name_id.to_string())
.add_attribute(NAME, name.name.to_string())
.add_attribute(name.address.event_tag(), name.address.to_string())
}
pub fn new_delete_name_event(name_id: NameId, name: RegisteredName) -> Event {
Event::new(NameEventType::DeleteId)
.add_attribute(ACTION, NameEventType::DeleteName)
.add_attribute(NAME_ID, name_id.to_string())
.add_attribute(NAME, name.name.to_string())
.add_attribute(name.address.event_tag(), name.address.to_string())
}
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
Event::new(NameEventType::UpdateDepositRequired)
.add_attribute(ACTION, NameEventType::UpdateDepositRequired)
.add_attribute(DEPOSIT_REQUIRED, deposit_required.to_string())
}
@@ -1,7 +0,0 @@
pub mod events;
pub mod msg;
pub mod response;
pub mod types;
// Re-export all types at the top-level
pub use types::*;
@@ -1,91 +0,0 @@
use crate::{Address, NameId, NymName};
use cosmwasm_std::Coin;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct InstantiateMsg {
pub deposit_required: Coin,
}
impl InstantiateMsg {
pub fn new(deposit_required: Coin) -> Self {
Self { deposit_required }
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
/// Announcing a name pointing to a nym-address
Register { name: NymName, address: Address },
/// Delete a name entry by id
DeleteId { name_id: NameId },
/// Delete a name entry by name
DeleteName { name: NymName },
/// Change the deposit required for announcing a name
UpdateDepositRequired { deposit_required: Coin },
}
impl ExecuteMsg {
pub fn delete_id(name_id: NameId) -> Self {
ExecuteMsg::DeleteId { name_id }
}
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::Register { name, address } => {
format!("registering {address} as name: {name}")
}
ExecuteMsg::DeleteId { name_id } => {
format!("deleting name with id {name_id}")
}
ExecuteMsg::DeleteName { name } => {
format!("deleting name: {name}")
}
ExecuteMsg::UpdateDepositRequired { deposit_required } => {
format!("updating the deposit required to {deposit_required}")
}
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
/// Query the name by it's assigned id
NameId {
name_id: NameId,
},
// Query the names by the registrator
ByOwner {
owner: String,
},
ByName {
name: NymName,
},
ByAddress {
address: Address,
},
All {
limit: Option<u32>,
start_after: Option<NameId>,
},
Config {},
GetContractVersion {},
#[serde(rename = "get_cw2_contract_version")]
GetCW2ContractVersion {},
}
impl QueryMsg {
pub fn all() -> QueryMsg {
QueryMsg::All {
limit: None,
start_after: None,
}
}
}
@@ -1,79 +0,0 @@
use crate::{msg::ExecuteMsg, NameEntry, NameId, RegisteredName};
use cosmwasm_std::Coin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// Like [`NameEntry`] but since it's a response type the name is an option depending on if
/// the name exists or not.
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct NameEntryResponse {
pub name_id: NameId,
pub name: Option<RegisteredName>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct NamesListResponse {
pub names: Vec<NameEntry>,
}
impl NamesListResponse {
pub fn new(names: Vec<(NameId, RegisteredName)>) -> NamesListResponse {
NamesListResponse {
names: names
.into_iter()
.map(|(name_id, name)| NameEntry::new(name_id, name))
.collect(),
}
}
}
impl From<&[NameEntry]> for NamesListResponse {
fn from(names: &[NameEntry]) -> Self {
NamesListResponse {
names: names.to_vec(),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedNamesListResponse {
pub names: Vec<NameEntry>,
pub per_page: usize,
pub start_next_after: Option<NameId>,
}
impl PagedNamesListResponse {
pub fn new(
names: Vec<(NameId, RegisteredName)>,
per_page: usize,
start_next_after: Option<NameId>,
) -> PagedNamesListResponse {
let names = names
.into_iter()
.map(|(name_id, name)| NameEntry::new(name_id, name))
.collect();
PagedNamesListResponse {
names,
per_page,
start_next_after,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ConfigResponse {
pub deposit_required: Coin,
}
impl From<RegisteredName> for ExecuteMsg {
fn from(name: RegisteredName) -> Self {
ExecuteMsg::Register {
name: name.name,
address: name.address,
}
}
}
@@ -1,165 +0,0 @@
use std::fmt::{Display, Formatter};
use cosmwasm_std::{Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// The directory of services are indexed by [`ServiceId`].
pub type NameId = u32;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct RegisteredName {
/// The name pointing to the nym address
pub name: NymName,
/// The address of the service.
pub address: Address,
/// Service owner.
pub owner: Addr,
/// Block height at which the service was added.
pub block_height: u64,
/// The deposit used to announce the service.
pub deposit: Coin,
}
/// String representation of a nym address, which is of the form
/// client_id.client_enc@gateway_id.
/// NOTE: entirely unvalidated.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Address {
NymAddress(String),
// Possible extension:
//Gateway(String)
}
impl Address {
/// Create a new nym address.
pub fn new(address: &str) -> Self {
Self::NymAddress(address.to_string())
}
pub fn as_str(&self) -> &str {
match self {
Address::NymAddress(address) => address,
}
}
pub fn event_tag(&self) -> &str {
match self {
Address::NymAddress(_) => "nym_address",
//Address::Gateway(_) => "gatway_address",
}
}
}
impl Display for Address {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
/// Name stored and pointing a to a nym-address
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct NymName(String);
#[derive(Debug)]
pub enum NymNameError {
InvalidName,
}
fn is_valid_name_char(c: char) -> bool {
// Normal lowercase letters
(c.is_alphabetic() && c.is_lowercase())
// or numbers
|| c.is_numeric()
// special case hyphen or underscore
|| c == '-' || c == '_'
}
impl NymName {
pub fn new(name: &str) -> Result<NymName, NymNameError> {
// We are a bit restrictive in which names we allow, to start out with. Consider relaxing
// this in the future.
if !name.chars().all(is_valid_name_char) {
return Err(NymNameError::InvalidName);
}
Ok(Self(name.to_string()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Display for NymName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
/// [`RegisterdName`] together with the assigned [`NameId`].
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct NameEntry {
pub name_id: NameId,
pub name: RegisteredName,
}
impl NameEntry {
pub fn new(name_id: NameId, name: RegisteredName) -> Self {
Self { name_id, name }
}
}
#[cfg(test)]
mod tests {
use super::NymName;
#[test]
fn parse_nym_name() {
// Test some valid cases
assert!(NymName::new("foo").is_ok());
assert!(NymName::new("foo-bar").is_ok());
assert!(NymName::new("foo-bar-123").is_ok());
assert!(NymName::new("foo_bar").is_ok());
assert!(NymName::new("foo_bar_123").is_ok());
// And now test all some invalid ones
assert!(NymName::new("Foo").is_err());
assert!(NymName::new("foo bar").is_err());
assert!(NymName::new("foo!bar").is_err());
assert!(NymName::new("foo#bar").is_err());
assert!(NymName::new("foo$bar").is_err());
assert!(NymName::new("foo%bar").is_err());
assert!(NymName::new("foo&bar").is_err());
assert!(NymName::new("foo'bar").is_err());
assert!(NymName::new("foo(bar").is_err());
assert!(NymName::new("foo)bar").is_err());
assert!(NymName::new("foo*bar").is_err());
assert!(NymName::new("foo+bar").is_err());
assert!(NymName::new("foo,bar").is_err());
assert!(NymName::new("foo.bar").is_err());
assert!(NymName::new("foo.bar").is_err());
assert!(NymName::new("foo/bar").is_err());
assert!(NymName::new("foo/bar").is_err());
assert!(NymName::new("foo:bar").is_err());
assert!(NymName::new("foo;bar").is_err());
assert!(NymName::new("foo<bar").is_err());
assert!(NymName::new("foo=bar").is_err());
assert!(NymName::new("foo>bar").is_err());
assert!(NymName::new("foo?bar").is_err());
assert!(NymName::new("foo@bar").is_err());
assert!(NymName::new("fooBar").is_err());
assert!(NymName::new("foo[bar").is_err());
assert!(NymName::new("foo\"bar").is_err());
assert!(NymName::new("foo\\bar").is_err());
assert!(NymName::new("foo]bar").is_err());
assert!(NymName::new("foo^bar").is_err());
assert!(NymName::new("foo`bar").is_err());
assert!(NymName::new("foo{bar").is_err());
assert!(NymName::new("foo|bar").is_err());
assert!(NymName::new("foo}bar").is_err());
assert!(NymName::new("foo~bar").is_err());
}
}
@@ -7,5 +7,4 @@ edition = "2021"
[dependencies]
cosmwasm-std = { workspace = true }
schemars = "0.8"
serde = { workspace = true, features = ["derive"] }
serde = { workspace = true, default-features = false, features = ["derive"] }
@@ -5,5 +5,3 @@ pub mod types;
// Re-export all types at the top-level
pub use types::*;
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
@@ -40,24 +40,6 @@ impl ExecuteMsg {
pub fn delete_id(service_id: ServiceId) -> Self {
ExecuteMsg::DeleteId { service_id }
}
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::Announce {
nym_address,
service_type,
} => format!("announcing {nym_address} as type {service_type}"),
ExecuteMsg::DeleteId { service_id } => {
format!("deleting service with service id {service_id}")
}
ExecuteMsg::DeleteNymAddress { nym_address } => {
format!("deleting service with nym address {nym_address}")
}
ExecuteMsg::UpdateDepositRequired { deposit_required } => {
format!("updating the deposit required to {deposit_required}")
}
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -1,6 +1,5 @@
use crate::{msg::ExecuteMsg, Service, ServiceId, ServiceInfo};
use cosmwasm_std::Coin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -10,7 +9,7 @@ pub struct ServiceInfoResponse {
pub service: Option<Service>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServicesListResponse {
pub services: Vec<ServiceInfo>,
@@ -27,14 +26,6 @@ impl ServicesListResponse {
}
}
impl From<&[ServiceInfo]> for ServicesListResponse {
fn from(services: &[ServiceInfo]) -> Self {
Self {
services: services.to_vec(),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedServicesListResponse {
@@ -1,13 +1,12 @@
use std::fmt::{Display, Formatter};
use cosmwasm_std::{Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// The directory of services are indexed by [`ServiceId`].
pub type ServiceId = u32;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Service {
/// The address of the service.
pub nym_address: NymAddress,
@@ -22,7 +21,7 @@ pub struct Service {
}
/// The types of addresses supported.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum NymAddress {
/// String representation of a nym address, which is of the form
@@ -52,7 +51,7 @@ impl Display for NymAddress {
}
/// The type of services provider supported
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ServiceType {
NetworkRequester,
@@ -67,7 +66,7 @@ impl std::fmt::Display for ServiceType {
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ServiceInfo {
pub service_id: ServiceId,
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-crypto"
version = "0.3.0"
version = "0.2.0"
description = "Crypto library for the nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
@@ -24,7 +24,6 @@ serde_bytes = { version = "0.11.6", optional = true }
serde_crate = { version = "1.0", optional = true, default_features = false, package = "serde" }
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
thiserror = "1.0.37"
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
# internal
nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0" }
@@ -3,12 +3,15 @@
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
use nym_sphinx_types::Error as SphinxError;
use nym_sphinx_types::{NymPacketError, SphinxError};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MixProcessingError {
#[error("failed to process received packet: {0}")]
NymPacketProcessingError(#[from] NymPacketError),
#[error("failed to process received sphinx packet: {0}")]
SphinxProcessingError(#[from] SphinxError),
#[error("the forward hop address was malformed: {0}")]
@@ -1,38 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use nym_sphinx_types::{
SharedSecret, RoutingKeys
};
#[derive(Clone, Debug)]
pub struct KeyStorage(Arc<Mutex<HashMap<SharedSecret, (RoutingKeys, Option<SharedSecret>)>>>);
impl KeyStorage {
pub fn new() -> Self {
KeyStorage(Arc::new(Mutex::new(
HashMap::new()
)))
}
pub fn lookup(&self, key : SharedSecret) -> Option<(RoutingKeys, Option<SharedSecret>)> {
match self.0.lock() {
Ok(map) => map.get(&key).cloned(),
Err(_) => None,
}
}
pub fn store(&self, key : SharedSecret, routing_key : RoutingKeys, blinded_shared_secret : Option<SharedSecret>) {
match self.0.lock() {
Ok(mut map) => map.insert(key, (routing_key, blinded_shared_secret)),
Err(_) => return,
};
}
}
@@ -3,4 +3,3 @@
pub mod error;
pub mod processor;
mod key_storage;
@@ -3,16 +3,15 @@
use crate::measure;
use crate::packet_processor::error::MixProcessingError;
use crate::packet_processor::key_storage::KeyStorage;
use log::*;
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_framing::packet::FramedSphinxPacket;
use nym_sphinx_params::{PacketMode, PacketSize};
use nym_sphinx_framing::packet::FramedNymPacket;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx_types::{
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, Payload, PrivateKey,
ProcessedPacket, RoutingKeys, SharedSecret, SphinxPacket,
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, Payload,
PrivateKey, ProcessedPacket,
};
use std::convert::TryFrom;
use std::sync::Arc;
@@ -39,7 +38,6 @@ pub enum MixProcessingResult {
pub struct SphinxPacketProcessor {
/// Private sphinx key of this node required to unwrap received sphinx packet.
sphinx_key: Arc<PrivateKey>,
key_storage: KeyStorage,
}
impl SphinxPacketProcessor {
@@ -47,7 +45,6 @@ impl SphinxPacketProcessor {
pub fn new(sphinx_key: PrivateKey) -> Self {
SphinxPacketProcessor {
sphinx_key: Arc::new(sphinx_key),
key_storage: KeyStorage::new(),
}
}
@@ -56,39 +53,18 @@ impl SphinxPacketProcessor {
feature = "cpucycles",
instrument(skip(self, packet), fields(cpucycles))
)]
fn perform_initial_sphinx_packet_processing(
fn perform_initial_packet_processing(
&self,
packet: SphinxPacket,
packet: NymPacket,
) -> Result<ProcessedPacket, MixProcessingError> {
measure!({
packet.process(&self.sphinx_key).map_err(|err| {
debug!("Failed to unwrap Sphinx packet: {err}");
MixProcessingError::SphinxProcessingError(err)
MixProcessingError::NymPacketProcessingError(err)
})
})
}
/// Performs a sphinx unwrapping using given keys.
#[cfg_attr(
feature = "cpucycles",
instrument(skip(self, packet), fields(cpucycles))
)]
fn perform_initial_sphinx_packet_processing_with_keys(
&self,
packet: SphinxPacket,
new_blinded_secret: &Option<SharedSecret>,
routing_keys: RoutingKeys,
) -> Result<ProcessedPacket, MixProcessingError> {
measure!({
packet
.process_with_derived_keys(new_blinded_secret, routing_keys)
.map_err(|err| {
debug!("Failed to unwrap Sphinx packet: {err}");
MixProcessingError::SphinxProcessingError(err)
})
})
}
/// Takes the received framed packet and tries to unwrap it from the sphinx encryption.
#[cfg_attr(
feature = "cpucycles",
@@ -96,37 +72,12 @@ impl SphinxPacketProcessor {
)]
fn perform_initial_unwrapping(
&self,
received: FramedSphinxPacket,
received: FramedNymPacket,
) -> Result<ProcessedPacket, MixProcessingError> {
measure!({
let packet_mode = received.packet_mode();
let sphinx_packet = received.into_inner();
let packet = received.into_inner();
if packet_mode.is_old_vpn() {
return Err(MixProcessingError::ReceivedOldTypeVpnPacket);
}
//here be shared secret retrieval and hashmap lookup
if let Some((routing_keys, blinded_shared_secret)) =
self.key_storage.lookup(sphinx_packet.shared_secret())
{
trace!("Packet already seen, reusing keys");
self.perform_initial_sphinx_packet_processing_with_keys(
sphinx_packet,
&blinded_shared_secret,
routing_keys,
)
} else {
trace!("New packet, deriving keys and storing them");
let key_secret = sphinx_packet.shared_secret();
let processed_packet =
self.perform_initial_sphinx_packet_processing(sphinx_packet)?;
self.key_storage.store(
key_secret,
processed_packet.routing_keys(),
processed_packet.shared_secret(),
);
Ok(processed_packet)
}
self.perform_initial_packet_processing(packet)
})
}
@@ -134,14 +85,14 @@ impl SphinxPacketProcessor {
/// and packs all the data in a way that can be easily sent to the next hop.
fn process_forward_hop(
&self,
packet: SphinxPacket,
packet: NymPacket,
forward_address: NodeAddressBytes,
delay: SphinxDelay,
packet_mode: PacketMode,
packet_type: PacketType,
) -> Result<MixProcessingResult, MixProcessingError> {
let next_hop_address = NymNodeRoutingAddress::try_from(forward_address)?;
let mix_packet = MixPacket::new(next_hop_address, packet, packet_mode);
let mix_packet = MixPacket::new(next_hop_address, packet, packet_type);
Ok(MixProcessingResult::ForwardHop(mix_packet, Some(delay)))
}
@@ -168,21 +119,25 @@ impl SphinxPacketProcessor {
&self,
data: Vec<u8>,
packet_size: PacketSize,
packet_mode: PacketMode,
packet_type: PacketType,
) -> Result<(Option<MixPacket>, Vec<u8>), MixProcessingError> {
match packet_size {
PacketSize::AckPacket => {
PacketSize::AckPacket | PacketSize::OutfoxAckPacket => {
trace!("received an ack packet!");
Ok((None, data))
}
PacketSize::RegularPacket
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => {
| PacketSize::ExtendedPacket32
| PacketSize::OutfoxRegularPacket
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => {
trace!("received a normal packet!");
let (ack_data, message) = self.split_hop_data_into_ack_and_message(data)?;
let (ack_first_hop, ack_packet) = SurbAck::try_recover_first_hop_packet(&ack_data)?;
let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_mode);
let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_type);
Ok((Some(forward_ack), message))
}
}
@@ -196,12 +151,12 @@ impl SphinxPacketProcessor {
destination: DestinationAddressBytes,
payload: Payload,
packet_size: PacketSize,
packet_mode: PacketMode,
packet_type: PacketType,
) -> Result<MixProcessingResult, MixProcessingError> {
let packet_message = payload.recover_plaintext()?;
let (forward_ack, message) =
self.split_into_ack_and_message(packet_message, packet_size, packet_mode)?;
self.split_into_ack_and_message(packet_message, packet_size, packet_type)?;
Ok(MixProcessingResult::FinalHop(ProcessedFinalHop {
destination,
@@ -216,16 +171,16 @@ impl SphinxPacketProcessor {
&self,
packet: ProcessedPacket,
packet_size: PacketSize,
packet_mode: PacketMode,
packet_type: PacketType,
) -> Result<MixProcessingResult, MixProcessingError> {
match packet {
ProcessedPacket::ForwardHop(packet, address, delay, _) => {
self.process_forward_hop(*packet, address, delay, packet_mode)
ProcessedPacket::ForwardHop(packet, address, delay) => {
self.process_forward_hop(NymPacket::Sphinx(*packet), address, delay, packet_type)
}
// 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, packet_size, packet_mode)
ProcessedPacket::FinalHop(destination, _, payload) => {
self.process_final_hop(destination, payload, packet_size, packet_type)
}
}
}
@@ -236,19 +191,19 @@ impl SphinxPacketProcessor {
)]
pub fn process_received(
&self,
received: FramedSphinxPacket,
received: FramedNymPacket,
) -> Result<MixProcessingResult, MixProcessingError> {
// explicit packet size will help to correctly parse final hop
measure!({
let packet_size = received.packet_size();
let packet_mode = received.packet_mode();
let packet_type = received.packet_type();
// unwrap the sphinx packet and if possible and appropriate, cache keys
let processed_packet = self.perform_initial_unwrapping(received)?;
// for forward packets, extract next hop and set delay (but do NOT delay here)
// for final packets, extract SURBAck
self.perform_final_processing(processed_packet, packet_size, packet_mode)
self.perform_final_processing(processed_packet, packet_size, packet_type)
})
}
}
+4 -37
View File
@@ -3,17 +3,15 @@
use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD};
use serde::{Deserialize, Serialize};
use std::{
env::{var, VarError},
ffi::OsStr,
ops::Not,
path::PathBuf,
};
use std::{env::var, ops::Not, path::PathBuf};
use url::Url;
pub mod mainnet;
pub mod var_names;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ChainDetails {
pub bech32_account_prefix: String,
@@ -30,8 +28,6 @@ pub struct NymContracts {
pub group_contract_address: Option<String>,
pub multisig_contract_address: Option<String>,
pub coconut_dkg_contract_address: Option<String>,
pub service_provider_directory_contract_address: Option<String>,
pub name_service_contract_address: Option<String>,
}
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
@@ -72,14 +68,6 @@ impl NymNetworkDetails {
}
pub fn new_from_env() -> Self {
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
match var(env) {
Ok(var) => Some(var),
Err(VarError::NotPresent) => None,
err => panic!("Unable to set: {:?}", err),
}
}
NymNetworkDetails::new_empty()
.with_bech32_account_prefix(
var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"),
@@ -129,10 +117,6 @@ impl NymNetworkDetails {
.with_coconut_dkg_contract(Some(
var(var_names::COCONUT_DKG_CONTRACT_ADDRESS).expect("coconut dkg contract not set"),
))
.with_service_provider_directory_contract(get_optional_env(
var_names::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS,
))
.with_name_service_contract(get_optional_env(var_names::NAME_SERVICE_CONTRACT_ADDRESS))
}
pub fn new_mainnet() -> Self {
@@ -162,8 +146,6 @@ impl NymNetworkDetails {
coconut_dkg_contract_address: parse_optional_str(
mainnet::COCONUT_DKG_CONTRACT_ADDRESS,
),
service_provider_directory_contract_address: None,
name_service_contract_address: None,
},
}
}
@@ -245,21 +227,6 @@ impl NymNetworkDetails {
self.contracts.coconut_dkg_contract_address = contract.map(Into::into);
self
}
#[must_use]
pub fn with_service_provider_directory_contract<S: Into<String>>(
mut self,
contract: Option<S>,
) -> Self {
self.contracts.service_provider_directory_contract_address = contract.map(Into::into);
self
}
#[must_use]
pub fn with_name_service_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
self.contracts.name_service_contract_address = contract.map(Into::into);
self
}
}
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
+4
View File
@@ -20,6 +20,10 @@ pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
-3
View File
@@ -19,9 +19,6 @@ pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_ADDRESS";
pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str =
"SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS";
pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS";
pub const NYXD: &str = "NYXD";
pub const NYM_API: &str = "NYM_API";
+1
View File
@@ -17,6 +17,7 @@ tokio = { workspace = true, features = ["macros"]}
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-task = { path = "../task" }
nym-topology = { path = "../topology" }
nym-sphinx-params = { path = "../nymsphinx/params" }
# TODO: do we need the whole nymsphinx?
nym-sphinx = { path = "../nymsphinx" }
-3
View File
@@ -46,7 +46,4 @@ pub enum NetworkTestingError {
#[error("received a packet that could not be reconstructed into a full message with a single fragment")]
NonReconstructablePacket,
#[error("the recipient of the test packet was never specified")]
UnknownPacketRecipient,
}
-2
View File
@@ -3,8 +3,6 @@
pub mod error;
pub mod message;
pub mod node;
pub mod processor;
pub mod receiver;
pub mod tester;
+32 -60
View File
@@ -2,18 +2,27 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::NetworkTestingError;
use crate::node::TestableNode;
use crate::MixId;
use nym_sphinx::message::NymMessage;
use nym_topology::{gateway, mix};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};
#[derive(Serialize, Deserialize, Clone, Copy)]
#[derive(Serialize, Deserialize, Hash, Clone, Copy)]
pub enum NodeType {
Mixnode(MixId),
Gateway,
}
#[derive(Serialize, Deserialize, Hash, Clone, Copy)]
pub struct Empty;
#[derive(Serialize, Deserialize, Clone)]
pub struct TestMessage<T = Empty> {
pub tested_node: TestableNode,
pub encoded_node_identity: String,
pub node_owner: String,
pub node_type: NodeType,
pub msg_id: u32,
pub total_msgs: u32,
@@ -25,72 +34,26 @@ pub struct TestMessage<T = Empty> {
}
impl<T> TestMessage<T> {
pub fn new<N: Into<TestableNode>>(node: N, msg_id: u32, total_msgs: u32, ext: T) -> Self {
pub fn new_mix(node: &mix::Node, msg_id: u32, total_msgs: u32, ext: T) -> Self {
TestMessage {
tested_node: node.into(),
encoded_node_identity: node.identity_key.to_base58_string(),
node_owner: node.owner.clone(),
node_type: NodeType::Mixnode(node.mix_id),
msg_id,
total_msgs,
ext,
}
}
pub fn new_mix(node: &mix::Node, msg_id: u32, total_msgs: u32, ext: T) -> Self {
Self::new(node, msg_id, total_msgs, ext)
}
pub fn new_gateway(node: &gateway::Node, msg_id: u32, total_msgs: u32, ext: T) -> Self {
Self::new(node, msg_id, total_msgs, ext)
}
pub fn new_serialized<N>(
node: N,
msg_id: u32,
total_msgs: u32,
ext: T,
) -> Result<Vec<u8>, NetworkTestingError>
where
N: Into<TestableNode>,
T: Serialize,
{
Self::new(node, msg_id, total_msgs, ext).as_bytes()
}
pub fn new_plaintexts<N>(
node: &N,
total_msgs: u32,
ext: T,
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
where
for<'a> &'a N: Into<TestableNode>,
T: Serialize + Clone,
{
let mut msgs = Vec::with_capacity(total_msgs as usize);
for msg_id in 1..=total_msgs {
msgs.push(Self::new(node, msg_id, total_msgs, ext.clone()).as_bytes()?)
TestMessage {
encoded_node_identity: node.identity_key.to_base58_string(),
node_owner: node.owner.clone(),
node_type: NodeType::Gateway,
msg_id,
total_msgs,
ext,
}
Ok(msgs)
}
pub fn mix_plaintexts(
node: &mix::Node,
total_msgs: u32,
ext: T,
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
where
T: Serialize + Clone,
{
Self::new_plaintexts(node, total_msgs, ext)
}
pub fn gateway_plaintexts(
node: &gateway::Node,
total_msgs: u32,
ext: T,
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
where
T: Serialize + Clone,
{
Self::new_plaintexts(node, total_msgs, ext)
}
pub fn as_json_string(&self) -> Result<String, NetworkTestingError>
@@ -125,3 +88,12 @@ impl<T> TestMessage<T> {
.map_err(|source| NetworkTestingError::MalformedTestMessageReceived { source })
}
}
impl<T: Hash> Hash for TestMessage<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.encoded_node_identity.hash(state);
self.node_owner.hash(state);
self.node_type.hash(state);
self.ext.hash(state)
}
}
-92
View File
@@ -1,92 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::MixId;
use nym_topology::{gateway, mix};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)]
pub struct TestableNode {
pub encoded_identity: String,
pub owner: String,
#[serde(rename = "type")]
pub typ: NodeType,
}
impl TestableNode {
pub fn new(encoded_identity: String, owner: String, typ: NodeType) -> Self {
TestableNode {
encoded_identity,
owner,
typ,
}
}
pub fn new_mixnode(encoded_identity: String, owner: String, mix_id: MixId) -> Self {
TestableNode::new(encoded_identity, owner, NodeType::Mixnode { mix_id })
}
pub fn new_gateway(encoded_identity: String, owner: String) -> Self {
TestableNode::new(encoded_identity, owner, NodeType::Gateway)
}
pub fn is_mixnode(&self) -> bool {
self.typ.is_mixnode()
}
}
impl<'a> From<&'a mix::Node> for TestableNode {
fn from(value: &'a mix::Node) -> Self {
TestableNode {
encoded_identity: value.identity_key.to_base58_string(),
owner: value.owner.clone(),
typ: NodeType::Mixnode {
mix_id: value.mix_id,
},
}
}
}
impl<'a> From<&'a gateway::Node> for TestableNode {
fn from(value: &'a gateway::Node) -> Self {
TestableNode {
encoded_identity: value.identity_key.to_base58_string(),
owner: value.owner.clone(),
typ: NodeType::Gateway,
}
}
}
impl Display for TestableNode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} {} owned by {}",
self.typ, self.encoded_identity, self.owner
)
}
}
#[derive(Serialize, Deserialize, Hash, Clone, Copy, Debug, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum NodeType {
Mixnode { mix_id: MixId },
Gateway,
}
impl NodeType {
pub fn is_mixnode(&self) -> bool {
matches!(self, NodeType::Mixnode { .. })
}
}
impl Display for NodeType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
NodeType::Mixnode { mix_id } => write!(f, "mixnode (mix_id {mix_id})"),
NodeType::Gateway => write!(f, "gateway"),
}
}
}
-99
View File
@@ -1,99 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::NetworkTestingError;
use crate::TestMessage;
use nym_crypto::asymmetric::encryption;
use nym_sphinx::acknowledgements::identifier::recover_identifier;
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::chunking::fragment::FragmentIdentifier;
use nym_sphinx::receiver::{MessageReceiver, SphinxMessageReceiver};
use serde::de::DeserializeOwned;
use std::marker::PhantomData;
use std::sync::Arc;
// simple enum containing aggregated processed results
pub enum Received<T> {
Message(TestMessage<T>),
Ack(FragmentIdentifier),
}
impl<T> From<TestMessage<T>> for Received<T> {
fn from(value: TestMessage<T>) -> Self {
Received::Message(value)
}
}
impl<T> From<FragmentIdentifier> for Received<T> {
fn from(value: FragmentIdentifier) -> Self {
Received::Ack(value)
}
}
pub struct TestPacketProcessor<T, R: MessageReceiver = SphinxMessageReceiver> {
local_encryption_keypair: Arc<encryption::KeyPair>,
ack_key: Arc<AckKey>,
/// Structure responsible for decrypting and recovering plaintext message from received ciphertexts.
message_receiver: R,
_ext_phantom: PhantomData<T>,
}
impl<T> TestPacketProcessor<T, SphinxMessageReceiver> {
pub fn new_sphinx_processor(
local_encryption_keypair: Arc<encryption::KeyPair>,
ack_key: Arc<AckKey>,
) -> Self {
Self::new(local_encryption_keypair, ack_key)
}
}
impl<T, R> TestPacketProcessor<T, R>
where
R: MessageReceiver,
{
pub fn new(local_encryption_keypair: Arc<encryption::KeyPair>, ack_key: Arc<AckKey>) -> Self {
TestPacketProcessor {
local_encryption_keypair,
ack_key,
message_receiver: R::new(),
_ext_phantom: PhantomData,
}
}
pub fn process_mixnet_message(
&mut self,
mut raw_message: Vec<u8>,
) -> Result<TestMessage<T>, NetworkTestingError>
where
T: DeserializeOwned,
{
let plaintext = self
.message_receiver
.recover_plaintext_from_regular_packet(
self.local_encryption_keypair.private_key(),
&mut raw_message,
)?;
let fragment = self.message_receiver.recover_fragment(plaintext)?;
// test messages must consist of a single fragment
let (serialized, _) = self
.message_receiver
.insert_new_fragment(fragment)?
.ok_or(NetworkTestingError::NonReconstructablePacket)?;
TestMessage::try_recover(serialized)
}
pub fn process_ack(
&mut self,
raw_ack: Vec<u8>,
) -> Result<FragmentIdentifier, NetworkTestingError> {
let serialized_ack = recover_identifier(&self.ack_key, &raw_ack)
.ok_or(NetworkTestingError::UnrecoverableAck)?;
FragmentIdentifier::try_from_bytes(serialized_ack)
.map_err(|source| NetworkTestingError::MalformedAckIdentifier { source })
}
}
+61 -42
View File
@@ -2,95 +2,114 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::NetworkTestingError;
use crate::processor::{Received, TestPacketProcessor};
use crate::{log_err, log_info, log_warn};
use futures::channel::mpsc;
use futures::StreamExt;
use nym_crypto::asymmetric::encryption;
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::message::NymMessage;
use nym_sphinx::receiver::{MessageReceiver, SphinxMessageReceiver};
use nym_sphinx::{
acknowledgements::{identifier::recover_identifier, AckKey},
chunking::fragment::FragmentIdentifier,
};
use nym_task::TaskClient;
use serde::de::DeserializeOwned;
use std::sync::Arc;
pub type ReceivedSender<T> = mpsc::UnboundedSender<Received<T>>;
pub type ReceivedReceiver<T> = mpsc::UnboundedReceiver<Received<T>>;
pub type ReceivedSender = mpsc::UnboundedSender<Received>;
pub type ReceivedReceiver = mpsc::UnboundedReceiver<Received>;
// simple enum containing aggregated processed results
pub enum Received {
Message(NymMessage),
Ack(FragmentIdentifier),
}
impl From<NymMessage> for Received {
fn from(value: NymMessage) -> Self {
Received::Message(value)
}
}
impl From<FragmentIdentifier> for Received {
fn from(value: FragmentIdentifier) -> Self {
Received::Ack(value)
}
}
// the 'Simple' bit comes from the fact that it expects all received messages to consist of a single `Fragment`
pub struct SimpleMessageReceiver<T, R: MessageReceiver = SphinxMessageReceiver> {
message_processor: TestPacketProcessor<T, R>,
pub struct SimpleMessageReceiver<R: MessageReceiver = SphinxMessageReceiver> {
local_encryption_keypair: Arc<encryption::KeyPair>,
ack_key: Arc<AckKey>,
/// Structure responsible for decrypting and recovering plaintext message from received ciphertexts.
message_receiver: R,
mixnet_message_receiver: mpsc::UnboundedReceiver<Vec<Vec<u8>>>,
acks_receiver: mpsc::UnboundedReceiver<Vec<Vec<u8>>>,
received_sender: ReceivedSender<T>,
received_sender: ReceivedSender,
shutdown: TaskClient,
}
impl<T> SimpleMessageReceiver<T, SphinxMessageReceiver> {
impl SimpleMessageReceiver<SphinxMessageReceiver> {
pub fn new_sphinx_receiver(
local_encryption_keypair: Arc<encryption::KeyPair>,
ack_key: Arc<AckKey>,
mixnet_message_receiver: mpsc::UnboundedReceiver<Vec<Vec<u8>>>,
acks_receiver: mpsc::UnboundedReceiver<Vec<Vec<u8>>>,
received_sender: ReceivedSender<T>,
shutdown: TaskClient,
) -> Self {
Self::new(
local_encryption_keypair,
ack_key,
mixnet_message_receiver,
acks_receiver,
received_sender,
shutdown,
)
}
}
impl<T, R: MessageReceiver> SimpleMessageReceiver<T, R> {
pub fn new(
local_encryption_keypair: Arc<encryption::KeyPair>,
ack_key: Arc<AckKey>,
mixnet_message_receiver: mpsc::UnboundedReceiver<Vec<Vec<u8>>>,
acks_receiver: mpsc::UnboundedReceiver<Vec<Vec<u8>>>,
received_sender: ReceivedSender<T>,
received_sender: ReceivedSender,
shutdown: TaskClient,
) -> Self {
SimpleMessageReceiver {
message_processor: TestPacketProcessor::new(local_encryption_keypair, ack_key),
local_encryption_keypair,
ack_key,
message_receiver: SphinxMessageReceiver::new(),
mixnet_message_receiver,
acks_receiver,
received_sender,
shutdown,
}
}
}
fn forward_received<U: Into<Received<T>>>(&self, received: U) {
impl<R: MessageReceiver> SimpleMessageReceiver<R> {
fn forward_received<T: Into<Received>>(&self, received: T) {
// TODO: remove the unwrap once/if we do graceful shutdowns here
self.received_sender
.unbounded_send(received.into())
.expect("ReceivedReceiver has stopped receiving");
}
fn on_mixnet_message(&mut self, raw_message: Vec<u8>) -> Result<(), NetworkTestingError>
where
T: DeserializeOwned,
{
let recovered = self.message_processor.process_mixnet_message(raw_message)?;
fn on_mixnet_message(&mut self, mut raw_message: Vec<u8>) -> Result<(), NetworkTestingError> {
let plaintext = self
.message_receiver
.recover_plaintext_from_regular_packet(
self.local_encryption_keypair.private_key(),
&mut raw_message,
)?;
let fragment = self.message_receiver.recover_fragment(plaintext)?;
let (recovered, _) = self
.message_receiver
.insert_new_fragment(fragment)?
.ok_or(NetworkTestingError::NonReconstructablePacket)?; // by definition of this receiver, the message must consist of a single fragment
self.forward_received(recovered);
Ok(())
}
fn on_ack(&mut self, raw_ack: Vec<u8>) -> Result<(), NetworkTestingError> {
let frag_id = self.message_processor.process_ack(raw_ack)?;
let serialized_ack = recover_identifier(&self.ack_key, &raw_ack)
.ok_or(NetworkTestingError::UnrecoverableAck)?;
let frag_id = FragmentIdentifier::try_from_bytes(serialized_ack)
.map_err(|source| NetworkTestingError::MalformedAckIdentifier { source })?;
self.forward_received(frag_id);
Ok(())
}
pub async fn run(&mut self)
where
T: DeserializeOwned,
{
pub async fn run(&mut self) {
while !self.shutdown.is_shutdown() {
tokio::select! {
biased;
+28 -106
View File
@@ -10,6 +10,7 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::message::NymMessage;
use nym_sphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx::preparer::{FragmentPreparer, PreparedFragment};
use nym_sphinx_params::PacketType;
use nym_topology::{gateway, mix, NymTopology};
use rand::{CryptoRng, Rng};
use serde::Serialize;
@@ -21,10 +22,7 @@ pub struct NodeTester<R> {
base_topology: NymTopology,
/// Generally test packets are designed to be sent from ourselves to ourselves,
/// However, one might want to customise this behaviour.
/// In that case an explicit `Recipient` has to be provided when constructing test packets.
self_address: Option<Recipient>,
recipient: Recipient,
packet_size: PacketSize,
@@ -50,7 +48,7 @@ where
pub fn new(
rng: R,
base_topology: NymTopology,
self_address: Option<Recipient>,
recipient: Recipient,
packet_size: PacketSize,
average_packet_delay: Duration,
average_ack_delay: Duration,
@@ -59,7 +57,7 @@ where
Self {
rng,
base_topology,
self_address,
recipient,
packet_size,
average_packet_delay,
average_ack_delay,
@@ -92,7 +90,7 @@ where
mix: &mix::Node,
test_packets: u32,
) -> Result<Vec<PreparedFragment>, NetworkTestingError> {
self.mixnode_test_packets(mix, Empty, test_packets, None)
self.mixnode_test_packets(mix, Empty, test_packets)
}
pub fn mixnode_test_packets<T>(
@@ -100,7 +98,6 @@ where
mix: &mix::Node,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
@@ -108,35 +105,9 @@ where
let ephemeral_topology = self.testable_mix_topology(mix);
let mut packets = Vec::with_capacity(test_packets as usize);
for plaintext in TestMessage::mix_plaintexts(mix, test_packets, msg_ext)? {
packets.push(self.wrap_plaintext_data(
plaintext,
&ephemeral_topology,
custom_recipient,
)?);
}
Ok(packets)
}
pub fn mixnodes_test_packets<T>(
&mut self,
nodes: &[mix::Node],
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let mut packets = Vec::new();
for node in nodes {
packets.append(&mut self.mixnode_test_packets(
node,
msg_ext.clone(),
test_packets,
custom_recipient,
)?)
for i in 1..=test_packets {
let msg = TestMessage::new_mix(mix, i, test_packets, msg_ext.clone());
packets.push(self.create_test_packet(&msg, &ephemeral_topology)?);
}
Ok(packets)
@@ -147,7 +118,6 @@ where
mix_id: MixId,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
@@ -156,7 +126,7 @@ where
return Err(NetworkTestingError::NonExistentMixnode {mix_id})
};
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets)
}
pub fn existing_identity_mixnode_test_packets<T>(
@@ -164,7 +134,6 @@ where
encoded_mix_identity: String,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
@@ -173,57 +142,19 @@ where
return Err(NetworkTestingError::NonExistentMixnodeIdentity { mix_identity: encoded_mix_identity })
};
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets)
}
pub fn gateway_test_packets<T>(
pub fn create_test_packet<T>(
&mut self,
gateway: &gateway::Node,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let ephemeral_topology = self.testable_gateway_topology(gateway);
let mut packets = Vec::with_capacity(test_packets as usize);
for plaintext in TestMessage::gateway_plaintexts(gateway, test_packets, msg_ext)? {
packets.push(self.wrap_plaintext_data(
plaintext,
&ephemeral_topology,
custom_recipient,
)?);
}
Ok(packets)
}
pub fn existing_gateway_test_packets<T>(
&mut self,
encoded_gateway_identity: String,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let Some(node) = self.base_topology.find_gateway(&encoded_gateway_identity) else {
return Err(NetworkTestingError::NonExistentGateway { gateway_identity: encoded_gateway_identity })
};
self.gateway_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
}
pub fn wrap_plaintext_data(
&mut self,
plaintext: Vec<u8>,
message: &TestMessage<T>,
topology: &NymTopology,
custom_recipient: Option<Recipient>,
) -> Result<PreparedFragment, NetworkTestingError> {
let message = NymMessage::new_plain(plaintext);
) -> Result<PreparedFragment, NetworkTestingError>
where
T: Serialize,
{
let serialized = message.as_bytes()?;
let message = NymMessage::new_plain(serialized);
let mut fragments = self.pad_and_split_message(message, self.packet_size);
@@ -235,28 +166,19 @@ where
// we would have returned the error when checking for its length
let fragment = fragments.pop().unwrap();
// either `self_address` or `custom_recipient` has to be specified.
let address = custom_recipient.unwrap_or(
self.self_address
.ok_or(NetworkTestingError::UnknownPacketRecipient)?,
);
// the packet is designed to be sent from ourselves to ourselves
let address = self.recipient;
// TODO: can we avoid this arc clone?
let ack_key = Arc::clone(&self.ack_key);
Ok(self.prepare_chunk_for_sending(fragment, topology, &ack_key, &address, &address)?)
}
pub fn create_test_packet<T>(
&mut self,
message: &TestMessage<T>,
topology: &NymTopology,
custom_recipient: Option<Recipient>,
) -> Result<PreparedFragment, NetworkTestingError>
where
T: Serialize,
{
let serialized = message.as_bytes()?;
self.wrap_plaintext_data(serialized, topology, custom_recipient)
Ok(self.prepare_chunk_for_sending(
fragment,
topology,
&ack_key,
&address,
&address,
&PacketType::Mix,
)?)
}
}
+2 -3
View File
@@ -20,12 +20,11 @@ nym-sphinx-chunking = { path = "chunking" }
nym-sphinx-cover = { path = "cover" }
nym-sphinx-forwarding = { path = "forwarding" }
nym-sphinx-params = { path = "params" }
nym-sphinx-routing = { path = "routing" }
nym-sphinx-types = { path = "types" }
# those dependencies are due to intriducing preparer and receiver. Perpaphs that indicates they should be moved
# to separate crate?
nym-crypto = { path = "../crypto", version = "0.3.0" }
nym-crypto = { path = "../crypto", version = "0.2.0" }
nym-topology = { path = "../topology" }
# outfox
@@ -33,7 +32,7 @@ nym-outfox = { path = "../../nym-outfox" }
[dev-dependencies]
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-crypto = { path = "../crypto", version = "0.3.0", features = ["asymmetric"] }
nym-crypto = { path = "../crypto", version = "0.2.0", features = ["asymmetric"] }
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2
@@ -10,11 +10,8 @@ use nym_sphinx_addressing::nodes::{
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::DEFAULT_NUM_MIX_HOPS;
use nym_sphinx_types::builder::SphinxPacketBuilder;
use nym_sphinx_types::Error as SphinxError;
use nym_sphinx_types::{
delays::{self, Delay},
SphinxPacket,
};
use nym_sphinx_types::delays::{self, Delay};
use nym_sphinx_types::{NymPacket, NymPacketError};
use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, RngCore};
use std::convert::TryFrom;
@@ -22,7 +19,7 @@ use std::time;
use thiserror::Error;
pub struct SurbAck {
surb_ack_packet: SphinxPacket,
surb_ack_packet: NymPacket,
first_hop_address: NymNodeRoutingAddress,
expected_total_delay: Delay,
}
@@ -35,8 +32,8 @@ pub enum SurbAckRecoveryError {
#[error("could not extract first hop address information - {0}")]
InvalidAddress(#[from] NymNodeRoutingAddressError),
#[error("the contained sphinx packet was not correctly formed - {0}")]
InvalidSphinxPacket(#[from] SphinxError),
#[error("packet: {0}")]
NymPacket(#[from] NymPacketError),
}
impl SurbAck {
@@ -58,11 +55,12 @@ impl SurbAck {
let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id);
let surb_ack_packet = SphinxPacketBuilder::new()
.with_payload_size(PacketSize::AckPacket.payload_size())
.with_initial_secret(&[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42].into())
.build_packet(surb_ack_payload, &route, &destination, &delays)
.unwrap();
let surb_ack_packet = NymPacket::Sphinx(
SphinxPacketBuilder::new()
.with_payload_size(PacketSize::AckPacket.payload_size())
.build_packet(surb_ack_payload, &route, &destination, &delays)
.unwrap(),
);
// in our case, the last hop is a gateway that does NOT do any delays
let expected_total_delay = delays.iter().take(delays.len() - 1).sum();
@@ -86,21 +84,21 @@ impl SurbAck {
self.expected_total_delay
}
pub fn prepare_for_sending(self) -> (Delay, Vec<u8>) {
pub fn prepare_for_sending(self) -> Result<(Delay, Vec<u8>), SurbAckRecoveryError> {
// SURB_FIRST_HOP || SURB_ACK
let surb_bytes: Vec<_> = self
.first_hop_address
.as_zero_padded_bytes(MAX_NODE_ADDRESS_UNPADDED_LEN)
.into_iter()
.chain(self.surb_ack_packet.to_bytes().into_iter())
.chain(self.surb_ack_packet.to_bytes()?.into_iter())
.collect();
(self.expected_total_delay, surb_bytes)
Ok((self.expected_total_delay, surb_bytes))
}
// partial reciprocal of `prepare_for_sending` performed by the gateway
pub fn try_recover_first_hop_packet(
b: &[u8],
) -> Result<(NymNodeRoutingAddress, SphinxPacket), SurbAckRecoveryError> {
) -> Result<(NymNodeRoutingAddress, NymPacket), SurbAckRecoveryError> {
if b.len() != Self::len() {
Err(SurbAckRecoveryError::InvalidPacketSize {
received: b.len(),
@@ -112,7 +110,7 @@ impl SurbAck {
// TODO: this will be variable once/if we decide to introduce optimization described
// in common/nymsphinx/chunking/src/lib.rs:available_plaintext_size()
let address_offset = MAX_NODE_ADDRESS_UNPADDED_LEN;
let packet = SphinxPacket::from_bytes(&b[address_offset..])?;
let packet = NymPacket::sphinx_from_bytes(&b[address_offset..])?;
Ok((address, packet))
}
@@ -6,8 +6,8 @@ use nym_crypto::{generic_array::typenum::Unsigned, Digest};
use nym_sphinx_addressing::clients::Recipient;
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx_types::{delays, Error as SphinxError, SURBMaterial, SphinxPacket, SURB};
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx_types::{delays, NymPacket, SURBMaterial, SphinxError, SURB};
use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, RngCore};
use serde::de::{Error as SerdeError, Visitor};
@@ -173,7 +173,8 @@ impl ReplySurb {
self,
message: M,
packet_size: PacketSize,
) -> Result<(SphinxPacket, NymNodeRoutingAddress), ReplySurbError> {
_packet_type: PacketType,
) -> Result<(NymPacket, NymNodeRoutingAddress), ReplySurbError> {
let message_bytes = message.as_ref();
if message_bytes.len() != packet_size.plaintext_size() {
return Err(ReplySurbError::UnpaddedMessageError);
@@ -187,6 +188,6 @@ impl ReplySurb {
let first_hop_address = NymNodeRoutingAddress::try_from(first_hop).unwrap();
Ok((packet, first_hop_address))
Ok((NymPacket::Sphinx(packet), first_hop_address))
}
}
+13 -12
View File
@@ -3,7 +3,7 @@
use nym_crypto::shared_key::new_ephemeral_shared_key;
use nym_crypto::symmetric::stream_cipher;
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
use nym_sphinx_acknowledgements::surb_ack::{SurbAck, SurbAckRecoveryError};
use nym_sphinx_acknowledgements::AckKey;
use nym_sphinx_addressing::clients::Recipient;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
@@ -11,10 +11,10 @@ use nym_sphinx_chunking::fragment::COVER_FRAG_ID;
use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketMode, DEFAULT_NUM_MIX_HOPS,
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS,
};
use nym_sphinx_types::builder::SphinxPacketBuilder;
use nym_sphinx_types::{delays, Error as SphinxError};
use nym_sphinx_types::{delays, NymPacket};
use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, RngCore};
use std::convert::TryFrom;
@@ -28,8 +28,8 @@ pub enum CoverMessageError {
#[error("Could not construct cover message due to invalid topology - {0}")]
InvalidTopologyError(#[from] NymTopologyError),
#[error("Could not construct a valid sphinx packet - {0}")]
SphinxError(#[from] SphinxError),
#[error("SurbAck: {0}")]
SurbAck(#[from] SurbAckRecoveryError),
}
pub fn generate_loop_cover_surb_ack<R>(
@@ -67,7 +67,7 @@ where
// we don't care about total ack delay - we will not be retransmitting it anyway
let (_, ack_bytes) =
generate_loop_cover_surb_ack(rng, topology, ack_key, full_address, average_ack_delay)?
.prepare_for_sending();
.prepare_for_sending()?;
// cover message can't be distinguishable from a normal traffic so we have to go through
// all the effort of key generation, encryption, etc. Note here we are generating shared key
@@ -111,16 +111,17 @@ where
let destination = full_address.as_sphinx_destination();
// once merged, that's an easy rng injection point for sphinx packets : )
let packet = SphinxPacketBuilder::new()
.with_payload_size(packet_size.payload_size())
.with_initial_secret(&[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42].into())
.build_packet(packet_payload, &route, &destination, &delays)
.unwrap();
let packet = NymPacket::Sphinx(
SphinxPacketBuilder::new()
.with_payload_size(packet_size.payload_size())
.build_packet(packet_payload, &route, &destination, &delays)
.unwrap(),
);
let first_hop_address =
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
Ok(MixPacket::new(first_hop_address, packet, PacketMode::Mix))
Ok(MixPacket::new(first_hop_address, packet, PacketType::Mix))
}
/// Helper function used to determine if given message represents a loop cover message.
+1
View File
@@ -12,3 +12,4 @@ nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" }
nym-sphinx-types = { path = "../types" }
nym-outfox = { path = "../../../nym-outfox" }
thiserror = "1"
+41 -58
View File
@@ -2,42 +2,28 @@
// SPDX-License-Identifier: Apache-2.0
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use nym_sphinx_params::{PacketMode, PacketSize};
use nym_sphinx_types::SphinxPacket;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx_types::{NymPacket, NymPacketError};
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter};
use std::fmt::{self, Debug, Formatter};
use thiserror::Error;
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum MixPacketFormattingError {
#[error("too few bytes provided to recover from bytes")]
TooFewBytesProvided,
InvalidPacketMode,
#[error("provided packet mode is invalid")]
InvalidPacketType,
#[error("received request had invalid size - received {0}")]
InvalidPacketSize(usize),
#[error("address field was incorrectly encoded")]
InvalidAddress,
#[error("received sphinx packet was malformed")]
MalformedSphinxPacket,
#[error("Packet: {0}")]
Packet(#[from] NymPacketError),
}
impl Display for MixPacketFormattingError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
use MixPacketFormattingError::*;
match self {
TooFewBytesProvided => write!(f, "Too few bytes provided to recover from bytes"),
InvalidAddress => write!(f, "address field was incorrectly encoded"),
InvalidPacketSize(actual) =>
write!(
f,
"received request had invalid size. (actual: {}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))",
actual, PacketSize::AckPacket.size(), PacketSize::RegularPacket.size(),
PacketSize::ExtendedPacket8.size(), PacketSize::ExtendedPacket16.size(),
PacketSize::ExtendedPacket32.size()
),
MalformedSphinxPacket => write!(f, "received sphinx packet was malformed"),
InvalidPacketMode => write!(f, "provided packet mode is invalid")
}
}
}
impl std::error::Error for MixPacketFormattingError {}
impl From<NymNodeRoutingAddressError> for MixPacketFormattingError {
fn from(_: NymNodeRoutingAddressError) -> Self {
MixPacketFormattingError::InvalidAddress
@@ -46,19 +32,16 @@ impl From<NymNodeRoutingAddressError> for MixPacketFormattingError {
pub struct MixPacket {
next_hop: NymNodeRoutingAddress,
sphinx_packet: SphinxPacket,
packet_mode: PacketMode,
packet: NymPacket,
packet_type: PacketType,
}
impl Debug for MixPacket {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"MixPacket to {:?} with packet_mode {:?}. Sphinx header: {:?}, payload length: {}",
self.next_hop,
self.packet_mode,
self.sphinx_packet.header,
self.sphinx_packet.payload.len()
"MixPacket to {:?} with packet_type {:?}. Packet {:?}",
self.next_hop, self.packet_type, self.packet
)
}
}
@@ -66,13 +49,13 @@ impl Debug for MixPacket {
impl MixPacket {
pub fn new(
next_hop: NymNodeRoutingAddress,
sphinx_packet: SphinxPacket,
packet_mode: PacketMode,
packet: NymPacket,
packet_type: PacketType,
) -> Self {
MixPacket {
next_hop,
sphinx_packet,
packet_mode,
packet,
packet_type,
}
}
@@ -80,52 +63,52 @@ impl MixPacket {
self.next_hop
}
pub fn sphinx_packet(&self) -> &SphinxPacket {
&self.sphinx_packet
pub fn packet(&self) -> &NymPacket {
&self.packet
}
pub fn into_sphinx_packet(self) -> SphinxPacket {
self.sphinx_packet
pub fn into_packet(self) -> NymPacket {
self.packet
}
pub fn packet_mode(&self) -> PacketMode {
self.packet_mode
pub fn packet_type(&self) -> PacketType {
self.packet_type
}
// the message is formatted as follows:
// PACKET_MODE || FIRST_HOP || SPHINX_PACKET
// packet_type || FIRST_HOP || packet
pub fn try_from_bytes(b: &[u8]) -> Result<Self, MixPacketFormattingError> {
let packet_mode = match PacketMode::try_from(b[0]) {
let packet_type = match PacketType::try_from(b[0]) {
Ok(mode) => mode,
Err(_) => return Err(MixPacketFormattingError::InvalidPacketMode),
Err(_) => return Err(MixPacketFormattingError::InvalidPacketType),
};
let next_hop = NymNodeRoutingAddress::try_from_bytes(&b[1..])?;
let addr_offset = next_hop.bytes_min_len();
let sphinx_packet_data = &b[addr_offset + 1..];
let packet_size = sphinx_packet_data.len();
let packet_data = &b[addr_offset + 1..];
let packet_size = packet_data.len();
if PacketSize::get_type(packet_size).is_err() {
Err(MixPacketFormattingError::InvalidPacketSize(packet_size))
} else {
let sphinx_packet = match SphinxPacket::from_bytes(sphinx_packet_data) {
Ok(packet) => packet,
Err(_) => return Err(MixPacketFormattingError::MalformedSphinxPacket),
let packet = match packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(packet_data)?,
_ => NymPacket::sphinx_from_bytes(packet_data)?,
};
Ok(MixPacket {
next_hop,
sphinx_packet,
packet_mode,
packet,
packet_type,
})
}
}
pub fn into_bytes(self) -> Vec<u8> {
std::iter::once(self.packet_mode as u8)
pub fn into_bytes(self) -> Result<Vec<u8>, MixPacketFormattingError> {
Ok(std::iter::once(self.packet_type as u8)
.chain(self.next_hop.as_bytes().into_iter())
.chain(self.sphinx_packet.to_bytes().into_iter())
.collect()
.chain(self.packet.to_bytes()?.into_iter())
.collect())
}
}
+133 -93
View File
@@ -1,65 +1,55 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::packet::{FramedSphinxPacket, Header};
use crate::packet::{FramedNymPacket, Header};
use bytes::{Buf, BufMut, BytesMut};
use nym_sphinx_params::packet_modes::InvalidPacketMode;
use nym_sphinx_params::packet_sizes::{InvalidPacketSize, PacketSize};
use nym_sphinx_types::Error as SphinxError;
use nym_sphinx_types::SphinxPacket;
use nym_sphinx_params::packet_types::InvalidPacketType;
use nym_sphinx_params::PacketType;
use nym_sphinx_types::{NymPacket, NymPacketError};
use std::io;
use thiserror::Error;
use tokio_util::codec::{Decoder, Encoder};
#[derive(Error, Debug)]
pub enum SphinxCodecError {
pub enum NymCodecError {
#[error("the packet size information was malformed - {0}")]
InvalidPacketSize(#[from] InvalidPacketSize),
#[error("the packet mode information was malformed - {0}")]
InvalidPacketMode(#[from] InvalidPacketMode),
#[error("the actual sphinx packet was malformed - {0}")]
MalformedSphinxPacket(#[from] SphinxError),
InvalidPacketType(#[from] InvalidPacketType),
#[error("encountered an IO error - {0}")]
IoError(#[from] io::Error),
}
impl From<SphinxCodecError> for io::Error {
fn from(err: SphinxCodecError) -> Self {
match err {
SphinxCodecError::InvalidPacketSize(source) => {
io::Error::new(io::ErrorKind::InvalidInput, source)
}
SphinxCodecError::InvalidPacketMode(source) => {
io::Error::new(io::ErrorKind::InvalidInput, source)
}
SphinxCodecError::MalformedSphinxPacket(source) => {
io::Error::new(io::ErrorKind::InvalidData, source)
}
SphinxCodecError::IoError(err) => err,
}
}
#[error("encountered a packet error - {0}")]
NymPacket(#[from] NymPacketError),
#[error("could not convert to bytes")]
ToBytes,
#[error("could not convert to bytes")]
FromBytes,
}
// TODO: in the future it could be extended to have state containing symmetric encryption key
// so that all data could be encrypted easily (alternatively we could just slap TLS)
pub struct SphinxCodec;
pub struct NymCodec;
impl Encoder<FramedSphinxPacket> for SphinxCodec {
type Error = SphinxCodecError;
impl Encoder<FramedNymPacket> for NymCodec {
type Error = NymCodecError;
fn encode(&mut self, item: FramedSphinxPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
fn encode(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
item.header.encode(dst);
dst.put(item.packet.to_bytes().as_ref());
let packet_bytes = item.packet.to_bytes()?;
dst.put(packet_bytes.as_slice());
Ok(())
}
}
impl Decoder for SphinxCodec {
type Item = FramedSphinxPacket;
type Error = SphinxCodecError;
impl Decoder for NymCodec {
type Item = FramedNymPacket;
type Error = NymCodecError;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if src.is_empty() {
@@ -76,23 +66,32 @@ impl Decoder for SphinxCodec {
None => return Ok(None), // we have some data but not enough to get header back
};
let sphinx_packet_size = header.packet_size.size();
let frame_len = header.size() + sphinx_packet_size;
let packet_size = header.packet_size.size();
let frame_len = header.size() + packet_size;
if src.len() < frame_len {
// we don't have enough bytes to read the rest of frame
src.reserve(sphinx_packet_size);
src.reserve(packet_size);
return Ok(None);
}
// advance buffer past the header - at this point we have enough bytes
src.advance(header.size());
let sphinx_packet_bytes = src.split_to(sphinx_packet_size);
let packet_bytes = src.split_to(packet_size);
let packet = if let Some(slice) = packet_bytes.get(..) {
// here it could be debatable whether stream is corrupt or not,
// but let's go with the safer approach and assume it is.
match header.packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?,
PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?,
PacketType::Vpn => NymPacket::sphinx_from_bytes(slice)?,
}
} else {
return Ok(None);
};
// here it could be debatable whether stream is corrupt or not,
// but let's go with the safer approach and assume it is.
let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
let nymsphinx_packet = FramedSphinxPacket { header, packet };
// let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
let nymsphinx_packet = FramedNymPacket { header, packet };
// As per docs:
// Before returning from the function, implementations should ensure that the buffer
@@ -120,7 +119,6 @@ impl Decoder for SphinxCodec {
};
}
src.reserve(allocate_for_next_packet);
Ok(Some(nymsphinx_packet))
}
}
@@ -128,13 +126,42 @@ impl Decoder for SphinxCodec {
#[cfg(test)]
mod packet_encoding {
use super::*;
use nym_sphinx_types::builder::SphinxPacketBuilder;
use nym_sphinx_types::{
crypto, Delay as SphinxDelay, Destination, DestinationAddressBytes, Node, NodeAddressBytes,
DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH,
};
fn make_valid_sphinx_packet(size: PacketSize) -> SphinxPacket {
fn make_valid_outfox_packet(size: PacketSize) -> NymPacket {
let (_, node1_pk) = crypto::keygen();
let node1 = Node::new(
NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]),
node1_pk,
);
let (_, node2_pk) = crypto::keygen();
let node2 = Node::new(
NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]),
node2_pk,
);
let (_, node3_pk) = crypto::keygen();
let node3 = Node::new(
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
node3_pk,
);
let (_, node4_pk) = crypto::keygen();
let node4 = Node::new(
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
node4_pk,
);
let route = &[node1, node2, node3, node4];
let payload = vec![1; 48];
NymPacket::outfox_build(payload, route, Some(size.plaintext_size())).unwrap()
}
fn make_valid_sphinx_packet(size: PacketSize) -> NymPacket {
let (_, node1_pk) = crypto::keygen();
let node1 = Node::new(
NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]),
@@ -161,9 +188,7 @@ mod packet_encoding {
SphinxDelay::new_from_nanos(42),
SphinxDelay::new_from_nanos(42),
];
SphinxPacketBuilder::new()
.with_payload_size(size.payload_size())
.build_packet(b"foomp", &route, &destination, &delays)
NymPacket::sphinx_build(size.payload_size(), b"foomp", &route, &destination, &delays)
.unwrap()
}
@@ -171,32 +196,50 @@ mod packet_encoding {
fn whole_packet_can_be_decoded_from_a_valid_encoded_instance() {
let header = Default::default();
let sphinx_packet = make_valid_sphinx_packet(Default::default());
let sphinx_bytes = sphinx_packet.to_bytes();
let sphinx_bytes = sphinx_packet.to_bytes().unwrap();
let packet = FramedSphinxPacket {
let packet = FramedNymPacket {
header,
packet: sphinx_packet,
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(packet, &mut bytes).unwrap();
let decoded = SphinxCodec.decode(&mut bytes).unwrap().unwrap();
NymCodec.encode(packet, &mut bytes).unwrap();
let decoded = NymCodec.decode(&mut bytes).unwrap().unwrap();
assert_eq!(decoded.header, header);
assert_eq!(decoded.packet.to_bytes(), sphinx_bytes)
assert_eq!(decoded.packet.to_bytes().unwrap(), sphinx_bytes)
}
#[test]
fn whole_outfox_can_be_decoded_from_a_valid_encoded_instance() {
let header = Header::outfox();
let packet = make_valid_outfox_packet(PacketSize::OutfoxRegularPacket);
let packet_bytes = packet.to_bytes().unwrap();
NymPacket::outfox_from_bytes(packet_bytes.as_slice()).unwrap();
let packet = FramedNymPacket { header, packet };
let mut bytes = BytesMut::new();
NymCodec.encode(packet, &mut bytes).unwrap();
let decoded = NymCodec.decode(&mut bytes).unwrap().unwrap();
assert_eq!(decoded.header, header);
assert_eq!(decoded.packet.to_bytes().unwrap(), packet_bytes)
}
#[cfg(test)]
mod decode_will_allocate_enough_bytes_for_next_call {
use super::*;
use nym_sphinx_params::packet_version::PacketVersion;
use nym_sphinx_params::PacketMode;
use nym_sphinx_params::PacketType;
#[test]
fn for_empty_bytes() {
// empty bytes should allocate for header + ack packet
let mut empty_bytes = BytesMut::new();
assert!(SphinxCodec.decode(&mut empty_bytes).unwrap().is_none());
assert!(NymCodec.decode(&mut empty_bytes).unwrap().is_none());
assert_eq!(
empty_bytes.capacity(),
Header::LEGACY_SIZE + PacketSize::AckPacket.size()
@@ -217,11 +260,11 @@ mod packet_encoding {
let header = Header {
packet_version: PacketVersion::Legacy,
packet_size,
packet_mode: Default::default(),
..Default::default()
};
let mut bytes = BytesMut::new();
header.encode(&mut bytes);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert!(NymCodec.decode(&mut bytes).unwrap().is_none());
assert_eq!(bytes.capacity(), Header::LEGACY_SIZE + packet_size.size())
}
@@ -241,11 +284,11 @@ mod packet_encoding {
let header = Header {
packet_version: PacketVersion::Versioned(123),
packet_size,
packet_mode: Default::default(),
..Default::default()
};
let mut bytes = BytesMut::new();
header.encode(&mut bytes);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert!(NymCodec.decode(&mut bytes).unwrap().is_none());
assert_eq!(
bytes.capacity(),
@@ -257,18 +300,17 @@ mod packet_encoding {
#[test]
fn for_full_frame_with_legacy_header() {
// if full frame is used exactly, there should be enough space for header + ack packet
let packet = FramedSphinxPacket {
let packet = FramedNymPacket {
header: Header {
packet_version: PacketVersion::Legacy,
packet_size: Default::default(),
packet_mode: Default::default(),
..Default::default()
},
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(packet, &mut bytes).unwrap();
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
NymCodec.encode(packet, &mut bytes).unwrap();
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert_eq!(
bytes.capacity(),
Header::LEGACY_SIZE + PacketSize::AckPacket.size()
@@ -278,14 +320,14 @@ mod packet_encoding {
#[test]
fn for_full_frame_with_versioned_header() {
// if full frame is used exactly, there should be enough space for header + ack packet
let packet = FramedSphinxPacket {
let packet = FramedNymPacket {
header: Header::default(),
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(packet, &mut bytes).unwrap();
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
NymCodec.encode(packet, &mut bytes).unwrap();
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert_eq!(
bytes.capacity(),
Header::VERSIONED_SIZE + PacketSize::AckPacket.size()
@@ -304,20 +346,19 @@ mod packet_encoding {
];
for packet_size in packet_sizes {
let first_packet = FramedSphinxPacket {
let first_packet = FramedNymPacket {
header: Header {
packet_version: PacketVersion::Legacy,
packet_size: Default::default(),
packet_mode: Default::default(),
..Default::default()
},
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(first_packet, &mut bytes).unwrap();
NymCodec.encode(first_packet, &mut bytes).unwrap();
bytes.put_u8(packet_size as u8);
bytes.put_u8(PacketMode::default() as u8);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
bytes.put_u8(PacketType::default() as u8);
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert!(bytes.capacity() >= Header::LEGACY_SIZE + packet_size.size())
}
@@ -335,53 +376,53 @@ mod packet_encoding {
];
for packet_size in packet_sizes {
let first_packet = FramedSphinxPacket {
let first_packet = FramedNymPacket {
header: Header::default(),
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(first_packet, &mut bytes).unwrap();
NymCodec.encode(first_packet, &mut bytes).unwrap();
bytes.put_u8(PacketVersion::new_versioned(123).as_u8().unwrap());
bytes.put_u8(packet_size as u8);
bytes.put_u8(PacketMode::default() as u8);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
bytes.put_u8(PacketType::default() as u8);
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert!(bytes.capacity() >= Header::VERSIONED_SIZE + packet_size.size())
// assert!(bytes.capacity() >= Header::VERSIONED_SIZE + packet_size.size())
}
}
}
#[test]
fn can_decode_two_packets_immediately() {
let packet1 = FramedSphinxPacket {
let packet1 = FramedNymPacket {
header: Header::default(),
packet: make_valid_sphinx_packet(Default::default()),
};
let packet2 = FramedSphinxPacket {
let packet2 = FramedNymPacket {
header: Header::default(),
packet: make_valid_sphinx_packet(Default::default()),
};
let mut bytes = BytesMut::new();
SphinxCodec.encode(packet1, &mut bytes).unwrap();
SphinxCodec.encode(packet2, &mut bytes).unwrap();
NymCodec.encode(packet1, &mut bytes).unwrap();
NymCodec.encode(packet2, &mut bytes).unwrap();
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert!(NymCodec.decode(&mut bytes).unwrap().is_none());
}
#[test]
fn can_decode_two_packets_in_separate_calls() {
let packet1 = FramedSphinxPacket {
let packet1 = FramedNymPacket {
header: Header::default(),
packet: make_valid_sphinx_packet(Default::default()),
};
let packet2 = FramedSphinxPacket {
let packet2 = FramedNymPacket {
header: Header::default(),
packet: make_valid_sphinx_packet(Default::default()),
};
@@ -389,18 +430,17 @@ mod packet_encoding {
let mut bytes = BytesMut::new();
let mut bytes_tmp = BytesMut::new();
SphinxCodec.encode(packet1, &mut bytes).unwrap();
SphinxCodec.encode(packet2, &mut bytes_tmp).unwrap();
NymCodec.encode(packet1, &mut bytes).unwrap();
NymCodec.encode(packet2, &mut bytes_tmp).unwrap();
let tmp = bytes_tmp.split_off(100);
bytes.put(bytes_tmp);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert!(NymCodec.decode(&mut bytes).unwrap().is_none());
bytes.put(tmp);
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
assert!(NymCodec.decode(&mut bytes).unwrap().is_some());
assert!(NymCodec.decode(&mut bytes).unwrap().is_none());
}
}
+38 -25
View File
@@ -1,47 +1,52 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::codec::SphinxCodecError;
use crate::codec::NymCodecError;
use bytes::{BufMut, BytesMut};
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::packet_version::PacketVersion;
use nym_sphinx_params::PacketMode;
use nym_sphinx_types::SphinxPacket;
use nym_sphinx_params::PacketType;
use nym_sphinx_types::NymPacket;
use std::convert::TryFrom;
pub struct FramedSphinxPacket {
#[derive(Debug)]
pub struct FramedNymPacket {
/// Contains any metadata helping receiver to handle the underlying packet.
pub(crate) header: Header,
/// The actual SphinxPacket being sent.
pub(crate) packet: SphinxPacket,
pub(crate) packet: NymPacket,
}
impl FramedSphinxPacket {
pub fn new(packet: SphinxPacket, packet_mode: PacketMode, use_legacy_version: bool) -> Self {
impl FramedNymPacket {
pub fn new(packet: NymPacket, packet_type: PacketType, use_legacy_version: bool) -> Self {
// If this fails somebody is using the library in a super incorrect way, because they
// already managed to somehow create a sphinx packet
let packet_size = PacketSize::get_type(packet.len()).unwrap();
FramedSphinxPacket {
FramedNymPacket {
header: Header {
packet_version: PacketVersion::new(use_legacy_version),
packet_size,
packet_mode,
packet_type,
},
packet,
}
}
pub fn header(&self) -> Header {
self.header
}
pub fn packet_size(&self) -> PacketSize {
self.header.packet_size
}
pub fn packet_mode(&self) -> PacketMode {
self.header.packet_mode
pub fn packet_type(&self) -> PacketType {
self.header.packet_type
}
pub fn into_inner(self) -> SphinxPacket {
pub fn into_inner(self) -> NymPacket {
self.packet
}
}
@@ -64,15 +69,23 @@ pub struct Header {
///
/// TODO: ask @AP whether this can be sent like this - could it introduce some anonymity issues?
/// (note: this will be behind some encryption, either something implemented by us or some SSL action)
// Note: currently packet_mode is deprecated but is still left as a concept behind to not break
// Note: currently packet_type is deprecated but is still left as a concept behind to not break
// compatibility with existing network
pub(crate) packet_mode: PacketMode,
pub(crate) packet_type: PacketType,
}
impl Header {
pub(crate) const LEGACY_SIZE: usize = 2;
pub(crate) const VERSIONED_SIZE: usize = 3;
pub fn outfox() -> Header {
Header {
packet_version: PacketVersion::default(),
packet_size: PacketSize::OutfoxRegularPacket,
packet_type: PacketType::Outfox,
}
}
pub(crate) fn size(&self) -> usize {
if self.packet_version.is_legacy() {
Self::LEGACY_SIZE
@@ -90,12 +103,12 @@ impl Header {
}
dst.put_u8(self.packet_size as u8);
dst.put_u8(self.packet_mode as u8);
dst.put_u8(self.packet_type as u8);
// reserve bytes for the actual packet
dst.reserve(self.packet_size.size());
}
pub(crate) fn decode(src: &mut BytesMut) -> Result<Option<Self>, SphinxCodecError> {
pub(crate) fn decode(src: &mut BytesMut) -> Result<Option<Self>, NymCodecError> {
if src.len() < Self::LEGACY_SIZE {
// can't do anything if we don't have enough bytes - but reserve enough for the next call
src.reserve(Self::LEGACY_SIZE);
@@ -107,7 +120,7 @@ impl Header {
Ok(Some(Header {
packet_version,
packet_size: PacketSize::try_from(src[0])?,
packet_mode: PacketMode::try_from(src[1])?,
packet_type: PacketType::try_from(src[1])?,
}))
} else if src.len() < Self::VERSIONED_SIZE {
// we're missing that 1 byte to read the full header...
@@ -117,7 +130,7 @@ impl Header {
Ok(Some(Header {
packet_version,
packet_size: PacketSize::try_from(src[1])?,
packet_mode: PacketMode::try_from(src[2])?,
packet_type: PacketType::try_from(src[2])?,
}))
}
}
@@ -148,7 +161,7 @@ mod header_encoding {
[
PacketVersion::new_versioned(123).as_u8().unwrap(),
unknown_packet_size,
PacketMode::default() as u8,
PacketType::default() as u8,
]
.as_ref(),
);
@@ -156,12 +169,12 @@ mod header_encoding {
}
#[test]
fn decoding_will_fail_for_unknown_packet_mode() {
let unknown_packet_mode: u8 = 255;
fn decoding_will_fail_for_unknown_packet_type() {
let unknown_packet_type: u8 = 255;
// make sure this is still 'unknown' for if we make changes in the future
assert!(PacketMode::try_from(unknown_packet_mode).is_err());
assert!(PacketType::try_from(unknown_packet_type).is_err());
let mut bytes = BytesMut::from([PacketSize::default() as u8, unknown_packet_mode].as_ref());
let mut bytes = BytesMut::from([PacketSize::default() as u8, unknown_packet_type].as_ref());
assert!(Header::decode(&mut bytes).is_err())
}
@@ -191,7 +204,7 @@ mod header_encoding {
let header = Header {
packet_version: PacketVersion::Legacy,
packet_size,
packet_mode: Default::default(),
..Default::default()
};
let mut bytes = BytesMut::new();
header.encode(&mut bytes);
@@ -212,7 +225,7 @@ mod header_encoding {
let header = Header {
packet_version: PacketVersion::Versioned(123),
packet_size,
packet_mode: Default::default(),
..Default::default()
};
let mut bytes = BytesMut::new();
header.encode(&mut bytes);
+3 -3
View File
@@ -8,11 +8,11 @@ use nym_crypto::ctr;
type Aes128Ctr = ctr::Ctr64BE<Aes128>;
// Re-export for ease of use
pub use packet_modes::PacketMode;
pub use packet_sizes::PacketSize;
pub use packet_types::PacketType;
pub mod packet_modes;
pub mod packet_sizes;
pub mod packet_types;
pub mod packet_version;
// If somebody can provide an argument why it might be reasonable to have more than 255 mix hops,
@@ -29,7 +29,7 @@ pub type SerializedFragmentIdentifier = [u8; FRAG_ID_LEN];
// when packet header gets serialized, the following bytes (in that order) are put onto the wire:
// - packet_version (starting with v1.1.0)
// - packet_size indicator
// - packet_mode
// - packet_type
// it also just so happens that the only valid values for packet_size indicator include values 1-6
// therefore if we receive byte `7` (or larger than that) we'll know we received a versioned packet,
// otherwise we should treat it as legacy
@@ -1,46 +0,0 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryFrom;
use thiserror::Error;
#[derive(Error, Debug)]
#[error("{received} is not a valid packet mode tag")]
pub struct InvalidPacketMode {
received: u8,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum PacketMode {
/// Represents 'normal' packet sent through the network that should be delayed by an appropriate
/// value at each hop.
#[default]
Mix = 0,
/// Represents a VPN packet that should not be delayed and ideally cached pre-computed keys
/// should be used for unwrapping data. Note that it does not offer the same level of anonymity.
Vpn = 1,
}
impl PacketMode {
pub fn is_mix(self) -> bool {
self == PacketMode::Mix
}
pub fn is_old_vpn(self) -> bool {
self == PacketMode::Vpn
}
}
impl TryFrom<u8> for PacketMode {
type Error = InvalidPacketMode;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
_ if value == (PacketMode::Mix as u8) => Ok(Self::Mix),
_ if value == (PacketMode::Vpn as u8) => Ok(Self::Vpn),
v => Err(InvalidPacketMode { received: v }),
}
}
}
+118 -13
View File
@@ -3,7 +3,7 @@
use crate::FRAG_ID_LEN;
use nym_sphinx_types::header::HEADER_SIZE;
use nym_sphinx_types::PAYLOAD_OVERHEAD_SIZE;
use nym_sphinx_types::{MIX_PARAMS_LEN, OUTFOX_PACKET_OVERHEAD, PAYLOAD_OVERHEAD_SIZE};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::convert::TryFrom;
@@ -12,20 +12,27 @@ use std::str::FromStr;
use thiserror::Error;
// each sphinx packet contains mandatory header and payload padding + markers
const PACKET_OVERHEAD: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE;
const SPHINX_PACKET_OVERHEAD: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE;
// it's up to the smart people to figure those values out : )
const REGULAR_PACKET_SIZE: usize = 2 * 1024 + PACKET_OVERHEAD;
// TODO: even though we have 16B IV, is having just 5B (FRAG_ID_LEN) of the ID possibly insecure?
// TODO: I'm not entirely sure if we can easily extract `<AckEncryptionAlgorithm as NewStreamCipher>::NonceSize`
// into a const usize before relevant stuff is stabilised in rust...
const ACK_IV_SIZE: usize = 16;
const ACK_PACKET_SIZE: usize = ACK_IV_SIZE + FRAG_ID_LEN + PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + PACKET_OVERHEAD;
const ACK_PACKET_SIZE: usize = ACK_IV_SIZE + FRAG_ID_LEN + SPHINX_PACKET_OVERHEAD;
const REGULAR_PACKET_SIZE: usize = 2 * 1024 + SPHINX_PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + SPHINX_PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + SPHINX_PACKET_OVERHEAD;
const EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + SPHINX_PACKET_OVERHEAD;
const OUTFOX_ACK_PACKET_SIZE: usize = ACK_IV_SIZE + FRAG_ID_LEN + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_REGULAR_PACKET_SIZE: usize = 2 * 1024 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + OUTFOX_PACKET_OVERHEAD;
const OUTFOX_EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + OUTFOX_PACKET_OVERHEAD;
#[derive(Debug, Error)]
pub enum InvalidPacketSize {
@@ -62,6 +69,25 @@ pub enum PacketSize {
// for example for streaming fast and furious in compressed XviD quality
#[serde(rename = "extended16")]
ExtendedPacket16 = 5,
#[serde(rename = "outfox_regular")]
OutfoxRegularPacket = 6,
// for sending SURB-ACKs
#[serde(rename = "outfox_ack")]
OutfoxAckPacket = 7,
// for example for streaming fast and furious in uncompressed 10bit 4K HDR quality
#[serde(rename = "outfox_extended32")]
OutfoxExtendedPacket32 = 8,
// for example for streaming fast and furious in heavily compressed lossy RealPlayer quality
#[serde(rename = "outfox_extended8")]
OutfoxExtendedPacket8 = 9,
// for example for streaming fast and furious in compressed XviD quality
#[serde(rename = "outfox_extended16")]
OutfoxExtendedPacket16 = 10,
}
impl PartialOrd for PacketSize {
@@ -88,6 +114,11 @@ impl FromStr for PacketSize {
"extended8" => Ok(Self::ExtendedPacket8),
"extended16" => Ok(Self::ExtendedPacket16),
"extended32" => Ok(Self::ExtendedPacket32),
"outfox_regular" => Ok(Self::OutfoxRegularPacket),
"outfox_ack" => Ok(Self::OutfoxAckPacket),
"outfox_extended8" => Ok(Self::OutfoxExtendedPacket8),
"outfox_extended16" => Ok(Self::OutfoxExtendedPacket16),
"outfox_extended32" => Ok(Self::OutfoxExtendedPacket32),
s => Err(InvalidPacketSize::UnknownExtendedPacketVariant {
received: s.to_string(),
}),
@@ -103,6 +134,11 @@ impl Display for PacketSize {
PacketSize::ExtendedPacket32 => write!(f, "extended32"),
PacketSize::ExtendedPacket8 => write!(f, "extended8"),
PacketSize::ExtendedPacket16 => write!(f, "extended16"),
PacketSize::OutfoxRegularPacket => write!(f, "outfox_regular"),
PacketSize::OutfoxAckPacket => write!(f, "outfox_ack"),
PacketSize::OutfoxExtendedPacket32 => write!(f, "outfox_extended32"),
PacketSize::OutfoxExtendedPacket8 => write!(f, "outfox_extended8"),
PacketSize::OutfoxExtendedPacket16 => write!(f, "outfox_extended16"),
}
}
}
@@ -127,6 +163,17 @@ impl TryFrom<u8> for PacketSize {
_ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8),
_ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16),
_ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32),
_ if value == (PacketSize::OutfoxRegularPacket as u8) => Ok(Self::OutfoxRegularPacket),
_ if value == (PacketSize::OutfoxAckPacket as u8) => Ok(Self::OutfoxAckPacket),
_ if value == (PacketSize::OutfoxExtendedPacket8 as u8) => {
Ok(Self::OutfoxExtendedPacket8)
}
_ if value == (PacketSize::OutfoxExtendedPacket16 as u8) => {
Ok(Self::OutfoxExtendedPacket16)
}
_ if value == (PacketSize::OutfoxExtendedPacket32 as u8) => {
Ok(Self::OutfoxExtendedPacket32)
}
v => Err(InvalidPacketSize::UnknownPacketTag { received: v }),
}
}
@@ -140,15 +187,50 @@ impl PacketSize {
PacketSize::ExtendedPacket8 => EXTENDED_PACKET_SIZE_8,
PacketSize::ExtendedPacket16 => EXTENDED_PACKET_SIZE_16,
PacketSize::ExtendedPacket32 => EXTENDED_PACKET_SIZE_32,
PacketSize::OutfoxRegularPacket => OUTFOX_REGULAR_PACKET_SIZE,
PacketSize::OutfoxAckPacket => OUTFOX_ACK_PACKET_SIZE,
PacketSize::OutfoxExtendedPacket8 => OUTFOX_EXTENDED_PACKET_SIZE_8,
PacketSize::OutfoxExtendedPacket16 => OUTFOX_EXTENDED_PACKET_SIZE_16,
PacketSize::OutfoxExtendedPacket32 => OUTFOX_EXTENDED_PACKET_SIZE_32,
}
}
pub const fn header_size(&self) -> usize {
match self {
PacketSize::RegularPacket
| PacketSize::AckPacket
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => HEADER_SIZE,
PacketSize::OutfoxRegularPacket
| PacketSize::OutfoxAckPacket
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => MIX_PARAMS_LEN,
}
}
pub const fn payload_overhead(&self) -> usize {
match self {
PacketSize::RegularPacket
| PacketSize::AckPacket
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => PAYLOAD_OVERHEAD_SIZE,
PacketSize::OutfoxRegularPacket
| PacketSize::OutfoxAckPacket
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => OUTFOX_PACKET_OVERHEAD - MIX_PARAMS_LEN,
}
}
pub const fn plaintext_size(self) -> usize {
self.size() - HEADER_SIZE - PAYLOAD_OVERHEAD_SIZE
self.size() - self.header_size() - self.payload_overhead()
}
pub const fn payload_size(self) -> usize {
self.size() - HEADER_SIZE
self.size() - self.header_size()
}
pub fn get_type(size: usize) -> Result<Self, InvalidPacketSize> {
@@ -162,6 +244,16 @@ impl PacketSize {
Ok(PacketSize::ExtendedPacket16)
} else if PacketSize::ExtendedPacket32.size() == size {
Ok(PacketSize::ExtendedPacket32)
} else if PacketSize::OutfoxRegularPacket.size() == size {
Ok(PacketSize::OutfoxRegularPacket)
} else if PacketSize::OutfoxAckPacket.size() == size {
Ok(PacketSize::OutfoxAckPacket)
} else if PacketSize::OutfoxExtendedPacket8.size() == size {
Ok(PacketSize::OutfoxExtendedPacket8)
} else if PacketSize::OutfoxExtendedPacket16.size() == size {
Ok(PacketSize::OutfoxExtendedPacket16)
} else if PacketSize::OutfoxExtendedPacket32.size() == size {
Ok(PacketSize::OutfoxExtendedPacket32)
} else {
Err(InvalidPacketSize::UnknownPacketSize { received: size })
}
@@ -169,10 +261,16 @@ impl PacketSize {
pub fn is_extended_size(&self) -> bool {
match self {
PacketSize::RegularPacket | PacketSize::AckPacket => false,
PacketSize::RegularPacket
| PacketSize::AckPacket
| PacketSize::OutfoxAckPacket
| PacketSize::OutfoxRegularPacket => false,
PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => true,
| PacketSize::ExtendedPacket32
| PacketSize::OutfoxExtendedPacket8
| PacketSize::OutfoxExtendedPacket16
| PacketSize::OutfoxExtendedPacket32 => true,
}
}
@@ -185,8 +283,15 @@ impl PacketSize {
}
pub fn get_type_from_plaintext(plaintext_size: usize) -> Result<Self, InvalidPacketSize> {
let packet_size = plaintext_size + PACKET_OVERHEAD;
Self::get_type(packet_size)
let sphinx_packet_size = plaintext_size + SPHINX_PACKET_OVERHEAD;
let outfox_packet_size = plaintext_size + OUTFOX_PACKET_OVERHEAD;
match Self::get_type(sphinx_packet_size) {
Ok(t) => Ok(t),
Err(_) => {
println!("Got Outfox!");
Self::get_type(outfox_packet_size)
}
}
}
}
@@ -0,0 +1,49 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use thiserror::Error;
#[derive(Error, Debug)]
#[error("{received} is not a valid packet mode tag")]
pub struct InvalidPacketType {
received: u8,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum PacketType {
/// Represents 'normal' packet sent through the network that should be delayed by an appropriate
/// value at each hop.
#[default]
Mix = 0,
/// Represents a packet that should be sent through the network as fast as possible.
Vpn = 1,
/// Abusing this to add Outfox support
Outfox = 2,
}
impl PacketType {
pub fn is_mix(self) -> bool {
self == PacketType::Mix
}
pub fn is_outfox(self) -> bool {
self == PacketType::Outfox
}
}
impl TryFrom<u8> for PacketType {
type Error = InvalidPacketType;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
_ if value == (PacketType::Mix as u8) => Ok(Self::Mix),
_ if value == (PacketType::Outfox as u8) => Ok(Self::Outfox),
v => Err(InvalidPacketType { received: v }),
}
}
}
@@ -1,9 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use crate::{PacketSize, CURRENT_PACKET_VERSION_NUMBER};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PacketVersion {
// this will allow updated mixnodes to still understand packets from before the update
Legacy,

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