Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cd4a49a07 | |||
| 5a8bad4503 | |||
| 0e37084f34 | |||
| 27a9557c7b | |||
| c143fef912 | |||
| e7e48f0e53 | |||
| c62b344349 | |||
| 441fbf8255 | |||
| b5cde68e62 | |||
| c3d38fb904 | |||
| 2922306e25 | |||
| 204b2e1101 | |||
| c022486e63 | |||
| 2a7a681b7d | |||
| a50b4ad211 | |||
| ca613ad3aa | |||
| f518c8377b | |||
| e1a30ea01a | |||
| 9f5a0a7ca6 | |||
| c7de97d6dd | |||
| 85a7ec9f02 | |||
| 2df42e222c | |||
| a1482a2887 | |||
| b57df35f8c | |||
| 7f0a02f6ec | |||
| cfc86ba9f5 | |||
| 0812a0f599 | |||
| 5d454f2efc | |||
| ffac0a1f92 | |||
| 8004d54d5e | |||
| b6febc51a3 | |||
| 5a0255fd01 | |||
| 5b86646bd8 | |||
| 067a501d98 | |||
| fa9908413b | |||
| d6369ea784 | |||
| e49c8588c6 | |||
| e504def9e4 | |||
| 50acec575f | |||
| ffb9ab9019 | |||
| 1a195d151d | |||
| dd4f4c44c3 | |||
| c04993e49c | |||
| 464984a83c | |||
| 40ffb6b65d | |||
| bb263f8c5e | |||
| 82872ae02a | |||
| 6db396e877 | |||
| d8925fe234 | |||
| ee98945773 | |||
| f8c1049678 | |||
| 1bee13a52d | |||
| 0103ae9b75 | |||
| 5b1b2100bd | |||
| ed8e54c18a | |||
| b5da9392ae | |||
| b2322a9570 | |||
| ea8057ed93 | |||
| 627fe72cf6 | |||
| 4f762d171f | |||
| dd317f0965 | |||
| a3a34a59f0 | |||
| 44e4d49ca0 | |||
| 5f8dc3815f | |||
| 00d0e9cb2c | |||
| f02dd3ffe0 | |||
| 68e2edcf0a | |||
| d80061c243 | |||
| d1d082b282 | |||
| 9abb403e01 | |||
| f6e19e8233 | |||
| f78f30e4bf | |||
| d628ce9534 | |||
| c4f1be4c9c | |||
| fdb6ddccba | |||
| 2dbd47a85b | |||
| 123c1983c8 | |||
| 3d299f80fd | |||
| 83f58aa1a3 | |||
| bae142f740 | |||
| f6b200d25e | |||
| de16ca2bd5 | |||
| 374d874427 | |||
| 5243d5b426 | |||
| ae60b263df | |||
| 2dc65fb657 | |||
| 3ec9c3b216 | |||
| 509b31ad93 | |||
| c8c00c9eec | |||
| b35813ee47 | |||
| da5ada1403 | |||
| 5c351101cc | |||
| 33612ce34d | |||
| 92dc7c8375 | |||
| 5243dd86ee | |||
| 682151f76c | |||
| 4c37931a00 | |||
| 55505a59d1 | |||
| e7643eb982 | |||
| 6c0d63af9a | |||
| 980ad45496 | |||
| bbd6904205 | |||
| 4b1680a856 | |||
| 988d46127b | |||
| 00ce995789 | |||
| c692995987 | |||
| 6128d42ec8 | |||
| 4a012e17c3 | |||
| 6f0c8f06e3 | |||
| 62b5591e1b | |||
| 84bbe9c814 | |||
| fcdcde8427 | |||
| 8f27d041ac | |||
| 1de6a8efab | |||
| 410f79c476 | |||
| f403572caa | |||
| bc565c494b | |||
| 7f37c95599 | |||
| 97693a496c | |||
| 80d38f795c | |||
| 33bc7c0c7c | |||
| abbadcfa81 | |||
| fbc106f8a5 | |||
| 18de02a229 | |||
| d64de95f52 | |||
| 71f11ffefe | |||
| 8a0330b493 | |||
| e1e11149b4 | |||
| 37b0aa4d08 | |||
| a86ed7afb8 | |||
| b03d8f57d3 | |||
| 0b311e2c05 | |||
| 301482bfe4 | |||
| dfce9ced1d | |||
| 79ee623a1a | |||
| a519658432 |
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
outputs:
|
||||
release_id: ${{ steps.create-release.outputs.id }}
|
||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_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,7 +40,6 @@ 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
|
||||
@@ -97,11 +96,6 @@ 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: |
|
||||
@@ -130,12 +124,6 @@ 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
|
||||
@@ -151,7 +139,6 @@ 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:
|
||||
@@ -169,7 +156,6 @@ 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:
|
||||
@@ -187,7 +173,6 @@ 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:
|
||||
@@ -205,7 +190,6 @@ 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:
|
||||
@@ -223,7 +207,6 @@ 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:
|
||||
@@ -241,7 +224,6 @@ 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:
|
||||
@@ -259,5 +241,4 @@ jobs:
|
||||
file_hash: ${{ needs.publish-nym.outputs.netstat_hash }}
|
||||
name: Network Statistics
|
||||
category: binaries
|
||||
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||
secrets: inherit
|
||||
|
||||
+22
-2
@@ -4,7 +4,27 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.1.28] (2023-08-22)
|
||||
## [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)
|
||||
|
||||
- [final step3]: add [rust] support to nyxd client in wasm ([#3743])
|
||||
- Feature/ephemera upgrade ([#3791])
|
||||
@@ -19,7 +39,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#3767]: https://github.com/nymtech/nym/pull/3767
|
||||
|
||||
|
||||
## [1.1.27] (2023-08-16)
|
||||
## [v1.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
+40
-9
@@ -1066,6 +1066,15 @@ 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"
|
||||
@@ -2768,7 +2777,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.3.21",
|
||||
@@ -5638,7 +5647,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.27"
|
||||
version = "1.1.28"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
@@ -5785,7 +5794,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -5858,7 +5867,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"clap 4.3.21",
|
||||
"dirs 4.0.0",
|
||||
@@ -6153,7 +6162,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -6308,7 +6317,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.27"
|
||||
version = "1.1.28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58 0.4.0",
|
||||
@@ -6426,7 +6435,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-file-watcher",
|
||||
@@ -6473,7 +6482,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"log",
|
||||
@@ -6638,7 +6647,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
dependencies = [
|
||||
"clap 4.3.21",
|
||||
"lazy_static",
|
||||
@@ -8627,6 +8636,24 @@ 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"
|
||||
@@ -8660,10 +8687,13 @@ 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"
|
||||
@@ -8740,6 +8770,7 @@ 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"
|
||||
|
||||
@@ -74,6 +74,7 @@ members = [
|
||||
"common/topology",
|
||||
"common/types",
|
||||
"common/wasm-utils",
|
||||
"demos/rust-cosmos-broadcaster",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"gateway",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
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());
|
||||
|
||||
@@ -498,6 +498,15 @@ 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 {
|
||||
|
||||
@@ -149,7 +149,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
|
||||
Ok(GatewayWithLatency::new(gateway, avg))
|
||||
}
|
||||
|
||||
pub(super) async fn choose_gateway_by_latency<R: Rng>(
|
||||
pub async fn choose_gateway_by_latency<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: &[gateway::Node],
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
[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"
|
||||
@@ -0,0 +1,35 @@
|
||||
### 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>
|
||||
```
|
||||
@@ -0,0 +1,109 @@
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
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))
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
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 +1,13 @@
|
||||
# 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)
|
||||
|
||||
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`).
|
||||
## 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.
|
||||
|
||||
|
||||
|
||||
@@ -1,37 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# commands assume you run script from `nym/documentation/`
|
||||
# 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.
|
||||
|
||||
# array of project dirs
|
||||
declare -a projects=("docs" "dev-portal" "operators")
|
||||
|
||||
## 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
|
||||
# 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
|
||||
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/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
|
||||
@@ -17,4 +17,6 @@ book
|
||||
|
||||
theme/
|
||||
theme
|
||||
theme/*
|
||||
theme/*
|
||||
|
||||
.idea
|
||||
@@ -40,6 +40,7 @@ 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`
|
||||
@@ -48,8 +49,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 = "v1.1.28"
|
||||
wallet_release_version = "v1.2.7"
|
||||
platform_release_version = "1.1.29"
|
||||
wallet_release_version = "1.2.8"
|
||||
|
||||
[preprocessor.last-changed]
|
||||
command = "mdbook-last-changed"
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
- [NymConnect Monero](tutorials/monero.md)
|
||||
- [NymConnect Matrix](tutorials/matrix.md)
|
||||
- [NymConnect Telegram](tutorials/telegram.md)
|
||||
|
||||
# Integrations
|
||||
|
||||
@@ -30,14 +31,25 @@
|
||||
|
||||
# Tutorials
|
||||
|
||||
- [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)
|
||||
- [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)
|
||||
|
||||
|
||||
# Community Resources
|
||||
|
||||
|
||||
@@ -36,12 +36,11 @@ 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.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/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.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/simple-service-provider.md).
|
||||
|
||||
#### Webassembly client
|
||||
If you’re working in JavaScript or Typescript in the browser, or building an edge computing app, you’ll 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 that’s 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).
|
||||
|
||||
It’s 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.
|
||||
It’s 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 |
|
||||
+-----------+
|
||||
^
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
+-------------------+
|
||||
| +---------------+ |
|
||||
| | Nym client | |
|
||||
| +---------------+ |
|
||||
| ^ |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| v |
|
||||
| +---------------+ |
|
||||
| | Your app code | |
|
||||
| +---------------+ |
|
||||
+-------------------+
|
||||
Your Local Machine
|
||||
| Gateway |
|
||||
+-----------+
|
||||
^
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
+-------------------+
|
||||
| +---------------+ |
|
||||
| | 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,38 +139,37 @@ 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.
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# 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`.
|
||||
@@ -0,0 +1,79 @@
|
||||
# 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(())
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
# 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).
|
||||
@@ -0,0 +1,165 @@
|
||||
# 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")
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
# 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.
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
# 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).
|
||||
@@ -0,0 +1,43 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,45 @@
|
||||
# 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,
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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(())
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
# Building a Image Upload Service Provider with IPFS (coming soon)
|
||||
@@ -0,0 +1,5 @@
|
||||
# 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).
|
||||
@@ -0,0 +1 @@
|
||||
# Rust SDK
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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
|
||||
|
||||
Here’s 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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# 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).
|
||||
@@ -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 = "v1.1.28"
|
||||
wallet_release_version = "v1.2.7"
|
||||
platform_release_version = "1.1.29"
|
||||
wallet_release_version = "1.2.8"
|
||||
|
||||
[preprocessor.last-changed]
|
||||
command = "mdbook-last-changed"
|
||||
|
||||
@@ -52,6 +52,26 @@ 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`)
|
||||
|
||||
@@ -70,7 +90,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}}
|
||||
|
||||
@@ -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 = "v1.1.28"
|
||||
wallet_release_version = "v1.2.7"
|
||||
platform_release_version = "1.1.29"
|
||||
wallet_release_version = "1.2.8"
|
||||
|
||||
[preprocessor.last-changed]
|
||||
command = "mdbook-last-changed"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.27"
|
||||
version = "1.1.28"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-api"
|
||||
version = "1.1.27"
|
||||
version = "1.1.28"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
## [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])
|
||||
|
||||
Generated
+2
@@ -4014,8 +4014,10 @@ 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,2 +1,3 @@
|
||||
[workspace]
|
||||
members = ["src-tauri"]
|
||||
resolver = "2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nym/nym-connect",
|
||||
"version": "1.1.18",
|
||||
"version": "1.1.19",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-connect"
|
||||
version = "1.1.18"
|
||||
version = "1.1.19"
|
||||
description = "nym-connect"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
@@ -30,6 +30,7 @@ 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"] }
|
||||
@@ -58,6 +59,7 @@ 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]
|
||||
|
||||
@@ -86,8 +86,10 @@ 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_services,
|
||||
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::export::export_keys,
|
||||
crate::operations::window::hide_window,
|
||||
crate::operations::growth::test_and_earn::growth_tne_get_client_id,
|
||||
|
||||
@@ -11,6 +11,7 @@ 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;
|
||||
@@ -134,25 +135,47 @@ 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 filtered_gateways = all_gateways
|
||||
.iter()
|
||||
.filter(|g| {
|
||||
g.node_performance.most_recent
|
||||
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
|
||||
})
|
||||
let gateways_filtered = filter_out_low_performance_gateways(all_gateways.clone())
|
||||
.into_iter()
|
||||
.map(|g| Gateway {
|
||||
identity: g.identity().clone(),
|
||||
})
|
||||
.collect_vec();
|
||||
log::trace!("Filtered: {:#?}", filtered_gateways);
|
||||
log::trace!("Filtered: {:#?}", gateways_filtered);
|
||||
|
||||
if filtered_gateways.is_empty() {
|
||||
if gateways_filtered.is_empty() {
|
||||
log::warn!("No gateways with high enough performance score found! Using all gateways instead as fallback");
|
||||
return Ok(all_gateways
|
||||
.iter()
|
||||
@@ -162,5 +185,38 @@ pub async fn get_gateways() -> Result<Vec<Gateway>> {
|
||||
.collect_vec());
|
||||
}
|
||||
|
||||
Ok(filtered_gateways)
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 baseon the location of: {address}");
|
||||
log::warn!("Using geo-aware mixnode selection based on the location of: {address}");
|
||||
config
|
||||
.core
|
||||
.base
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-connect",
|
||||
"version": "1.1.18"
|
||||
"version": "1.1.19"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -214,7 +214,12 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
|
||||
const setGateway = async () => {
|
||||
if (gateways) {
|
||||
const randomGateway = getRandomFromList(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 withUserDefinitions = await buildGateway(randomGateway);
|
||||
await invoke('set_gateway', {
|
||||
gateway: shouldUseUserGateway ? userDefinedGateway.address : withUserDefinitions.identity,
|
||||
|
||||
@@ -2,9 +2,10 @@ 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 } = useClientContext();
|
||||
const { userData, setPrivacyLevel, connectionStatus } = useClientContext();
|
||||
const [speedBoost, setSpeedBoost] = useState(userData?.privacy_level !== 'High');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -29,7 +30,7 @@ export const PrivacyLevelSettings = () => {
|
||||
<Switch
|
||||
checked={speedBoost}
|
||||
onChange={handleChange}
|
||||
disabled={loading}
|
||||
disabled={loading || connectionStatus === ConnectionStatusKind.connected}
|
||||
size="small"
|
||||
sx={{ ml: 1, mr: 1 }}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version = "1.65"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.26"
|
||||
version = "1.1.27"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user