Compare commits

..

3 Commits

Author SHA1 Message Date
Tommy Verrall e20ed854df check outputs 2023-08-29 11:46:14 +02:00
Tommy Verrall c6509c3a95 fix string 2023-08-29 11:35:31 +02:00
Tommy Verrall 8a9d242d03 check to see if this fixes strapi 2023-08-29 11:32:10 +02:00
60 changed files with 212 additions and 1583 deletions
+20 -1
View File
@@ -25,7 +25,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
@@ -40,6 +40,7 @@ jobs:
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
netstat_version: ${{ steps.binary-versions.outputs.netstat_version }}
platform: ${{ steps.get-platform-version.outputs.platform }}"
steps:
- uses: actions/checkout@v3
@@ -96,6 +97,11 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
- id: echo-outputs
name: echo output for assets
run: |
echo "data from release: $ ${{ steps.create-release.outputs }}"
- id: release-info
name: Prepare release info
run: |
@@ -124,6 +130,12 @@ jobs:
v=$(rg '^version = "(.*)"' -or '$1' tools/nym-cli/Cargo.toml) && echo "cli_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-statistics/Cargo.toml) && echo "netstat_version=$v" >> "$GITHUB_OUTPUT"
- id: get-platform-version
name: get platform version
run: |
echo "::set-output name=platform::$(uname -r)"
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
@@ -139,6 +151,7 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.client_hash }}
name: Client
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
push-release-data-mixnode:
@@ -156,6 +169,7 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.mixnode_hash }}
name: Mixnode
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
push-release-data-gateway:
@@ -173,6 +187,7 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.gateway_hash }}
name: Gateway
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
push-release-data-socks5:
@@ -190,6 +205,7 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.socks5_hash }}
name: Socks5 Client
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
push-release-data-network-requester:
@@ -207,6 +223,7 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.netreq_hash }}
name: Network Requester
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
push-release-data-cli:
@@ -224,6 +241,7 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.cli_hash }}
name: Cli
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
push-release-data-network-stat:
@@ -241,4 +259,5 @@ jobs:
file_hash: ${{ needs.publish-nym.outputs.netstat_hash }}
name: Network Statistics
category: binaries
platform: "${{ needs.publish-nym.outputs.platform }}"
secrets: inherit
+2 -22
View File
@@ -4,27 +4,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.29-snickers] (2023-08-29)
- Add EXPLORER_API configurable url ([#3810])
- Bugfix/use correct tendermint dialect ([#3802])
- Explorer - look up gateways based on geo-location ([#3776])
- Speedy mode - select the mixnodes based on the location of the NR ([#3775])
- NR - reduce response time by removing poisson delay ([#3774])
- [demo] libp2p example with nym-sdk ([#3763])
- introduced /network/details endpoint to nym-api to return used network information ([#3758])
- Feature/issue credentials ([#3691])
[#3810]: https://github.com/nymtech/nym/pull/3810
[#3802]: https://github.com/nymtech/nym/pull/3802
[#3776]: https://github.com/nymtech/nym/issues/3776
[#3775]: https://github.com/nymtech/nym/issues/3775
[#3774]: https://github.com/nymtech/nym/issues/3774
[#3763]: https://github.com/nymtech/nym/pull/3763
[#3758]: https://github.com/nymtech/nym/pull/3758
[#3691]: https://github.com/nymtech/nym/pull/3691
## [v1.1.28] (2023-08-22)
## [1.1.28] (2023-08-22)
- [final step3]: add [rust] support to nyxd client in wasm ([#3743])
- Feature/ephemera upgrade ([#3791])
@@ -39,7 +19,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#3767]: https://github.com/nymtech/nym/pull/3767
## [v1.1.27] (2023-08-16)
## [1.1.27] (2023-08-16)
- fix serialisation of contract types ([#3752])
- Investigate spending credentials from the main API (coconut enabled to a gateway) from feature/ephemera branch ([#3741])
Generated
+9 -40
View File
@@ -1066,15 +1066,6 @@ dependencies = [
"sha2 0.10.7",
]
[[package]]
name = "bs58"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896"
dependencies = [
"tinyvec",
]
[[package]]
name = "bumpalo"
version = "3.13.0"
@@ -2777,7 +2768,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"chrono",
"clap 4.3.21",
@@ -5647,7 +5638,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.28"
version = "1.1.27"
dependencies = [
"actix-web",
"anyhow",
@@ -5794,7 +5785,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -5867,7 +5858,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"clap 4.3.21",
"dirs 4.0.0",
@@ -6162,7 +6153,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"anyhow",
"async-trait",
@@ -6317,7 +6308,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.28"
version = "1.1.27"
dependencies = [
"anyhow",
"bs58 0.4.0",
@@ -6435,7 +6426,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"anyhow",
"async-file-watcher",
@@ -6482,7 +6473,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"dirs 4.0.0",
"log",
@@ -6647,7 +6638,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.27"
version = "1.1.26"
dependencies = [
"clap 4.3.21",
"lazy_static",
@@ -8636,24 +8627,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "rust-cosmos-broadcaster"
version = "0.1.0"
dependencies = [
"anyhow",
"bip39",
"bs58 0.5.0",
"clap 4.2.7",
"cosmrs",
"nym-bin-common",
"nym-sdk",
"nym-sphinx-addressing",
"nym-sphinx-anonymous-replies",
"nym-validator-client",
"serde",
"serde_json",
]
[[package]]
name = "rocksdb"
version = "0.21.0"
@@ -8687,13 +8660,10 @@ dependencies = [
"netlink-proto",
"nix",
"thiserror",
>>>>>>> 5a8bad45036735f9d088a96490435721179465e7
"tokio",
]
[[package]]
<<<<<<< HEAD
=======
name = "rtp"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8770,7 +8740,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
>>>>>>> 5a8bad45036735f9d088a96490435721179465e7
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-1
View File
@@ -74,7 +74,6 @@ members = [
"common/topology",
"common/types",
"common/wasm-utils",
"demos/rust-cosmos-broadcaster",
"explorer-api",
"explorer-api/explorer-api-requests",
"gateway",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.27"
version = "1.1.26"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.27"
version = "1.1.26"
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"
@@ -303,7 +303,7 @@ impl GeoAwareTopologyProvider {
filter_on: GroupBy,
) -> GeoAwareTopologyProvider {
log::info!(
"Creating geo-aware topology provider with filter on {}",
"Creating geo-aware topology provider with filter on {:?}",
filter_on
);
nym_api_urls.shuffle(&mut thread_rng());
-9
View File
@@ -498,15 +498,6 @@ pub enum GroupBy {
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {}", group),
GroupBy::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for Topology {
fn default() -> Self {
Topology {
+1 -1
View File
@@ -149,7 +149,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
Ok(GatewayWithLatency::new(gateway, avg))
}
pub async fn choose_gateway_by_latency<R: Rng>(
pub(super) async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
-30
View File
@@ -1,30 +0,0 @@
[package]
name = "rust-cosmos-broadcaster"
version = "0.1.0"
authors.workspace = true
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.0", features = ["derive"] }
nym-sphinx-addressing = { path = "../../common/nymsphinx/addressing" }
nym-sdk = { path = "../../sdk/rust/nym-sdk" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
nym-bin-common = { path = "../../common/bin-common" }
bip39 = { workspace = true, features = ["rand"] }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
bs58 = "0.5.0"
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
anyhow.workspace = true
nym-sphinx-anonymous-replies = { path = "../../common/nymsphinx/anonymous-replies" }
[[bin]]
name = "client"
path = "bin/client.rs"
[[bin]]
name = "service"
path = "bin/service.rs"
-35
View File
@@ -1,35 +0,0 @@
### Nym mixnet cosmos tx broadcaster demo
A demo showing how to:
* sign a cosmos tx (simple token transfer) offline
* broadcast this tx from a service on the other side of the mixnet
For the moment the fact its a token transfer is hardcoded. This code could be built out to allow for queries, custom txs, wasm contract interaction, etc but goes beyond the bounds of this demo.
Built using:
* rust sdk
* validator client libs (that will soon be part of the sdk)
#### Useage
```
# compile
cargo build --release
example 1: sign & send in one go
# start service
../../target/release/service
# copy service's nym address to use as value of <SERVICE_NYM_ADDRESS>
# sign tx - when prompted enter 'y'
../../target/release/client offline-sign-tx ${SENDER_MNEMONIC} <RECIPIENT_NYX_ADDRESS> <SERVICE_NYM_ADDRESS>
example 2: create signed tx
# start service
../../target/release/service
# sign tx - when prompted enter 'n' and copy encoded tx bytes from terminal
../../target/release/client offline-sign-tx ${SENDER_MNEMONIC} <RECIPIENT_NYX_ADDRESS> <SERVICE_NYM_ADDRESS>
# send tx using encoded bytes as arg
../../target/release/client send-tx <COPIED_BYTES> <SERVICE_NYM_ADDRESS>
```
-109
View File
@@ -1,109 +0,0 @@
use clap::{Args, Parser, Subcommand};
use nym_sdk::mixnet::Recipient;
use nym_validator_client::nyxd::AccountId;
use rust_cosmos_broadcaster::{
client::{offline_sign, send_tx},
create_client,
};
use nym_bin_common::logging::setup_logging;
#[derive(Debug, Parser)]
#[clap(name = "nym cosmos tx signer ")]
#[clap(
about = "demo binary with which users can perform offline signing and transmission of signed token tx to broadcaster via the mixnet "
)]
struct Cli {
#[clap(subcommand)]
command: Option<Commands>,
}
#[derive(Debug, Subcommand)]
enum Commands {
/// sign a transaction offline
OfflineSignTx(OfflineSignTx),
/// send signed tx to SP for broadcast
SendTx(SendTx),
}
#[derive(Debug, Clone, Args)]
struct OfflineSignTx {
/// mnemonic of signing + sending account (you!)
mnemonic: bip39::Mnemonic,
/// recipient nyx chain address for token transfer
nyx_token_receipient: AccountId,
/// the address of the broadcaster service - this submits txs and queries the chain on our behalf
sp_address: String,
}
#[derive(Debug, Args)]
struct SendTx {
/// the base58 encoded signed payload created in OfflineSign()
base58_payload: String,
/// the address of the broadcaster service - this submits txs and queries the chain on our behalf
sp_address: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
setup_logging();
let cli = Cli::parse();
let mut client = create_client("/tmp/cosmos-broadcaster-mixnet-client-5".into()).await;
let our_address = client.nym_address();
println!("\nclient's nym address: {our_address}");
match cli.command {
Some(Commands::OfflineSignTx(OfflineSignTx {
mnemonic,
nyx_token_receipient,
sp_address,
})) => {
println!("\nsending offline sign info to broadcaster via the mixnet: getting signing account sequence and chain ID");
let sp_address = Recipient::try_from_base58_string(sp_address).unwrap();
let base58_tx_bytes = offline_sign(
mnemonic.clone(),
nyx_token_receipient.clone(),
&mut client,
sp_address,
)
.await?;
println!(
"Encoded response (signed tx data) as base58 for tx broadcast: \n\n{:?}\n",
&base58_tx_bytes
);
println!("do you also wish to send the tx? y/n");
let mut input = String::new();
let stdin = std::io::stdin();
stdin.read_line(&mut input)?;
if input.starts_with('y') {
println!("\nsending pre-signed tx through the mixnet to broadcaster service");
let (tx_hash, success) = send_tx(base58_tx_bytes, sp_address, &mut client).await?;
println!(
"tx hash returned from the broadcaster: {}\ntx was successful: {}",
tx_hash, success
);
} else if input.starts_with('n') {
println!("\nok, you can send the signed tx at a later date by passing the base58 string above as the argument for send-tx");
} else {
println!("\nunrecognised user input");
}
}
Some(Commands::SendTx(SendTx {
base58_payload,
sp_address,
})) => {
let sp_address = Recipient::try_from_base58_string(sp_address).unwrap();
let tx_hash = send_tx(base58_payload.clone(), sp_address, &mut client).await?;
println!("response from the broadcaster (tx hash) {:#?}", tx_hash);
}
None => {
println!("\nno command specified - nothing to do")
}
}
println!("\ndisconnecting client");
client.disconnect().await;
println!("end");
Ok(())
}
@@ -1,56 +0,0 @@
use nym_sphinx_anonymous_replies::{self, requests::AnonymousSenderTag};
use rust_cosmos_broadcaster::{
create_client, listen_and_parse_request,
service::{broadcast, create_broadcaster, get_sequence},
BroadcastResponse, RequestTypes, SequenceRequestResponse,
};
use nym_bin_common::logging::setup_logging;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
setup_logging();
let mut client = create_client("/tmp/cosmos-broadcaster-mixnet-server-3".into()).await;
let our_address = client.nym_address();
println!("\nservice's nym address: {our_address}");
// the httpclient we will use to broadcast our signed tx to the blockchain
let broadcaster = create_broadcaster().await?;
println!("listening for messages, press CTRL-C to exit");
loop {
// listen out for incoming requests from mixnet, parse and match them
let request: (RequestTypes, AnonymousSenderTag) =
listen_and_parse_request(&mut client).await?;
// grab sender_tag from parsed request for anonymous replies
let return_recipient: AnonymousSenderTag = request.1;
match request.0 {
RequestTypes::Sequence(request) => {
println!(
"\nincoming sequence request details:\nsigner address: {} \nquerying blockchain on behalf of requesting client",
request.signer_address
);
// query chain for sequence information on behalf of request sender
let sequence: SequenceRequestResponse =
get_sequence(broadcaster.clone(), request.signer_address).await?;
println!("sequence information returned from chain: account number: {}, sequence:{}, chain id: {} \nsending response to requesting client via mixnet", sequence.account_number, sequence.sequence, sequence.chain_id);
// send serialised sequence response back to request sender via mixnet
client
.send_str_reply(return_recipient, &serde_json::to_string(&sequence)?)
.await;
}
RequestTypes::Broadcast(request) => {
println!(
"\nincoming broadcast request: {}\n",
request.base58_tx_bytes
);
// broadcast the signed tx on behalf of request sender
let tx_hash: BroadcastResponse =
broadcast(request.base58_tx_bytes, broadcaster.clone()).await?;
println!("return recipient surb bucket: {}", &return_recipient);
// send response to tx (transaction hash) back to request sender via mixnet
client
.send_str_reply(return_recipient, &serde_json::to_string(&tx_hash)?)
.await;
}
}
}
}
-125
View File
@@ -1,125 +0,0 @@
use crate::{DEFAULT_DENOM, DEFAULT_PREFIX, DEFAULT_VALIDATOR_RPC};
use bip39;
use bs58;
use cosmrs::bank::MsgSend;
use cosmrs::tx::Msg;
use cosmrs::{tx, AccountId, Coin, Denom};
use nym_sdk::mixnet::MixnetClient;
use nym_sphinx_addressing::clients::Recipient;
use nym_validator_client::nyxd::cosmwasm_client::types;
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
use nym_validator_client::signing::tx_signer::TxSigner;
use nym_validator_client::signing::SignerData;
pub async fn offline_sign(
mnemonic: bip39::Mnemonic,
to: AccountId,
client: &mut MixnetClient,
sp_address: Recipient,
) -> anyhow::Result<String> {
let denom: Denom = DEFAULT_DENOM.parse().unwrap();
let signer = DirectSecp256k1HdWallet::from_mnemonic(DEFAULT_PREFIX, mnemonic.clone());
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
// local 'client' ONLY signing messages
let tx_signer = TxSigner::new(signer);
// sequence request type
let message = crate::SequenceRequest {
validator: DEFAULT_VALIDATOR_RPC.to_owned(), // rpc endpoint for broadcaster to use
signer_address: signer_address.clone(), // our (sender) address, derived from mnemonic
};
// send request to service via the mixnet
client
.send_str(sp_address, &serde_json::to_string(&message)?)
.await;
// listen for response from service
let sp_response = crate::listen_and_parse_response(client).await?;
// match JSON -> ResponseType
let res = match sp_response {
crate::ResponseTypes::Sequence(request) => {
println!(
"got a response to the chain sequence request. using this to sign our tx offline"
);
// use the response to create SignerData instance
let sequence_response = types::SequenceResponse {
account_number: request.account_number,
sequence: request.sequence,
};
let signer_data =
SignerData::new_from_sequence_response(sequence_response, request.chain_id);
// create (and sign) the send message
let amount = vec![Coin {
denom: denom.clone(),
amount: 12345u32.into(),
}];
let send_msg = MsgSend {
from_address: signer_address.clone(),
to_address: to.clone(),
amount,
}
.to_any()
.unwrap();
let memo = "example memo";
let fee = tx::Fee::from_amount_and_gas(
Coin {
denom,
amount: 2500u32.into(),
},
100000,
);
let tx_raw = tx_signer
.sign_direct(&signer_address, vec![send_msg], fee, memo, signer_data)
.unwrap();
let tx_bytes = tx_raw.to_bytes().unwrap();
// encode tx bytes as base58 for ease of logging + copying for user
let base58_tx_bytes = bs58::encode(tx_bytes).into_string();
base58_tx_bytes
}
_ => String::from("unexpected response"),
};
Ok(res)
}
pub async fn send_tx(
base58_tx: String,
sp_address: Recipient,
client: &mut MixnetClient,
) -> anyhow::Result<(String, bool)> {
let broadcast_request = crate::BroadcastRequest {
base58_tx_bytes: base58_tx,
};
// send broadcast request containing base58 encoded signed tx to service via mixnet
client
.send_str(sp_address, &serde_json::to_string(&broadcast_request)?)
.await;
println!("Waiting for reply");
// again, listen for response and parse accordingly
let sp_response = crate::listen_and_parse_response(client).await?;
let res = match sp_response {
crate::ResponseTypes::Broadcast(response) => {
let broadcast_response = crate::BroadcastResponse {
tx_hash: response.tx_hash,
success: response.success,
};
(broadcast_response.tx_hash, broadcast_response.success)
}
_ => (
String::from("Got strange incoming response, couldn't match"),
false,
),
};
Ok(res)
}
-115
View File
@@ -1,115 +0,0 @@
use cosmrs::{tendermint, AccountId};
use nym_sdk::mixnet::{
AnonymousSenderTag, MixnetClient, MixnetClientBuilder, ReconstructedMessage, StoragePaths,
};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub mod client;
pub mod service;
pub const DEFAULT_VALIDATOR_RPC: &str = "https://sandbox-validator1.nymtech.net";
pub const DEFAULT_DENOM: &str = "unym";
pub const DEFAULT_PREFIX: &str = "n";
#[derive(Deserialize, Serialize, Debug)]
pub struct SequenceRequest {
pub validator: String,
pub signer_address: AccountId,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct SequenceRequestResponse {
pub account_number: u64,
pub sequence: u64,
pub chain_id: tendermint::chain::Id,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct BroadcastRequest {
pub base58_tx_bytes: String,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct BroadcastResponse {
pub tx_hash: String,
pub success: bool,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum RequestTypes {
Sequence(SequenceRequest),
Broadcast(BroadcastRequest),
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ResponseTypes {
Sequence(SequenceRequestResponse),
Broadcast(BroadcastResponse),
}
pub async fn create_client(config_path: PathBuf) -> MixnetClient {
let config_dir = config_path;
let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.unwrap()
.build()
.await
.unwrap();
client.connect_to_mixnet().await.unwrap()
}
// parse returned response from service: ignore empty SURB data packets + parse incoming message to struct or error
// we know we are expecting JSON here but an irl helper would parse conditionally on bytes / string incoming
pub async fn listen_and_parse_response(client: &mut MixnetClient) -> anyhow::Result<ResponseTypes> {
let mut message: Vec<ReconstructedMessage> = Vec::new();
// get the actual message - discard the empty vec sent along with the SURB topup request
while let Some(new_message) = client.wait_for_messages().await {
if new_message.is_empty() {
println!("got a request for more SURBs from service - sending top up SURBs");
continue;
}
message = new_message;
break;
}
// parse vec<u8> -> JSON String
let mut parsed = String::new();
if let Some(r) = message.iter().next() {
parsed = String::from_utf8(r.message.clone())?;
}
let sp_response: crate::ResponseTypes = serde_json::from_str(&parsed)?;
Ok(sp_response)
}
// parse incoming request: parse incoming message to struct + get sender_tag for SURB reply
// we know we are expecting JSON here but an irl helper would parse conditionally on bytes / string incoming
pub async fn listen_and_parse_request(
client: &mut MixnetClient,
) -> anyhow::Result<(RequestTypes, AnonymousSenderTag)> {
let mut message: Vec<ReconstructedMessage> = Vec::new();
// get the actual message - discard the empty vec sent along with the SURBs
while let Some(new_message) = client.wait_for_messages().await {
if new_message.is_empty() {
continue;
}
message = new_message;
break;
}
// parse vec<u8> -> JSON String
let mut parsed = String::new();
if let Some(r) = message.iter().next() {
parsed = String::from_utf8(r.message.clone())?;
}
let client_request: crate::RequestTypes = serde_json::from_str(&parsed)?;
// get the sender_tag for anon reply
let return_recipient = message[0].sender_tag.unwrap();
Ok((client_request, return_recipient))
}
@@ -1,69 +0,0 @@
use crate::DEFAULT_VALIDATOR_RPC;
use bs58;
use cosmrs::rpc::{Client, HttpClient};
use cosmrs::{tendermint, AccountId};
use nym_validator_client::nyxd::{error::NyxdError, CosmWasmClient};
pub async fn create_broadcaster() -> anyhow::Result<HttpClient> {
let broadcaster: HttpClient = HttpClient::new(DEFAULT_VALIDATOR_RPC)?;
Ok(broadcaster)
}
pub async fn get_sequence(
broadcaster: HttpClient,
signer_address: AccountId,
) -> Result<crate::SequenceRequestResponse, NyxdError> {
// get signer information
let sequence = broadcaster.get_sequence(&signer_address).await?;
let chain_id: tendermint::chain::Id = broadcaster.get_chain_id().await?;
Ok(crate::SequenceRequestResponse {
account_number: sequence.account_number,
sequence: sequence.sequence,
chain_id,
})
}
pub async fn broadcast(
base58_tx_bytes: String,
broadcaster: HttpClient,
) -> anyhow::Result<crate::BroadcastResponse> {
// decode the base58 tx to vec<u8>
let tx_bytes = bs58::decode(base58_tx_bytes).into_vec()?;
// this is our sender address hardcoded for ease of the demo logging
let from_address: AccountId = "n19wln95zj5r3wnepgk6nf7lqx0zgufvgtlvyawf".parse().unwrap();
// compare balances from before and after the tx
let before = broadcaster
.get_balance(&from_address, "unym".to_string())
.await
.unwrap()
.unwrap();
// broadcast the tx
println!("broadcasting tx to validator");
let broadcast_res = Client::broadcast_tx_commit(&broadcaster, tx_bytes.into())
.await
.unwrap();
let after = broadcaster
.get_balance(&from_address, "unym".to_string())
.await
.unwrap()
.unwrap();
println!(
"returned transaction hash: {:#?}",
broadcast_res.hash.to_string()
);
println!("balance before transaction: {before}");
println!("balance after transaction: {after}");
println!("returning tx hash to sender");
let success: bool = broadcast_res.deliver_tx.code.is_ok();
Ok(crate::BroadcastResponse {
tx_hash: serde_json::to_string(&broadcast_res.hash).unwrap(),
success,
})
}
+1 -7
View File
@@ -1,13 +1,7 @@
# Documentation
## Doc projects
Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`).
* `docs` contains technical documentation hosted at [https://nymtech.net/docs](https://nymtech.net/docs)
* `dev-portal` contains developer documentation hosted at [https://nymtech.net/developers](https://nymtech.net/developers)
* `operators` contains node setup and maintenance guides hosted at [https://nymtech.net/operators](https://nymtech.net/operators)
## Scripts
* `bump_versions.sh` allows you to update the `platform_release_version` and `wallet_release_version` variables in the `book.toml` of each mdbook project at once. You can also optionally update the `minimum_rust_version` as well. Helpful for lazy-updating when cutting a new version of the docs.
* `build_all_to_dist.sh` is used by the `ci-dev.yml` and `cd-dev.yml` scripts for building all mdbook projects and moving the rendered html to `../dist/` to be rsynced with various servers.
Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`).
+32 -40
View File
@@ -1,45 +1,37 @@
#!/bin/bash
# this is a script called by the github CI and CD workflows to build all 3 docs projects
# and move them to /dist/ in the root of the monorepo. They are rsynced to various servers
# from there by subsequent workflow tasks.
# commands assume you run script from `nym/documentation/`
# array of project dirs
declare -a projects=("docs" "dev-portal" "operators")
# check you're calling from the right place
if [ $(pwd | awk -F/ '{print $NF}') != "documentation" ]
then
echo "failure: please run script from documentation/"
else
for i in "${projects[@]}"
do
# cd to project dir
cd "./$i" &&
# little sanity checks
echo $(pwd) && echo $(mdbook --version) &&
# clean old book
echo "cleaning old book"
rm -rf ./book/
# build book
mdbook build
# check for destination, if ! then mkdir & check again else echo thumbs up
if [ ! -d ../../dist/docs/$i ]; then
echo "dest doesn't exist: creating dir"
mkdir -p ../../dist/docs/$i
fi
if [ -d ../../dist/docs/$i ]; then
echo "cp destination exists, all good"
fi
# clean old dist/$i
rm -rf ../../dist/docs/$i
# move newly rendered book/ to dist
rsync -r ./book/html/ ../../dist/docs/$i
# sanity check
ls -laF ../../dist/docs/
# cd back to ../documentation/
cd ../
done
# rename for server paths
rm -rf ../dist/docs/developers
mv ../dist/docs/dev-portal ../dist/docs/developers
fi
## now loop through the above array
for i in "${projects[@]}"
do
# cd to project dir
cd "./$i" &&
# little sanity checks
echo $(pwd) && echo $(mdbook --version) &&
# clean old book
echo "cleaning old book"
rm -rf ./book/
# build book
mdbook build
# check for destination, if ! then mkdir & check again else echo thumbs up
if [ ! -d ../../dist/docs/$i ]; then
echo "dest doesn't exist: creating dir"
mkdir -p ../../dist/docs/$i
fi
if [ -d ../../dist/docs/$i ]; then
echo "cp destination exists, all good"
fi
# clean old dist/$i
rm -rf ../../dist/docs/$i
# move newly rendered book/ to dist
rsync -r ./book/html/ ../../dist/docs/$i
# sanity check
ls -laF ../../dist/docs/
# cd back to ../documentation/
cd ../
done
mv ../dist/docs/dev-portal ../dist/docs/developers
-41
View File
@@ -1,41 +0,0 @@
#!/usr/bin/env bash
# this takes two args: platform release version and wallet release version.
# it then uses sed to bump them in the three book.toml files.
#
# e.g if the upcoming platform release was v1.1.29 and the release version 1.2.9 you'd run this as:
# `./bump_versions.sh "1.1.29" "1.2.9"`
#
# you can also set the minumum rust version by passing an optional 3rd argument:
# `./bump_versions.sh "1.1.29" "1.2.9" "1.67"`
# array of project dirs
declare -a projects=("docs" "dev-portal" "operators")
# check number of args passed
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ];
then
echo "failure: please pass at least 2 and at most 3 args: "
echo "./bump_version.sh <new platform_release_version> <new wallet_release_version> [OPTIONAL]<new minimum_rust_version>"
exit 0
fi
# check you're calling from the right place
if [ $(pwd | awk -F/ '{print $NF}') != "documentation" ]
then
echo "failure: please run script from documentation/"
exit 0
else
## now loop through the above array sed-ing the variable values in the book.toml files
for i in "${projects[@]}"
do
# sed the vars in the book.toml file for each project
echo "setting platform and wallet versions in $i/"
sed -i 's/platform_release_version =.*/platform_release_version = "'$1'"/' "$i"/book.toml
sed -i 's/wallet_release_version =.*/wallet_release_version = "'$2'"/' "$i"/book.toml
if [ "$3" ]
then
echo "setting minimum rust version in $i/"
sed -i 's/minimum_rust_version = .*/minimum_rust_version = "'$3'"/' "$i"/book.toml
fi
done
fi
+1 -3
View File
@@ -17,6 +17,4 @@ book
theme/
theme
theme/*
.idea
theme/*
+2 -3
View File
@@ -40,7 +40,6 @@ section-line-height = "1.5em"
# if true, never read and touch the files in theme dir
turn-off = false
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
@@ -49,8 +48,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables]
minimum_rust_version = "1.66"
platform_release_version = "1.1.29"
wallet_release_version = "1.2.8"
platform_release_version = "v1.1.28"
wallet_release_version = "v1.2.7"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+8 -20
View File
@@ -20,7 +20,6 @@
- [NymConnect Monero](tutorials/monero.md)
- [NymConnect Matrix](tutorials/matrix.md)
- [NymConnect Telegram](tutorials/telegram.md)
# Integrations
@@ -31,25 +30,14 @@
# Tutorials
- [Rust SDK](tutorials/rust-sdk.md)
- [Blockchain Service pt1](tutorials/cosmos-service/intro.md)
- [Tutorial Overview](tutorials/cosmos-service/overview.md)
- [Preparing Your Environment](tutorials/cosmos-service/preparing-env.md)
- [Preparing Your Lib](tutorials/cosmos-service/lib.md)
- [Preparing Your Client](tutorials/cosmos-service/client.md)
- [Preparing Your Client pt2](tutorials/cosmos-service/client-src.md)
- [Preparing Your Service](tutorials/cosmos-service/service.md)
- [Preparing Your Service pt2](tutorials/cosmos-service/service-src.md)
- [Querying the Chain](tutorials/cosmos-service/querying.md)
- [Typescript](tutorials/typescript.md)
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
- [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md)
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
- [Simple Service Provider](tutorials/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
- [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md)
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
- [IPFS Service Provider (coming soon)](tutorials/ipfs-service-provider.md)
# Community Resources
@@ -36,11 +36,12 @@ For example, a Service Provider could receive a request to check a mail server a
Maybe you would like to concentrate on building a application that uses the mixnet:
* Explore the Tutorials section of the Developer Portal. Our in-depth tutorial on [Building a Simple Service Provider](../tutorials/simple-service-provider/simple-service-provider.md) give a good understanding of building User Clients and Service Providers in TypeScript, and how to configure Nym Websocket Clients for seamless communication with the mixnet.
* Explore the Tutorials section of the Developer Portal. Our in-depth tutorial on [Building a Simple Service Provider](../tutorials/simple-service-provider.md) give a good understanding of building User Clients and Service Providers in TypeScript, and how to configure Nym Websocket Clients for seamless communication with the mixnet.
* Get started with using the Nym Mixnet quickly and easily by exploring the [Quickstart](../quickstart/overview.md) options, such a NymConnect, proxying traffic through the Nym Socks5 client, or dive into integrating Nym into your existing application with the [Integrations](../integrations/integration-options.md) section.
Or perhaps you a developer that would like to run a infrastructure node such as a Gateway, Mix node or Network Requestor:
* Check out the [Network Overview](https://nymtech.net/docs/architecture/network-overview.html) docs page.
Or perhaps you a developer that would like to run a infrastructure node such as a Gateway, Mix node or Network Requestor:
* Check out the [Network Overview](https://nymtech.net/docs/architecture/network-overview.html) docs page.
* Take a look at our [Node Setup Guide](https://nymtech.net/docs/nodes/setup-guides.html) with our Nym Docs, containing setup guides for setting up you own infrastructure node.
@@ -1,19 +1,19 @@
# Integrating with Nym for network privacy
If you are wanting to integrate Nym by using the Mixnet as a transport layer for application traffic, you will have to run one of the three Nym clients in order to connect to the Mixnet.
If you are wanting to integrate Nym by using the Mixnet as a transport layer for application traffic, you will have to run one of the three Nym clients in order to connect to the Mixnet.
## Connecting applications to the mixnet
### SDK support
### SDK support
If your app is written in Typescript or Rust, then you can use the [Typescript](https://nymtech.net/docs/sdk/typescript.html) or [Rust](https://nymtech.net/docs/sdk/rust.html) SDKs. These SDKs abstract away much of the messaging logic from your app, and allow you to run a Nym client as part of your application process, instead of having to run them seperately.
### Choosing a client
In order to connect your application to the mixnet, you need to select one of three clients to use. These clients do the majority of the heavy-lifting with regards to cryptographic operations and routing under the hood, and all do basically the same thing: create a connection to a gateway, encrypt and decrypt packets sent to and received from the mixnet, and send cover traffic to hide the flow of actual app traffic from observers.
In order to connect your application to the mixnet, you need to select one of three clients to use. These clients do the majority of the heavy-lifting with regards to cryptographic operations and routing under the hood, and all do basically the same thing: create a connection to a gateway, encrypt and decrypt packets sent to and received from the mixnet, and send cover traffic to hide the flow of actual app traffic from observers.
As outlined in the [clients overview documentation](https://nymtech.net/docs/clients/overview.html) there are three clients availiable to developers to use when connecting applications to the mixnet:
As outlined in the [clients overview documentation](https://nymtech.net/docs/clients/overview.html) there are three clients availiable to developers to use when connecting applications to the mixnet:
#### Websocket client
Your first option is the native websocket client. This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with any language that supports websockets.
Your first option is the native websocket client. This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with any language that supports websockets.
You can see an example of how to connect to and manage interactions with this client in the [Simple Service Provider tutorial](../tutorials/simple-service-provider/simple-service-provider.md).
You can see an example of how to connect to and manage interactions with this client in the [Simple Service Provider tutorial](../tutorials/simple-service-provider.md).
#### Webassembly client
If youre working in JavaScript or Typescript in the browser, or building an edge computing app, youll likely want to choose the webassembly client.
@@ -27,16 +27,16 @@ You can find example code in the [examples section](https://github.com/nymtech/n
#### SOCKS client
This client is useful for allowing existing applications to use the Nym mixnet without any code changes. All thats necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc).
Its less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes.
Its less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes.
You can find examples of how to utilise this client in the [Quickstart](../quickstart/socks-proxy.md) section, and the [SOCKS5 documentation](https://nymtech.net/docs/clients/socks5-client.html).
You can find examples of how to utilise this client in the [Quickstart](../quickstart/socks-proxy.md) section, and the [SOCKS5 documentation](https://nymtech.net/docs/clients/socks5-client.html).
## Recommended infrastructure setup
In order to ensure uptime and reliability, it is recommended that you run some pieces of mixnet infrastructure. What infrastructure is necessary to run depends on the architecture of your application, and the endpoints that it needs to hit!
## Recommended infrastructure setup
In order to ensure uptime and reliability, it is recommended that you run some pieces of mixnet infrastructure. What infrastructure is necessary to run depends on the architecture of your application, and the endpoints that it needs to hit!
* If you're running a purely P2P application, then just integrating clients and having some method of sharing addresses should be enough to route your traffic through the mixnet.
* If you're wanting to place the mixnet between your users' application instances and a server-based backend, you can use the [network requester](https://nymtech.net/docs/nodes/network-requester-setup.html) service provider binary to proxy these requests to your application backend, with the mixnet 'between' the user and your service, in order to prevent metadata leakage being broadcast to the internet.
* If you're wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the [community applications](../community-resources/community-applications-and-guides.md) page.
* If you're running a purely P2P application, then just integrating clients and having some method of sharing addresses should be enough to route your traffic through the mixnet.
* If you're wanting to place the mixnet between your users' application instances and a server-based backend, you can use the [network requester](https://nymtech.net/docs/nodes/network-requester-setup.html) service provider binary to proxy these requests to your application backend, with the mixnet 'between' the user and your service, in order to prevent metadata leakage being broadcast to the internet.
* If you're wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the [community applications](../community-resources/community-applications-and-guides.md) page.
## Example application traffic flow
### Initialization
@@ -44,36 +44,36 @@ First, we need to initalise an app and connect it to Nym.
```
+-----------+
| Gateway |
+-----------+
| Gateway |
+-----------+
^
|
|
|
|
|
|
+-------------------+
| +---------------+ |
| | Nym client | |
| +---------------+ |
| ^ |
| | |
| | |
| | |
| v |
| +---------------+ |
| | Your app code | |
| +---------------+ |
+-------------------+
Your Local Machine
^
|
|
|
|
|
|
+-------------------+
| +---------------+ |
| | Nym client | |
| +---------------+ |
| ^ |
| | |
| | |
| | |
| v |
| +---------------+ |
| | Your app code | |
| +---------------+ |
+-------------------+
Your Local Machine
```
At the bottom we have an app. It consists of two parts:
* your application specific logic
* your Nym client - either running as a standalone process, or as part of the process of your app code if you're using an SDK
* your application specific logic
* your Nym client - either running as a standalone process, or as part of the process of your app code if you're using an SDK
Nym apps have a stable, potentially long-lasting relation to a gateway node. A client will register itself with a gateway, and get back an authentication token that it can then use to retrieve messages from the gateway later on.
@@ -89,40 +89,40 @@ The Nym client part of the app accepts messages from your code and automatically
The app has now connected to the Gateway, but we haven't sent a message to ourselves yet. Let's do that now.
```
+----------+ +----------+ +----------+
| Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
| Layer 1 | | Layer 2 | | Layer 3 |
+----------+ +----------+ +----------+
^ ^
| |
+----------+ +----------+ +----------+
| Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
| Layer 1 | | Layer 2 | | Layer 3 |
+----------+ +----------+ +----------+
^ ^
| |
|<--------------------------------------------------+
|
v
+--------------+
| Your gateway |
+--------------+
^
|
|
v
+-------------------+
| +---------------+ |
| | Nym client | |
| +---------------+ |
| ^ |
| | |
| | |
| v |
| +---------------+ |
| | Your app code | |
| +---------------+ |
+-------------------+
Your Local Machine**
|
v
+--------------+
| Your gateway |
+--------------+
^
|
|
v
+-------------------+
| +---------------+ |
| | Nym client | |
| +---------------+ |
| ^ |
| | |
| | |
| v |
| +---------------+ |
| | Your app code | |
| +---------------+ |
+-------------------+
Your Local Machine**
** note that depending on the technical setup, the Nym client running on this machine may
be either a seperate process or embedded in the same process as the app code via one of our SDKs.
be either a seperate process or embedded in the same process as the app code via one of our SDKs.
```
Let's say your code code pokes a message `hello world` into the Nym client. The Nym client automatically wraps that message up into a layer encrypted Sphinx packet, adds some routing information and encryption, and sends it to its own gateway. The gateway strips the first layer of encryption, ending up with the address of the first mixnode it should forward to, and a Sphinx packet.
@@ -139,37 +139,38 @@ Messages are end-to-end encrypted. Although the gateway knows our app's IP when
The process for sending messages to other apps is exactly the same, you simply specify a different recipient address. Address discovery happens outside the Nym system: in the case of a Service Provider app, the service provider has presumably advertised its own address. If you're sending to a friend of yours, you'll need to get a hold of their address out of band, maybe through a private messaging app such as Signal.
```
+----------+ +----------+ +----------+
| Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
| Layer 1 | | Layer 2 | | Layer 3 |
+----------+ +----------+ +----------+
^ ^
| |
| |
v v
+--------------+ +-----------------+
| Your gateway | | Service gateway |
+--------------+ +-----------------+
^ ^
| |
| |
v v
+-------------------+ +-------------------+
| +---------------+ | | +---------------+ |
| | Nym client | | | | Nym Client | |
| +---------------+ | | +---------------+ |
| ^ | | ^ |
| | | | | |
| | | | | |
| v | | v |
| +---------------+ | | +---------------+ |
| | Your app code | | | | Service Code | |
| +---------------+ | | +---------------+ |
+-------------------+ +-------------------+
Your Local Machine** Service Provider Machine**
+----------+ +----------+ +----------+
| Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
| Layer 1 | | Layer 2 | | Layer 3 |
+----------+ +----------+ +----------+
^ ^
| |
| |
v v
+--------------+ +-----------------+
| Your gateway | | Service gateway |
+--------------+ +-----------------+
^ ^
| |
| |
v v
+-------------------+ +-------------------+
| +---------------+ | | +---------------+ |
| | Nym client | | | | Nym Client | |
| +---------------+ | | +---------------+ |
| ^ | | ^ |
| | | | | |
| | | | | |
| v | | v |
| +---------------+ | | +---------------+ |
| | Your app code | | | | Service Code | |
| +---------------+ | | +---------------+ |
+-------------------+ +-------------------+
Your Local Machine** Service Provider Machine**
** note that depending on the technical setup, the Nym client running on these machines may
be either a seperate process or embedded in the same process as the app code via one of our SDKs.
be either a seperate process or embedded in the same process as the app code via one of our SDKs.
```
@@ -1,59 +0,0 @@
# Preparing Your Client pt2
Open `src/client.rs`. This is where the logic of the command from the `match` statement in `bin/client.rs` is defined.
# Dependencies
```rust
use crate::{handle_response, wait_for_non_empty_message, RequestTypes, DEFAULT_VALIDATOR_RPC};
use cosmrs::AccountId;
use nym_sdk::mixnet::MixnetClient;
use nym_sphinx_addressing::clients::Recipient;
use nym_validator_client::nyxd::Coin;
```
As well as importing message-handling functionality, request types, and the default RPC endpoint, this file relies on the `AccountId` type to construct blockchain addresses, the `MixnetClient` for interacting with the mixnet, the `Recipient` type to construct mixnet recipient addresses, and the `Coin` type for properly handling the returned balance of the account that will be queried.
# Querying via the Mixnet
The following is used to construct a `BalanceRequest`, send this to the supplied `service` address, and then handle the response, matching it to a `ResponseType` (in this case the only expected response, a `BalanceResponse`).
The actual sending of the request is performed by `client.send_bytes`: sending the serialised `BalanceRequest` to the supplied Nym address (the `Recipient` imported from the `nym_sphinx_addressing` crate). It is sending the default number of SURBs along with the message, defined [here](https://github.com/nymtech/nym/blob/develop/sdk/rust/nym-sdk/src/mixnet/client.rs#L34).
```rust
pub async fn query_balance(
account: AccountId,
client: &mut MixnetClient,
sp_address: Recipient,
) -> anyhow::Result<Coin> {
// construct balance request
let message = RequestTypes::Balance(crate::BalanceRequest {
validator: DEFAULT_VALIDATOR_RPC.to_owned(), // rpc endpoint for broadcaster to use
account,
});
// send serialised request to service via mixnet
client
<<<<<<< HEAD
.send_message(sp_address, message.serialize(), Default::default())
=======
.send_bytes(sp_address, message.serialize(), Default::default())
>>>>>>> e504def9e40b472f224244cfa8d58a94c2e48d20
.await;
let received = wait_for_non_empty_message(client).await?;
// listen for response from service
let sp_response = handle_response(received)?;
// match JSON -> ResponseType
let res = match sp_response {
crate::ResponseTypes::Balance(response) => {
println!("{:#?}", response);
response.balance
}
};
Ok(res)
}
```
That is all the client code written: now to move on to the `service` that will be interacting with the blockchain on behalf of the `client`.
@@ -1,79 +0,0 @@
# Preparing Your Client
Start by creating the startup logic of your `client` in `bin/client.rs` - creating a Nym client and connecting to the mixnet (or just connecting if your client has been started before and config already exists for it), and defining and running commands.
## Dependencies
Import the following dependencies:
```
use clap::{Args, Parser, Subcommand};
use chain_query::{client::query_balance, create_client};
use nym_sdk::mixnet::Recipient;
use nym_validator_client::nyxd::AccountId;
use nym_bin_common::logging::setup_logging;
```
`clap` is used so different commands can be passed to the `client` (even though we're only defining one function in this first part of the tutorial, more will be added in subsequent chapters). `nym_sdk::mixnet::Recipient` is the type used to define the recipient of a mixnet message, `nym_bin_common::logging::setup_logging` is the logging setup for `client`'s Nym client, and `chain_query` imports the `create_client` and `query_balance` functions created on the previous page.
## CLI Command with Clap
The following simply defines the commands that the client can perform. For the moment, there is only one: the `query_balance` function created in the previous section.
As with the data structures, this structure is being used for ease of adding future commands in subsequent tutorials.
```rust
#[derive(Debug, Parser)]
#[clap(name = "rust sdk demo - chain query service")]
#[clap(about = "query the sandbox testnet blockchain via the mixnet... part 2 coming soon")]
struct Cli {
#[clap(subcommand)]
command: Option<Commands>,
}
#[derive(Debug, Subcommand)]
enum Commands {
QueryBalance(QueryBalance),
}
#[derive(Debug, Args)]
struct QueryBalance {
/// the account we want to query
account: AccountId,
/// the address of the broadcaster service - this submits txs and queries the chain on our behalf
sp_address: String,
}
```
## `main()`
This is the root logic of the `client`. Using `[tokio](https://tokio.rs/)` for the async runtime, this function performs the following functions:
* If not already existing, create a Nym client with config at `/tmp/client`. Otherwise load the already existing client from this config.
* Matche the command from the CLI - in this instance, the `QueryBalance` function which will be defined in the next section. This creates a `BalanceRequest` and sends this to the `service`, before returning the response back to the main thread and print this to the console.
* Perform a proper shutdown of the Nym client.
```rust
#[tokio::main]
async fn main() -> anyhow::Result<()> {
setup_logging();
let cli = Cli::parse();
let mut client = create_client("/tmp/client2".into()).await;
let our_address = client.nym_address();
println!("\nclient's nym address: {our_address}");
match cli.command {
Some(Commands::QueryBalance(QueryBalance {
account,
sp_address,
})) => {
println!("\nsending bank balance request to service via mixnet");
let sp_address = Recipient::try_from_base58_string(sp_address).unwrap();
let returned_balance = query_balance(account, &mut client, sp_address).await?;
println!("\nreturned balance is: {}", returned_balance);
}
None => {
println!("\nno command specified - nothing to do")
}
}
println!("\ndisconnecting client");
client.disconnect().await;
println!("client disconnected");
Ok(())
}
```
@@ -1,7 +0,0 @@
# Interacting with a Cosmos SDK Blockchain via the Mixnet with the Rust SDK
This tutorial is for Rust developers wanting to interact with the Rust SDK and take a first step at building a service with which to interact with a Cosmos SDK blockchain.
The key here is to think of the service as a proxy: it interacts with the blockchain _on the client's behalf_, shielding the client from the Validator it interacts with, whilst also being shielded from the client by the mixnet.
> This service also nicely highlights the limitations of the mixnet - even though with this code your metadata is shielded from the Validator, and even the service does not know your Nym address, application-level information such as a blockchain address is not made private, in virtue of the fact that using the mixnet provides solely network-level privacy. For information on what application-level privacy Nym offers, check out the [coconut credential SDK example](https://nymtech.net/docs/sdk/rust.html#coconut-credential-generation).
@@ -1,165 +0,0 @@
# Preparing Your Lib
Now move on to preparing shared data structures and functions in `src/lib.rs`.
These include the request and response types the client and the service will be passing through the mixnet, as well as shared functions such as client creation, and message parsing.
## Dependencies
The dependencies for the shared `lib` file are the following:
```rust
use anyhow::bail;
use cosmrs::AccountId;
use nym_sdk::mixnet::{
AnonymousSenderTag, MixnetClient, MixnetClientBuilder, ReconstructedMessage, StoragePaths,
};
use nym_validator_client::nyxd::Coin;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub mod client;
pub mod service;
```
Since this is the file where client creation and message parsing are handled, the various `nym_sdk` imports, as well as `serde`'s (de)serialisation functionality, are required. `PathBuf` is for reading filepaths, `cosmrs` types are required for defining Nyx blockchain accounts, and the `Coin` type from the `nyxd_validator_client` is for our Coin balance request and response. `anyhow` is for easy error handing.
## Constants
Below this are the chain-related `const` variables. These have been hardcoded for this demo.
```rust
pub const DEFAULT_VALIDATOR_RPC: &str = "https://sandbox-validator1.nymtech.net";
pub const DEFAULT_DENOM: &str = "unym";
pub const DEFAULT_PREFIX: &str = "n";
```
These define the RPC endpoint your service will use to interact with the blockchain - in this case the Sandbox testnet - as well as the expected coin denomination, and Bech32-prefix of addresses.
## Shared Data Structures
Define the following structs for our different request and responses that will be serialised and sent through the mixnet between your client and service binaries:
```rust
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct BalanceRequest {
pub validator: String,
pub account: AccountId,
}
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct BalanceResponse {
pub balance: Coin,
}
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum RequestTypes {
Balance(BalanceRequest),
}
impl RequestTypes {
pub fn serialize(&self) -> Vec<u8> {
serde_json::to_vec(self).expect("serde failure")
}
pub fn try_deserialize<M: AsRef<[u8]>>(raw: M) -> anyhow::Result<Self> {
serde_json::from_slice(raw.as_ref()).map_err(Into::into)
}
}
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum ResponseTypes {
Balance(BalanceResponse),
}
impl ResponseTypes {
pub fn serialize(&self) -> Vec<u8> {
serde_json::to_vec(self).expect("serde failure")
}
pub fn try_deserialize<M: AsRef<[u8]>>(raw: M) -> anyhow::Result<Self> {
serde_json::from_slice(raw.as_ref()).map_err(Into::into)
}
}
```
The above data types are pretty straightforward. Even though there are only one instance of a request type (sent from `client` -> mixnet -> `service`) and one of a response type (`service` -> mixnet -> `client`) so far, a pair of enums has been defined to contain additional response and request types that will be added in part 2 of this tutorial, when adding credential functionality.
`BalanceRequest` will be used when requesting the service to query the token balance of the supplied address on the client's behalf. You can see the information that will be returned from the chain to the service, and from the service to the client, in `BalanceResponse`.
Custom serialistion and deserialisation have been implemented for each enum for ease of future modification and testing.
## Shared Functions
Now to define functions shared by the `client` and `service` binaries.
### Client Creation
The following function is called on startup by each binary, with the `config_path` being a filepath for storing client config:
```rust
// create our client with specified path for key storage
pub async fn create_client(config_path: PathBuf) -> MixnetClient {
let config_dir = config_path;
let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.unwrap()
.build()
.await
.unwrap();
client.connect_to_mixnet().await.unwrap()
}
```
If no config files exist at the location designated by `config_path` (in this case `/tmp/service`) then the following files are generated:
```sh
service
├── ack_key.pem
├── db.sqlite
├── db.sqlite-shm
├── db.sqlite-wal
├── gateway_details.json
├── gateway_shared.pem
├── persistent_reply_store.sqlite
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
1 directory, 11 files
```
> If keys and config already exist at this location, re-running this function **will not** overwrite them.
### Listening for & Parsing Incoming messages
Next to define two functions: one for listening _for_ messages from the mixnet (used by `service`), and one for handling a _response_ to a request (used by `client`).
Both functions attempt to deserialise the vec of `ReconstructedMessages` that are reconstructed by the client from delivered Sphinx packets after decryption.
`handle_request` performs one additional function - parsing the `sender_tag` from the incoming reconstructed message. This is the randomised alphanumeric string used to identify a bucket of _SURBs_ (Single Use Reply Blocks) that are sent along with any outgoing message by default. More information about them can be found [here](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs) but all that is necessary to know for now is that these are pre-addressed packets that clients send out with their messages. Any reply to their message that is to be sent back to them back be written to the payload of these packets, but without the replying party being able to see the destination that the reply is being sent to. This allows for services to **anonymously reply to clients without being able to doxx them by knowing their Nym address**.
```rust
pub fn handle_response(message: ReconstructedMessage) -> anyhow::Result<ResponseTypes> {
ResponseTypes::try_deserialize(message.message)
}
pub fn handle_request(
message: ReconstructedMessage,
) -> anyhow::Result<(RequestTypes, Option<AnonymousSenderTag>)> {
let request = RequestTypes::try_deserialize(message.message)?;
Ok((request, message.sender_tag))
}
```
Before moving on to the `client` and `service` code, one more function is needed. This allows for both binaries to parse empty incoming messages that they might receive. This is necessary as incoming SURBs, as well as requests for more SURBs, contain empty data fields.
```rust
pub async fn wait_for_non_empty_message(
client: &mut MixnetClient,
) -> anyhow::Result<ReconstructedMessage> {
while let Some(mut new_message) = client.wait_for_messages().await {
if !new_message.is_empty() {
return Ok(new_message.pop().unwrap());
}
}
bail!("did not receive any non-empty message")
}
```
@@ -1,47 +0,0 @@
# Tutorial Overview
This tutorial involves writing two pieces of code in Rust:
- A client side binary used to construct a blockchain query and send this query to a service, which will query the Cosmos SDK blockchain and then pass the response back to the client (bear in mind this principle works for all blockchains - we're just utilising the `cosmrs` library to interact with the Sandbox testnet blockchain in this tutorial). This query will be to query the balance of an account, in preparation for spending these tokens on a [bandwidth credential](https://nymtech.net/docs/bandwidth-credentials.html) in a subsequent tutorial.
- A service which will listen out for requests from the mixnet, act on those requests, and anonymously reply to the client sending the requests.
You will learn how to do the following with the Rust SDK:
- Create clients with manual storage settings.
- Parse incoming traffic from the mixnet and reply anonymously using [SURBs](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs).
> Services usually run on remote servers to assure reliable uptime and to unlink sender and receiver metadata. For demonstration purposes however, you will run both components on your local machine, looping messages through the mixnet to yourself.
```
TODO
REDRAW
ASCII
DIAGRAM
FROM
PREVIOUS
TUTORIAL
```
You can find the code for these components [here](https://github.com/nymtech/developer-tutorials). You can use it as a reference while building or simply download it and follow along as you progress through the tutorial.
Notice that this tutorial attempts to use very few external libraries. This tutorial is not showing you how to build production-grade code, but **to understand how to connect and send messages to, as well as receive messages from, the mixnet.**
```admonish note title="Sidenote: What is a Service / Service Provider?"
'Service' or 'Service Provider' are catchall names used to refer to any type of app that can communicate with the mixnet via a Nym client - in this case, one embedded in its app process via the Rust SDK.
The first SP to have been released is the [Network Requester](https://nymtech.net/docs/nodes/network-requester-setup.html) - a binary that receives a network request from the mixnet, performs that request (e.g. authenticating with a message server and receiving new messages for a user) and then passes the response back to the user who requested it anonymously, shielding their metadata from the message server.
The SP you will build in this tutorial is far more simple than this, showing you how to approach building something that can:
* connect to the mixnet,
* listen for messages, and
* perform some action with them - in this case, query a Cosmos SDK blockchain.
However, once you see how easy it is to integrate with the mixnet for traffic transport, you will be able to build apps with real-world uses easily.
```
@@ -1,62 +0,0 @@
# Preparing Your Environment
## Prerequisites
* `Rust` & `cargo`
## Creating your Project Structure
* Make a new cargo project:
```
cargo new nym-cosmos-service
```
* Create the following directory structure and files:
```
.
├── Cargo.toml
├── bin
│   ├── client.rs
│   └── service.rs
└── src
├── client.rs
├── lib.rs
└── service.rs
3 directories, 6 files
```
* Add the following dependencies to your `Cargo.toml` file:
```
[dependencies]
clap = { version = "4.0", features = ["derive"] }
cosmrs = "=0.14.0"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
serde = "1.0.152"
serde_json = "1.0.91"
anyhow = "1.0.72"
```
These are non Nym-specific dependencies for the project. `clap` is for setting up the CLI commands, `cosmrs` for cosmos-specific types and functionality, `tokio` for the async/await environment, and `serde` for (de)serialisation. `anyhow` is for catch-all error handling.
* Next add Nym-specific dependencies. Since these libraries are not yet on [crates io](https://crates.io) then you need to import them from the Nym monorepo:
```
nym-sdk = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" }
nym-sphinx-addressing = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" }
nym-validator-client = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" }
nym-bin-common = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" }
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" }
```
The `sphinx` dependencies are for packet- and address-related functionality, the `validator-client` for Nyx blockchain specific configs, `common` for client logging, and the `sdk` for SDK functionality: creating and managing client storage and connections, and sending and receiving messages to and from the mixnet.
* Finally add the following underneath your `[dependencies]`:
```
[[bin]]
name = "client"
path = "bin/client.rs"
[[bin]]
name = "service"
path = "bin/service.rs"
```
This defines multiple binaries to run in a single cargo project, as outlined [here](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries).
@@ -1,43 +0,0 @@
# Querying the Chain
Now that all the code has been written, time to query the blockchain.
To test against the account operating one of the mix nodes on the testnet, use `n1lcutqz94k739s39u26rvexql40ehf42zd27fwe` in the following instructions:
```sh
# build the binaries
cargo build --release
# open two console windows. In one run the following
./target/release/service
# copy the printed Nym address from the console - this is used by the client when running the chain query
# in the other run
./target/release/client query-balance n1lcutqz94k739s39u26rvexql40ehf42zd27fwe <SERVICE_ADDRESS_FROM_CLIPBOARD>
```
The following happens:
* `client` and `service` both start their Nym clients and log the address. `service` is listening for incoming messages from the mixnet.
* `client` sends a request to `service` using the supplied `n1...` address as the Nyx account to query the balance of, and the supplied Nym address to communicate with this instance of `service`.
* `service` queries the Sandbox testnet blockchain using the `broadcaster` http client. It then serialises the response and returns it using SURBs to the `client`.
All in all, quite simple. By using the service as a proxy, the client never interacts with the blockchain, thus is not revealing metadata to the operator of the Validator nor any entities monitoring its incoming and outgoing traffic. Furthermore, the client doesn't even need to share its Nym address with the service, as the service is able to reply via SURBs.
## Creating Sandbox Account
If you wish to create an account on Sandbox to use instead of the supplied account above, the easiest way is by building the `nym-cli` tool and using that to create one:
```sh
# start from the root of the nym monorepo
cd tools/nym-cli
# build
cargo build --release
# create account using Sandbox testnet environment
../../target/release/nym-cli --config-env-file ../../envs/sandbox.env account create
```
However, since this account is fresh, it won't have any tokens. Querying the balance will still work obviously, it will just return `0`.
## Get Tokens
We're working on getting the faucet up and running again in preparation for part 2 of this tutorial: using the tokens you have privately checked the balance of to generate a [bandwidth credential](https://nymtech.net/docs/bandwidth-credentials.html).
If you wish to get testnet tokens already then feel free to ask in the [Dev channel](https://matrix.to/#/#dev:nymtech.chat) on Matrix.
@@ -1,45 +0,0 @@
# Preparing Your Service pt2
Now to define the logic of creating the `broadcaster` for interacting with the blockchain, and querying it in `src/service.rs`.
## Dependencies
The following dependencies are for creating a client to interact with the blockchain, and deal with the returned `Coin` type in `BalanceResponse`.
```rust
use crate::{BalanceResponse, DEFAULT_DENOM, DEFAULT_VALIDATOR_RPC};
use cosmrs::rpc::HttpClient;
use cosmrs::AccountId;
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
```
## Creating a `broadcaster`
The `broadcaster` is an `HttpClient` taken from `cosmrs`, created with the `DEFAULT_VALIDATOR_RPC` as its default endpoint. The service will use this to query the Sandbox testnet chain.
```rust
pub async fn create_broadcaster() -> anyhow::Result<HttpClient> {
let broadcaster: HttpClient = HttpClient::new(DEFAULT_VALIDATOR_RPC)?;
Ok(broadcaster)
}
```
## Querying the Chain
Now to write the logic for querying the chain, using the `broadcaster` created in the previous step to query for the balance of the `account` that the client sent via the mixnet in the `BalanceRequest`. This function returns a `BalanceResponse` containing a `Coin` type, denoting the `balance` and `denom` returned by `get_balance()`.
```rust
pub async fn get_balance(
broadcaster: HttpClient,
account: AccountId,
) -> anyhow::Result<BalanceResponse> {
let balance = broadcaster
.get_balance(&account, DEFAULT_DENOM.to_string())
.await
.unwrap()
.unwrap();
Ok(BalanceResponse {
balance: Coin {
amount: balance.amount,
denom: balance.denom,
},
})
}
```
@@ -1,71 +0,0 @@
# Preparing Your Service
In `bin/src.rs` define the startup and response logic of the `service`. Client connection / config reading happens as it does in `bin/client.rs`.
## Dependencies
```
use chain_query::{
create_client, handle_request,
service::{create_broadcaster, get_balance},
BalanceResponse, RequestTypes, ResponseTypes,
};
use nym_sphinx_anonymous_replies::{self, requests::AnonymousSenderTag};
use nym_bin_common::logging::setup_logging
```
The imports from `chain_query` are most of the data types and functions defined in the previous sections of this tutorial.
The `AnonymousSenderTag` type is used for SURBs.
## main()
Also using tokio for the async runtime, `main` does the following:
* Create a Nym client with config at `/tmp/service`, or load the existing client from this config directory.
* Create a `broadcaster` - this is used by the service to interact with the blockchain, using the consts defined in `src/lib.rs` as chain config.
* Listen out for incoming messages, and in much the same way as the `client`, handle and match the incoming request.
* Using the `sender_tag`, anonymously reply to the `client` with the response from the blockchain **without having to know the client's Nym address**.
```rust
#[tokio::main]
async fn main() -> anyhow::Result<()> {
setup_logging();
let mut client = create_client("/tmp/service".into()).await;
let our_address = client.nym_address();
println!("\nservice's nym address: {our_address}");
// the httpclient we will use to broadcast our query to the blockchain
let broadcaster = create_broadcaster().await?;
println!("listening for messages, press CTRL-C to exit");
while let Some(received) = client.wait_for_messages().await {
for msg in received {
let request = match handle_request(msg) {
Ok(request) => request,
Err(err) => {
eprintln!("failed to handle received request: {err}");
continue;
}
};
let return_recipient: AnonymousSenderTag = request.1.expect("no sender tag received");
match request.0 {
RequestTypes::Balance(request) => {
println!("\nincoming balance request for: {}\n", request.account);
let balance: BalanceResponse =
get_balance(broadcaster.clone(), request.account).await?;
let response = ResponseTypes::Balance(balance);
println!("response from chain: {:#?}", response);
println!("\nreturn recipient surb bucket: {}", &return_recipient);
println!("\nsending response to {}", &return_recipient);
// send response back to anon requesting client via mixnet
client
.send_reply(return_recipient, &serde_json::to_string(&response)?)
.await;
}
}
}
}
Ok(())
```
@@ -0,0 +1 @@
# Building a Image Upload Service Provider with IPFS (coming soon)
@@ -1,5 +0,0 @@
# Rust SDK
The Rust SDK allows developers building applications in Rust to import and interact with Nym clients as they would any other dependency, instead of running the client as a seperate process on their machine.
[Read the docs](https://nymtech.net/docs/sdk/rust.html).
@@ -1 +0,0 @@
# Rust SDK
@@ -1,41 +0,0 @@
# Telegram NymConnect Integration
*This is a shortened version of a [Nym Community post](https://blog.nymtech.net/how-to-use-telegram-in-iraq-with-nymconnect-106a3b8dd050) written by Saliveja.*
The purpose of the following manual is not to promote Telegram but so people can use it with the Nym mixnet if they wish to, should a situation ask for that. This privacy-enhances Telegram at the network level and allows users to access the application from locations like where the application was banned.
See also: [Element (Matrix) over the Nym mixnet](./matrix.md): private, decentralised and secure messaging.
## Setup & Run
Heres how to configure Telegram with NymConnect:
1. Download and install NymConnect ().**
For more releases, check out [Github](https://github.com/nymtech/nym/tags). NymConnect is available for Linux, Windows, and MacOS.
On Linux make sure NymConnect is executable. Opening a terminal in the same directory and run:
```sh
chmod +x ./<YOUR-NYM-CONNECT-VERSION>.AppImage
```
2. **Start NymConnect**
Telegram is added to NymConnect by default.
3. **Click connect - the host and port will now be displayed.**
4. **Click on host or port to copy** the value to the clipboard.
5. **Open the Telegram proxy settings.**
Linux: Telegram -> Settings -> Advanced -> Connection type -> Use custom proxy
MacOS: Telegram -> Settings -> Advanced -> Data & Storage -> Connection Type -> Use custom Proxy
Windows: Telegram -> Settings -> Data and Storage -> Use proxy
6. **Add a proxy** with the Add proxy button.
7. **Select SOCKS5** and make sure the port details are the same as those generated by NymConnect. Alternatively, follow this link: https://t.me/socks?server=127.0.0.1&port=1080
8. **Save the proxy settings** in Telegram.
9. **Telegram is now running through the Nym Mixnet and is privacy-enhanced!**
This allows you to connect from regions which blocked Telegram.
10. Note if you remain idle on Telegram for a while you might lose connectivity and your messages might not get through via SOCKS5 proxy. If that happens reconnect your NymConnect and reset the proxy again.
Follow this [video](https://youtu.be/quj8H2qeOwY?t=97) to see the steps on Telegram setup.
<!---It's set to particular time as the NC setup part is outdated --->
**Now your Telegram runs over NymConnect.**
NymConnect is currently available for several applications and service providers. Support for more apps is on the way. For any bug reports or feedback please reach out to us on Telegram or our [Discord](http://discord.gg/nym) server.
@@ -1,3 +0,0 @@
# Typescript
Tutorial code in this section is built to interact with a standalone Nym client. You can read about interacting with standalone clients [here](https://nymtech.net/docs/clients/websocket-client.html#connecting-to-the-local-websocket), although it is usually preferable to use the [Typescript SDK](https://nymtech.net/docs/sdk/typescript.html).
+2 -2
View File
@@ -48,8 +48,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables]
minimum_rust_version = "1.66"
platform_release_version = "1.1.29"
wallet_release_version = "1.2.8"
platform_release_version = "v1.1.28"
wallet_release_version = "v1.2.7"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+1 -21
View File
@@ -52,26 +52,6 @@ The example above involves ephemeral keys - if we want to create and then mainta
As seen in the example above, the `mixnet::MixnetClientBuilder::new()` function handles checking for keys in a storage location, loading them if present, or creating them and storing them if not, making client key management very simple.
Assuming our client config is stored in `/tmp/mixnet-client`, the following files are generated:
```
$ tree /tmp/mixnet-client
mixnet-client
├── ack_key.pem
├── db.sqlite
├── db.sqlite-shm
├── db.sqlite-wal
├── gateway_details.json
├── gateway_shared.pem
├── persistent_reply_store.sqlite
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
1 directory, 11 files
```
### Manually handling storage
If you're integrating mixnet functionality into an existing app and want to integrate saving client configs and keys into your existing storage logic, you can manually perform the actions taken automatically above (`examples/manually_handle_keys_and_config.rs`)
@@ -90,7 +70,7 @@ The number of SURBs is set [here](https://github.com/nymtech/nym/blob/release/{{
You can read more about how SURBs function under the hood [here](../architecture/traffic-flow.md#private-replies-using-surbs).
In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to:
In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to:
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/surb-reply.rs}}
+2 -2
View File
@@ -48,8 +48,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables]
minimum_rust_version = "1.66"
platform_release_version = "1.1.29"
wallet_release_version = "1.2.8"
platform_release_version = "v1.1.28"
wallet_release_version = "v1.2.7"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.27"
version = "1.1.26"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "1.1.27"
version = "1.1.26"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.1.28"
version = "1.1.27"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-api"
version = "1.1.28"
version = "1.1.27"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
-6
View File
@@ -2,12 +2,6 @@
## [Unreleased]
## [v1.1.19-snickers] (2023-08-29)
- NymConnect sometimes fails to connect because the gateway it fetches from the validator-api to use is running an old version (of the gateway binary) ([#3788])
[#3788]: https://github.com/nymtech/nym/issues/3788
## [1.1.18] (2023-08-22)
- refactor(nc-desktop): use userdata storage to save user gateway&sp ([#3723])
-2
View File
@@ -4014,10 +4014,8 @@ dependencies = [
"nym-socks5-client-core",
"nym-sphinx",
"nym-task",
"nym-topology",
"nym-validator-client",
"pretty_env_logger",
"rand 0.7.3",
"rand 0.8.5",
"reqwest",
"rust-embed",
-1
View File
@@ -1,3 +1,2 @@
[workspace]
members = ["src-tauri"]
resolver = "2"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nym/nym-connect",
"version": "1.1.19",
"version": "1.1.18",
"main": "index.js",
"license": "MIT",
"scripts": {
+1 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-connect"
version = "1.1.19"
version = "1.1.18"
description = "nym-connect"
authors = ["Nym Technologies SA"]
license = ""
@@ -30,7 +30,6 @@ itertools = "0.10.5"
log = { version = "0.4", features = ["serde"] }
pretty_env_logger = "0.4.0"
rand = "0.8"
rand-07 = { package = "rand", version = "0.7.3" }
reqwest = { version = "0.11.18", features = ["json", "socks"] }
rust-embed = { version = "6.4.2", features = ["include-exclude"] }
serde = { version = "1.0", features = ["derive"] }
@@ -59,7 +58,6 @@ nym-bin-common = { path = "../../../common/bin-common"}
nym-socks5-client-core = { path = "../../../common/socks5-client-core" }
nym-sphinx = { path = "../../../common/nymsphinx" }
nym-task = { path = "../../../common/task" }
nym-topology = { path = "../../../common/topology" }
nym-validator-client = { path = "../../../common/client-libs/validator-client" }
[dev-dependencies]
+1 -3
View File
@@ -86,10 +86,8 @@ fn main() {
crate::operations::connection::status::get_connection_status,
crate::operations::connection::status::get_gateway_connection_status,
crate::operations::connection::status::start_connection_health_check_task,
crate::operations::directory::get_gateway_with_low_latency,
crate::operations::directory::get_gateways,
crate::operations::directory::get_services,
crate::operations::directory::select_gateway_with_low_latency_from_list,
crate::operations::directory::get_gateways,
crate::operations::export::export_keys,
crate::operations::window::hide_window,
crate::operations::growth::test_and_earn::growth_tne_get_client_id,
@@ -11,7 +11,6 @@ use nym_api_requests::models::GatewayBondAnnotated;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_config::defaults::var_names::{NETWORK_NAME, NYM_API};
use nym_contracts_common::types::Percent;
use nym_topology::gateway;
use nym_validator_client::nym_api::Client as ApiClient;
use std::str::FromStr;
use std::sync::Arc;
@@ -135,47 +134,25 @@ async fn fetch_gateways() -> Result<Vec<GatewayBondAnnotated>> {
Ok(gateways)
}
fn filter_out_low_performance_gateways(
gateways: Vec<GatewayBondAnnotated>,
) -> Vec<GatewayBondAnnotated> {
gateways
.into_iter()
.filter(|g| {
g.node_performance.most_recent
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
})
.collect()
}
async fn select_gateway_by_latency(gateways: Vec<GatewayBondAnnotated>) -> Result<gateway::Node> {
let gateways_as_nodes: Vec<gateway::Node> = gateways
.into_iter()
.filter_map(|g| g.gateway_bond.try_into().ok())
.collect();
let mut rng = rand_07::rngs::OsRng;
let selected_gateway =
nym_client_core::init::helpers::choose_gateway_by_latency(&mut rng, &gateways_as_nodes)
.await?;
Ok(selected_gateway)
}
// Get all gateways satisfying the performance threshold.
#[tauri::command]
pub async fn get_gateways() -> Result<Vec<Gateway>> {
log::trace!("Fetching gateways");
let all_gateways = fetch_gateways().await?;
log::trace!("Received: {:#?}", all_gateways);
let gateways_filtered = filter_out_low_performance_gateways(all_gateways.clone())
.into_iter()
let filtered_gateways = all_gateways
.iter()
.filter(|g| {
g.node_performance.most_recent
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
})
.map(|g| Gateway {
identity: g.identity().clone(),
})
.collect_vec();
log::trace!("Filtered: {:#?}", gateways_filtered);
log::trace!("Filtered: {:#?}", filtered_gateways);
if gateways_filtered.is_empty() {
if filtered_gateways.is_empty() {
log::warn!("No gateways with high enough performance score found! Using all gateways instead as fallback");
return Ok(all_gateways
.iter()
@@ -185,38 +162,5 @@ pub async fn get_gateways() -> Result<Vec<Gateway>> {
.collect_vec());
}
Ok(gateways_filtered)
}
// Lookup and select a single gateway with low latency.
#[tauri::command]
pub async fn get_gateway_with_low_latency() -> Result<Gateway> {
log::trace!("Fetching gateways");
let all_gateways = fetch_gateways().await?;
log::trace!("Received: {:#?}", all_gateways);
let gateways_filtered = filter_out_low_performance_gateways(all_gateways);
let selected_gateway = select_gateway_by_latency(gateways_filtered).await?;
log::debug!("Selected gateway: {}", selected_gateway);
Ok(Gateway {
identity: selected_gateway.identity().to_base58_string(),
})
}
// From a given list of gateways, select the one with low latency.
#[tauri::command]
pub async fn select_gateway_with_low_latency_from_list(gateways: Vec<Gateway>) -> Result<Gateway> {
log::debug!("Selecting a gateway with low latency");
let gateways = gateways.into_iter().map(|g| g.identity).collect_vec();
let all_gateways = fetch_gateways().await?;
let gateways_union_set: Vec<GatewayBondAnnotated> = all_gateways
.into_iter()
.filter(|g| gateways.contains(g.identity()))
.collect();
let gateways_filtered = filter_out_low_performance_gateways(gateways_union_set);
let selected_gateway = select_gateway_by_latency(gateways_filtered).await?;
log::debug!("Selected gateway: {}", selected_gateway);
Ok(Gateway {
identity: selected_gateway.identity().to_base58_string(),
})
Ok(filtered_gateways)
}
+1 -1
View File
@@ -57,7 +57,7 @@ fn override_config_from_env(config: &mut Config, privacy_level: &PrivacyLevel) {
.provider_mix_address
.parse()
.expect("failed to parse provider mix address");
log::warn!("Using geo-aware mixnode selection based on the location of: {address}");
log::warn!("Using geo-aware mixnode selection baseon the location of: {address}");
config
.core
.base
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-connect",
"version": "1.1.19"
"version": "1.1.18"
},
"build": {
"distDir": "../dist",
+1 -6
View File
@@ -214,12 +214,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
const setGateway = async () => {
if (gateways) {
let randomGateway;
if (userData?.privacy_level === 'Medium') {
randomGateway = await invoke<Gateway>('select_gateway_with_low_latency_from_list', { gateways });
} else {
randomGateway = getRandomFromList(gateways);
}
const randomGateway = getRandomFromList(gateways);
const withUserDefinitions = await buildGateway(randomGateway);
await invoke('set_gateway', {
gateway: shouldUseUserGateway ? userDefinedGateway.address : withUserDefinitions.identity,
@@ -2,10 +2,9 @@ import React, { ChangeEvent, useState } from 'react';
import * as Sentry from '@sentry/react';
import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material';
import { useClientContext } from '../../context/main';
import { ConnectionStatusKind } from '../../types';
export const PrivacyLevelSettings = () => {
const { userData, setPrivacyLevel, connectionStatus } = useClientContext();
const { userData, setPrivacyLevel } = useClientContext();
const [speedBoost, setSpeedBoost] = useState(userData?.privacy_level !== 'High');
const [loading, setLoading] = useState(false);
@@ -30,7 +29,7 @@ export const PrivacyLevelSettings = () => {
<Switch
checked={speedBoost}
onChange={handleChange}
disabled={loading || connectionStatus === ConnectionStatusKind.connected}
disabled={loading}
size="small"
sx={{ ml: 1, mr: 1 }}
/>
@@ -3,7 +3,7 @@
[package]
name = "nym-network-requester"
version = "1.1.27"
version = "1.1.26"
authors.workspace = true
edition.workspace = true
rust-version = "1.65"
@@ -1,6 +1,6 @@
[package]
name = "nym-network-statistics"
version = "1.1.27"
version = "1.1.26"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.27"
version = "1.1.26"
authors.workspace = true
edition = "2021"