Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cd4a49a07 | |||
| 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 |
Generated
+31
@@ -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"
|
||||
|
||||
@@ -74,6 +74,7 @@ members = [
|
||||
"common/topology",
|
||||
"common/types",
|
||||
"common/wasm-utils",
|
||||
"demos/rust-cosmos-broadcaster",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"gateway",
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user