Skip to content

Commit

Permalink
Merge pull request #84 from sephynox/wallet-generation
Browse files Browse the repository at this point in the history
Wallet generation
  • Loading branch information
LimpidCrypto authored Sep 7, 2024
2 parents 6e6d87d + 696b32d commit 3a62396
Show file tree
Hide file tree
Showing 13 changed files with 327 additions and 74 deletions.
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@ transactions = ["core", "amounts", "currencies"]
requests = ["core", "amounts", "currencies"]
results = ["core", "amounts", "currencies"]
ledger = ["core", "amounts", "currencies"]
helpers = ["account-helpers", "ledger-helpers", "transaction-helpers"]
helpers = [
"account-helpers",
"ledger-helpers",
"transaction-helpers",
"wallet-helpers",
]
account-helpers = ["amounts", "currencies", "requests", "results"]
ledger-helpers = ["amounts", "currencies", "requests", "results"]
wallet-helpers = ["requests", "results"]
transaction-helpers = [
"wallet",
"amounts",
Expand All @@ -128,8 +134,8 @@ transaction-helpers = [
]
amounts = ["core"]
currencies = ["core"]
json-rpc = ["url", "reqwless", "embedded-nal-async"]
json-rpc-std = ["url", "reqwest"]
json-rpc = ["url", "reqwless", "embassy-sync", "embedded-nal-async"]
json-rpc-std = ["url", "reqwest", "embassy-sync", "tokio"]
wallet = ["core"]
websocket = [
"url",
Expand Down
25 changes: 11 additions & 14 deletions src/asynch/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,17 @@ where
Err(e) => return Err!(e),
};
}
let account_info = client
.request(
AccountInfo::new(
None,
classic_address,
None,
Some(ledger_index),
None,
None,
None,
)
.into(),
)
.await?;
let request = AccountInfo::new(
None,
classic_address,
None,
Some(ledger_index),
None,
None,
None,
)
.into();
let account_info = client.request(request).await?;

Ok(account_info
.try_into_result::<results::AccountInfo<'_>>()?
Expand Down
3 changes: 3 additions & 0 deletions src/asynch/clients/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ use crate::models::{
use crate::utils::get_random_id;
use alloc::borrow::{Cow, ToOwned};

Check warning on line 7 in src/asynch/clients/client.rs

View workflow job for this annotation

GitHub Actions / xrpl-rust

unused import: `Cow`

Check warning on line 7 in src/asynch/clients/client.rs

View workflow job for this annotation

GitHub Actions / xrpl-rust

unused import: `Cow`
use anyhow::Result;
use url::Url;

#[allow(async_fn_in_trait)]
pub trait Client {
async fn request_impl<'a: 'b, 'b>(&self, request: XRPLRequest<'a>) -> Result<XRPLResponse<'b>>;

fn get_host(&self) -> Url;

fn set_request_id(&self, request: &mut XRPLRequest<'_>) -> () {

Check warning on line 17 in src/asynch/clients/client.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded unit return type

warning: unneeded unit return type --> src/asynch/clients/client.rs:17:60 | 17 | fn set_request_id(&self, request: &mut XRPLRequest<'_>) -> () { | ^^^^^^ help: remove the `-> ()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit = note: `#[warn(clippy::unused_unit)]` on by default
let common_fields = request.get_common_fields_mut();
common_fields.id = match &common_fields.id {
Expand Down
4 changes: 4 additions & 0 deletions src/asynch/clients/json_rpc/exceptions.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use reqwest::Response;
use thiserror_no_std::Error;

#[derive(Debug, Error)]
pub enum XRPLJsonRpcException {
#[cfg(feature = "json-rpc")]
#[error("Reqwless error")]
ReqwlessError,
#[cfg(feature = "json-rpc-std")]
#[error("Request error: {0:?}")]
RequestError(Response),
}
144 changes: 91 additions & 53 deletions src/asynch/clients/json_rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,101 +1,134 @@
use alloc::{string::String, sync::Arc};
use alloc::{string::ToString, vec};
use anyhow::Result;
use embassy_sync::{blocking_mutex::raw::RawMutex, mutex::Mutex};
use serde::Serialize;
use serde_json::{Map, Value};
use url::Url;

use crate::{models::results::XRPLResponse, Err};
use crate::{
asynch::wallet::get_faucet_url,
models::{requests::FundFaucet, results::XRPLResponse},
Err,
};

mod exceptions;
pub use exceptions::XRPLJsonRpcException;

use super::{client::Client, SingleExecutorMutex};
use super::client::Client;

#[allow(async_fn_in_trait)]
pub trait XRPLFaucet: Client {
fn get_faucet_url(&self, url: Option<Url>) -> Result<Url>
where
Self: Sized + Client,
{
get_faucet_url(self, url)
}

async fn request_funding(&self, url: Option<Url>, request: FundFaucet<'_>) -> Result<()>;
}

/// Renames the requests field `command` to `method` for JSON-RPC.
fn request_to_json_rpc(request: &impl Serialize) -> Result<String> {
fn request_to_json_rpc(request: &impl Serialize) -> Result<Value> {
let mut json_rpc_request = Map::new();
let mut request = match serde_json::to_value(request) {
Ok(request) => request,
Ok(request) => match request.as_object().cloned() {
Some(request) => request,
None => todo!("Handle non-object requests"),
},
Err(error) => return Err!(error),
};
if let Some(command) = request.get_mut("command") {
let method = command.take();
request["method"] = method;
}
match serde_json::to_string(&request) {
Ok(request) => Ok(request),
Err(error) => Err!(error),
if let Some(command) = request.remove("command") {
json_rpc_request.insert("method".to_string(), command);
json_rpc_request.insert(
"params".to_string(),
serde_json::Value::Array(vec![Value::Object(request)]),
);
}

Ok(Value::Object(json_rpc_request))
}

#[cfg(feature = "json-rpc-std")]
mod _std {
use crate::models::requests::XRPLRequest;
use crate::models::requests::{FundFaucet, XRPLRequest};

use super::*;
use alloc::string::ToString;
use reqwest::Client as HttpClient;
use url::Url;

pub struct AsyncJsonRpcClient<M = SingleExecutorMutex>
where
M: RawMutex,
{
pub struct AsyncJsonRpcClient {
url: Url,
client: Arc<Mutex<M, HttpClient>>,
}

impl<M> AsyncJsonRpcClient<M>
where
M: RawMutex,
{
pub fn new(url: Url) -> Self {
Self {
url,
client: Arc::new(Mutex::new(HttpClient::new())),
}
}
}

impl<M> AsyncJsonRpcClient<M>
where
M: RawMutex,
{
fn from(url: Url, client: HttpClient) -> Self {
Self {
url,
client: Arc::new(Mutex::new(client)),
}
impl AsyncJsonRpcClient {
pub fn connect(url: Url) -> Self {
Self { url }
}
}

impl<M> Client for AsyncJsonRpcClient<M>
where
M: RawMutex,
{
impl Client for AsyncJsonRpcClient {
async fn request_impl<'a: 'b, 'b>(
&self,
request: XRPLRequest<'a>,
) -> Result<XRPLResponse<'b>> {
let client = self.client.lock().await;
match client
let client = HttpClient::new();
let request_json_rpc = request_to_json_rpc(&request)?;
let response = client
.post(self.url.as_ref())
.body(request_to_json_rpc(&request)?)
.json(&request_json_rpc)
.send()
.await
{
Ok(response) => match response.json().await {
Ok(response) => Ok(response),
.await;
match response {
Ok(response) => match response.text().await {
Ok(response) => {
Ok(serde_json::from_str::<XRPLResponse<'b>>(&response).unwrap())
}
Err(error) => Err!(error),
},
Err(error) => Err!(error),
}
}

fn get_host(&self) -> Url {
self.url.clone()
}
}

impl XRPLFaucet for AsyncJsonRpcClient {
async fn request_funding(&self, url: Option<Url>, request: FundFaucet<'_>) -> Result<()> {
let faucet_url = self.get_faucet_url(url)?;
let client = HttpClient::new();
let request_json_rpc = serde_json::to_value(&request).unwrap();
let response = client
.post(&faucet_url.to_string())

Check warning on line 104 in src/asynch/clients/json_rpc/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

the borrowed expression implements the required traits

warning: the borrowed expression implements the required traits --> src/asynch/clients/json_rpc/mod.rs:104:23 | 104 | .post(&faucet_url.to_string()) | ^^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `faucet_url.to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args = note: `#[warn(clippy::needless_borrows_for_generic_args)]` on by default
.json(&request_json_rpc)
.send()
.await;
match response {
Ok(response) => {
if response.status().is_success() {
Ok(())
} else {
todo!()
// Err!(XRPLJsonRpcException::RequestError())
}
}
Err(error) => {
Err!(error)
}
}
}
}
}

#[cfg(feature = "json-rpc")]
mod _no_std {
use crate::models::requests::XRPLRequest;
use crate::{asynch::clients::SingleExecutorMutex, models::requests::XRPLRequest};

use super::*;
use alloc::sync::Arc;
use embassy_sync::{blocking_mutex::raw::RawMutex, mutex::Mutex};
use embedded_nal_async::{Dns, TcpConnect};
use reqwless::{
client::{HttpClient, TlsConfig},
Expand Down Expand Up @@ -146,7 +179,8 @@ mod _no_std {
request: XRPLRequest<'a>,
) -> Result<XRPLResponse<'b>> {
let request_json_rpc = request_to_json_rpc(&request)?;
let request_buf = request_json_rpc.as_bytes();
let request_string = request_json_rpc.to_string();
let request_buf = request_string.as_bytes();
let mut rx_buffer = [0; BUF];
let mut client = self.client.lock().await;
let response = match client.request(Method::POST, self.url.as_str()).await {
Expand All @@ -170,6 +204,10 @@ mod _no_std {

response

Check warning on line 205 in src/asynch/clients/json_rpc/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

returning the result of a `let` binding from a block

warning: returning the result of a `let` binding from a block --> src/asynch/clients/json_rpc/mod.rs:205:13 | 186 | / let response = match client.request(Method::POST, self.url.as_str()).await { 187 | | Ok(client) => { 188 | | if let Err(_error) = client 189 | | .body(request_buf) ... | 202 | | Err(_error) => Err!(XRPLJsonRpcException::ReqwlessError), 203 | | }; | |______________- unnecessary `let` binding 204 | 205 | response | ^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return = note: `#[warn(clippy::let_and_return)]` on by default help: return the expression directly | 186 ~ 187 | 188 ~ match client.request(Method::POST, self.url.as_str()).await { 189 + Ok(client) => { 190 + if let Err(_error) = client 191 + .body(request_buf) 192 + .content_type(ContentType::TextPlain) 193 + .send(&mut rx_buffer) 194 + .await 195 + { 196 + Err!(XRPLJsonRpcException::ReqwlessError) 197 + } else { 198 + match serde_json::from_slice::<XRPLResponse<'_>>(&rx_buffer) { 199 + Ok(response) => Ok(response), 200 + Err(error) => Err!(error), 201 + } 202 + } 203 + } 204 + Err(_error) => Err!(XRPLJsonRpcException::ReqwlessError), 205 + } |
}

fn get_host(&self) -> Url {
self.url.clone()
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/asynch/clients/websocket/_no_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct AsyncWebsocketClient<
websocket: Arc<Mutex<M, Framer<Rng, Client>>>,
tx_buffer: [u8; BUF],
websocket_base: Arc<Mutex<M, WebsocketBase<M>>>,
uri: Url,
status: PhantomData<Status>,
}

Expand Down Expand Up @@ -124,6 +125,7 @@ where
websocket,
tx_buffer: buffer,
websocket_base: Arc::new(Mutex::new(WebsocketBase::new())),
uri: url,
status: PhantomData::<WebsocketOpen>,
})
}
Expand Down Expand Up @@ -245,6 +247,10 @@ where
E: Debug + Display,
Tcp: Stream<Item = Result<B, E>> + for<'b> Sink<&'b [u8], Error = E> + Unpin,
{
fn get_host(&self) -> Url {
self.uri.clone()
}

async fn request_impl<'a: 'b, 'b>(
&self,
mut request: XRPLRequest<'a>,
Expand Down
6 changes: 6 additions & 0 deletions src/asynch/clients/websocket/_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ where
{
websocket: Arc<Mutex<M, TokioTungsteniteMaybeTlsStream>>,
websocket_base: Arc<Mutex<M, WebsocketBase<M>>>,
uri: Url,
status: PhantomData<Status>,
}

Expand Down Expand Up @@ -141,6 +142,7 @@ where
Ok(AsyncWebsocketClient {
websocket: Arc::new(Mutex::new(stream)),
websocket_base: Arc::new(Mutex::new(WebsocketBase::new())),
uri,
status: PhantomData::<WebsocketOpen>,
})
}
Expand Down Expand Up @@ -199,6 +201,10 @@ impl<M> Client for AsyncWebsocketClient<M, WebsocketOpen>
where
M: RawMutex,
{
fn get_host(&self) -> Url {
self.uri.clone()
}

async fn request_impl<'a: 'b, 'b>(
&self,
mut request: XRPLRequest<'a>,
Expand Down
2 changes: 2 additions & 0 deletions src/asynch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ pub mod clients;
pub mod ledger;
#[cfg(feature = "transaction-helpers")]
pub mod transaction;
#[cfg(all(feature = "wallet-helpers", feature = "json-rpc-std"))]
pub mod wallet;
13 changes: 13 additions & 0 deletions src/asynch/wallet/exceptions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use thiserror_no_std::Error;

#[derive(Error, Debug)]
pub enum XRPLFaucetException {
#[error(
"Cannot fund an account on an issuing chain. Accounts must be created via the bridge."
)]
CannotFundSidechainAccount,
#[error("Cannot derive a faucet URL from the client host.")]
CannotDeriveFaucetUrl,
#[error("Funding request timed out.")]
FundingTimeout,
}
Loading

0 comments on commit 3a62396

Please sign in to comment.