Compare commits

...

26 Commits

Author SHA1 Message Date
Mark Sinclair c720b430b7 sdk: examples: UI to send and show sender client address 2022-11-29 14:15:47 +00:00
Mark Sinclair 934a8c3735 sdk: add headers to wasm client 2022-11-29 14:15:47 +00:00
Mark Sinclair ab1479f363 wasm-client: make headers Option<String> and add to all methods 2022-11-29 14:15:47 +00:00
Jon Häggblad 24e2eee547 Merge remote-tracking branch 'origin/release/v1.1.2' into develop 2022-11-29 12:47:52 +01:00
Gala bdb724e9ca Merge pull request #1812 from nymtech/no-display-maintenance
No display maintenance banner
2022-11-29 09:51:55 +01:00
Gala 76ef50dc17 Merge branch 'develop' into no-display-maintenance 2022-11-29 09:32:55 +01:00
Gala f663623768 set flag to false 2022-11-29 09:32:22 +01:00
Jon Häggblad 136202f329 Merge remote-tracking branch 'origin/release/v1.1.2' into develop 2022-11-28 13:49:46 +01:00
Raphaël Walther d25848e6f8 Added nightly build workflow on second latest release 2022-11-28 10:41:17 +01:00
Raphaël Walther 0084ba221b Set build on latest release on schedule event 2022-11-25 16:03:43 +01:00
Jędrzej Stuczyński 186896bb37 Feature/gateway client protocol version (#1795)
* Introducing concept of gateway protocol version

* Remove version-based gateway filtering

* Fixed the unit test

* grammar
2022-11-25 13:29:42 +00:00
Mark Sinclair df90ff8658 nym-cli: improve error reporting/handling and changed vesting-schedule queries to use query client instead of signing client 2022-11-25 13:16:44 +00:00
Bogdan-Ștefan Neacşu bff079a3f8 Fix export dkg contract addr (#1800)
* Export dkg contract for mainnet when no config file present

* Remove redundant env files
2022-11-25 14:18:07 +02:00
Jon Häggblad 9c361385a7 websocket-requests: fix length check before deserialize (#1799) 2022-11-24 23:45:25 +01:00
Jon Häggblad a9983003d4 changelog: add missing entry for fixing message decrypt in gateway-client 2022-11-24 23:29:37 +01:00
Jon Häggblad e645d14005 Make connection_id optional in ClientRequest::Send (#1798) 2022-11-24 23:13:51 +01:00
Jędrzej Stuczyński cbf9db91ab Feature/use expect instead of panicking (#1797)
* Implementation of 'Debug' on 'RealMessage'

* expect with failed channel name instead of throwing empty panics

* Introduced Debug trait constraint in ProxyRunner

* Derive Debug for socks5_requests::Message
2022-11-24 17:02:31 +00:00
Jon Häggblad 8304146195 Fix decrypting stored received msg (#1786)
* Fix decrypting stored received msg

* rustfmt

* Moving binary message recovery to separate function

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2022-11-24 10:26:09 +01:00
Jon Häggblad c5c16cd6b0 client-core: add warning when delay multiplier is larger than 1 2022-11-23 21:00:48 +01:00
Jon Häggblad 258fa41271 Update wallet and connect lock files (#1793) 2022-11-23 20:59:12 +01:00
Jon Häggblad 0a41834fbe real_traffic_stream: reduce frequency of status print (#1794) 2022-11-23 16:56:09 +01:00
Mark Sinclair 9637afea85 Update contracts-build.yml 2022-11-23 15:51:21 +00:00
cgi-bin/ c8b454a085 Possibilty to change gateway ws listener (#1779)
* add: set gatewayListener

* Update types.ts

* Update worker.ts
2022-11-23 15:14:43 +00:00
Fran Arbanas 81f7457e0e Add step to release GH actions (#1792)
* feat: add a release step to nym contracts GH action

* feat: add shrinking the size of wasm
2022-11-23 15:13:18 +00:00
Jon Häggblad 63ae568cc2 rust: bump required version to 1.65 in some crates that need it 2022-11-23 15:52:58 +01:00
Jon Häggblad f3c1ff02e2 Network-requester: throttle inbound connections (#1789)
* Return and handle ClientRequest::LaneQueueLenghts

* Pass lane queue lengths to inbound future

* Remove unused self reference

* Request lane queue lengths periodically for all open connections

* Add timeouts

* Rename to ConnectionCommandSender and Receiver

* Rename to client_connection_tx/rx

* Fix wasm build

* Replace bool with enum
2022-11-23 12:03:58 +01:00
29 changed files with 570 additions and 202 deletions
+4 -4
View File
@@ -9,12 +9,12 @@ jobs:
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_release_matrix.json
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_release_matrix.json'
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
get_release:
runs-on: ubuntu-latest
@@ -174,7 +174,7 @@ jobs:
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: build
needs: [build,get_release]
runs-on: ubuntu-latest
steps:
- name: Collect jobs status
@@ -192,7 +192,7 @@ jobs:
NYM_PROJECT_NAME: "Nym nightly build on latest release"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
GIT_BRANCH: "https://github.com/nymtech/nym/tree/${{needs.get_release.outputs.output1}}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
@@ -0,0 +1,203 @@
name: Nightly builds on second latest release
on:
schedule:
- cron: '24 2 * * *'
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
get_release:
runs-on: ubuntu-latest
needs: matrix_prep
outputs:
output1: ${{ steps.step2.outputs.latest_release }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Fetch all branches
run: git fetch --all
- name: Set output variable to latest release branch
id: step2
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+' | tail -n 2 | head -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
build:
needs: [get_release,matrix_prep]
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
if: matrix.os == 'ubuntu-latest'
- name: Check out latest release branch
uses: actions/checkout@v3
with:
ref: ${{needs.get_release.outputs.output1}}
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features -- --ignored
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }}
with:
command: clean
# COCONUT stuff
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets --features=coconut -- -D warnings
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: [build,get_release]
runs-on: ubuntu-latest
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v3
- name: Keybase - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build on latest release"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "https://github.com/nymtech/nym/tree/${{needs.get_release.outputs.output1}}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nightly-release"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -1,50 +0,0 @@
[
{
"os":"ubuntu-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"ubuntu-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"ubuntu-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
}
]
+5
View File
@@ -17,6 +17,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- clients,validator-api: take coconut signers from the chain instead of specifying them via CLI ([#1747])
- multisig contract: add DKG contract to the list of addresses that can create proposals ([#1747])
- socks5-client: wait closing inbound connection until data is sent, and throttle incoming data in general ([#1783])
- nym-cli: improve error reporting/handling and changed `vesting-schedule` queries to use query client instead of signing client
### Fixed
- gateway-client: fix decrypting stored messages on reconnect ([#1786])
### Fixed
Generated
+2
View File
@@ -3139,6 +3139,7 @@ dependencies = [
"pretty_env_logger",
"serde",
"serde_json",
"tap",
"tokio",
"validator-client",
]
@@ -3164,6 +3165,7 @@ dependencies = [
"rand 0.6.5",
"serde",
"serde_json",
"tap",
"thiserror",
"time 0.3.14",
"toml",
@@ -42,19 +42,28 @@ pub struct StringMessage {
/// Create a new binary message with a user-specified `kind`.
#[wasm_bindgen]
pub fn create_binary_message(kind: u8, payload: Vec<u8>) -> Vec<u8> {
create_binary_message_with_headers(kind, payload, "".to_string())
pub fn create_binary_message(kind: u8, payload: Vec<u8>, headers: Option<String>) -> Vec<u8> {
create_binary_message_with_headers(kind, payload, headers)
}
/// Create a new message with a UTF-8 encoded string `payload` and a user-specified `kind`.
#[wasm_bindgen]
pub fn create_binary_message_from_string(kind: u8, payload: String) -> Vec<u8> {
create_binary_message_with_headers(kind, payload.as_bytes().to_vec(), "".to_string())
pub fn create_binary_message_from_string(
kind: u8,
payload: String,
headers: Option<String>,
) -> Vec<u8> {
create_binary_message_with_headers(kind, payload.as_bytes().to_vec(), headers)
}
/// Create a new binary message with a user-specified `kind`, and `headers` as a string.
#[wasm_bindgen]
pub fn create_binary_message_with_headers(kind: u8, payload: Vec<u8>, headers: String) -> Vec<u8> {
pub fn create_binary_message_with_headers(
kind: u8,
payload: Vec<u8>,
headers: Option<String>,
) -> Vec<u8> {
let headers = headers.unwrap_or_else(|| "".to_string());
let headers = headers.as_bytes().to_vec();
let size = (headers.len() as u64).to_be_bytes().to_vec();
vec![vec![kind], size, headers, payload].concat()
@@ -169,7 +178,7 @@ mod tests {
let message_as_bytes = create_binary_message_with_headers(
42u8,
vec![0u8, 1u8, 2u8],
"test headers".to_string(),
Some("test headers".to_string()),
);
// calculate header size
@@ -197,7 +206,7 @@ mod tests {
#[wasm_bindgen_test]
fn test_binary_with_empty_headers() {
let message_as_bytes =
create_binary_message_with_headers(42u8, vec![0u8, 1u8, 2u8], "".to_string());
create_binary_message_with_headers(42u8, vec![0u8, 1u8, 2u8], Some("".to_string()));
let expected_size = 0;
@@ -14,7 +14,7 @@ use futures::{FutureExt, SinkExt, StreamExt};
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
use gateway_requests::iv::IV;
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, PROTOCOL_VERSION};
use log::*;
use network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nymsphinx::forwarding::packet::MixPacket;
@@ -447,6 +447,33 @@ impl GatewayClient {
}
}
fn check_gateway_protocol(
&self,
gateway_protocol: Option<u8>,
) -> Result<(), GatewayClientError> {
// right now there are no failure cases here, but this might change in the future
match gateway_protocol {
None => {
warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
// note: in 1.2.0 we will have to return a hard error here
Ok(())
}
Some(v) if v != PROTOCOL_VERSION => {
let err = GatewayClientError::IncompatibleProtocol {
gateway: Some(v),
current: PROTOCOL_VERSION,
};
error!("{err}");
Err(err)
}
Some(_) => {
info!("the gateway is using exactly the same protocol version as we are. We're good to continue!");
Ok(())
}
}
}
async fn register(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
@@ -469,11 +496,20 @@ impl GatewayClient {
.map_err(GatewayClientError::RegistrationFailure),
_ => unreachable!(),
}?;
self.authenticated = match self.read_control_response().await? {
ServerResponse::Register { status } => Ok(status),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
}?;
let (authentication_status, gateway_protocol) = match self.read_control_response().await? {
ServerResponse::Register {
protocol_version,
status,
} => (status, protocol_version),
ServerResponse::Error { message } => {
return Err(GatewayClientError::GatewayError(message))
}
_ => return Err(GatewayClientError::UnexpectedResponse),
};
self.check_gateway_protocol(gateway_protocol)?;
self.authenticated = authentication_status;
if self.authenticated {
self.shared_key = Some(Arc::new(shared_key));
}
@@ -512,9 +548,11 @@ impl GatewayClient {
match self.send_websocket_message(msg).await? {
ServerResponse::Authenticate {
protocol_version,
status,
bandwidth_remaining,
} => {
self.check_gateway_protocol(protocol_version)?;
self.authenticated = status;
self.bandwidth_remaining = bandwidth_remaining;
Ok(())
@@ -85,6 +85,9 @@ pub enum GatewayClientError {
#[error("Failed to send mixnet message")]
MixnetMsgSenderFailedToSend,
#[error("Attempted to negotiate connection with gateway using incompatible protocol version. Ours is {current} and the gateway reports {gateway:?}")]
IncompatibleProtocol { gateway: Option<u8>, current: u8 },
}
impl GatewayClientError {
+1
View File
@@ -22,6 +22,7 @@ thiserror = "1"
time = { version = "0.3.6", features = ["parsing", "formatting"] }
toml = "0.5.6"
url = "2.2"
tap = "1"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
cosmwasm-std = { version = "1.0.0" }
+6
View File
@@ -15,4 +15,10 @@ pub enum ContextError {
// TODO: improve this to return known errors
#[error("failed to create client - {0}")]
NymdError(String),
#[error("{0}")]
NymdErrorPassthrough(#[from] validator_client::nymd::error::NymdError),
#[error("{0}")]
ValidatorClientError(#[from] validator_client::ValidatorClientError),
}
+5 -4
View File
@@ -6,6 +6,7 @@ use network_defaults::{
var_names::{API_VALIDATOR, MIXNET_CONTRACT_ADDRESS, NYMD_VALIDATOR, VESTING_CONTRACT_ADDRESS},
NymNetworkDetails,
};
use tap::prelude::*;
use validator_client::nymd::{self, AccountId, NymdClient, QueryNymdClient, SigningNymdClient};
pub use validator_client::validator_api::Client as ValidatorApiClient;
@@ -58,7 +59,7 @@ pub fn create_signing_client(
network_details: &NymNetworkDetails,
) -> Result<SigningClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
@@ -87,7 +88,7 @@ pub fn create_query_client(
network_details: &NymNetworkDetails,
) -> Result<QueryClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
let nymd_url = network_details
.endpoints
@@ -107,7 +108,7 @@ pub fn create_signing_client_with_validator_api(
network_details: &NymNetworkDetails,
) -> Result<SigningClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
@@ -129,7 +130,7 @@ pub fn create_query_client_with_validator_api(
network_details: &NymNetworkDetails,
) -> Result<QueryClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
match validator_client::client::Client::new_query(client_config) {
Ok(client) => Ok(client),
@@ -3,11 +3,11 @@
use clap::Parser;
use cosmrs::AccountId;
use log::info;
use log::{error, info};
use validator_client::nymd::{Coin, VestingQueryClient};
use crate::context::SigningClient;
use crate::context::QueryClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
@@ -18,8 +18,16 @@ pub struct Args {
pub address: Option<AccountId>,
}
pub async fn balance(args: Args, client: SigningClient) {
let account_id = args.address.unwrap_or_else(|| client.address().clone());
pub async fn balance(args: Args, client: QueryClient, address_from_mnemonic: Option<AccountId>) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let account_id = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
@@ -4,11 +4,11 @@
use clap::Parser;
use cosmrs::AccountId;
use cosmwasm_std::Coin as CosmWasmCoin;
use log::info;
use log::{error, info};
use validator_client::nymd::{Coin, VestingQueryClient};
use crate::context::SigningClient;
use crate::context::QueryClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
@@ -19,8 +19,18 @@ pub struct Args {
pub address: Option<AccountId>,
}
pub async fn query(args: Args, client: SigningClient) {
let account_id = args.address.unwrap_or_else(|| client.address().clone());
pub async fn query(args: Args, client: QueryClient, address_from_mnemonic: Option<AccountId>) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let account_id = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
info!("Checking account {} for a vesting schedule...", account_id);
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
-20
View File
@@ -1,20 +0,0 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090"
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
API_VALIDATOR="https://validator.nymtech.net/api/"
-20
View File
@@ -1,20 +0,0 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep
VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw
MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs
REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYMD_VALIDATOR="https://qa-validator.nymtech.net"
API_VALIDATOR="https://qa-validator-api.nymtech.net/api"
+8
View File
@@ -85,6 +85,10 @@ pub fn export_to_env() {
var_names::MULTISIG_CONTRACT_ADDRESS,
MULTISIG_CONTRACT_ADDRESS,
);
set_var_to_default(
var_names::COCONUT_DKG_CONTRACT_ADDRESS,
COCONUT_DKG_CONTRACT_ADDRESS,
);
set_var_to_default(
var_names::REWARDING_VALIDATOR_ADDRESS,
REWARDING_VALIDATOR_ADDRESS,
@@ -125,6 +129,10 @@ pub fn export_to_env_if_not_set() {
var_names::MULTISIG_CONTRACT_ADDRESS,
MULTISIG_CONTRACT_ADDRESS,
);
set_var_conditionally_to_default(
var_names::COCONUT_DKG_CONTRACT_ADDRESS,
COCONUT_DKG_CONTRACT_ADDRESS,
);
set_var_conditionally_to_default(
var_names::REWARDING_VALIDATOR_ADDRESS,
REWARDING_VALIDATOR_ADDRESS,
+3 -7
View File
@@ -209,18 +209,14 @@ impl NymTopology {
#[must_use]
pub fn filter_system_version(&self, expected_version: &str) -> Self {
self.filter_node_versions(expected_version, expected_version)
self.filter_node_versions(expected_version)
}
#[must_use]
pub fn filter_node_versions(
&self,
expected_mix_version: &str,
expected_gateway_version: &str,
) -> Self {
pub fn filter_node_versions(&self, expected_mix_version: &str) -> Self {
NymTopology {
mixes: self.mixes.filter_by_version(expected_mix_version),
gateways: self.gateways.filter_by_version(expected_gateway_version),
gateways: self.gateways.clone(),
}
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ export const MobileNav: React.FC<{ children: React.ReactNode }> = ({ children }:
const { navState, updateNavState } = useMainContext();
const [drawerOpen, setDrawerOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(true);
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const toggleDrawer = () => {
setDrawerOpen(!drawerOpen);
+1 -1
View File
@@ -235,7 +235,7 @@ export const Nav: React.FC = ({ children }) => {
const [drawerIsOpen, setDrawerToOpen] = React.useState(false);
const [fixedOpen, setFixedOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(true);
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const theme = useTheme();
const setToActive = (id: number) => {
+5 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use crypto::generic_array;
@@ -12,6 +12,10 @@ pub mod iv;
pub mod registration;
pub mod types;
/// Defines the current version of the communication protocol between gateway and clients.
/// It has to be incremented for any breaking change.
pub const PROTOCOL_VERSION: u8 = 1;
pub type GatewayMac = HmacOutput<GatewayIntegrityHmacAlgorithm>;
// TODO: could using `Mac` trait here for OutputSize backfire?
@@ -210,7 +210,10 @@ impl<'a, S> State<'a, S> {
match msg {
WsMessage::Text(ws_msg) => match types::RegistrationHandshake::try_from(ws_msg) {
Ok(reg_handshake_msg) => return match reg_handshake_msg {
types::RegistrationHandshake::HandshakePayload { data } => Ok(data),
// hehe, that's a bit disgusting that the type system requires we explicitly ignore the
// protocol_version field that we actually never attach at this point
// yet another reason for the overdue refactor
types::RegistrationHandshake::HandshakePayload { data, .. } => Ok(data),
types::RegistrationHandshake::HandshakeError { message } => Err(HandshakeError::RemoteError(message)),
},
Err(_) => error!("Received a non-handshake message during the registration handshake! It's getting dropped."),
+48 -7
View File
@@ -4,7 +4,7 @@
use crate::authentication::encrypted_address::EncryptedAddressBytes;
use crate::iv::IV;
use crate::registration::handshake::SharedKeys;
use crate::GatewayMacSize;
use crate::{GatewayMacSize, PROTOCOL_VERSION};
use crypto::generic_array::typenum::Unsigned;
use crypto::hmac::recompute_keyed_hmac_and_verify_tag;
use crypto::symmetric::stream_cipher;
@@ -28,13 +28,22 @@ use credentials::token::bandwidth::TokenCredential;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum RegistrationHandshake {
HandshakePayload { data: Vec<u8> },
HandshakeError { message: String },
HandshakePayload {
#[serde(default)]
protocol_version: Option<u8>,
data: Vec<u8>,
},
HandshakeError {
message: String,
},
}
impl RegistrationHandshake {
pub fn new_payload(data: Vec<u8>) -> Self {
RegistrationHandshake::HandshakePayload { data }
RegistrationHandshake::HandshakePayload {
protocol_version: Some(PROTOCOL_VERSION),
data,
}
}
pub fn new_error<S: Into<String>>(message: S) -> Self {
@@ -115,12 +124,16 @@ pub enum ClientControlRequest {
// TODO: should this also contain a MAC considering that at this point we already
// have the shared key derived?
Authenticate {
#[serde(default)]
protocol_version: Option<u8>,
address: String,
enc_address: String,
iv: String,
},
#[serde(alias = "handshakePayload")]
RegisterHandshakeInitRequest {
#[serde(default)]
protocol_version: Option<u8>,
data: Vec<u8>,
},
BandwidthCredential {
@@ -137,6 +150,7 @@ impl ClientControlRequest {
iv: IV,
) -> Self {
ClientControlRequest::Authenticate {
protocol_version: Some(PROTOCOL_VERSION),
address: address.as_base58_string(),
enc_address: enc_address.to_base58_string(),
iv: iv.to_base58_string(),
@@ -223,10 +237,14 @@ impl TryInto<String> for ClientControlRequest {
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ServerResponse {
Authenticate {
#[serde(default)]
protocol_version: Option<u8>,
status: bool,
bandwidth_remaining: i64,
},
Register {
#[serde(default)]
protocol_version: Option<u8>,
status: bool,
},
Bandwidth {
@@ -381,14 +399,37 @@ mod tests {
#[test]
fn handshake_payload_can_be_deserialized_into_register_handshake_init_request() {
let handshake_data = vec![1, 2, 3, 4, 5, 6];
let handshake_payload = RegistrationHandshake::HandshakePayload {
let handshake_payload_with_protocol = RegistrationHandshake::HandshakePayload {
protocol_version: Some(42),
data: handshake_data.clone(),
};
let serialized = serde_json::to_string(&handshake_payload).unwrap();
let serialized = serde_json::to_string(&handshake_payload_with_protocol).unwrap();
let deserialized = ClientControlRequest::try_from(serialized).unwrap();
match deserialized {
ClientControlRequest::RegisterHandshakeInitRequest { data } => {
ClientControlRequest::RegisterHandshakeInitRequest {
protocol_version,
data,
} => {
assert_eq!(protocol_version, Some(42));
assert_eq!(data, handshake_data)
}
_ => unreachable!("this branch shouldn't have been reached!"),
}
let handshake_payload_without_protocol = RegistrationHandshake::HandshakePayload {
protocol_version: None,
data: handshake_data.clone(),
};
let serialized = serde_json::to_string(&handshake_payload_without_protocol).unwrap();
let deserialized = ClientControlRequest::try_from(serialized).unwrap();
match deserialized {
ClientControlRequest::RegisterHandshakeInitRequest {
protocol_version,
data,
} => {
assert!(protocol_version.is_none());
assert_eq!(data, handshake_data)
}
_ => unreachable!("this branch shouldn't have been reached!"),
@@ -18,7 +18,7 @@ use gateway_requests::iv::{IVConversionError, IV};
use gateway_requests::registration::handshake::error::HandshakeError;
use gateway_requests::registration::handshake::{gateway_handshake, SharedKeys};
use gateway_requests::types::{ClientControlRequest, ServerResponse};
use gateway_requests::BinaryResponse;
use gateway_requests::{BinaryResponse, PROTOCOL_VERSION};
use log::*;
use mixnet_client::forwarder::MixForwardingSender;
use nymsphinx::DestinationAddressBytes;
@@ -55,6 +55,9 @@ enum InitialAuthenticationError {
#[error("Experienced connection error - {0}")]
ConnectionError(#[from] WsError),
#[error("Attempted to negotiate connection with client using incompatible protocol version. Ours is {current} and the client reports {client:?}")]
IncompatibleProtocol { client: Option<u8>, current: u8 },
}
impl InitialAuthenticationError {
@@ -279,10 +282,7 @@ where
// push them to the client
if let Err(err) = self.push_packets_to_client(shared_keys, messages).await {
warn!(
"We failed to send stored messages to fresh client - {}",
err
);
warn!("We failed to send stored messages to fresh client - {err}",);
return Err(InitialAuthenticationError::ConnectionError(err));
} else {
// if it was successful - remove them from the store
@@ -342,6 +342,33 @@ where
}
}
fn check_client_protocol(
&self,
client_protocol: Option<u8>,
) -> Result<(), InitialAuthenticationError> {
// right now there are no failure cases here, but this might change in the future
match client_protocol {
None => {
warn!("the client we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
// note: in 1.2.0 we will have to return a hard error here
Ok(())
}
Some(v) if v != PROTOCOL_VERSION => {
let err = InitialAuthenticationError::IncompatibleProtocol {
client: Some(v),
current: PROTOCOL_VERSION,
};
error!("{err}");
Err(err)
}
Some(_) => {
info!("the client is using exactly the same protocol version as we are. We're good to continue!");
Ok(())
}
}
}
/// Using the received challenge data, i.e. client's address as well the ciphertext of it plus
/// a fresh IV, attempts to authenticate the client by checking whether the ciphertext matches
/// the expected value if encrypted with the shared key.
@@ -389,6 +416,7 @@ where
/// * `iv`: fresh IV received with the request.
async fn handle_authenticate(
&mut self,
client_protocol_version: Option<u8>,
address: String,
enc_address: String,
iv: String,
@@ -396,6 +424,8 @@ where
where
S: AsyncRead + AsyncWrite + Unpin,
{
self.check_client_protocol(client_protocol_version)?;
let address = DestinationAddressBytes::try_from_base58_string(address)
.map_err(|err| InitialAuthenticationError::MalformedClientAddress(err.to_string()))?;
let encrypted_address = EncryptedAddressBytes::try_from_base58_string(enc_address)?;
@@ -420,6 +450,7 @@ where
Ok(InitialAuthResult::new(
client_details,
ServerResponse::Authenticate {
protocol_version: Some(PROTOCOL_VERSION),
status,
bandwidth_remaining,
},
@@ -474,11 +505,14 @@ where
/// * `init_data`: init payload of the registration handshake.
async fn handle_register(
&mut self,
client_protocol_version: Option<u8>,
init_data: Vec<u8>,
) -> Result<InitialAuthResult, InitialAuthenticationError>
where
S: AsyncRead + AsyncWrite + Unpin + Send,
{
self.check_client_protocol(client_protocol_version)?;
let remote_identity = Self::extract_remote_identity_from_register_init(&init_data)?;
let remote_address = remote_identity.derive_destination_address();
@@ -493,7 +527,10 @@ where
Ok(InitialAuthResult::new(
Some(client_details),
ServerResponse::Register { status },
ServerResponse::Register {
protocol_version: Some(PROTOCOL_VERSION),
status,
},
))
}
@@ -513,13 +550,18 @@ where
if let Ok(request) = ClientControlRequest::try_from(raw_request) {
match request {
ClientControlRequest::Authenticate {
protocol_version,
address,
enc_address,
iv,
} => self.handle_authenticate(address, enc_address, iv).await,
ClientControlRequest::RegisterHandshakeInitRequest { data } => {
self.handle_register(data).await
} => {
self.handle_authenticate(protocol_version, address, enc_address, iv)
.await
}
ClientControlRequest::RegisterHandshakeInitRequest {
protocol_version,
data,
} => self.handle_register(protocol_version, data).await,
// won't accept anything else (like bandwidth) without prior authentication
_ => Err(InitialAuthenticationError::InvalidRequest),
}
@@ -5,9 +5,12 @@ import {
Chip,
CircularProgress,
Container,
FormControlLabel,
FormGroup,
InputAdornment,
Link,
Stack,
Switch,
TextField,
Tooltip,
Typography,
@@ -20,6 +23,8 @@ import CallReceivedIcon from '@mui/icons-material/CallReceived';
import PersonIcon from '@mui/icons-material/Person';
import PersonOffIcon from '@mui/icons-material/PersonOff';
import ErrorIcon from '@mui/icons-material/Error';
import VisibilityIcon from '@mui/icons-material/Visibility';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import { NymLogo } from '@nymproject/react/logo/NymLogo';
import { NymThemeProvider } from '@nymproject/mui-theme';
import { useTheme } from '@mui/material/styles';
@@ -28,9 +33,10 @@ import { DropzoneDialog } from 'react-mui-dropzone';
import UploadFileIcon from '@mui/icons-material/UploadFile';
import ArticleIcon from '@mui/icons-material/Article';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import { Headers } from '@nymproject/sdk';
import { ThemeToggle } from './ThemeToggle';
import { AppContextProvider, useAppContext } from './context';
import { MixnetContextProvider, parseBinaryMessageHeaders, useMixnetContext } from './context/mixnet';
import { BinaryMessageHeaders, MixnetContextProvider, useMixnetContext } from './context/mixnet';
export const AppTheme: React.FC = ({ children }) => {
const { mode } = useAppContext();
@@ -45,6 +51,7 @@ interface Log {
fileDownloadUrl?: string;
filesize?: number;
timestamp: Date;
headers?: Headers;
}
interface UploadState {
@@ -52,11 +59,48 @@ interface UploadState {
files: File[];
}
const ClientAddress: React.FC<{ label?: string; tooltip?: string; address?: string }> = ({
label,
address,
tooltip,
}) => {
const copy = useClipboard();
if (!address) {
return <Chip label="Anonymous" icon={<VisibilityOffIcon />} />;
}
const addressShort = `${address.slice(0, 24)}...`;
return (
<Tooltip arrow title={tooltip || ''}>
<Chip
clickable
label={
label ? (
<>
<strong>{label}</strong> {addressShort}
</>
) : (
<>{addressShort}</>
)
}
onClick={() => {
if (address) {
copy.copy(address);
}
}}
icon={<ContentCopyIcon />}
/>
</Tooltip>
);
};
export const Content: React.FC = () => {
const theme = useTheme();
const { isReady, address, connect, events, sendTextMessage, sendBinaryMessage } = useMixnetContext();
const copy = useClipboard();
const [revealSenderAddress, setRevealSenderAddress] = React.useState(false);
const [sendToSelf, setSendToSelf] = React.useState(false);
const [recipient, setRecipient] = React.useState<string>();
const handleRecipientChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -94,13 +138,8 @@ export const Content: React.FC = () => {
React.useEffect(() => {
if (isReady) {
// // mixnet v1
// const validatorApiUrl = 'https://validator.nymtech.net/api';
// const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// mixnet v2
const validatorApiUrl = 'https://qwerty-validator-api.qa.nymte.ch/api'; // "http://localhost:8081";
const preferredGatewayIdentityKey = undefined; // '36vfvEyBzo5cWEFbnP7fqgY39kFw9PQhvwzbispeNaxL';
const validatorApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
connect({
clientId: 'Example Client',
@@ -117,6 +156,7 @@ export const Content: React.FC = () => {
kind: 'rx',
timestamp: new Date(),
message: e.args.payload,
headers: e.args.headers,
});
setLogTrigger(Date.now());
});
@@ -142,8 +182,12 @@ export const Content: React.FC = () => {
React.useEffect(() => {
if (events) {
const unsubcribe = events.subscribeToBinaryMessageReceivedEvent((e) => {
// the headers will be JSON (see the mixnet context for how they are created), so parse them
const headers = parseBinaryMessageHeaders(e.args.headers);
if (!e.args.headers) {
console.error('Expected headers, got undefined 😢', e.args);
return;
}
const headers = e.args.headers as BinaryMessageHeaders;
const blob = new Blob([new Uint8Array(e.args.payload)], { type: headers.mimeType });
log.current.push({
@@ -152,6 +196,7 @@ export const Content: React.FC = () => {
filename: headers.filename,
fileDownloadUrl: URL.createObjectURL(blob),
filesize: e.args.payload.length,
headers: e.args.headers,
});
setLogTrigger(Date.now());
});
@@ -184,7 +229,8 @@ export const Content: React.FC = () => {
message,
});
setLogTrigger(Date.now());
await sendTextMessage({ payload: message, recipient });
const senderAddress = revealSenderAddress ? address : undefined;
await sendTextMessage({ payload: message, recipient, headers: { senderAddress } });
await Promise.all(
files.map(async (f) => {
@@ -200,7 +246,7 @@ export const Content: React.FC = () => {
return await sendBinaryMessage({
payload: new Uint8Array(buffer),
recipient,
headers: { filename: f.name, mimeType: f.type },
headers: { filename: f.name, mimeType: f.type, senderAddress },
});
} catch (e) {
addErrorLog('Failed to send file', f.name);
@@ -249,20 +295,7 @@ export const Content: React.FC = () => {
) : (
<>
<Chip color="success" icon={<CheckCircleIcon />} label="Connected" variant="outlined" />
{address && (
<Tooltip arrow title="Copy your client address to the clipboard">
<Chip
clickable
label={`${address.slice(0, 24)}...`}
onClick={() => {
if (address) {
copy.copy(address);
}
}}
icon={<ContentCopyIcon />}
/>
</Tooltip>
)}
<ClientAddress address={address} tooltip="Copy your client address to the clipboard" />
</>
)}
</Stack>
@@ -347,9 +380,34 @@ export const Content: React.FC = () => {
/>
</Box>
<Button variant="contained" sx={{ width: 100 }} onClick={handleSend}>
Send
</Button>
<Stack direction="row" spacing={2}>
<Button variant="contained" sx={{ width: 100 }} onClick={handleSend}>
Send
</Button>
<FormGroup>
<FormControlLabel
control={
<Switch
color={revealSenderAddress ? 'warning' : 'default'}
onClick={() => setRevealSenderAddress((prevState) => !prevState)}
/>
}
label={
revealSenderAddress ? (
<Stack direction="row" spacing={1}>
<VisibilityIcon color="warning" />
<Typography color={theme.palette.warning.main}>Reveal your address to the recipient</Typography>
</Stack>
) : (
<Stack direction="row" spacing={1}>
<VisibilityOffIcon />
<Typography>Hide your address from the recipient</Typography>
</Stack>
)
}
/>
</FormGroup>
</Stack>
</Stack>
)}
</Box>
@@ -398,6 +456,16 @@ export const Content: React.FC = () => {
)}
</>
)}
{item.kind === 'rx' &&
(item.headers?.senderAddress ? (
<ClientAddress
label="Sender"
tooltip="Click to copy the message sender's address"
address={item.headers?.senderAddress}
/>
) : (
<ClientAddress label="Sender" />
))}
</Stack>
</Box>
))}
@@ -1,14 +1,11 @@
import * as React from 'react';
import { createNymMixnetClient, IWebWorkerEvents, NymClientConfig, NymMixnetClient } from '@nymproject/sdk';
import { createNymMixnetClient, IWebWorkerEvents, NymClientConfig, NymMixnetClient, Headers } from '@nymproject/sdk';
export interface BinaryMessageHeaders {
export interface BinaryMessageHeaders extends Headers {
filename: string;
mimeType: string;
}
export const parseBinaryMessageHeaders = (headers: string): BinaryMessageHeaders =>
JSON.parse(headers) as BinaryMessageHeaders;
interface State {
// data
isReady: boolean;
@@ -17,7 +14,7 @@ interface State {
// methods
connect: (config: NymClientConfig) => Promise<void>;
sendTextMessage: (args: { payload: string; recipient: string }) => Promise<void>;
sendTextMessage: (args: { payload: string; recipient: string; headers?: Headers }) => Promise<void>;
sendBinaryMessage: (args: { payload: Uint8Array; recipient: string; headers: BinaryMessageHeaders }) => Promise<void>;
}
@@ -62,7 +59,7 @@ export const MixnetContextProvider: React.FC = ({ children }) => {
await nym.current.client.start(config);
};
const sendTextMessage = async (args: { payload: string; recipient: string }) => {
const sendTextMessage = async (args: { payload: string; recipient: string; headers?: Headers }) => {
if (!nym.current?.client) {
console.error('Nym client has not initialised. Please wrap in useEffect on `isReady` prop of this context.');
return;
@@ -76,8 +73,7 @@ export const MixnetContextProvider: React.FC = ({ children }) => {
return;
}
// convert headers to JSON
const sendArgs = { ...args, headers: JSON.stringify(args.headers) };
await nym.current.client.sendBinaryMessage(sendArgs);
await nym.current.client.sendBinaryMessage(args);
};
const value = React.useMemo<State>(
@@ -27,7 +27,7 @@ export interface NymClientConfig {
* Optional. The identity key of the preferred gateway to connect to.
*/
preferredGatewayIdentityKey?: string;
/**
* Optional. The listener websocket of the preferred gateway to connect to.
*/
@@ -39,11 +39,16 @@ export interface NymClientConfig {
debug?: wasm_bindgen.Debug;
}
export interface Headers {
senderAddress?: string;
[key: string]: unknown;
}
export interface IWebWorker {
start: (config: NymClientConfig) => void;
selfAddress: () => string | undefined;
sendMessage: (args: { payload: string; recipient: string }) => void;
sendBinaryMessage: (args: { payload: Uint8Array; recipient: string; headers?: string }) => void;
sendMessage: (args: { payload: string; recipient: string; headers?: Headers }) => void;
sendBinaryMessage: (args: { payload: Uint8Array; recipient: string; headers?: Headers }) => void;
}
export enum EventKinds {
@@ -72,6 +77,7 @@ export interface StringMessageReceivedEvent {
args: {
kind: number;
payload: string;
headers?: Headers;
};
}
@@ -80,7 +86,7 @@ export interface BinaryMessageReceivedEvent {
args: {
kind: number;
payload: Uint8Array;
headers: string;
headers?: Headers;
};
}
@@ -16,6 +16,7 @@ import type {
StringMessageReceivedEvent,
BinaryMessageReceivedEvent,
NymClientConfig,
Headers,
} from './types';
import { EventKinds } from './types';
@@ -91,12 +92,13 @@ class ClientWrapper {
this.client = await this.client.start();
};
sendMessage = async ({ payload, recipient }: { recipient: string; payload: string }) => {
sendMessage = async ({ payload, recipient, headers }: { recipient: string; payload: string; headers?: Headers }) => {
if (!this.client) {
console.error('Client has not been initialised. Please call `init` first.');
return;
}
const message = wasm_bindgen.create_binary_message_from_string(PAYLOAD_KIND_TEXT, payload);
const headersAsJsonString = headers ? JSON.stringify(headers) : '';
const message = wasm_bindgen.create_binary_message_from_string(PAYLOAD_KIND_TEXT, payload, headersAsJsonString);
this.client = await this.client.send_binary_message(message, recipient);
};
@@ -107,13 +109,14 @@ class ClientWrapper {
}: {
recipient: string;
payload: Uint8Array;
headers?: string;
headers?: Headers;
}) => {
if (!this.client) {
console.error('Client has not been initialised. Please call `init` first.');
return;
}
const message = wasm_bindgen.create_binary_message_with_headers(PAYLOAD_KIND_BINARY, payload, headers || '');
const headersAsJsonString = headers ? JSON.stringify(headers) : '';
const message = wasm_bindgen.create_binary_message_with_headers(PAYLOAD_KIND_BINARY, payload, headersAsJsonString);
this.client = await this.client.send_binary_message(message, recipient);
};
}
@@ -133,11 +136,10 @@ wasm_bindgen(wasmUrl)
config.validatorApiUrl,
config.preferredGatewayIdentityKey,
);
// set a different gatewayListener in order to avoid workaround ws over https error
if (config.gatewayListener)
gatewayEndpoint.gateway_listener = config.gatewayListener;
if (config.gatewayListener) gatewayEndpoint.gateway_listener = config.gatewayListener;
// create the client, passing handlers for events
wrapper.init(
new wasm_bindgen.Config(
@@ -153,19 +155,20 @@ wasm_bindgen(wasmUrl)
async (message) => {
try {
const { kind, payload, headers } = await wasm_bindgen.parse_binary_message_with_headers(message);
const parsedHeaders = headers?.length > 0 ? JSON.parse(headers) : undefined;
switch (kind) {
case PAYLOAD_KIND_TEXT: {
const stringMessage = await wasm_bindgen.parse_string_message_with_headers(message);
postMessageWithType<StringMessageReceivedEvent>({
kind: EventKinds.StringMessageReceived,
args: { kind, payload: stringMessage.payload },
args: { kind, payload: stringMessage.payload, headers: parsedHeaders },
});
break;
}
case PAYLOAD_KIND_BINARY:
postMessageWithType<BinaryMessageReceivedEvent>({
kind: EventKinds.BinaryMessageReceived,
args: { kind, payload, headers: headers || '' },
args: { kind, payload, headers: parsedHeaders },
});
break;
default:
+1
View File
@@ -18,6 +18,7 @@ serde_json = "1"
tokio = { version = "1.11", features = [ "net", "rt-multi-thread", "macros", "signal"] }
bip39 = "1.0.1"
anyhow = "1"
tap = "1"
nym-cli-commands = { path = "../../common/commands" }
logging = { path = "../../common/logging"}
+7 -3
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use network_defaults::NymNetworkDetails;
use nym_cli_commands::context::{create_signing_client, ClientArgs};
use nym_cli_commands::context::{create_query_client, create_signing_client, ClientArgs};
pub(crate) async fn execute(
global_args: ClientArgs,
@@ -19,18 +19,22 @@ pub(crate) async fn execute(
.await
}
Some(nym_cli_commands::validator::vesting::VestingScheduleCommands::Query(args)) => {
let address_from_args = args.address.clone();
nym_cli_commands::validator::vesting::query_vesting_schedule::query(
args,
create_signing_client(global_args, network_details)?,
create_query_client(network_details)?,
address_from_args,
)
.await
}
Some(nym_cli_commands::validator::vesting::VestingScheduleCommands::VestedBalance(
args,
)) => {
let address_from_args = args.address.clone();
nym_cli_commands::validator::vesting::balance::balance(
args,
create_signing_client(global_args, network_details)?,
create_query_client(network_details)?,
address_from_args,
)
.await
}