continued working on tutorial; finished bin/service
This commit is contained in:
committed by
mfahampshire
parent
c3d38fb904
commit
b5cde68e62
@@ -0,0 +1,53 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
# 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](LINK_TO_SURB_DEFAULT_NUMBEr).
|
||||
|
||||
```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
|
||||
.send_bytes(sp_address, message.serialize(), Default::default())
|
||||
.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 of the client code written: now to move on to the `service` that will be interacting with the blockchain on behalf of the `client`.
|
||||
@@ -1,23 +1,79 @@
|
||||
# Preparing Your Client
|
||||
|
||||
Start by creating the startup logic of your `client` - 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 commands.
|
||||
|
||||
Start in `bin/client.rs`.
|
||||
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_cosmos_service::create_client;
|
||||
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 `nym_cosmos_service::create_client` imports the `create_client` function created on the previous page.
|
||||
`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.
|
||||
|
||||
< simple account query first - then maybe a contract query >
|
||||
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](URL_TO_DO)` for the async runtime, this function performs the following functions:
|
||||
* If not already existing, creates a Nym client with config at `/tmp/client`. Otherwise it loads the already existing client from this config.
|
||||
* Matches the command from the CLI - in this instance, the `QueryBalance` function which will be defined in the next section. This will create a `BalanceRequest` and send this to the `service`, before returning the response sent back to it to the main thread and printing this to the console.
|
||||
* Performs a proper shutdown of the running 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(())
|
||||
}
|
||||
```
|
||||
|
||||
@@ -4,4 +4,4 @@ This tutorial is for Rust developers wanting to interact with the Rust SDK and t
|
||||
|
||||
The key here is to think of the service as a proxy: it interacts with the blockchain _on the client's behalf_, shielding it 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]().
|
||||
> 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](RELATIVE_PATH).
|
||||
|
||||
@@ -7,17 +7,20 @@ These include the request and response types the client and the service will be
|
||||
## Dependencies
|
||||
The dependecies for the shared `lib` file are the following:
|
||||
```rust
|
||||
use cosmrs::{tendermint, AccountId};
|
||||
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, and `cosmrs` types are required for defining Nyx blockchain accounts.
|
||||
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.
|
||||
@@ -34,37 +37,53 @@ These define the RPC endpoint your service will use to interact with the blockch
|
||||
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(Deserialize, Serialize, Debug)]
|
||||
pub struct SequenceRequest {
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct BalanceRequest {
|
||||
pub validator: String,
|
||||
pub signer_address: AccountId,
|
||||
pub account: AccountId,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct SequenceRequestResponse {
|
||||
pub account_number: u64,
|
||||
pub sequence: u64,
|
||||
pub chain_id: [tendermint](tendermint)::chain::Id,
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct BalanceResponse {
|
||||
pub balance: Coin,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub enum RequestTypes {
|
||||
Sequence(SequenceRequest),
|
||||
Balance(BalanceRequest),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
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 {
|
||||
Sequence(SequenceRequestResponse),
|
||||
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 to service) and one of a response type (service -> client) so far, a pair of enums has been defined to contain additional response or request types that will be added in the future.
|
||||
|
||||
`SequenceRequest` will be used when requesting the service to query the chain on the client's behalf for an address' sequence information (used for offline signing). You can see the information that will be returned from the chain to the service, and from the service to the client, in `SequenceRequestResponse`.
|
||||
`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`.
|
||||
|
||||
> Although `SequenceResponse` would have been more succinct, there is already a `cosmrs` type with this name. As such the response type was given a different name to avoid confusion.
|
||||
Custom serialistion and deserialisation have been implemented for each enum for ease of future modification if required, and testing.
|
||||
|
||||
## Shared Functions
|
||||
Now to define functions shared by the `client` and `service` binaries.
|
||||
@@ -90,64 +109,38 @@ pub async fn create_client(config_path: PathBuf) -> MixnetClient {
|
||||
|
||||
> If keys and config already exist at this location, re-running this function **will not** overwrite them.
|
||||
|
||||
### Parsing Incoming messages
|
||||
Next to define two functions: one for listening _for_ messages from the mixnet (used by our `service`), and one for listening out for a _reply_ after sending a message to another Nym client (in this case, when sending a message from the `client` to the `service`).
|
||||
### 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_ (_S_ingle _U_se _R_eply _B_locks) that are sent along with any outgoing message by default. More information about them can be found [here](LINK_TO_SURBS_DOCS) 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 entity seeing the destination address. This allows for services to _anonymously reply_ to clients without being able to doxx them.
|
||||
|
||||
```rust
|
||||
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 additional SURBs to sender");
|
||||
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)
|
||||
pub fn handle_response(message: ReconstructedMessage) -> anyhow::Result<ResponseTypes> {
|
||||
ResponseTypes::try_deserialize(message.message)
|
||||
}
|
||||
|
||||
pub async fn listen_and_parse_request(
|
||||
client: &mut MixnetClient,
|
||||
) -> anyhow::Result<(RequestTypes, AnonymousSenderTag)> {
|
||||
let mut message: Vec<ReconstructedMessage> = Vec::new();
|
||||
|
||||
while let Some(new_message) = client.wait_for_messages().await {
|
||||
if new_message.is_empty() {
|
||||
println!("got empty vec - probably the SURBs sent along with the request");
|
||||
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))
|
||||
pub fn handle_request(
|
||||
message: ReconstructedMessage,
|
||||
) -> anyhow::Result<(RequestTypes, Option<AnonymousSenderTag>)> {
|
||||
let request = RequestTypes::try_deserialize(message.message)?;
|
||||
Ok((request, message.sender_tag))
|
||||
}
|
||||
```
|
||||
|
||||
Aside from the return types that each function has, the main difference is that any incoming requests to the `service` will also have SURB packets attached to it.
|
||||
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 for the moment.
|
||||
|
||||
< smol explanation of SURBs + link >
|
||||
```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());
|
||||
}
|
||||
}
|
||||
|
||||
As such, this function returns a tuple containing the `RequestType` _and_ the `sender_tag` used by the `service` to identify which bucket of pre-addressed replies it will use to respond to a request.
|
||||
|
||||
< note concerning parsing out the empty vecs >
|
||||
bail!("did not receive any non-empty message")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
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 interact with a Cosmos SDK blockchain on our behalf (bear in mind this principle works for all blockchains - we're just utilising the `cosmrs` library to interact with the Nyx blockchain in this tutorial).
|
||||
- A client side binary used to (for now) construct a blockchain query and send this query to a service, which will interact with a Cosmos SDK blockchain on our behalf (bear in mind this principle works for all blockchains - we're just utilising the `cosmrs` library to interact with the Nyx blockchain in this tutorial). This query will be to query the balance of an account, in preparation for spending these tokens on a credential 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:
|
||||
@@ -44,7 +44,7 @@ You can find the code for these components [here](). You can use it as a referen
|
||||
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 recieve 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 your app process via the Rust SDK.
|
||||
'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.
|
||||
|
||||
|
||||
@@ -28,16 +28,17 @@ cargo new nym-cosmos-service
|
||||
* Add the following dependencies to your `Cargo.toml` file:
|
||||
```
|
||||
[dependencies]
|
||||
anyhow = "1.0.72"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cosmrs = "=0.14.0"
|
||||
TODO
|
||||
# tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
tokio = "1.24.1"
|
||||
bs58 = "0.5.0"
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
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" }
|
||||
anyhow = "1.0.72"
|
||||
```
|
||||
|
||||
These are non Nym-specific dependencies for the project. `anyhow` is for catch-all error handling, `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.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Preparing Your Service pt2
|
||||
@@ -1 +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](URL) for the async runtime, `main` does the following:
|
||||
* If not already existing, creates a Nym client with config at `/tmp/client`. Otherwise it loads the already existing client from this config.
|
||||
* 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 Nym client in `client` with the response from the blockchain.
|
||||
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
setup_logging();
|
||||
let mut client = create_client("/tmp/service2".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_str_reply(return_recipient, &serde_json::to_string(&response)?)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user