Compare commits

...

88 Commits

Author SHA1 Message Date
mfahampshire 3cd4a49a07 Merge branches 'feature/rust-sdk-tutorial-chain-querier' and 'feature/rust-cosmos-broadcaster' of github.com:nymtech/nym into feature/rust-cosmos-broadcaster 2023-09-01 15:56:18 +02:00
mfahampshire ee98945773 Merge branch 'develop' into feature/rust-cosmos-broadcaster 2023-07-27 11:09:21 +02:00
mfahampshire f8c1049678 noodling with nicer logging:
* reintroduced logging
* added a println! for when client gets surb request
2023-07-25 16:13:01 +02:00
mfahampshire 1bee13a52d removal of missed unwrap() 2023-07-21 16:22:42 +02:00
mfahampshire 0103ae9b75 minor comments 2023-07-21 13:13:10 +02:00
mfahampshire 5b1b2100bd passing SP address as arg 2023-07-21 12:32:34 +02:00
mfahampshire ed8e54c18a clippy + fmt + minor logging change 2023-07-21 11:50:19 +02:00
mfahampshire b5da9392ae removed unnecessary doubled signer var 2023-07-21 11:34:27 +02:00
mfahampshire b2322a9570 cont. with error propogation 2023-07-21 11:06:25 +02:00
mfahampshire ea8057ed93 using anyhow::Result to solve multiple error type issue 2023-07-21 10:34:08 +02:00
mfahampshire 627fe72cf6 error handling 2023-07-20 22:47:50 +02:00
mfahampshire 4f762d171f * working on error propogation
* better comments
2023-07-20 22:32:57 +02:00
mfahampshire dd317f0965 * removed borrow for cli matching
* deleted commented out line of now-optimised stdin matching
2023-07-20 18:45:51 +02:00
mfahampshire a3a34a59f0 error propogation on send_tx() 2023-07-20 18:43:41 +02:00
mfahampshire 44e4d49ca0 removed unused var when reading stdin 2023-07-20 18:39:52 +02:00
mfahampshire 5f8dc3815f removed unnecessary imports + updated tokio import 2023-07-20 18:36:09 +02:00
mfahampshire 00d0e9cb2c removed commented out logging setup command 2023-07-20 18:24:48 +02:00
mfahampshire f02dd3ffe0 removed accidentally committed file 2023-07-20 18:22:19 +02:00
mfahampshire 68e2edcf0a * added client disconnect to client side code
* extra logging for demo
2023-07-20 18:21:21 +02:00
mfahampshire d80061c243 updated sdk documentation with surb example 2023-07-19 12:34:26 +02:00
mfahampshire d1d082b282 cleanedup readme 2023-07-19 12:13:10 +02:00
mfahampshire 9abb403e01 removed comment + removed todo from readme 2023-07-19 12:09:10 +02:00
mfahampshire f6e19e8233 removed unused import 2023-07-19 11:58:37 +02:00
mfahampshire f78f30e4bf added rust sdk surb example 2023-07-19 11:57:21 +02:00
mfahampshire d628ce9534 cargo lock update 2023-07-19 10:14:31 +02:00
mfahampshire c4f1be4c9c nicer comments for demo logging 2023-07-19 10:13:34 +02:00
mfahampshire fdb6ddccba tidyup and code commenting 2023-07-19 09:20:28 +02:00
mfahampshire 2dbd47a85b fmt 2023-07-18 17:06:05 +02:00
mfahampshire 123c1983c8 removed redundant doubled code 2023-07-18 17:04:26 +02:00
mfahampshire 3d299f80fd * fmt
* cleanup
* error handling
2023-07-18 15:28:59 +02:00
mfahampshire 83f58aa1a3 cont. 2023-07-18 10:35:31 +02:00
mfahampshire bae142f740 comment tidyup 2023-07-14 16:07:19 +02:00
mfahampshire f6b200d25e tidied up logging for demo recording 2023-07-14 15:52:36 +02:00
mfahampshire de16ca2bd5 comment and import tidyup 2023-07-13 22:09:50 +02:00
mfahampshire 374d874427 clippy's suggested changes 2023-07-13 17:24:34 +02:00
mfahampshire 5243d5b426 removed old demo dirs from workspace 2023-07-13 17:22:46 +02:00
mfahampshire ae60b263df added note to readme for final tweaks 2023-07-13 16:45:03 +02:00
mfahampshire 2dc65fb657 tidyup 2023-07-13 16:44:14 +02:00
mfahampshire 3ec9c3b216 removed old cargo projects 2023-07-13 13:43:19 +02:00
mfahampshire 509b31ad93 tidyup 2023-07-13 13:42:35 +02:00
mfahampshire c8c00c9eec squashed everything into one cargo project 2023-07-13 11:38:23 +02:00
mfahampshire b35813ee47 commit before big reorg 2023-07-12 17:54:09 +02:00
mfahampshire da5ada1403 removed unnecessary imports 2023-07-12 12:06:07 +02:00
mfahampshire 5c351101cc hacky first version 2023-07-12 11:50:43 +02:00
mfahampshire 33612ce34d tidyup 2023-07-11 14:44:22 +02:00
mfahampshire 92dc7c8375 got surb reply working across client and sp 2023-07-11 10:02:34 +02:00
mfahampshire 5243dd86ee Merge branch 'feature/rust-cosmos-broadcaster' of github.com:nymtech/nym into feature/rust-cosmos-broadcaster 2023-07-10 15:53:47 +02:00
mfahampshire 682151f76c push to share 2023-07-10 15:48:24 +02:00
mfahampshire 4c37931a00 almost got surb reply.. 2023-07-10 10:54:43 +02:00
mfahampshire 55505a59d1 passing message types properly between client and sp 2023-07-10 10:54:43 +02:00
mfahampshire e7643eb982 working on sending client seq request to sp 2023-07-10 10:54:43 +02:00
mfahampshire 6c0d63af9a starting on serde for sp responses 2023-07-10 10:54:43 +02:00
mfahampshire 980ad45496 setting up sdk links 2023-07-10 10:54:43 +02:00
mfahampshire bbd6904205 working on proper client init and passing 2023-07-10 10:54:43 +02:00
mfahampshire 4b1680a856 removed unused imports 2023-07-10 10:54:43 +02:00
mfahampshire 988d46127b first pass done of client 2023-07-10 10:54:43 +02:00
mfahampshire 00ce995789 sendTx(): fixed incorrect function arg 2023-07-10 10:54:43 +02:00
mfahampshire c692995987 trying to debug 'client in use' error 2023-07-10 10:54:43 +02:00
mfahampshire 6128d42ec8 push to share 2023-07-10 10:54:43 +02:00
mfahampshire 4a012e17c3 push to share code 2023-07-10 10:54:43 +02:00
mfahampshire 6f0c8f06e3 benchmark commit before adding network defaults 2023-07-10 10:54:43 +02:00
mfahampshire 62b5591e1b type changes 2023-07-10 10:54:43 +02:00
mfahampshire 84bbe9c814 parsed returned bytes 2023-07-10 10:54:43 +02:00
mfahampshire fcdcde8427 comment removal 2023-07-10 10:54:43 +02:00
mfahampshire 8f27d041ac imports for offline signing 2023-07-10 10:54:43 +02:00
mfahampshire 1de6a8efab scaffold of offlineSign() 2023-07-10 10:54:43 +02:00
mfahampshire 410f79c476 fixed bad cosmrs import 2023-07-10 10:54:43 +02:00
mfahampshire f403572caa first commit 2023-07-10 10:54:43 +02:00
mfahampshire bc565c494b almost got surb reply.. 2023-07-10 09:08:40 +02:00
mfahampshire 7f37c95599 passing message types properly between client and sp 2023-07-07 17:01:45 +02:00
mfahampshire 97693a496c working on sending client seq request to sp 2023-07-07 11:23:56 +02:00
mfahampshire 80d38f795c starting on serde for sp responses 2023-07-06 16:49:08 +02:00
mfahampshire 33bc7c0c7c setting up sdk links 2023-07-06 16:28:01 +02:00
mfahampshire abbadcfa81 working on proper client init and passing 2023-07-06 09:56:37 +02:00
mfahampshire fbc106f8a5 removed unused imports 2023-07-05 18:34:01 +02:00
mfahampshire 18de02a229 first pass done of client 2023-07-05 16:43:27 +02:00
mfahampshire d64de95f52 sendTx(): fixed incorrect function arg 2023-07-05 14:57:00 +02:00
mfahampshire 71f11ffefe trying to debug 'client in use' error 2023-07-05 14:51:53 +02:00
mfahampshire 8a0330b493 push to share 2023-07-05 09:28:21 +02:00
mfahampshire e1e11149b4 push to share code 2023-07-04 15:40:40 +02:00
mfahampshire 37b0aa4d08 benchmark commit before adding network defaults 2023-07-04 15:15:00 +02:00
mfahampshire a86ed7afb8 type changes 2023-07-03 22:45:15 +00:00
mfahampshire b03d8f57d3 parsed returned bytes 2023-07-03 21:57:03 +00:00
mfahampshire 0b311e2c05 comment removal 2023-07-03 21:33:36 +00:00
mfahampshire 301482bfe4 imports for offline signing 2023-07-03 21:30:09 +00:00
mfahampshire dfce9ced1d scaffold of offlineSign() 2023-07-03 21:17:29 +00:00
mfahampshire 79ee623a1a fixed bad cosmrs import 2023-07-03 17:06:20 +00:00
mfahampshire a519658432 first commit 2023-07-03 15:50:22 +00:00
9 changed files with 571 additions and 0 deletions
Generated
+31
View File
@@ -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"
@@ -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"
+1
View File
@@ -74,6 +74,7 @@ members = [
"common/topology",
"common/types",
"common/wasm-utils",
"demos/rust-cosmos-broadcaster",
"explorer-api",
"explorer-api/explorer-api-requests",
"gateway",
+30
View File
@@ -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"
+35
View File
@@ -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>
```
+109
View File
@@ -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;
}
}
}
}
+125
View File
@@ -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)
}
+115
View File
@@ -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,
})
}