From 7acf7dcd0515f2d6c9d0b1469101df85e25242f9 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 13 Apr 2023 18:56:21 +0200 Subject: [PATCH 01/33] get changes from networking branch and use dev branch as base so all commits are signed --- .gitignore | 1 + Cargo.toml | 13 +- src/_anyhow/mod.rs | 2 + src/asynch/clients/async_client.rs | 11 ++ src/asynch/clients/async_json_rpc_client.rs | 2 + src/asynch/clients/async_websocket_client.rs | 164 ++++++++++++++++++ src/asynch/clients/client.rs | 10 ++ src/asynch/clients/exceptions.rs | 9 + src/asynch/clients/json_rpc_base.rs | 2 + src/asynch/clients/mod.rs | 10 ++ src/asynch/clients/websocket_base.rs | 20 +++ src/asynch/mod.rs | 2 + src/lib.rs | 5 + src/models/mod.rs | 1 + src/models/requests/account_channels.rs | 2 +- src/models/requests/account_currencies.rs | 2 +- src/models/requests/account_info.rs | 2 +- src/models/requests/account_lines.rs | 2 +- src/models/requests/account_nfts.rs | 7 +- src/models/requests/account_objects.rs | 2 +- src/models/requests/account_offers.rs | 2 +- src/models/requests/account_tx.rs | 2 +- src/models/requests/book_offers.rs | 2 +- src/models/requests/channel_authorize.rs | 4 +- src/models/requests/channel_verify.rs | 2 +- src/models/requests/deposit_authorize.rs | 2 +- src/models/requests/fee.rs | 2 +- src/models/requests/gateway_balances.rs | 2 +- src/models/requests/ledger.rs | 2 +- src/models/requests/ledger_closed.rs | 2 +- src/models/requests/ledger_current.rs | 2 +- src/models/requests/ledger_data.rs | 2 +- src/models/requests/ledger_entry.rs | 4 +- src/models/requests/manifest.rs | 2 +- src/models/requests/mod.rs | 2 + src/models/requests/nft_buy_offers.rs | 2 +- src/models/requests/nft_sell_offers.rs | 2 +- src/models/requests/no_ripple_check.rs | 2 +- src/models/requests/path_find.rs | 2 +- src/models/requests/ping.rs | 2 +- src/models/requests/random.rs | 2 +- src/models/requests/responses/mod.rs | 1 + src/models/requests/ripple_path_find.rs | 2 +- src/models/requests/server_info.rs | 2 +- src/models/requests/server_state.rs | 2 +- src/models/requests/submit.rs | 2 +- src/models/requests/submit_multisigned.rs | 2 +- src/models/requests/subscribe.rs | 2 +- src/models/requests/transaction_entry.rs | 2 +- src/models/requests/tx.rs | 2 +- src/models/requests/unsubscribe.rs | 2 +- src/models/transactions/account_delete.rs | 2 +- src/models/transactions/account_set.rs | 4 +- src/models/transactions/check_cancel.rs | 2 +- src/models/transactions/check_cash.rs | 4 +- src/models/transactions/check_create.rs | 2 +- src/models/transactions/deposit_preauth.rs | 4 +- src/models/transactions/escrow_cancel.rs | 2 +- src/models/transactions/escrow_create.rs | 4 +- src/models/transactions/escrow_finish.rs | 4 +- .../transactions/nftoken_accept_offer.rs | 4 +- src/models/transactions/nftoken_burn.rs | 2 +- .../transactions/nftoken_cancel_offer.rs | 4 +- .../transactions/nftoken_create_offer.rs | 4 +- src/models/transactions/nftoken_mint.rs | 4 +- src/models/transactions/offer_cancel.rs | 2 +- src/models/transactions/offer_create.rs | 2 +- src/models/transactions/payment.rs | 3 +- .../transactions/payment_channel_claim.rs | 2 +- .../transactions/payment_channel_create.rs | 2 +- .../transactions/payment_channel_fund.rs | 2 +- .../pseudo_transactions/enable_amendment.rs | 2 +- .../pseudo_transactions/set_fee.rs | 2 +- .../pseudo_transactions/unl_modify.rs | 2 +- src/models/transactions/set_regular_key.rs | 2 +- src/models/transactions/signer_list_set.rs | 4 +- src/models/transactions/ticket_create.rs | 2 +- src/models/transactions/trust_set.rs | 2 +- tests/common.rs | 34 +++- 79 files changed, 351 insertions(+), 92 deletions(-) create mode 100644 src/asynch/clients/async_client.rs create mode 100644 src/asynch/clients/async_json_rpc_client.rs create mode 100644 src/asynch/clients/async_websocket_client.rs create mode 100644 src/asynch/clients/client.rs create mode 100644 src/asynch/clients/exceptions.rs create mode 100644 src/asynch/clients/json_rpc_base.rs create mode 100644 src/asynch/clients/mod.rs create mode 100644 src/asynch/clients/websocket_base.rs create mode 100644 src/asynch/mod.rs create mode 100644 src/models/requests/responses/mod.rs diff --git a/.gitignore b/.gitignore index 1ec67b52..0401f97f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ Cargo.lock # VSCode .vscode + .idea # Additional diff --git a/Cargo.toml b/Cargo.toml index 11b261e2..0a0fec7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,14 @@ fnv = { version = "1.0.7", default-features = false } derive-new = { version = "0.5.9", default-features = false } thiserror-no-std = "2.0.2" anyhow = { version ="1.0.69", default-features = false } +tokio = { version = "1.28.0", default-features = false, optional = true } + +# Use git version as long as there is no release for em-as-net. +[dependencies.em-as-net] +git = "https://github.com/LimpidCrypto/em-as-net" +package = "em-as-net" +default-features = false +optional = true [dev-dependencies] criterion = "0.4.0" @@ -71,13 +79,14 @@ name = "benchmarks" harness = false [features] -default = ["std", "core", "models", "utils"] +default = ["std", "core", "models", "utils", "net"] models = ["core", "transactions", "requests", "ledger"] transactions = ["core", "amounts", "currencies"] requests = ["core", "amounts", "currencies"] ledger = ["core", "amounts", "currencies"] amounts = ["core"] currencies = ["core"] +net = ["em-as-net"] core = ["utils"] utils = [] -std = ["rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] +std = ["em-as-net/std", "tokio/full", "rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] diff --git a/src/_anyhow/mod.rs b/src/_anyhow/mod.rs index 345a3a8b..7b4ab3ec 100644 --- a/src/_anyhow/mod.rs +++ b/src/_anyhow/mod.rs @@ -2,6 +2,8 @@ #[macro_export] macro_rules! Err { ($err:expr $(,)?) => {{ + use alloc::string::ToString; + let error = $err.to_string().replace("\"", ""); let boxed_error = ::alloc::boxed::Box::new(error); let leaked_error: &'static str = ::alloc::boxed::Box::leak(boxed_error); diff --git a/src/asynch/clients/async_client.rs b/src/asynch/clients/async_client.rs new file mode 100644 index 00000000..a5449ce5 --- /dev/null +++ b/src/asynch/clients/async_client.rs @@ -0,0 +1,11 @@ +use super::client::Client; +use crate::models::Model; +use anyhow::Result; +use serde::Serialize; + +/// Interface for all async network clients to follow. +pub trait AsyncClient<'a>: Client<'a> { + async fn request(&mut self, request: T) -> Result { + self.request_impl(request).await + } +} diff --git a/src/asynch/clients/async_json_rpc_client.rs b/src/asynch/clients/async_json_rpc_client.rs new file mode 100644 index 00000000..614db2f9 --- /dev/null +++ b/src/asynch/clients/async_json_rpc_client.rs @@ -0,0 +1,2 @@ +// /// An async client for interacting with the rippled JSON RPC. +// pub struct AsyncJsonRpcClient {} diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs new file mode 100644 index 00000000..4c355e24 --- /dev/null +++ b/src/asynch/clients/async_websocket_client.rs @@ -0,0 +1,164 @@ +use crate::asynch::clients::exceptions::XRPLWebsocketException; +use crate::asynch::clients::websocket_base::WebsocketBase; +use crate::models::Model; +use crate::Err; +use anyhow::Result; +use serde::Serialize; + +// exports +#[cfg(feature = "std")] +pub use if_std::AsyncWebsocketClient; + +pub use em_as_net::client::websocket::ReadResult; + +#[cfg(feature = "std")] +mod if_std { + use crate::asynch::clients::async_client::AsyncClient; + use crate::asynch::clients::client::Client; + use crate::asynch::clients::exceptions::XRPLWebsocketException; + use crate::asynch::clients::websocket_base::WebsocketBase; + use crate::models::Model; + use crate::Err; + use alloc::borrow::Cow; + use core::cell::RefCell; + use core::ops::Deref; + + use anyhow::Result; + + use crate::asynch::clients::Websocket; + use em_as_net::client::websocket::{ + ReadResult, WebsocketClient, WebsocketClientIo, WebsocketSendMessageType, + }; + use rand::rngs::ThreadRng; + use serde::Serialize; + use tokio::net; + + /// An async client for interacting with the rippled WebSocket API. + pub struct AsyncWebsocketClient<'a> { + pub uri: Cow<'a, str>, + inner: RefCell>>, + } + + impl<'a> AsyncWebsocketClient<'a> { + pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { + let ws = WebsocketClient::new(uri.clone(), buffer); + Self { + uri, + inner: RefCell::new(Some(ws)), + } + } + } + + impl<'a> Websocket<'a> for AsyncWebsocketClient<'a> {} + + impl<'a> WebsocketBase<'a> for AsyncWebsocketClient<'a> { + fn is_open(&self) -> bool { + if let Some(ws) = self.inner.borrow().deref() { + ws.is_open() + } else { + false + } + } + + async fn do_open(&self) -> Result<()> { + return match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => ws.connect(None).await, + }; + } + + async fn do_close(&self) -> Result<()> { + return match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => ws.close().await, + }; + } + + async fn do_write(&self, request: T) -> Result<()> { + return match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => { + let request_string = match serde_json::to_string(&request) { + Ok(as_string) => as_string, + Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), + }; + ws.write( + Cow::from(request_string), + Some(WebsocketSendMessageType::Text), + ) + .await + } + }; + } + + // TODO: Fix lifetime issue + async fn do_read(&'a mut self) -> Result>> { + return match self.inner.get_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => match ws.read().await { + None => Ok(None), + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(read_error)) => Err(read_error), + }, + }; + } + + async fn do_request_impl(&mut self, _request: T) -> Result { + todo!() + } + } + + impl<'a> AsyncClient<'a> for AsyncWebsocketClient<'a> {} + + impl<'a> Client<'a> for AsyncWebsocketClient<'a> { + async fn request_impl(&mut self, request: T) -> Result { + if ! as WebsocketBase<'_>>::is_open(self) { + return Err!(XRPLWebsocketException::NotOpen); + } + + self.do_request_impl(request).await + } + } +} + +pub trait Websocket<'a>: WebsocketBase<'a> { + async fn open(&mut self) -> Result<()> { + if !self.is_open() { + self.do_open().await + } else { + Ok(()) + } + } + + async fn close(&self) -> Result<()> { + if self.is_open() { + self.do_close().await + } else { + Ok(()) + } + } + + async fn write(&mut self, request: T) -> Result<()> { + if self.is_open() { + self.do_write(request).await + } else { + Err!(XRPLWebsocketException::NotOpen) + } + } + + async fn read(&'a mut self) -> Result>> { + if self.is_open() { + self.do_read().await + } else { + Err!(XRPLWebsocketException::NotOpen) + } + } +} diff --git a/src/asynch/clients/client.rs b/src/asynch/clients/client.rs new file mode 100644 index 00000000..611f7ec2 --- /dev/null +++ b/src/asynch/clients/client.rs @@ -0,0 +1,10 @@ +use crate::models::Model; +use anyhow::Result; +use serde::Serialize; + +/// Interface for all network clients to follow. +// TODO: `T` should implement a trait `Request` +// TODO: `R` should implement a trait `Response` +pub trait Client<'a> { + async fn request_impl(&mut self, request: T) -> Result; +} diff --git a/src/asynch/clients/exceptions.rs b/src/asynch/clients/exceptions.rs new file mode 100644 index 00000000..f7d212cf --- /dev/null +++ b/src/asynch/clients/exceptions.rs @@ -0,0 +1,9 @@ +use thiserror_no_std::Error; + +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum XRPLWebsocketException { + #[error("Websocket is not open")] + NotOpen, + #[error("Failed to serialize XRPL request to string")] + RequestSerializationError, +} diff --git a/src/asynch/clients/json_rpc_base.rs b/src/asynch/clients/json_rpc_base.rs new file mode 100644 index 00000000..b50e9d49 --- /dev/null +++ b/src/asynch/clients/json_rpc_base.rs @@ -0,0 +1,2 @@ +// /// A common interface for JsonRpc requests. +// pub trait JsonRpcBase {} diff --git a/src/asynch/clients/mod.rs b/src/asynch/clients/mod.rs new file mode 100644 index 00000000..80af82fc --- /dev/null +++ b/src/asynch/clients/mod.rs @@ -0,0 +1,10 @@ +mod async_client; +pub mod async_json_rpc_client; +pub mod async_websocket_client; +mod client; +pub mod exceptions; +mod json_rpc_base; +mod websocket_base; + +pub use async_json_rpc_client::*; +pub use async_websocket_client::*; diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs new file mode 100644 index 00000000..20c49799 --- /dev/null +++ b/src/asynch/clients/websocket_base.rs @@ -0,0 +1,20 @@ +use crate::asynch::clients::client::Client; +use crate::models::Model; +use anyhow::Result; +use em_as_net::client::websocket::ReadResult; +use serde::Serialize; + +// A client for interacting with the rippled WebSocket API. +pub trait WebsocketBase<'a>: Client<'a> { + fn is_open(&self) -> bool; + + async fn do_open(&self) -> Result<()>; + + async fn do_close(&self) -> Result<()>; + + async fn do_write(&self, request: T) -> Result<()>; + + async fn do_read(&'a mut self) -> Result>>; + + async fn do_request_impl(&mut self, request: T) -> Result; +} diff --git a/src/asynch/mod.rs b/src/asynch/mod.rs new file mode 100644 index 00000000..286b8f61 --- /dev/null +++ b/src/asynch/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "net")] +pub mod clients; diff --git a/src/lib.rs b/src/lib.rs index 58f3a239..437fbfb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,12 +19,17 @@ //! [XRP Ledger](https://xrpl.org/docs.html). #![no_std] #![allow(dead_code)] // Remove eventually +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] +#![feature(inherent_associated_types)] +#![feature(type_alias_impl_trait)] #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(feature = "std")] extern crate std as alloc; +pub mod asynch; pub mod constants; #[cfg(feature = "core")] pub mod core; diff --git a/src/models/mod.rs b/src/models/mod.rs index b3c6a22d..ff0bc5e9 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -9,6 +9,7 @@ pub mod exceptions; #[cfg(feature = "ledger")] +#[allow(clippy::too_many_arguments)] pub mod ledger; pub mod model; #[cfg(feature = "requests")] diff --git a/src/models/requests/account_channels.rs b/src/models/requests/account_channels.rs index 705d5cd2..a6f0ed97 100644 --- a/src/models/requests/account_channels.rs +++ b/src/models/requests/account_channels.rs @@ -76,7 +76,7 @@ impl<'a> Default for AccountChannels<'a> { impl<'a> Model for AccountChannels<'a> {} impl<'a> AccountChannels<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_currencies.rs b/src/models/requests/account_currencies.rs index 35a91428..1edc9008 100644 --- a/src/models/requests/account_currencies.rs +++ b/src/models/requests/account_currencies.rs @@ -50,7 +50,7 @@ impl<'a> Default for AccountCurrencies<'a> { impl<'a> Model for AccountCurrencies<'a> {} impl<'a> AccountCurrencies<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_info.rs b/src/models/requests/account_info.rs index 4f2ca2d7..038b06b4 100644 --- a/src/models/requests/account_info.rs +++ b/src/models/requests/account_info.rs @@ -59,7 +59,7 @@ impl<'a> Default for AccountInfo<'a> { impl<'a> Model for AccountInfo<'a> {} impl<'a> AccountInfo<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_lines.rs b/src/models/requests/account_lines.rs index d08e88f1..8ca47b56 100644 --- a/src/models/requests/account_lines.rs +++ b/src/models/requests/account_lines.rs @@ -56,7 +56,7 @@ impl<'a> Default for AccountLines<'a> { impl<'a> Model for AccountLines<'a> {} impl<'a> AccountLines<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_nfts.rs b/src/models/requests/account_nfts.rs index 2cf7fb3a..faa26391 100644 --- a/src/models/requests/account_nfts.rs +++ b/src/models/requests/account_nfts.rs @@ -41,7 +41,12 @@ impl<'a> Default for AccountNfts<'a> { impl<'a> Model for AccountNfts<'a> {} impl<'a> AccountNfts<'a> { - fn new(account: &'a str, id: Option<&'a str>, limit: Option, marker: Option) -> Self { + pub fn new( + account: &'a str, + id: Option<&'a str>, + limit: Option, + marker: Option, + ) -> Self { Self { account, id, diff --git a/src/models/requests/account_objects.rs b/src/models/requests/account_objects.rs index 0868a200..24a7a0ff 100644 --- a/src/models/requests/account_objects.rs +++ b/src/models/requests/account_objects.rs @@ -79,7 +79,7 @@ impl<'a> Default for AccountObjects<'a> { impl<'a> Model for AccountObjects<'a> {} impl<'a> AccountObjects<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_offers.rs b/src/models/requests/account_offers.rs index 90500ebf..d310d540 100644 --- a/src/models/requests/account_offers.rs +++ b/src/models/requests/account_offers.rs @@ -55,7 +55,7 @@ impl<'a> Default for AccountOffers<'a> { impl<'a> Model for AccountOffers<'a> {} impl<'a> AccountOffers<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_tx.rs b/src/models/requests/account_tx.rs index d3c63107..25827af4 100644 --- a/src/models/requests/account_tx.rs +++ b/src/models/requests/account_tx.rs @@ -71,7 +71,7 @@ impl<'a> Default for AccountTx<'a> { impl<'a> Model for AccountTx<'a> {} impl<'a> AccountTx<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/book_offers.rs b/src/models/requests/book_offers.rs index 75551e4a..e470a70c 100644 --- a/src/models/requests/book_offers.rs +++ b/src/models/requests/book_offers.rs @@ -61,7 +61,7 @@ impl<'a> Default for BookOffers<'a> { impl<'a> Model for BookOffers<'a> {} impl<'a> BookOffers<'a> { - fn new( + pub fn new( taker_gets: Currency<'a>, taker_pays: Currency<'a>, id: Option<&'a str>, diff --git a/src/models/requests/channel_authorize.rs b/src/models/requests/channel_authorize.rs index 348eb7b1..8024c20a 100644 --- a/src/models/requests/channel_authorize.rs +++ b/src/models/requests/channel_authorize.rs @@ -3,8 +3,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::requests::XRPLChannelAuthorizeException; use crate::{ constants::CryptoAlgorithm, @@ -120,7 +118,7 @@ impl<'a> ChannelAuthorizeError for ChannelAuthorize<'a> { } impl<'a> ChannelAuthorize<'a> { - fn new( + pub fn new( channel_id: &'a str, amount: &'a str, id: Option<&'a str>, diff --git a/src/models/requests/channel_verify.rs b/src/models/requests/channel_verify.rs index 4355e07f..73fb1dc3 100644 --- a/src/models/requests/channel_verify.rs +++ b/src/models/requests/channel_verify.rs @@ -43,7 +43,7 @@ impl<'a> Default for ChannelVerify<'a> { impl<'a> Model for ChannelVerify<'a> {} impl<'a> ChannelVerify<'a> { - fn new( + pub fn new( channel_id: &'a str, amount: &'a str, public_key: &'a str, diff --git a/src/models/requests/deposit_authorize.rs b/src/models/requests/deposit_authorize.rs index 21a4af44..087a00db 100644 --- a/src/models/requests/deposit_authorize.rs +++ b/src/models/requests/deposit_authorize.rs @@ -43,7 +43,7 @@ impl<'a> Default for DepositAuthorized<'a> { impl<'a> Model for DepositAuthorized<'a> {} impl<'a> DepositAuthorized<'a> { - fn new( + pub fn new( source_account: &'a str, destination_account: &'a str, id: Option<&'a str>, diff --git a/src/models/requests/fee.rs b/src/models/requests/fee.rs index a5507b32..8416a2ef 100644 --- a/src/models/requests/fee.rs +++ b/src/models/requests/fee.rs @@ -32,7 +32,7 @@ impl<'a> Default for Fee<'a> { impl<'a> Model for Fee<'a> {} impl<'a> Fee<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::Fee, diff --git a/src/models/requests/gateway_balances.rs b/src/models/requests/gateway_balances.rs index 122ff42c..ade93092 100644 --- a/src/models/requests/gateway_balances.rs +++ b/src/models/requests/gateway_balances.rs @@ -50,7 +50,7 @@ impl<'a> Default for GatewayBalances<'a> { impl<'a> Model for GatewayBalances<'a> {} impl<'a> GatewayBalances<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, strict: Option, diff --git a/src/models/requests/ledger.rs b/src/models/requests/ledger.rs index 76d22253..bc1eabe0 100644 --- a/src/models/requests/ledger.rs +++ b/src/models/requests/ledger.rs @@ -76,7 +76,7 @@ impl<'a> Default for Ledger<'a> { impl<'a> Model for Ledger<'a> {} impl<'a> Ledger<'a> { - fn new( + pub fn new( id: Option<&'a str>, ledger_hash: Option<&'a str>, ledger_index: Option<&'a str>, diff --git a/src/models/requests/ledger_closed.rs b/src/models/requests/ledger_closed.rs index 1b76d328..e7b5d309 100644 --- a/src/models/requests/ledger_closed.rs +++ b/src/models/requests/ledger_closed.rs @@ -31,7 +31,7 @@ impl<'a> Default for LedgerClosed<'a> { impl<'a> Model for LedgerClosed<'a> {} impl<'a> LedgerClosed<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::LedgerClosed, diff --git a/src/models/requests/ledger_current.rs b/src/models/requests/ledger_current.rs index 93e6bfac..6ccfd04d 100644 --- a/src/models/requests/ledger_current.rs +++ b/src/models/requests/ledger_current.rs @@ -31,7 +31,7 @@ impl<'a> Default for LedgerCurrent<'a> { impl<'a> Model for LedgerCurrent<'a> {} impl<'a> LedgerCurrent<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::LedgerCurrent, diff --git a/src/models/requests/ledger_data.rs b/src/models/requests/ledger_data.rs index e5197671..362e4291 100644 --- a/src/models/requests/ledger_data.rs +++ b/src/models/requests/ledger_data.rs @@ -50,7 +50,7 @@ impl<'a> Default for LedgerData<'a> { impl<'a> Model for LedgerData<'a> {} impl<'a> LedgerData<'a> { - fn new( + pub fn new( id: Option<&'a str>, ledger_hash: Option<&'a str>, ledger_index: Option<&'a str>, diff --git a/src/models/requests/ledger_entry.rs b/src/models/requests/ledger_entry.rs index a21bd5a6..78856383 100644 --- a/src/models/requests/ledger_entry.rs +++ b/src/models/requests/ledger_entry.rs @@ -3,8 +3,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::requests::XRPLLedgerEntryException; use crate::models::{requests::RequestMethod, Model}; @@ -178,7 +176,7 @@ impl<'a> LedgerEntryError for LedgerEntry<'a> { } impl<'a> LedgerEntry<'a> { - fn new( + pub fn new( id: Option<&'a str>, index: Option<&'a str>, account_root: Option<&'a str>, diff --git a/src/models/requests/manifest.rs b/src/models/requests/manifest.rs index 9705a6f3..7a02e289 100644 --- a/src/models/requests/manifest.rs +++ b/src/models/requests/manifest.rs @@ -37,7 +37,7 @@ impl<'a> Default for Manifest<'a> { impl<'a> Model for Manifest<'a> {} impl<'a> Manifest<'a> { - fn new(public_key: &'a str, id: Option<&'a str>) -> Self { + pub fn new(public_key: &'a str, id: Option<&'a str>) -> Self { Self { public_key, id, diff --git a/src/models/requests/mod.rs b/src/models/requests/mod.rs index 9473949d..1d5d9ca8 100644 --- a/src/models/requests/mod.rs +++ b/src/models/requests/mod.rs @@ -25,6 +25,7 @@ pub mod no_ripple_check; pub mod path_find; pub mod ping; pub mod random; +pub mod responses; pub mod ripple_path_find; pub mod server_info; pub mod server_state; @@ -62,6 +63,7 @@ pub use no_ripple_check::*; pub use path_find::*; pub use ping::*; pub use random::*; +pub use responses::*; pub use ripple_path_find::*; pub use server_info::*; pub use server_state::*; diff --git a/src/models/requests/nft_buy_offers.rs b/src/models/requests/nft_buy_offers.rs index b34308d3..3ecfa1ef 100644 --- a/src/models/requests/nft_buy_offers.rs +++ b/src/models/requests/nft_buy_offers.rs @@ -42,7 +42,7 @@ impl<'a> Default for NftBuyOffers<'a> { impl<'a> Model for NftBuyOffers<'a> {} impl<'a> NftBuyOffers<'a> { - fn new( + pub fn new( nft_id: &'a str, ledger_hash: Option<&'a str>, ledger_index: Option<&'a str>, diff --git a/src/models/requests/nft_sell_offers.rs b/src/models/requests/nft_sell_offers.rs index 35273506..354cafda 100644 --- a/src/models/requests/nft_sell_offers.rs +++ b/src/models/requests/nft_sell_offers.rs @@ -26,7 +26,7 @@ impl<'a> Default for NftSellOffers<'a> { impl<'a> Model for NftSellOffers<'a> {} impl<'a> NftSellOffers<'a> { - fn new(nft_id: &'a str) -> Self { + pub fn new(nft_id: &'a str) -> Self { Self { nft_id, command: RequestMethod::NftSellOffers, diff --git a/src/models/requests/no_ripple_check.rs b/src/models/requests/no_ripple_check.rs index 5c52ef61..37f5f0bc 100644 --- a/src/models/requests/no_ripple_check.rs +++ b/src/models/requests/no_ripple_check.rs @@ -73,7 +73,7 @@ impl<'a> Default for NoRippleCheck<'a> { impl<'a> Model for NoRippleCheck<'a> {} impl<'a> NoRippleCheck<'a> { - fn new( + pub fn new( account: &'a str, role: NoRippleCheckRole, id: Option<&'a str>, diff --git a/src/models/requests/path_find.rs b/src/models/requests/path_find.rs index 57bc751b..0eedc299 100644 --- a/src/models/requests/path_find.rs +++ b/src/models/requests/path_find.rs @@ -105,7 +105,7 @@ impl<'a> Default for PathFind<'a> { impl<'a> Model for PathFind<'a> {} impl<'a> PathFind<'a> { - fn new( + pub fn new( subcommand: PathFindSubcommand, source_account: &'a str, destination_account: &'a str, diff --git a/src/models/requests/ping.rs b/src/models/requests/ping.rs index 24e1c68b..9c100442 100644 --- a/src/models/requests/ping.rs +++ b/src/models/requests/ping.rs @@ -30,7 +30,7 @@ impl<'a> Default for Ping<'a> { impl<'a> Model for Ping<'a> {} impl<'a> Ping<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::Ping, diff --git a/src/models/requests/random.rs b/src/models/requests/random.rs index 8cf99192..0ecbfbdc 100644 --- a/src/models/requests/random.rs +++ b/src/models/requests/random.rs @@ -31,7 +31,7 @@ impl<'a> Default for Random<'a> { impl<'a> Model for Random<'a> {} impl<'a> Random<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::Random, diff --git a/src/models/requests/responses/mod.rs b/src/models/requests/responses/mod.rs new file mode 100644 index 00000000..13671b75 --- /dev/null +++ b/src/models/requests/responses/mod.rs @@ -0,0 +1 @@ +pub trait Response {} diff --git a/src/models/requests/ripple_path_find.rs b/src/models/requests/ripple_path_find.rs index cbafd6ed..77e4ce06 100644 --- a/src/models/requests/ripple_path_find.rs +++ b/src/models/requests/ripple_path_find.rs @@ -79,7 +79,7 @@ impl<'a> Default for RipplePathFind<'a> { impl<'a> Model for RipplePathFind<'a> {} impl<'a> RipplePathFind<'a> { - fn new( + pub fn new( source_account: &'a str, destination_account: &'a str, destination_amount: Currency<'a>, diff --git a/src/models/requests/server_info.rs b/src/models/requests/server_info.rs index 0308fbd5..f869a4ae 100644 --- a/src/models/requests/server_info.rs +++ b/src/models/requests/server_info.rs @@ -31,7 +31,7 @@ impl<'a> Default for ServerInfo<'a> { impl<'a> Model for ServerInfo<'a> {} impl<'a> ServerInfo<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::ServerInfo, diff --git a/src/models/requests/server_state.rs b/src/models/requests/server_state.rs index 2b7942c6..8f1997b8 100644 --- a/src/models/requests/server_state.rs +++ b/src/models/requests/server_state.rs @@ -36,7 +36,7 @@ impl<'a> Default for ServerState<'a> { impl<'a> Model for ServerState<'a> {} impl<'a> ServerState<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::ServerState, diff --git a/src/models/requests/submit.rs b/src/models/requests/submit.rs index 2fabd316..537125d7 100644 --- a/src/models/requests/submit.rs +++ b/src/models/requests/submit.rs @@ -60,7 +60,7 @@ impl<'a> Default for Submit<'a> { impl<'a> Model for Submit<'a> {} impl<'a> Submit<'a> { - fn new(tx_blob: &'a str, id: Option<&'a str>, fail_hard: Option) -> Self { + pub fn new(tx_blob: &'a str, id: Option<&'a str>, fail_hard: Option) -> Self { Self { tx_blob, id, diff --git a/src/models/requests/submit_multisigned.rs b/src/models/requests/submit_multisigned.rs index 368207a3..bb1d0cf6 100644 --- a/src/models/requests/submit_multisigned.rs +++ b/src/models/requests/submit_multisigned.rs @@ -40,7 +40,7 @@ impl<'a> Default for SubmitMultisigned<'a> { impl<'a> Model for SubmitMultisigned<'a> {} impl<'a> SubmitMultisigned<'a> { - fn new(id: Option<&'a str>, fail_hard: Option) -> Self { + pub fn new(id: Option<&'a str>, fail_hard: Option) -> Self { Self { id, fail_hard, diff --git a/src/models/requests/subscribe.rs b/src/models/requests/subscribe.rs index 5c1efd05..60adf24d 100644 --- a/src/models/requests/subscribe.rs +++ b/src/models/requests/subscribe.rs @@ -93,7 +93,7 @@ impl<'a> Default for Subscribe<'a> { impl<'a> Model for Subscribe<'a> {} impl<'a> Subscribe<'a> { - fn new( + pub fn new( id: Option<&'a str>, books: Option>>, streams: Option>, diff --git a/src/models/requests/transaction_entry.rs b/src/models/requests/transaction_entry.rs index cf0cf33c..b6687476 100644 --- a/src/models/requests/transaction_entry.rs +++ b/src/models/requests/transaction_entry.rs @@ -43,7 +43,7 @@ impl<'a> Default for TransactionEntry<'a> { impl<'a> Model for TransactionEntry<'a> {} impl<'a> TransactionEntry<'a> { - fn new( + pub fn new( tx_hash: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/tx.rs b/src/models/requests/tx.rs index 9d3056f9..2425536b 100644 --- a/src/models/requests/tx.rs +++ b/src/models/requests/tx.rs @@ -46,7 +46,7 @@ impl<'a> Default for Tx<'a> { impl<'a> Model for Tx<'a> {} impl<'a> Tx<'a> { - fn new( + pub fn new( id: Option<&'a str>, binary: Option, min_ledger: Option, diff --git a/src/models/requests/unsubscribe.rs b/src/models/requests/unsubscribe.rs index d8ac3d99..8ffc8210 100644 --- a/src/models/requests/unsubscribe.rs +++ b/src/models/requests/unsubscribe.rs @@ -75,7 +75,7 @@ impl<'a> Default for Unsubscribe<'a> { impl<'a> Model for Unsubscribe<'a> {} impl<'a> Unsubscribe<'a> { - fn new( + pub fn new( id: Option<&'a str>, books: Option>>, streams: Option>, diff --git a/src/models/transactions/account_delete.rs b/src/models/transactions/account_delete.rs index 6623b401..e30e1a05 100644 --- a/src/models/transactions/account_delete.rs +++ b/src/models/transactions/account_delete.rs @@ -121,7 +121,7 @@ impl<'a> Transaction for AccountDelete<'a> { } impl<'a> AccountDelete<'a> { - fn new( + pub fn new( account: &'a str, destination: &'a str, fee: Option>, diff --git a/src/models/transactions/account_set.rs b/src/models/transactions/account_set.rs index 8b37c14a..3c9fad90 100644 --- a/src/models/transactions/account_set.rs +++ b/src/models/transactions/account_set.rs @@ -5,8 +5,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_with::skip_serializing_none; use strum_macros::{AsRefStr, Display, EnumIter}; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLAccountSetException; use crate::{ @@ -387,7 +385,7 @@ impl<'a> AccountSetError for AccountSet<'a> { } impl<'a> AccountSet<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/check_cancel.rs b/src/models/transactions/check_cancel.rs index 26a81743..48bef9c1 100644 --- a/src/models/transactions/check_cancel.rs +++ b/src/models/transactions/check_cancel.rs @@ -114,7 +114,7 @@ impl<'a> Transaction for CheckCancel<'a> { } impl<'a> CheckCancel<'a> { - fn new( + pub fn new( account: &'a str, check_id: &'a str, fee: Option>, diff --git a/src/models/transactions/check_cash.rs b/src/models/transactions/check_cash.rs index 58648d39..c51951fc 100644 --- a/src/models/transactions/check_cash.rs +++ b/src/models/transactions/check_cash.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLCheckCashException; use crate::models::{ @@ -147,7 +145,7 @@ impl<'a> CheckCashError for CheckCash<'a> { } impl<'a> CheckCash<'a> { - fn new( + pub fn new( account: &'a str, check_id: &'a str, fee: Option>, diff --git a/src/models/transactions/check_create.rs b/src/models/transactions/check_create.rs index 772bbd11..b5c1f83a 100644 --- a/src/models/transactions/check_create.rs +++ b/src/models/transactions/check_create.rs @@ -121,7 +121,7 @@ impl<'a> Transaction for CheckCreate<'a> { } impl<'a> CheckCreate<'a> { - fn new( + pub fn new( account: &'a str, destination: &'a str, send_max: Amount<'a>, diff --git a/src/models/transactions/deposit_preauth.rs b/src/models/transactions/deposit_preauth.rs index ceb42a5e..0376f24c 100644 --- a/src/models/transactions/deposit_preauth.rs +++ b/src/models/transactions/deposit_preauth.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLDepositPreauthException; use crate::models::{ @@ -141,7 +139,7 @@ impl<'a> DepositPreauthError for DepositPreauth<'a> { } impl<'a> DepositPreauth<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/escrow_cancel.rs b/src/models/transactions/escrow_cancel.rs index 5bef7a5b..42eadca2 100644 --- a/src/models/transactions/escrow_cancel.rs +++ b/src/models/transactions/escrow_cancel.rs @@ -112,7 +112,7 @@ impl<'a> Transaction for EscrowCancel<'a> { } impl<'a> EscrowCancel<'a> { - fn new( + pub fn new( account: &'a str, owner: &'a str, offer_sequence: u32, diff --git a/src/models/transactions/escrow_create.rs b/src/models/transactions/escrow_create.rs index f5e3b102..32905e47 100644 --- a/src/models/transactions/escrow_create.rs +++ b/src/models/transactions/escrow_create.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLEscrowCreateException; use crate::models::{ @@ -152,7 +150,7 @@ impl<'a> EscrowCreateError for EscrowCreate<'a> { } impl<'a> EscrowCreate<'a> { - fn new( + pub fn new( account: &'a str, amount: XRPAmount<'a>, destination: &'a str, diff --git a/src/models/transactions/escrow_finish.rs b/src/models/transactions/escrow_finish.rs index 7f5f75e2..2ba50e6e 100644 --- a/src/models/transactions/escrow_finish.rs +++ b/src/models/transactions/escrow_finish.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::transactions::XRPLEscrowFinishException; use crate::models::{ amount::XRPAmount, @@ -144,7 +142,7 @@ impl<'a> EscrowFinishError for EscrowFinish<'a> { } impl<'a> EscrowFinish<'a> { - fn new( + pub fn new( account: &'a str, owner: &'a str, offer_sequence: u32, diff --git a/src/models/transactions/nftoken_accept_offer.rs b/src/models/transactions/nftoken_accept_offer.rs index 0b8f785c..68f3e5ff 100644 --- a/src/models/transactions/nftoken_accept_offer.rs +++ b/src/models/transactions/nftoken_accept_offer.rs @@ -6,8 +6,6 @@ use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::exceptions::XRPLAmountException; use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLNFTokenAcceptOfferException; @@ -174,7 +172,7 @@ impl<'a> NFTokenAcceptOfferError for NFTokenAcceptOffer<'a> { } impl<'a> NFTokenAcceptOffer<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/nftoken_burn.rs b/src/models/transactions/nftoken_burn.rs index 457d2382..e3320432 100644 --- a/src/models/transactions/nftoken_burn.rs +++ b/src/models/transactions/nftoken_burn.rs @@ -114,7 +114,7 @@ impl<'a> Transaction for NFTokenBurn<'a> { } impl<'a> NFTokenBurn<'a> { - fn new( + pub fn new( account: &'a str, nftoken_id: &'a str, fee: Option>, diff --git a/src/models/transactions/nftoken_cancel_offer.rs b/src/models/transactions/nftoken_cancel_offer.rs index 5c850ccd..205a5312 100644 --- a/src/models/transactions/nftoken_cancel_offer.rs +++ b/src/models/transactions/nftoken_cancel_offer.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLNFTokenCancelOfferException; use crate::models::{ @@ -139,7 +137,7 @@ impl<'a> NFTokenCancelOfferError for NFTokenCancelOffer<'a> { } impl<'a> NFTokenCancelOffer<'a> { - fn new( + pub fn new( account: &'a str, nftoken_offers: Vec<&'a str>, fee: Option>, diff --git a/src/models/transactions/nftoken_create_offer.rs b/src/models/transactions/nftoken_create_offer.rs index 658a061a..b2b53c5e 100644 --- a/src/models/transactions/nftoken_create_offer.rs +++ b/src/models/transactions/nftoken_create_offer.rs @@ -7,8 +7,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_with::skip_serializing_none; use strum_macros::{AsRefStr, Display, EnumIter}; -use alloc::string::ToString; - use crate::models::{ model::Model, transactions::{Flag, Memo, Signer, Transaction, TransactionType}, @@ -255,7 +253,7 @@ impl<'a> NFTokenCreateOfferError for NFTokenCreateOffer<'a> { } impl<'a> NFTokenCreateOffer<'a> { - fn new( + pub fn new( account: &'a str, nftoken_id: &'a str, amount: Amount<'a>, diff --git a/src/models/transactions/nftoken_mint.rs b/src/models/transactions/nftoken_mint.rs index e61dbc08..b6fbe7b1 100644 --- a/src/models/transactions/nftoken_mint.rs +++ b/src/models/transactions/nftoken_mint.rs @@ -5,8 +5,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_with::skip_serializing_none; use strum_macros::{AsRefStr, Display, EnumIter}; -use alloc::string::ToString; - use crate::{ constants::{MAX_TRANSFER_FEE, MAX_URI_LENGTH}, models::{ @@ -237,7 +235,7 @@ impl<'a> NFTokenMintError for NFTokenMint<'a> { } impl<'a> NFTokenMint<'a> { - fn new( + pub fn new( account: &'a str, nftoken_taxon: u32, fee: Option>, diff --git a/src/models/transactions/offer_cancel.rs b/src/models/transactions/offer_cancel.rs index 643252a2..560b62f2 100644 --- a/src/models/transactions/offer_cancel.rs +++ b/src/models/transactions/offer_cancel.rs @@ -110,7 +110,7 @@ impl<'a> Transaction for OfferCancel<'a> { } impl<'a> OfferCancel<'a> { - fn new( + pub fn new( account: &'a str, offer_sequence: u32, fee: Option>, diff --git a/src/models/transactions/offer_create.rs b/src/models/transactions/offer_create.rs index 1acd33de..13b2175e 100644 --- a/src/models/transactions/offer_create.rs +++ b/src/models/transactions/offer_create.rs @@ -174,7 +174,7 @@ impl<'a> Transaction for OfferCreate<'a> { } impl<'a> OfferCreate<'a> { - fn new( + pub fn new( account: &'a str, taker_gets: Amount<'a>, taker_pays: Amount<'a>, diff --git a/src/models/transactions/payment.rs b/src/models/transactions/payment.rs index 41c757a0..3358fc46 100644 --- a/src/models/transactions/payment.rs +++ b/src/models/transactions/payment.rs @@ -11,7 +11,6 @@ use crate::models::{ transactions::{Flag, Memo, Signer, Transaction, TransactionType}, PathStep, }; -use alloc::string::ToString; use crate::Err; use crate::_serde::txn_flags; @@ -260,7 +259,7 @@ impl<'a> PaymentError for Payment<'a> { } impl<'a> Payment<'a> { - fn new( + pub fn new( account: &'a str, amount: Amount<'a>, destination: &'a str, diff --git a/src/models/transactions/payment_channel_claim.rs b/src/models/transactions/payment_channel_claim.rs index 61ccbd34..4a30a21b 100644 --- a/src/models/transactions/payment_channel_claim.rs +++ b/src/models/transactions/payment_channel_claim.rs @@ -174,7 +174,7 @@ impl<'a> Transaction for PaymentChannelClaim<'a> { } impl<'a> PaymentChannelClaim<'a> { - fn new( + pub fn new( account: &'a str, channel: &'a str, fee: Option>, diff --git a/src/models/transactions/payment_channel_create.rs b/src/models/transactions/payment_channel_create.rs index 9188e8a2..0b444b5e 100644 --- a/src/models/transactions/payment_channel_create.rs +++ b/src/models/transactions/payment_channel_create.rs @@ -120,7 +120,7 @@ impl<'a> Transaction for PaymentChannelCreate<'a> { } impl<'a> PaymentChannelCreate<'a> { - fn new( + pub fn new( account: &'a str, amount: XRPAmount<'a>, destination: &'a str, diff --git a/src/models/transactions/payment_channel_fund.rs b/src/models/transactions/payment_channel_fund.rs index 4f9c3a34..7870a26e 100644 --- a/src/models/transactions/payment_channel_fund.rs +++ b/src/models/transactions/payment_channel_fund.rs @@ -115,7 +115,7 @@ impl<'a> Transaction for PaymentChannelFund<'a> { } impl<'a> PaymentChannelFund<'a> { - fn new( + pub fn new( account: &'a str, channel: &'a str, amount: XRPAmount<'a>, diff --git a/src/models/transactions/pseudo_transactions/enable_amendment.rs b/src/models/transactions/pseudo_transactions/enable_amendment.rs index a7476a73..463ea2b1 100644 --- a/src/models/transactions/pseudo_transactions/enable_amendment.rs +++ b/src/models/transactions/pseudo_transactions/enable_amendment.rs @@ -103,7 +103,7 @@ impl<'a> Transaction for EnableAmendment<'a> { } impl<'a> EnableAmendment<'a> { - fn new( + pub fn new( account: &'a str, amendment: &'a str, ledger_sequence: u32, diff --git a/src/models/transactions/pseudo_transactions/set_fee.rs b/src/models/transactions/pseudo_transactions/set_fee.rs index b1aeced5..f3782328 100644 --- a/src/models/transactions/pseudo_transactions/set_fee.rs +++ b/src/models/transactions/pseudo_transactions/set_fee.rs @@ -69,7 +69,7 @@ impl<'a> Transaction for SetFee<'a> { } impl<'a> SetFee<'a> { - fn new( + pub fn new( account: &'a str, base_fee: XRPAmount<'a>, reference_fee_units: u32, diff --git a/src/models/transactions/pseudo_transactions/unl_modify.rs b/src/models/transactions/pseudo_transactions/unl_modify.rs index b9a3d7fd..8ce72482 100644 --- a/src/models/transactions/pseudo_transactions/unl_modify.rs +++ b/src/models/transactions/pseudo_transactions/unl_modify.rs @@ -78,7 +78,7 @@ impl<'a> Transaction for UNLModify<'a> { } impl<'a> UNLModify<'a> { - fn new( + pub fn new( account: &'a str, ledger_sequence: u32, unlmodify_disabling: UNLModifyDisabling, diff --git a/src/models/transactions/set_regular_key.rs b/src/models/transactions/set_regular_key.rs index 38697431..d651dc48 100644 --- a/src/models/transactions/set_regular_key.rs +++ b/src/models/transactions/set_regular_key.rs @@ -114,7 +114,7 @@ impl<'a> Transaction for SetRegularKey<'a> { } impl<'a> SetRegularKey<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/signer_list_set.rs b/src/models/transactions/signer_list_set.rs index 8223c03e..961269b9 100644 --- a/src/models/transactions/signer_list_set.rs +++ b/src/models/transactions/signer_list_set.rs @@ -5,8 +5,6 @@ use derive_new::new; use serde::{ser::SerializeMap, Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::transactions::XRPLSignerListSetException; use crate::models::{ amount::XRPAmount, @@ -226,7 +224,7 @@ impl<'a> SignerListSetError for SignerListSet<'a> { } impl<'a> SignerListSet<'a> { - fn new( + pub fn new( account: &'a str, signer_quorum: u32, fee: Option>, diff --git a/src/models/transactions/ticket_create.rs b/src/models/transactions/ticket_create.rs index 2efb723d..3779eab2 100644 --- a/src/models/transactions/ticket_create.rs +++ b/src/models/transactions/ticket_create.rs @@ -110,7 +110,7 @@ impl<'a> Transaction for TicketCreate<'a> { } impl<'a> TicketCreate<'a> { - fn new( + pub fn new( account: &'a str, ticket_count: u32, fee: Option>, diff --git a/src/models/transactions/trust_set.rs b/src/models/transactions/trust_set.rs index 5b0727d8..3165cd3c 100644 --- a/src/models/transactions/trust_set.rs +++ b/src/models/transactions/trust_set.rs @@ -162,7 +162,7 @@ impl<'a> Transaction for TrustSet<'a> { } impl<'a> TrustSet<'a> { - fn new( + pub fn new( account: &'a str, limit_amount: IssuedCurrencyAmount<'a>, fee: Option>, diff --git a/tests/common.rs b/tests/common.rs index 7db0151d..01978d3c 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,3 +1,31 @@ -/// Setup common testing prerequisites here such as connecting a client -/// to a server or creating required files/directories if needed. -pub fn setup() {} +#[cfg(feature = "std")] +mod std_test { + use xrpl::asynch::clients::{AsyncWebsocketClient, ReadResult, Websocket}; + use xrpl::models::requests::AccountInfo; + + #[tokio::test] + async fn test_async_ws() { + let mut buffer = [0u8; 4096]; + let uri = "ws://limpidcrypto.de:6004"; + let mut ws = AsyncWebsocketClient::new(uri.into(), &mut buffer); + // connect + ws.open().await.unwrap(); + // send request + let account_info = AccountInfo::new( + "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", + None, + None, + None, + None, + None, + None, + ); + ws.write(account_info).await.unwrap(); + // read messages + while let Ok(Some(ReadResult::Text(response))) = ws.read().await { + println!("{:?}", response); + + break; + } + } +} From bd7d44b64e9d4ce250fccddb93fff3116e273fb2 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sun, 28 May 2023 11:31:36 +0200 Subject: [PATCH 02/33] make GitHub workflows work with dev branch instead of develop --- .github/workflows/audit_test.yml | 2 +- .github/workflows/quality_test.yml | 2 +- .github/workflows/unit_test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/audit_test.yml b/.github/workflows/audit_test.yml index ed37267a..cf4334cc 100644 --- a/.github/workflows/audit_test.yml +++ b/.github/workflows/audit_test.yml @@ -1,7 +1,7 @@ on: push: branches: - - develop + - dev pull_request: branches: - main diff --git a/.github/workflows/quality_test.yml b/.github/workflows/quality_test.yml index f553fea0..1e01d77c 100644 --- a/.github/workflows/quality_test.yml +++ b/.github/workflows/quality_test.yml @@ -1,7 +1,7 @@ on: push: branches: - - develop + - dev pull_request: branches: - main diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 0d2e2857..4dfe9a0c 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -1,7 +1,7 @@ on: push: branches: - - develop + - dev pull_request: branches: - main From 5d777c7ae8b76fc2e744719378fa5998668726d4 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 18:09:20 +0200 Subject: [PATCH 03/33] refactor websocket client --- Cargo.toml | 4 +- src/asynch/clients/async_client.rs | 11 - src/asynch/clients/async_websocket_client.rs | 232 +++++++++---------- src/asynch/clients/client.rs | 10 - src/asynch/clients/mod.rs | 2 - src/asynch/clients/websocket_base.rs | 34 ++- 6 files changed, 144 insertions(+), 149 deletions(-) delete mode 100644 src/asynch/clients/async_client.rs delete mode 100644 src/asynch/clients/client.rs diff --git a/Cargo.toml b/Cargo.toml index 0a0fec7f..e5cbdaab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,8 @@ criterion = "0.4.0" cargo-husky = { version = "1.5.0", default-features = false, features = [ "user-hooks", ] } +tokio = { version = "1.28.2", features = ["full"] } +rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } [[bench]] name = "benchmarks" @@ -86,7 +88,7 @@ requests = ["core", "amounts", "currencies"] ledger = ["core", "amounts", "currencies"] amounts = ["core"] currencies = ["core"] -net = ["em-as-net"] +net = ["em-as-net/core", "em-as-net/client"] core = ["utils"] utils = [] std = ["em-as-net/std", "tokio/full", "rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] diff --git a/src/asynch/clients/async_client.rs b/src/asynch/clients/async_client.rs deleted file mode 100644 index a5449ce5..00000000 --- a/src/asynch/clients/async_client.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::client::Client; -use crate::models::Model; -use anyhow::Result; -use serde::Serialize; - -/// Interface for all async network clients to follow. -pub trait AsyncClient<'a>: Client<'a> { - async fn request(&mut self, request: T) -> Result { - self.request_impl(request).await - } -} diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 4c355e24..869ddc1b 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -1,164 +1,162 @@ use crate::asynch::clients::exceptions::XRPLWebsocketException; -use crate::asynch::clients::websocket_base::WebsocketBase; use crate::models::Model; use crate::Err; -use anyhow::Result; -use serde::Serialize; -// exports +// ! EXPORTS +pub use crate::asynch::clients::websocket_base::{ + WebsocketBase, WebsocketClose, WebsocketIo, WebsocketOpen, +}; +pub use em_as_net::client::websocket::ReadResult; +pub use em_as_net::core::io::{AsyncRead, AsyncWrite}; +// AsyncWebSocketClient #[cfg(feature = "std")] -pub use if_std::AsyncWebsocketClient; +pub type AsyncWebsocketClient<'a, T, Rng, Status = Closed> = +if_std::AsyncWebsocketClient<'a, T, Rng, Status>; +pub use em_as_net::core::tcp::TcpSocket; +// TCP Adapters +pub use em_as_net::core::tcp::adapters::TcpAdapterTokio; +// Build your own TCP Socket +pub use em_as_net::core::tcp::TcpConnect; +// Build your own TCP Adapter +pub use em_as_net::core::tcp::adapters::AdapterConnect; +// Websocket statuses +pub struct Open; +pub struct Closed; -pub use em_as_net::client::websocket::ReadResult; +use anyhow::Result; +use em_as_net::client::websocket::{WebsocketClientIo, WebsocketSendMessageType}; +use rand::RngCore; +use serde::Serialize; #[cfg(feature = "std")] mod if_std { - use crate::asynch::clients::async_client::AsyncClient; - use crate::asynch::clients::client::Client; - use crate::asynch::clients::exceptions::XRPLWebsocketException; - use crate::asynch::clients::websocket_base::WebsocketBase; - use crate::models::Model; - use crate::Err; + use super::{AsyncRead, AsyncWrite, Closed, Open, WebsocketBase, WebsocketOpen}; + use alloc::borrow::Cow; use core::cell::RefCell; - use core::ops::Deref; + use core::marker::PhantomData; use anyhow::Result; - - use crate::asynch::clients::Websocket; - use em_as_net::client::websocket::{ - ReadResult, WebsocketClient, WebsocketClientIo, WebsocketSendMessageType, - }; + use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; + use em_as_net::core::tcp::adapters::AdapterConnect; + use em_as_net::core::tcp::{TcpConnect, TcpSocket}; use rand::rngs::ThreadRng; - use serde::Serialize; - use tokio::net; + use rand::{thread_rng, RngCore}; + use crate::asynch::clients::exceptions::XRPLWebsocketException; + use crate::Err; /// An async client for interacting with the rippled WebSocket API. - pub struct AsyncWebsocketClient<'a> { + pub struct AsyncWebsocketClient<'a, T, Rng, Status = Closed> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, + { pub uri: Cow<'a, str>, - inner: RefCell>>, + pub(crate) inner: RefCell>>, + pub(crate) status: PhantomData, } - impl<'a> AsyncWebsocketClient<'a> { + impl<'a, T, Rng, Status> AsyncWebsocketClient<'a, T, Rng, Status> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, + { pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { let ws = WebsocketClient::new(uri.clone(), buffer); Self { uri, inner: RefCell::new(Some(ws)), + status: PhantomData::default(), } } } - impl<'a> Websocket<'a> for AsyncWebsocketClient<'a> {} - - impl<'a> WebsocketBase<'a> for AsyncWebsocketClient<'a> { + impl<'a, T, Rng, Status> WebsocketBase for AsyncWebsocketClient<'a, T, Rng, Status> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, + { fn is_open(&self) -> bool { - if let Some(ws) = self.inner.borrow().deref() { - ws.is_open() - } else { - false - } - } - - async fn do_open(&self) -> Result<()> { - return match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => ws.connect(None).await, - }; + self.status == PhantomData:: } + } - async fn do_close(&self) -> Result<()> { - return match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => ws.close().await, + impl<'a, A> WebsocketOpen<'a, A, AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open>> + for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> + where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, + { + async fn open( + self, + adapter: A, + ) -> Result, ThreadRng, Open>> { + let tcp_socket = TcpSocket::new(adapter); + let mut websocket = match self.inner.take() { + None => { return Err!(XRPLWebsocketException::NotOpen) } + Some(ws) => ws }; + let rng = thread_rng(); + websocket + .connect(tcp_socket, None, rng) + .await + .expect("TODO: panic message"); + + Ok(AsyncWebsocketClient { + uri: self.uri, + inner: RefCell::new(Some(websocket)), + status: PhantomData::, + }) } + } +} - async fn do_write(&self, request: T) -> Result<()> { - return match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => { - let request_string = match serde_json::to_string(&request) { - Ok(as_string) => as_string, - Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), - }; - ws.write( - Cow::from(request_string), - Some(WebsocketSendMessageType::Text), - ) +impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, +{ + async fn write(&self, request: &R) -> Result<()> { + let request_json = match serde_json::to_string(&request) { + Ok(as_string) => as_string, + Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), + }; + match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => { + ws.write(request_json.into(), Some(WebsocketSendMessageType::Text)) .await - } - }; - } - - // TODO: Fix lifetime issue - async fn do_read(&'a mut self) -> Result>> { - return match self.inner.get_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => match ws.read().await { - None => Ok(None), - Some(Ok(read_result)) => Ok(Some(read_result)), - Some(Err(read_error)) => Err(read_error), - }, - }; - } + .expect("TODO: panic message"); - async fn do_request_impl(&mut self, _request: T) -> Result { - todo!() + Ok(()) + } } } - impl<'a> AsyncClient<'a> for AsyncWebsocketClient<'a> {} - - impl<'a> Client<'a> for AsyncWebsocketClient<'a> { - async fn request_impl(&mut self, request: T) -> Result { - if ! as WebsocketBase<'_>>::is_open(self) { - return Err!(XRPLWebsocketException::NotOpen); + async fn read(&mut self) -> Result>> { + return match self.inner.get_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) } - - self.do_request_impl(request).await - } + Some(ws) => match ws.read().await { + None => Ok(None), + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(read_error)) => Err(read_error), + }, + }; } } -pub trait Websocket<'a>: WebsocketBase<'a> { - async fn open(&mut self) -> Result<()> { - if !self.is_open() { - self.do_open().await - } else { - Ok(()) - } - } - +impl<'a, T, Rng> WebsocketClose for AsyncWebsocketClient<'a, T, Rng, Open> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, +{ async fn close(&self) -> Result<()> { - if self.is_open() { - self.do_close().await - } else { - Ok(()) - } - } - - async fn write(&mut self, request: T) -> Result<()> { - if self.is_open() { - self.do_write(request).await - } else { - Err!(XRPLWebsocketException::NotOpen) - } - } - - async fn read(&'a mut self) -> Result>> { - if self.is_open() { - self.do_read().await - } else { - Err!(XRPLWebsocketException::NotOpen) + match self.inner.borrow_mut().as_mut() { + None => Err!(XRPLWebsocketException::NotOpen), + Some(ws) => ws.close().await } } } diff --git a/src/asynch/clients/client.rs b/src/asynch/clients/client.rs deleted file mode 100644 index 611f7ec2..00000000 --- a/src/asynch/clients/client.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::models::Model; -use anyhow::Result; -use serde::Serialize; - -/// Interface for all network clients to follow. -// TODO: `T` should implement a trait `Request` -// TODO: `R` should implement a trait `Response` -pub trait Client<'a> { - async fn request_impl(&mut self, request: T) -> Result; -} diff --git a/src/asynch/clients/mod.rs b/src/asynch/clients/mod.rs index 80af82fc..0b80e3bf 100644 --- a/src/asynch/clients/mod.rs +++ b/src/asynch/clients/mod.rs @@ -1,7 +1,5 @@ -mod async_client; pub mod async_json_rpc_client; pub mod async_websocket_client; -mod client; pub mod exceptions; mod json_rpc_base; mod websocket_base; diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 20c49799..1209be01 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -1,20 +1,38 @@ -use crate::asynch::clients::client::Client; +//! Traits used for all websocket clients. + use crate::models::Model; use anyhow::Result; use em_as_net::client::websocket::ReadResult; +use em_as_net::core::io::{AsyncRead, AsyncWrite}; +use em_as_net::core::tcp::adapters::AdapterConnect; + use serde::Serialize; // A client for interacting with the rippled WebSocket API. -pub trait WebsocketBase<'a>: Client<'a> { +pub trait WebsocketBase { fn is_open(&self) -> bool; +} - async fn do_open(&self) -> Result<()>; - - async fn do_close(&self) -> Result<()>; +pub trait WebsocketOpen<'a, A, OpenWS> + where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, +{ + /// Connects to the host. To communicate with a host we need a TCP Socket to + /// send bytes from the client to the host. This socket requires a preferred + /// `adapter` like the `TcpAdapterTokio`. The adapter is the actual socket + /// which must implement `AdapterConnect + AsyncRead + AsyncWrite + Sized + Unpin` + async fn open(self, adapter: A) -> Result; +} - async fn do_write(&self, request: T) -> Result<()>; +pub trait WebsocketClose { + /// Closes the websocket stream and disconnects from the host. + async fn close(&self) -> Result<()>; +} - async fn do_read(&'a mut self) -> Result>>; +pub trait WebsocketIo { + /// Writes the request to the stream. + async fn write(&self, request: &R) -> Result<()>; - async fn do_request_impl(&mut self, request: T) -> Result; + /// Read messages from the stream. + async fn read(&mut self) -> Result>>; } From 6406bbc8abc8176efe4cc42ad2eacabc7c61bfd9 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 18:09:37 +0200 Subject: [PATCH 04/33] refactor integration websocket client test --- tests/common.rs | 31 ------------------------------- tests/common/constants.rs | 2 ++ tests/common/mod.rs | 19 +++++++++++++++++++ tests/integration/clients/mod.rs | 26 ++++++++++++++++++++++++++ tests/integration/mod.rs | 3 +++ tests/integration_tests.rs | 3 +++ 6 files changed, 53 insertions(+), 31 deletions(-) delete mode 100644 tests/common.rs create mode 100644 tests/common/constants.rs create mode 100644 tests/common/mod.rs create mode 100644 tests/integration/clients/mod.rs create mode 100644 tests/integration/mod.rs create mode 100644 tests/integration_tests.rs diff --git a/tests/common.rs b/tests/common.rs deleted file mode 100644 index 01978d3c..00000000 --- a/tests/common.rs +++ /dev/null @@ -1,31 +0,0 @@ -#[cfg(feature = "std")] -mod std_test { - use xrpl::asynch::clients::{AsyncWebsocketClient, ReadResult, Websocket}; - use xrpl::models::requests::AccountInfo; - - #[tokio::test] - async fn test_async_ws() { - let mut buffer = [0u8; 4096]; - let uri = "ws://limpidcrypto.de:6004"; - let mut ws = AsyncWebsocketClient::new(uri.into(), &mut buffer); - // connect - ws.open().await.unwrap(); - // send request - let account_info = AccountInfo::new( - "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", - None, - None, - None, - None, - None, - None, - ); - ws.write(account_info).await.unwrap(); - // read messages - while let Ok(Some(ReadResult::Text(response))) = ws.read().await { - println!("{:?}", response); - - break; - } - } -} diff --git a/tests/common/constants.rs b/tests/common/constants.rs new file mode 100644 index 00000000..ed2ec980 --- /dev/null +++ b/tests/common/constants.rs @@ -0,0 +1,2 @@ +pub const ECHO_WS_SERVER: &'static str = "ws://ws.vi-server.org/mirror/"; +// pub const ECHO_WSS_SERVER: &'static str = "wss://ws.vi-server.org/mirror/"; diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 00000000..a622f808 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,19 @@ +use rand::rngs::ThreadRng; +use xrpl::asynch::clients::{ + AsyncWebsocketClient, Open, TcpAdapterTokio, TcpSocket, WebsocketBase, WebsocketOpen, +}; + +mod constants; +pub use constants::*; + +pub async fn connect_to_ws_echo<'a>( + buffer: &'a mut [u8], +) -> AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open> { + let websocket = AsyncWebsocketClient::new(ECHO_WS_SERVER.into(), buffer); + let tcp_adapter = TcpAdapterTokio::new(); + + let websocket = websocket.open(tcp_adapter).await.unwrap(); + assert!(websocket.is_open()); + + websocket +} diff --git a/tests/integration/clients/mod.rs b/tests/integration/clients/mod.rs new file mode 100644 index 00000000..ec57cb21 --- /dev/null +++ b/tests/integration/clients/mod.rs @@ -0,0 +1,26 @@ +use super::common::connect_to_ws_echo; +use xrpl::asynch::clients::{ReadResult, WebsocketClose, WebsocketIo}; +use xrpl::models::requests::AccountInfo; + +#[tokio::test] +async fn test_websocket_non_tls() { + let mut buffer = [0u8; 4096]; + let mut websocket = connect_to_ws_echo(&mut buffer).await; + let account_info = AccountInfo::new( + "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", + None, + None, + None, + None, + None, + None, + ); + websocket.write(&account_info).await.unwrap(); + + while let Ok(Some(ReadResult::Text(response))) = websocket.read().await { + let account_info_echo: AccountInfo = serde_json::from_str(response).unwrap(); + assert_eq!(account_info, account_info_echo); + + websocket.close().await.unwrap(); + } +} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs new file mode 100644 index 00000000..d9510731 --- /dev/null +++ b/tests/integration/mod.rs @@ -0,0 +1,3 @@ +use super::common; + +mod clients; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 00000000..797cfdcd --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,3 @@ +mod common; + +mod integration; From b60613e573218b2f7370f573397ece9cee8acd2c Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 19:57:03 +0200 Subject: [PATCH 05/33] fix tests --- .github/workflows/unit_test.yml | 4 ++-- .gitignore | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 0d2e2857..d2c667dd 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -24,7 +24,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: build - args: --release --no-default-features --features core,models + args: --release --no-default-features --features core,models,net - uses: actions-rs/cargo@v1 with: command: test @@ -32,4 +32,4 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features --features core,models + args: --no-default-features --features core,models,net diff --git a/.gitignore b/.gitignore index 0401f97f..1ec67b52 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ Cargo.lock # VSCode .vscode - .idea # Additional From 464e7769c9162a8c0f425d667494916968e973a7 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 20:20:14 +0200 Subject: [PATCH 06/33] fix tests --- .github/workflows/unit_test.yml | 2 +- src/asynch/clients/async_websocket_client.rs | 48 ++++++++++---------- src/asynch/clients/websocket_base.rs | 4 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index d2c667dd..f2a7a265 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -1,7 +1,7 @@ on: push: branches: - - develop + - dev pull_request: branches: - main diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 869ddc1b..8c3d7e10 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -11,7 +11,7 @@ pub use em_as_net::core::io::{AsyncRead, AsyncWrite}; // AsyncWebSocketClient #[cfg(feature = "std")] pub type AsyncWebsocketClient<'a, T, Rng, Status = Closed> = -if_std::AsyncWebsocketClient<'a, T, Rng, Status>; + if_std::AsyncWebsocketClient<'a, T, Rng, Status>; pub use em_as_net::core::tcp::TcpSocket; // TCP Adapters pub use em_as_net::core::tcp::adapters::TcpAdapterTokio; @@ -36,20 +36,20 @@ mod if_std { use core::cell::RefCell; use core::marker::PhantomData; + use crate::asynch::clients::exceptions::XRPLWebsocketException; + use crate::Err; use anyhow::Result; use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; use em_as_net::core::tcp::adapters::AdapterConnect; use em_as_net::core::tcp::{TcpConnect, TcpSocket}; use rand::rngs::ThreadRng; use rand::{thread_rng, RngCore}; - use crate::asynch::clients::exceptions::XRPLWebsocketException; - use crate::Err; /// An async client for interacting with the rippled WebSocket API. pub struct AsyncWebsocketClient<'a, T, Rng, Status = Closed> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, { pub uri: Cow<'a, str>, pub(crate) inner: RefCell>>, @@ -57,9 +57,9 @@ mod if_std { } impl<'a, T, Rng, Status> AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, { pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { let ws = WebsocketClient::new(uri.clone(), buffer); @@ -72,9 +72,9 @@ mod if_std { } impl<'a, T, Rng, Status> WebsocketBase for AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, { fn is_open(&self) -> bool { self.status == PhantomData:: @@ -82,9 +82,9 @@ mod if_std { } impl<'a, A> WebsocketOpen<'a, A, AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open>> - for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> - where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, + for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> + where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, { async fn open( self, @@ -92,8 +92,8 @@ mod if_std { ) -> Result, ThreadRng, Open>> { let tcp_socket = TcpSocket::new(adapter); let mut websocket = match self.inner.take() { - None => { return Err!(XRPLWebsocketException::NotOpen) } - Some(ws) => ws + None => return Err!(XRPLWebsocketException::NotOpen), + Some(ws) => ws, }; let rng = thread_rng(); websocket @@ -111,9 +111,9 @@ mod if_std { } impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, - Rng: RngCore, +where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, { async fn write(&self, request: &R) -> Result<()> { let request_json = match serde_json::to_string(&request) { @@ -149,14 +149,14 @@ impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> } impl<'a, T, Rng> WebsocketClose for AsyncWebsocketClient<'a, T, Rng, Open> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, - Rng: RngCore, +where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, { async fn close(&self) -> Result<()> { match self.inner.borrow_mut().as_mut() { None => Err!(XRPLWebsocketException::NotOpen), - Some(ws) => ws.close().await + Some(ws) => ws.close().await, } } } diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 1209be01..9f799787 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -14,8 +14,8 @@ pub trait WebsocketBase { } pub trait WebsocketOpen<'a, A, OpenWS> - where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, +where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, { /// Connects to the host. To communicate with a host we need a TCP Socket to /// send bytes from the client to the host. This socket requires a preferred From 316a12222a46b187e84c9ca0185a47c18f1c194e Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 1 Jul 2023 15:51:28 +0200 Subject: [PATCH 07/33] fix cargo clippy --- src/asynch/clients/async_websocket_client.rs | 59 ++++++-------------- src/asynch/clients/websocket_base.rs | 4 +- src/models/requests/mod.rs | 1 + 3 files changed, 20 insertions(+), 44 deletions(-) diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 8c3d7e10..7250efa0 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -33,11 +33,8 @@ mod if_std { use super::{AsyncRead, AsyncWrite, Closed, Open, WebsocketBase, WebsocketOpen}; use alloc::borrow::Cow; - use core::cell::RefCell; use core::marker::PhantomData; - use crate::asynch::clients::exceptions::XRPLWebsocketException; - use crate::Err; use anyhow::Result; use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; use em_as_net::core::tcp::adapters::AdapterConnect; @@ -52,7 +49,7 @@ mod if_std { Rng: RngCore, { pub uri: Cow<'a, str>, - pub(crate) inner: RefCell>>, + pub(crate) inner: WebsocketClient<'a, T, Rng>, pub(crate) status: PhantomData, } @@ -62,10 +59,9 @@ mod if_std { Rng: RngCore, { pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { - let ws = WebsocketClient::new(uri.clone(), buffer); Self { - uri, - inner: RefCell::new(Some(ws)), + uri: uri.clone(), + inner: WebsocketClient::new(uri.clone(), buffer), status: PhantomData::default(), } } @@ -87,23 +83,19 @@ mod if_std { A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, { async fn open( - self, + mut self, adapter: A, ) -> Result, ThreadRng, Open>> { let tcp_socket = TcpSocket::new(adapter); - let mut websocket = match self.inner.take() { - None => return Err!(XRPLWebsocketException::NotOpen), - Some(ws) => ws, - }; let rng = thread_rng(); - websocket + self.inner .connect(tcp_socket, None, rng) .await .expect("TODO: panic message"); Ok(AsyncWebsocketClient { uri: self.uri, - inner: RefCell::new(Some(websocket)), + inner: self.inner, status: PhantomData::, }) } @@ -115,36 +107,22 @@ where T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn write(&self, request: &R) -> Result<()> { + async fn write(&mut self, request: &R) -> Result<()> { let request_json = match serde_json::to_string(&request) { Ok(as_string) => as_string, Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), }; - match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => { - ws.write(request_json.into(), Some(WebsocketSendMessageType::Text)) - .await - .expect("TODO: panic message"); - - Ok(()) - } - } + self.inner + .write(request_json.into(), Some(WebsocketSendMessageType::Text)) + .await } async fn read(&mut self) -> Result>> { - return match self.inner.get_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => match ws.read().await { - None => Ok(None), - Some(Ok(read_result)) => Ok(Some(read_result)), - Some(Err(read_error)) => Err(read_error), - }, - }; + match self.inner.read().await { + None => Ok(None), + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(read_error)) => Err(read_error), + } } } @@ -153,10 +131,7 @@ where T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn close(&self) -> Result<()> { - match self.inner.borrow_mut().as_mut() { - None => Err!(XRPLWebsocketException::NotOpen), - Some(ws) => ws.close().await, - } + async fn close(&mut self) -> Result<()> { + self.inner.close().await } } diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 9f799787..6abc376f 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -26,12 +26,12 @@ where pub trait WebsocketClose { /// Closes the websocket stream and disconnects from the host. - async fn close(&self) -> Result<()>; + async fn close(&mut self) -> Result<()>; } pub trait WebsocketIo { /// Writes the request to the stream. - async fn write(&self, request: &R) -> Result<()>; + async fn write(&mut self, request: &R) -> Result<()>; /// Read messages from the stream. async fn read(&mut self) -> Result>>; diff --git a/src/models/requests/mod.rs b/src/models/requests/mod.rs index 1d5d9ca8..297799df 100644 --- a/src/models/requests/mod.rs +++ b/src/models/requests/mod.rs @@ -17,6 +17,7 @@ pub mod ledger; pub mod ledger_closed; pub mod ledger_current; pub mod ledger_data; +#[allow(clippy::result_large_err)] pub mod ledger_entry; pub mod manifest; pub mod nft_buy_offers; From 3ae950eef07daf90cfa5782393e3743e2d0eb6ba Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 3 Aug 2023 20:45:19 +0200 Subject: [PATCH 08/33] add code examples --- examples/std/.gitignore | 20 +++++++++++ examples/std/Cargo.toml | 33 +++++++++++++++++++ .../std/src/bin/tokio/net/send_request.rs | 4 +++ .../src/bin/transaction/sign_transaction.rs | 4 +++ .../std/src/bin/wallet/generate_wallet.rs | 7 ++++ .../std/src/bin/wallet/wallet_from_seed.rs | 9 +++++ 6 files changed, 77 insertions(+) create mode 100644 examples/std/.gitignore create mode 100644 examples/std/Cargo.toml create mode 100644 examples/std/src/bin/tokio/net/send_request.rs create mode 100644 examples/std/src/bin/transaction/sign_transaction.rs create mode 100644 examples/std/src/bin/wallet/generate_wallet.rs create mode 100644 examples/std/src/bin/wallet/wallet_from_seed.rs diff --git a/examples/std/.gitignore b/examples/std/.gitignore new file mode 100644 index 00000000..1ec67b52 --- /dev/null +++ b/examples/std/.gitignore @@ -0,0 +1,20 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# Added by cargo +/target + +# VSCode +.vscode +.idea + +# Additional +src/main.rs diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml new file mode 100644 index 00000000..1e4a813c --- /dev/null +++ b/examples/std/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "std" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +xrpl-rust = { path = "../.." } + +[[bin]] +name = "wallet_from_seed" +path = "src/bin/wallet/wallet_from_seed.rs" +required-features = [ +] + +[[bin]] +name = "generate_wallet" +path = "src/bin/wallet/generate_wallet.rs" +required-features = [ +] + +[[bin]] +name = "send_request" +path = "src/bin/tokio/net/send_request.rs" +required-features = [ +] + +[[bin]] +name = "send_request" +path = "src/bin/transaction/sign_transaction.rs" +required-features = [ +] diff --git a/examples/std/src/bin/tokio/net/send_request.rs b/examples/std/src/bin/tokio/net/send_request.rs new file mode 100644 index 00000000..dc017cce --- /dev/null +++ b/examples/std/src/bin/tokio/net/send_request.rs @@ -0,0 +1,4 @@ +// TODO: add as soon as `AsyncWebsocketClient` is implemented +fn main() { + todo!() +} diff --git a/examples/std/src/bin/transaction/sign_transaction.rs b/examples/std/src/bin/transaction/sign_transaction.rs new file mode 100644 index 00000000..170c7df8 --- /dev/null +++ b/examples/std/src/bin/transaction/sign_transaction.rs @@ -0,0 +1,4 @@ +// TODO: add as soon as `sign` is implemented +fn main() { + todo!() +} diff --git a/examples/std/src/bin/wallet/generate_wallet.rs b/examples/std/src/bin/wallet/generate_wallet.rs new file mode 100644 index 00000000..4af7da74 --- /dev/null +++ b/examples/std/src/bin/wallet/generate_wallet.rs @@ -0,0 +1,7 @@ +use xrpl::wallet::Wallet; + +fn main() { + let wallet = Wallet::create(None).expect("Failed to generate new wallet"); + + println!("Wallet: {:?}", wallet); +} diff --git a/examples/std/src/bin/wallet/wallet_from_seed.rs b/examples/std/src/bin/wallet/wallet_from_seed.rs new file mode 100644 index 00000000..c561ecfd --- /dev/null +++ b/examples/std/src/bin/wallet/wallet_from_seed.rs @@ -0,0 +1,9 @@ +use xrpl::wallet::Wallet; + +fn main() { + let wallet = Wallet::new("sEdVWgwiHxBmFoMGJBoPZf6H1XSLLGd", 0) + .expect("Failed to create wallet from seed"); + + assert_eq!(wallet.classic_address, "rsAhdjbE7YXqQtubcaSwb6xHn6mU2bSFHY"); + println!("Wallet: {:?}", wallet); +} From 0d7f9e980ce6a32e7d63adf66e9259b8ffe29f73 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 3 Aug 2023 20:46:56 +0200 Subject: [PATCH 09/33] fix model constructor to public; wallet scope public --- src/models/requests/account_channels.rs | 2 +- src/models/requests/account_currencies.rs | 2 +- src/models/requests/account_info.rs | 2 +- src/models/requests/account_lines.rs | 2 +- src/models/requests/account_nfts.rs | 7 ++++++- src/models/requests/account_objects.rs | 2 +- src/models/requests/account_offers.rs | 2 +- src/models/requests/account_tx.rs | 2 +- src/models/requests/book_offers.rs | 2 +- src/models/requests/channel_authorize.rs | 2 +- src/models/requests/channel_verify.rs | 2 +- src/models/requests/deposit_authorize.rs | 2 +- src/models/requests/fee.rs | 2 +- src/models/requests/gateway_balances.rs | 2 +- src/models/requests/ledger.rs | 2 +- src/models/requests/ledger_closed.rs | 2 +- src/models/requests/ledger_current.rs | 2 +- src/models/requests/ledger_data.rs | 2 +- src/models/requests/ledger_entry.rs | 2 +- src/models/requests/manifest.rs | 2 +- src/models/requests/nft_buy_offers.rs | 2 +- src/models/requests/nft_sell_offers.rs | 2 +- src/models/requests/no_ripple_check.rs | 2 +- src/models/requests/path_find.rs | 2 +- src/models/requests/ping.rs | 2 +- src/models/requests/random.rs | 2 +- src/models/requests/ripple_path_find.rs | 2 +- src/models/requests/server_info.rs | 2 +- src/models/requests/server_state.rs | 2 +- src/models/requests/submit.rs | 2 +- src/models/requests/submit_multisigned.rs | 2 +- src/models/requests/subscribe.rs | 2 +- src/models/requests/transaction_entry.rs | 2 +- src/models/requests/tx.rs | 2 +- src/models/requests/unsubscribe.rs | 2 +- src/models/transactions/account_delete.rs | 2 +- src/models/transactions/account_set.rs | 2 +- src/models/transactions/check_cancel.rs | 2 +- src/models/transactions/check_cash.rs | 2 +- src/models/transactions/check_create.rs | 2 +- src/models/transactions/deposit_preauth.rs | 2 +- src/models/transactions/escrow_cancel.rs | 2 +- src/models/transactions/escrow_create.rs | 2 +- src/models/transactions/escrow_finish.rs | 2 +- src/models/transactions/nftoken_accept_offer.rs | 2 +- src/models/transactions/nftoken_burn.rs | 2 +- src/models/transactions/nftoken_cancel_offer.rs | 2 +- src/models/transactions/nftoken_create_offer.rs | 2 +- src/models/transactions/nftoken_mint.rs | 2 +- src/models/transactions/offer_cancel.rs | 2 +- src/models/transactions/offer_create.rs | 2 +- src/models/transactions/payment.rs | 2 +- src/models/transactions/payment_channel_claim.rs | 2 +- src/models/transactions/payment_channel_create.rs | 2 +- src/models/transactions/payment_channel_fund.rs | 2 +- .../transactions/pseudo_transactions/enable_amendment.rs | 2 +- src/models/transactions/pseudo_transactions/set_fee.rs | 2 +- src/models/transactions/pseudo_transactions/unl_modify.rs | 2 +- src/models/transactions/set_regular_key.rs | 2 +- src/models/transactions/signer_list_set.rs | 2 +- src/models/transactions/ticket_create.rs | 2 +- src/models/transactions/trust_set.rs | 2 +- src/wallet/mod.rs | 3 ++- 63 files changed, 69 insertions(+), 63 deletions(-) diff --git a/src/models/requests/account_channels.rs b/src/models/requests/account_channels.rs index 705d5cd2..a6f0ed97 100644 --- a/src/models/requests/account_channels.rs +++ b/src/models/requests/account_channels.rs @@ -76,7 +76,7 @@ impl<'a> Default for AccountChannels<'a> { impl<'a> Model for AccountChannels<'a> {} impl<'a> AccountChannels<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_currencies.rs b/src/models/requests/account_currencies.rs index 35a91428..1edc9008 100644 --- a/src/models/requests/account_currencies.rs +++ b/src/models/requests/account_currencies.rs @@ -50,7 +50,7 @@ impl<'a> Default for AccountCurrencies<'a> { impl<'a> Model for AccountCurrencies<'a> {} impl<'a> AccountCurrencies<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_info.rs b/src/models/requests/account_info.rs index 4f2ca2d7..038b06b4 100644 --- a/src/models/requests/account_info.rs +++ b/src/models/requests/account_info.rs @@ -59,7 +59,7 @@ impl<'a> Default for AccountInfo<'a> { impl<'a> Model for AccountInfo<'a> {} impl<'a> AccountInfo<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_lines.rs b/src/models/requests/account_lines.rs index d08e88f1..8ca47b56 100644 --- a/src/models/requests/account_lines.rs +++ b/src/models/requests/account_lines.rs @@ -56,7 +56,7 @@ impl<'a> Default for AccountLines<'a> { impl<'a> Model for AccountLines<'a> {} impl<'a> AccountLines<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_nfts.rs b/src/models/requests/account_nfts.rs index 2cf7fb3a..faa26391 100644 --- a/src/models/requests/account_nfts.rs +++ b/src/models/requests/account_nfts.rs @@ -41,7 +41,12 @@ impl<'a> Default for AccountNfts<'a> { impl<'a> Model for AccountNfts<'a> {} impl<'a> AccountNfts<'a> { - fn new(account: &'a str, id: Option<&'a str>, limit: Option, marker: Option) -> Self { + pub fn new( + account: &'a str, + id: Option<&'a str>, + limit: Option, + marker: Option, + ) -> Self { Self { account, id, diff --git a/src/models/requests/account_objects.rs b/src/models/requests/account_objects.rs index 0868a200..24a7a0ff 100644 --- a/src/models/requests/account_objects.rs +++ b/src/models/requests/account_objects.rs @@ -79,7 +79,7 @@ impl<'a> Default for AccountObjects<'a> { impl<'a> Model for AccountObjects<'a> {} impl<'a> AccountObjects<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_offers.rs b/src/models/requests/account_offers.rs index 90500ebf..d310d540 100644 --- a/src/models/requests/account_offers.rs +++ b/src/models/requests/account_offers.rs @@ -55,7 +55,7 @@ impl<'a> Default for AccountOffers<'a> { impl<'a> Model for AccountOffers<'a> {} impl<'a> AccountOffers<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/account_tx.rs b/src/models/requests/account_tx.rs index d3c63107..25827af4 100644 --- a/src/models/requests/account_tx.rs +++ b/src/models/requests/account_tx.rs @@ -71,7 +71,7 @@ impl<'a> Default for AccountTx<'a> { impl<'a> Model for AccountTx<'a> {} impl<'a> AccountTx<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/book_offers.rs b/src/models/requests/book_offers.rs index 75551e4a..e470a70c 100644 --- a/src/models/requests/book_offers.rs +++ b/src/models/requests/book_offers.rs @@ -61,7 +61,7 @@ impl<'a> Default for BookOffers<'a> { impl<'a> Model for BookOffers<'a> {} impl<'a> BookOffers<'a> { - fn new( + pub fn new( taker_gets: Currency<'a>, taker_pays: Currency<'a>, id: Option<&'a str>, diff --git a/src/models/requests/channel_authorize.rs b/src/models/requests/channel_authorize.rs index 348eb7b1..bd21f346 100644 --- a/src/models/requests/channel_authorize.rs +++ b/src/models/requests/channel_authorize.rs @@ -120,7 +120,7 @@ impl<'a> ChannelAuthorizeError for ChannelAuthorize<'a> { } impl<'a> ChannelAuthorize<'a> { - fn new( + pub fn new( channel_id: &'a str, amount: &'a str, id: Option<&'a str>, diff --git a/src/models/requests/channel_verify.rs b/src/models/requests/channel_verify.rs index 4355e07f..73fb1dc3 100644 --- a/src/models/requests/channel_verify.rs +++ b/src/models/requests/channel_verify.rs @@ -43,7 +43,7 @@ impl<'a> Default for ChannelVerify<'a> { impl<'a> Model for ChannelVerify<'a> {} impl<'a> ChannelVerify<'a> { - fn new( + pub fn new( channel_id: &'a str, amount: &'a str, public_key: &'a str, diff --git a/src/models/requests/deposit_authorize.rs b/src/models/requests/deposit_authorize.rs index 21a4af44..087a00db 100644 --- a/src/models/requests/deposit_authorize.rs +++ b/src/models/requests/deposit_authorize.rs @@ -43,7 +43,7 @@ impl<'a> Default for DepositAuthorized<'a> { impl<'a> Model for DepositAuthorized<'a> {} impl<'a> DepositAuthorized<'a> { - fn new( + pub fn new( source_account: &'a str, destination_account: &'a str, id: Option<&'a str>, diff --git a/src/models/requests/fee.rs b/src/models/requests/fee.rs index a5507b32..8416a2ef 100644 --- a/src/models/requests/fee.rs +++ b/src/models/requests/fee.rs @@ -32,7 +32,7 @@ impl<'a> Default for Fee<'a> { impl<'a> Model for Fee<'a> {} impl<'a> Fee<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::Fee, diff --git a/src/models/requests/gateway_balances.rs b/src/models/requests/gateway_balances.rs index 122ff42c..ade93092 100644 --- a/src/models/requests/gateway_balances.rs +++ b/src/models/requests/gateway_balances.rs @@ -50,7 +50,7 @@ impl<'a> Default for GatewayBalances<'a> { impl<'a> Model for GatewayBalances<'a> {} impl<'a> GatewayBalances<'a> { - fn new( + pub fn new( account: &'a str, id: Option<&'a str>, strict: Option, diff --git a/src/models/requests/ledger.rs b/src/models/requests/ledger.rs index 76d22253..bc1eabe0 100644 --- a/src/models/requests/ledger.rs +++ b/src/models/requests/ledger.rs @@ -76,7 +76,7 @@ impl<'a> Default for Ledger<'a> { impl<'a> Model for Ledger<'a> {} impl<'a> Ledger<'a> { - fn new( + pub fn new( id: Option<&'a str>, ledger_hash: Option<&'a str>, ledger_index: Option<&'a str>, diff --git a/src/models/requests/ledger_closed.rs b/src/models/requests/ledger_closed.rs index 1b76d328..e7b5d309 100644 --- a/src/models/requests/ledger_closed.rs +++ b/src/models/requests/ledger_closed.rs @@ -31,7 +31,7 @@ impl<'a> Default for LedgerClosed<'a> { impl<'a> Model for LedgerClosed<'a> {} impl<'a> LedgerClosed<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::LedgerClosed, diff --git a/src/models/requests/ledger_current.rs b/src/models/requests/ledger_current.rs index 93e6bfac..6ccfd04d 100644 --- a/src/models/requests/ledger_current.rs +++ b/src/models/requests/ledger_current.rs @@ -31,7 +31,7 @@ impl<'a> Default for LedgerCurrent<'a> { impl<'a> Model for LedgerCurrent<'a> {} impl<'a> LedgerCurrent<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::LedgerCurrent, diff --git a/src/models/requests/ledger_data.rs b/src/models/requests/ledger_data.rs index e5197671..362e4291 100644 --- a/src/models/requests/ledger_data.rs +++ b/src/models/requests/ledger_data.rs @@ -50,7 +50,7 @@ impl<'a> Default for LedgerData<'a> { impl<'a> Model for LedgerData<'a> {} impl<'a> LedgerData<'a> { - fn new( + pub fn new( id: Option<&'a str>, ledger_hash: Option<&'a str>, ledger_index: Option<&'a str>, diff --git a/src/models/requests/ledger_entry.rs b/src/models/requests/ledger_entry.rs index a21bd5a6..a7e851bb 100644 --- a/src/models/requests/ledger_entry.rs +++ b/src/models/requests/ledger_entry.rs @@ -178,7 +178,7 @@ impl<'a> LedgerEntryError for LedgerEntry<'a> { } impl<'a> LedgerEntry<'a> { - fn new( + pub fn new( id: Option<&'a str>, index: Option<&'a str>, account_root: Option<&'a str>, diff --git a/src/models/requests/manifest.rs b/src/models/requests/manifest.rs index 9705a6f3..7a02e289 100644 --- a/src/models/requests/manifest.rs +++ b/src/models/requests/manifest.rs @@ -37,7 +37,7 @@ impl<'a> Default for Manifest<'a> { impl<'a> Model for Manifest<'a> {} impl<'a> Manifest<'a> { - fn new(public_key: &'a str, id: Option<&'a str>) -> Self { + pub fn new(public_key: &'a str, id: Option<&'a str>) -> Self { Self { public_key, id, diff --git a/src/models/requests/nft_buy_offers.rs b/src/models/requests/nft_buy_offers.rs index b34308d3..3ecfa1ef 100644 --- a/src/models/requests/nft_buy_offers.rs +++ b/src/models/requests/nft_buy_offers.rs @@ -42,7 +42,7 @@ impl<'a> Default for NftBuyOffers<'a> { impl<'a> Model for NftBuyOffers<'a> {} impl<'a> NftBuyOffers<'a> { - fn new( + pub fn new( nft_id: &'a str, ledger_hash: Option<&'a str>, ledger_index: Option<&'a str>, diff --git a/src/models/requests/nft_sell_offers.rs b/src/models/requests/nft_sell_offers.rs index 35273506..354cafda 100644 --- a/src/models/requests/nft_sell_offers.rs +++ b/src/models/requests/nft_sell_offers.rs @@ -26,7 +26,7 @@ impl<'a> Default for NftSellOffers<'a> { impl<'a> Model for NftSellOffers<'a> {} impl<'a> NftSellOffers<'a> { - fn new(nft_id: &'a str) -> Self { + pub fn new(nft_id: &'a str) -> Self { Self { nft_id, command: RequestMethod::NftSellOffers, diff --git a/src/models/requests/no_ripple_check.rs b/src/models/requests/no_ripple_check.rs index 5c52ef61..37f5f0bc 100644 --- a/src/models/requests/no_ripple_check.rs +++ b/src/models/requests/no_ripple_check.rs @@ -73,7 +73,7 @@ impl<'a> Default for NoRippleCheck<'a> { impl<'a> Model for NoRippleCheck<'a> {} impl<'a> NoRippleCheck<'a> { - fn new( + pub fn new( account: &'a str, role: NoRippleCheckRole, id: Option<&'a str>, diff --git a/src/models/requests/path_find.rs b/src/models/requests/path_find.rs index 57bc751b..0eedc299 100644 --- a/src/models/requests/path_find.rs +++ b/src/models/requests/path_find.rs @@ -105,7 +105,7 @@ impl<'a> Default for PathFind<'a> { impl<'a> Model for PathFind<'a> {} impl<'a> PathFind<'a> { - fn new( + pub fn new( subcommand: PathFindSubcommand, source_account: &'a str, destination_account: &'a str, diff --git a/src/models/requests/ping.rs b/src/models/requests/ping.rs index 24e1c68b..9c100442 100644 --- a/src/models/requests/ping.rs +++ b/src/models/requests/ping.rs @@ -30,7 +30,7 @@ impl<'a> Default for Ping<'a> { impl<'a> Model for Ping<'a> {} impl<'a> Ping<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::Ping, diff --git a/src/models/requests/random.rs b/src/models/requests/random.rs index 8cf99192..0ecbfbdc 100644 --- a/src/models/requests/random.rs +++ b/src/models/requests/random.rs @@ -31,7 +31,7 @@ impl<'a> Default for Random<'a> { impl<'a> Model for Random<'a> {} impl<'a> Random<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::Random, diff --git a/src/models/requests/ripple_path_find.rs b/src/models/requests/ripple_path_find.rs index cbafd6ed..77e4ce06 100644 --- a/src/models/requests/ripple_path_find.rs +++ b/src/models/requests/ripple_path_find.rs @@ -79,7 +79,7 @@ impl<'a> Default for RipplePathFind<'a> { impl<'a> Model for RipplePathFind<'a> {} impl<'a> RipplePathFind<'a> { - fn new( + pub fn new( source_account: &'a str, destination_account: &'a str, destination_amount: Currency<'a>, diff --git a/src/models/requests/server_info.rs b/src/models/requests/server_info.rs index 0308fbd5..f869a4ae 100644 --- a/src/models/requests/server_info.rs +++ b/src/models/requests/server_info.rs @@ -31,7 +31,7 @@ impl<'a> Default for ServerInfo<'a> { impl<'a> Model for ServerInfo<'a> {} impl<'a> ServerInfo<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::ServerInfo, diff --git a/src/models/requests/server_state.rs b/src/models/requests/server_state.rs index 2b7942c6..8f1997b8 100644 --- a/src/models/requests/server_state.rs +++ b/src/models/requests/server_state.rs @@ -36,7 +36,7 @@ impl<'a> Default for ServerState<'a> { impl<'a> Model for ServerState<'a> {} impl<'a> ServerState<'a> { - fn new(id: Option<&'a str>) -> Self { + pub fn new(id: Option<&'a str>) -> Self { Self { id, command: RequestMethod::ServerState, diff --git a/src/models/requests/submit.rs b/src/models/requests/submit.rs index 2fabd316..537125d7 100644 --- a/src/models/requests/submit.rs +++ b/src/models/requests/submit.rs @@ -60,7 +60,7 @@ impl<'a> Default for Submit<'a> { impl<'a> Model for Submit<'a> {} impl<'a> Submit<'a> { - fn new(tx_blob: &'a str, id: Option<&'a str>, fail_hard: Option) -> Self { + pub fn new(tx_blob: &'a str, id: Option<&'a str>, fail_hard: Option) -> Self { Self { tx_blob, id, diff --git a/src/models/requests/submit_multisigned.rs b/src/models/requests/submit_multisigned.rs index 368207a3..bb1d0cf6 100644 --- a/src/models/requests/submit_multisigned.rs +++ b/src/models/requests/submit_multisigned.rs @@ -40,7 +40,7 @@ impl<'a> Default for SubmitMultisigned<'a> { impl<'a> Model for SubmitMultisigned<'a> {} impl<'a> SubmitMultisigned<'a> { - fn new(id: Option<&'a str>, fail_hard: Option) -> Self { + pub fn new(id: Option<&'a str>, fail_hard: Option) -> Self { Self { id, fail_hard, diff --git a/src/models/requests/subscribe.rs b/src/models/requests/subscribe.rs index 5c1efd05..60adf24d 100644 --- a/src/models/requests/subscribe.rs +++ b/src/models/requests/subscribe.rs @@ -93,7 +93,7 @@ impl<'a> Default for Subscribe<'a> { impl<'a> Model for Subscribe<'a> {} impl<'a> Subscribe<'a> { - fn new( + pub fn new( id: Option<&'a str>, books: Option>>, streams: Option>, diff --git a/src/models/requests/transaction_entry.rs b/src/models/requests/transaction_entry.rs index cf0cf33c..b6687476 100644 --- a/src/models/requests/transaction_entry.rs +++ b/src/models/requests/transaction_entry.rs @@ -43,7 +43,7 @@ impl<'a> Default for TransactionEntry<'a> { impl<'a> Model for TransactionEntry<'a> {} impl<'a> TransactionEntry<'a> { - fn new( + pub fn new( tx_hash: &'a str, id: Option<&'a str>, ledger_hash: Option<&'a str>, diff --git a/src/models/requests/tx.rs b/src/models/requests/tx.rs index 9d3056f9..2425536b 100644 --- a/src/models/requests/tx.rs +++ b/src/models/requests/tx.rs @@ -46,7 +46,7 @@ impl<'a> Default for Tx<'a> { impl<'a> Model for Tx<'a> {} impl<'a> Tx<'a> { - fn new( + pub fn new( id: Option<&'a str>, binary: Option, min_ledger: Option, diff --git a/src/models/requests/unsubscribe.rs b/src/models/requests/unsubscribe.rs index d8ac3d99..8ffc8210 100644 --- a/src/models/requests/unsubscribe.rs +++ b/src/models/requests/unsubscribe.rs @@ -75,7 +75,7 @@ impl<'a> Default for Unsubscribe<'a> { impl<'a> Model for Unsubscribe<'a> {} impl<'a> Unsubscribe<'a> { - fn new( + pub fn new( id: Option<&'a str>, books: Option>>, streams: Option>, diff --git a/src/models/transactions/account_delete.rs b/src/models/transactions/account_delete.rs index 6623b401..e30e1a05 100644 --- a/src/models/transactions/account_delete.rs +++ b/src/models/transactions/account_delete.rs @@ -121,7 +121,7 @@ impl<'a> Transaction for AccountDelete<'a> { } impl<'a> AccountDelete<'a> { - fn new( + pub fn new( account: &'a str, destination: &'a str, fee: Option>, diff --git a/src/models/transactions/account_set.rs b/src/models/transactions/account_set.rs index 8b37c14a..43f40478 100644 --- a/src/models/transactions/account_set.rs +++ b/src/models/transactions/account_set.rs @@ -387,7 +387,7 @@ impl<'a> AccountSetError for AccountSet<'a> { } impl<'a> AccountSet<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/check_cancel.rs b/src/models/transactions/check_cancel.rs index 26a81743..48bef9c1 100644 --- a/src/models/transactions/check_cancel.rs +++ b/src/models/transactions/check_cancel.rs @@ -114,7 +114,7 @@ impl<'a> Transaction for CheckCancel<'a> { } impl<'a> CheckCancel<'a> { - fn new( + pub fn new( account: &'a str, check_id: &'a str, fee: Option>, diff --git a/src/models/transactions/check_cash.rs b/src/models/transactions/check_cash.rs index 58648d39..53816f65 100644 --- a/src/models/transactions/check_cash.rs +++ b/src/models/transactions/check_cash.rs @@ -147,7 +147,7 @@ impl<'a> CheckCashError for CheckCash<'a> { } impl<'a> CheckCash<'a> { - fn new( + pub fn new( account: &'a str, check_id: &'a str, fee: Option>, diff --git a/src/models/transactions/check_create.rs b/src/models/transactions/check_create.rs index 772bbd11..b5c1f83a 100644 --- a/src/models/transactions/check_create.rs +++ b/src/models/transactions/check_create.rs @@ -121,7 +121,7 @@ impl<'a> Transaction for CheckCreate<'a> { } impl<'a> CheckCreate<'a> { - fn new( + pub fn new( account: &'a str, destination: &'a str, send_max: Amount<'a>, diff --git a/src/models/transactions/deposit_preauth.rs b/src/models/transactions/deposit_preauth.rs index ceb42a5e..5e32517b 100644 --- a/src/models/transactions/deposit_preauth.rs +++ b/src/models/transactions/deposit_preauth.rs @@ -141,7 +141,7 @@ impl<'a> DepositPreauthError for DepositPreauth<'a> { } impl<'a> DepositPreauth<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/escrow_cancel.rs b/src/models/transactions/escrow_cancel.rs index 5bef7a5b..42eadca2 100644 --- a/src/models/transactions/escrow_cancel.rs +++ b/src/models/transactions/escrow_cancel.rs @@ -112,7 +112,7 @@ impl<'a> Transaction for EscrowCancel<'a> { } impl<'a> EscrowCancel<'a> { - fn new( + pub fn new( account: &'a str, owner: &'a str, offer_sequence: u32, diff --git a/src/models/transactions/escrow_create.rs b/src/models/transactions/escrow_create.rs index f5e3b102..d0eacaba 100644 --- a/src/models/transactions/escrow_create.rs +++ b/src/models/transactions/escrow_create.rs @@ -152,7 +152,7 @@ impl<'a> EscrowCreateError for EscrowCreate<'a> { } impl<'a> EscrowCreate<'a> { - fn new( + pub fn new( account: &'a str, amount: XRPAmount<'a>, destination: &'a str, diff --git a/src/models/transactions/escrow_finish.rs b/src/models/transactions/escrow_finish.rs index 7f5f75e2..e049edcd 100644 --- a/src/models/transactions/escrow_finish.rs +++ b/src/models/transactions/escrow_finish.rs @@ -144,7 +144,7 @@ impl<'a> EscrowFinishError for EscrowFinish<'a> { } impl<'a> EscrowFinish<'a> { - fn new( + pub fn new( account: &'a str, owner: &'a str, offer_sequence: u32, diff --git a/src/models/transactions/nftoken_accept_offer.rs b/src/models/transactions/nftoken_accept_offer.rs index 0b8f785c..dcfcc532 100644 --- a/src/models/transactions/nftoken_accept_offer.rs +++ b/src/models/transactions/nftoken_accept_offer.rs @@ -174,7 +174,7 @@ impl<'a> NFTokenAcceptOfferError for NFTokenAcceptOffer<'a> { } impl<'a> NFTokenAcceptOffer<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/nftoken_burn.rs b/src/models/transactions/nftoken_burn.rs index 457d2382..e3320432 100644 --- a/src/models/transactions/nftoken_burn.rs +++ b/src/models/transactions/nftoken_burn.rs @@ -114,7 +114,7 @@ impl<'a> Transaction for NFTokenBurn<'a> { } impl<'a> NFTokenBurn<'a> { - fn new( + pub fn new( account: &'a str, nftoken_id: &'a str, fee: Option>, diff --git a/src/models/transactions/nftoken_cancel_offer.rs b/src/models/transactions/nftoken_cancel_offer.rs index 5c850ccd..3d18160d 100644 --- a/src/models/transactions/nftoken_cancel_offer.rs +++ b/src/models/transactions/nftoken_cancel_offer.rs @@ -139,7 +139,7 @@ impl<'a> NFTokenCancelOfferError for NFTokenCancelOffer<'a> { } impl<'a> NFTokenCancelOffer<'a> { - fn new( + pub fn new( account: &'a str, nftoken_offers: Vec<&'a str>, fee: Option>, diff --git a/src/models/transactions/nftoken_create_offer.rs b/src/models/transactions/nftoken_create_offer.rs index 658a061a..44cc792f 100644 --- a/src/models/transactions/nftoken_create_offer.rs +++ b/src/models/transactions/nftoken_create_offer.rs @@ -255,7 +255,7 @@ impl<'a> NFTokenCreateOfferError for NFTokenCreateOffer<'a> { } impl<'a> NFTokenCreateOffer<'a> { - fn new( + pub fn new( account: &'a str, nftoken_id: &'a str, amount: Amount<'a>, diff --git a/src/models/transactions/nftoken_mint.rs b/src/models/transactions/nftoken_mint.rs index e61dbc08..dbe29528 100644 --- a/src/models/transactions/nftoken_mint.rs +++ b/src/models/transactions/nftoken_mint.rs @@ -237,7 +237,7 @@ impl<'a> NFTokenMintError for NFTokenMint<'a> { } impl<'a> NFTokenMint<'a> { - fn new( + pub fn new( account: &'a str, nftoken_taxon: u32, fee: Option>, diff --git a/src/models/transactions/offer_cancel.rs b/src/models/transactions/offer_cancel.rs index 643252a2..560b62f2 100644 --- a/src/models/transactions/offer_cancel.rs +++ b/src/models/transactions/offer_cancel.rs @@ -110,7 +110,7 @@ impl<'a> Transaction for OfferCancel<'a> { } impl<'a> OfferCancel<'a> { - fn new( + pub fn new( account: &'a str, offer_sequence: u32, fee: Option>, diff --git a/src/models/transactions/offer_create.rs b/src/models/transactions/offer_create.rs index 1acd33de..13b2175e 100644 --- a/src/models/transactions/offer_create.rs +++ b/src/models/transactions/offer_create.rs @@ -174,7 +174,7 @@ impl<'a> Transaction for OfferCreate<'a> { } impl<'a> OfferCreate<'a> { - fn new( + pub fn new( account: &'a str, taker_gets: Amount<'a>, taker_pays: Amount<'a>, diff --git a/src/models/transactions/payment.rs b/src/models/transactions/payment.rs index 41c757a0..6cd3ef0d 100644 --- a/src/models/transactions/payment.rs +++ b/src/models/transactions/payment.rs @@ -260,7 +260,7 @@ impl<'a> PaymentError for Payment<'a> { } impl<'a> Payment<'a> { - fn new( + pub fn new( account: &'a str, amount: Amount<'a>, destination: &'a str, diff --git a/src/models/transactions/payment_channel_claim.rs b/src/models/transactions/payment_channel_claim.rs index 61ccbd34..4a30a21b 100644 --- a/src/models/transactions/payment_channel_claim.rs +++ b/src/models/transactions/payment_channel_claim.rs @@ -174,7 +174,7 @@ impl<'a> Transaction for PaymentChannelClaim<'a> { } impl<'a> PaymentChannelClaim<'a> { - fn new( + pub fn new( account: &'a str, channel: &'a str, fee: Option>, diff --git a/src/models/transactions/payment_channel_create.rs b/src/models/transactions/payment_channel_create.rs index 9188e8a2..0b444b5e 100644 --- a/src/models/transactions/payment_channel_create.rs +++ b/src/models/transactions/payment_channel_create.rs @@ -120,7 +120,7 @@ impl<'a> Transaction for PaymentChannelCreate<'a> { } impl<'a> PaymentChannelCreate<'a> { - fn new( + pub fn new( account: &'a str, amount: XRPAmount<'a>, destination: &'a str, diff --git a/src/models/transactions/payment_channel_fund.rs b/src/models/transactions/payment_channel_fund.rs index 4f9c3a34..7870a26e 100644 --- a/src/models/transactions/payment_channel_fund.rs +++ b/src/models/transactions/payment_channel_fund.rs @@ -115,7 +115,7 @@ impl<'a> Transaction for PaymentChannelFund<'a> { } impl<'a> PaymentChannelFund<'a> { - fn new( + pub fn new( account: &'a str, channel: &'a str, amount: XRPAmount<'a>, diff --git a/src/models/transactions/pseudo_transactions/enable_amendment.rs b/src/models/transactions/pseudo_transactions/enable_amendment.rs index a7476a73..463ea2b1 100644 --- a/src/models/transactions/pseudo_transactions/enable_amendment.rs +++ b/src/models/transactions/pseudo_transactions/enable_amendment.rs @@ -103,7 +103,7 @@ impl<'a> Transaction for EnableAmendment<'a> { } impl<'a> EnableAmendment<'a> { - fn new( + pub fn new( account: &'a str, amendment: &'a str, ledger_sequence: u32, diff --git a/src/models/transactions/pseudo_transactions/set_fee.rs b/src/models/transactions/pseudo_transactions/set_fee.rs index b1aeced5..f3782328 100644 --- a/src/models/transactions/pseudo_transactions/set_fee.rs +++ b/src/models/transactions/pseudo_transactions/set_fee.rs @@ -69,7 +69,7 @@ impl<'a> Transaction for SetFee<'a> { } impl<'a> SetFee<'a> { - fn new( + pub fn new( account: &'a str, base_fee: XRPAmount<'a>, reference_fee_units: u32, diff --git a/src/models/transactions/pseudo_transactions/unl_modify.rs b/src/models/transactions/pseudo_transactions/unl_modify.rs index b9a3d7fd..8ce72482 100644 --- a/src/models/transactions/pseudo_transactions/unl_modify.rs +++ b/src/models/transactions/pseudo_transactions/unl_modify.rs @@ -78,7 +78,7 @@ impl<'a> Transaction for UNLModify<'a> { } impl<'a> UNLModify<'a> { - fn new( + pub fn new( account: &'a str, ledger_sequence: u32, unlmodify_disabling: UNLModifyDisabling, diff --git a/src/models/transactions/set_regular_key.rs b/src/models/transactions/set_regular_key.rs index 38697431..d651dc48 100644 --- a/src/models/transactions/set_regular_key.rs +++ b/src/models/transactions/set_regular_key.rs @@ -114,7 +114,7 @@ impl<'a> Transaction for SetRegularKey<'a> { } impl<'a> SetRegularKey<'a> { - fn new( + pub fn new( account: &'a str, fee: Option>, sequence: Option, diff --git a/src/models/transactions/signer_list_set.rs b/src/models/transactions/signer_list_set.rs index 8223c03e..75519cad 100644 --- a/src/models/transactions/signer_list_set.rs +++ b/src/models/transactions/signer_list_set.rs @@ -226,7 +226,7 @@ impl<'a> SignerListSetError for SignerListSet<'a> { } impl<'a> SignerListSet<'a> { - fn new( + pub fn new( account: &'a str, signer_quorum: u32, fee: Option>, diff --git a/src/models/transactions/ticket_create.rs b/src/models/transactions/ticket_create.rs index 2efb723d..3779eab2 100644 --- a/src/models/transactions/ticket_create.rs +++ b/src/models/transactions/ticket_create.rs @@ -110,7 +110,7 @@ impl<'a> Transaction for TicketCreate<'a> { } impl<'a> TicketCreate<'a> { - fn new( + pub fn new( account: &'a str, ticket_count: u32, fee: Option>, diff --git a/src/models/transactions/trust_set.rs b/src/models/transactions/trust_set.rs index 5b0727d8..3165cd3c 100644 --- a/src/models/transactions/trust_set.rs +++ b/src/models/transactions/trust_set.rs @@ -162,7 +162,7 @@ impl<'a> Transaction for TrustSet<'a> { } impl<'a> TrustSet<'a> { - fn new( + pub fn new( account: &'a str, limit_amount: IssuedCurrencyAmount<'a>, fee: Option>, diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 0d36f11c..acc3bbb8 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -18,7 +18,8 @@ use zeroize::Zeroize; /// /// See Cryptographic Keys: /// `` -struct Wallet { +#[derive(Debug)] +pub struct Wallet { /// The seed from which the public and private keys /// are derived. pub seed: String, From e68ae4ecd98df2bb2cc68af3edac9d556c2e5709 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 3 Aug 2023 20:54:26 +0200 Subject: [PATCH 10/33] add changes to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c88321c..3cf681dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Performance Benchmarks ## [[Unreleased]] +- Examples + - Wallet from seed + - New wallet generation ## [[v0.2.0-beta]] ### Added From aacb322475ec60cc8dc8ecbda25ef1ac0c0c43c3 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 3 Aug 2023 21:02:57 +0200 Subject: [PATCH 11/33] add changes to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cf681dd..15c526bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Examples - Wallet from seed - New wallet generation +- make `new` methods of models public ## [[v0.2.0-beta]] ### Added From f651a0fdd5a6366f2efdf99c2e35513771e8c828 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 9 Aug 2023 22:40:21 +0200 Subject: [PATCH 12/33] implement new async_websocket_client --- src/asynch/clients/async_websocket_client.rs | 341 +++++++++++++------ src/asynch/clients/exceptions.rs | 39 ++- src/asynch/clients/websocket_base.rs | 37 -- 3 files changed, 275 insertions(+), 142 deletions(-) diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 7250efa0..fbb8956c 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -1,137 +1,272 @@ -use crate::asynch::clients::exceptions::XRPLWebsocketException; -use crate::models::Model; use crate::Err; - -// ! EXPORTS -pub use crate::asynch::clients::websocket_base::{ - WebsocketBase, WebsocketClose, WebsocketIo, WebsocketOpen, +use super::exceptions::XRPLWebsocketException; +use anyhow::Result; +use core::{ + fmt::{Debug, Display}, + marker::PhantomData, + ops::Deref, + pin::Pin, + task::Poll, +}; +use embedded_websocket::{ + framer_async::Framer as EmbeddedWebsocketFramer, Client as EmbeddedWebsocketClient, + WebSocket as EmbeddedWebsocket, }; -pub use em_as_net::client::websocket::ReadResult; -pub use em_as_net::core::io::{AsyncRead, AsyncWrite}; -// AsyncWebSocketClient +use futures::{Sink, Stream}; +use rand_core::RngCore; +use url::Url; + +#[cfg(feature = "std")] +use tokio::net::TcpStream; #[cfg(feature = "std")] -pub type AsyncWebsocketClient<'a, T, Rng, Status = Closed> = - if_std::AsyncWebsocketClient<'a, T, Rng, Status>; -pub use em_as_net::core::tcp::TcpSocket; -// TCP Adapters -pub use em_as_net::core::tcp::adapters::TcpAdapterTokio; -// Build your own TCP Socket -pub use em_as_net::core::tcp::TcpConnect; -// Build your own TCP Adapter -pub use em_as_net::core::tcp::adapters::AdapterConnect; -// Websocket statuses -pub struct Open; -pub struct Closed; +use tokio_tungstenite::{ + connect_async as tungstenite_connect_async, MaybeTlsStream as TungsteniteMaybeTlsStream, + WebSocketStream as TungsteniteWebsocketStream, +}; -use anyhow::Result; -use em_as_net::client::websocket::{WebsocketClientIo, WebsocketSendMessageType}; -use rand::RngCore; -use serde::Serialize; +// Exports +pub use embedded_websocket::{ + framer_async::{ + FramerError as EmbeddedWebsocketFramerError, ReadResult as EmbeddedWebsocketReadMessageType, + }, + Error as EmbeddedWebsocketError, WebSocketCloseStatusCode as EmbeddedWebsocketCloseStatusCode, + WebSocketOptions as EmbeddedWebsocketOptions, + WebSocketSendMessageType as EmbeddedWebsocketSendMessageType, + WebSocketState as EmbeddedWebsocketState, +}; #[cfg(feature = "std")] -mod if_std { - use super::{AsyncRead, AsyncWrite, Closed, Open, WebsocketBase, WebsocketOpen}; +pub type AsyncWebsocketClientTungstenite = + AsyncWebsocketClient>, Status>; +pub type AsyncWebsocketClientEmbeddedWebsocketTokio = + AsyncWebsocketClient, Status>; +#[cfg(feature = "std")] +pub use tokio_tungstenite::tungstenite::Message as TungsteniteMessage; - use alloc::borrow::Cow; - use core::marker::PhantomData; +pub struct WebsocketOpen; +pub struct WebsocketClosed; - use anyhow::Result; - use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; - use em_as_net::core::tcp::adapters::AdapterConnect; - use em_as_net::core::tcp::{TcpConnect, TcpSocket}; - use rand::rngs::ThreadRng; - use rand::{thread_rng, RngCore}; +pub struct AsyncWebsocketClient { + inner: T, + status: PhantomData, +} - /// An async client for interacting with the rippled WebSocket API. - pub struct AsyncWebsocketClient<'a, T, Rng, Status = Closed> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, - { - pub uri: Cow<'a, str>, - pub(crate) inner: WebsocketClient<'a, T, Rng>, - pub(crate) status: PhantomData, +impl AsyncWebsocketClient { + pub fn is_open(&self) -> bool { + core::any::type_name::() == core::any::type_name::() } +} - impl<'a, T, Rng, Status> AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, - { - pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { - Self { - uri: uri.clone(), - inner: WebsocketClient::new(uri.clone(), buffer), - status: PhantomData::default(), - } +impl Sink for AsyncWebsocketClient +where + T: Sink + Unpin, + >::Error: Display, + I: serde::Serialize, +{ + type Error = anyhow::Error; + + fn poll_ready( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + match Pin::new(&mut self.inner).poll_ready(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(error)) => Poll::Ready(Err!(error)), + Poll::Pending => Poll::Pending, } } - impl<'a, T, Rng, Status> WebsocketBase for AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, - { - fn is_open(&self) -> bool { - self.status == PhantomData:: + fn start_send( + mut self: core::pin::Pin<&mut Self>, + item: I, + ) -> core::result::Result<(), Self::Error> { + match Pin::new(&mut self.inner).start_send(TungsteniteMessage::Text(serde_json::to_string(&item).unwrap())) { // TODO: unwrap + Ok(()) => Ok(()), + Err(error) => Err!(error), } } - impl<'a, A> WebsocketOpen<'a, A, AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open>> - for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> - where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, - { - async fn open( - mut self, - adapter: A, - ) -> Result, ThreadRng, Open>> { - let tcp_socket = TcpSocket::new(adapter); - let rng = thread_rng(); - self.inner - .connect(tcp_socket, None, rng) - .await - .expect("TODO: panic message"); - - Ok(AsyncWebsocketClient { - uri: self.uri, - inner: self.inner, - status: PhantomData::, - }) + fn poll_flush( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + match Pin::new(&mut self.inner).poll_flush(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(error)) => Poll::Ready(Err!(error)), + Poll::Pending => Poll::Pending, + } + } + + fn poll_close( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + match Pin::new(&mut self.inner).poll_close(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(error)) => Poll::Ready(Err!(error)), + Poll::Pending => Poll::Pending, + } + } +} + +impl Stream for AsyncWebsocketClient +where + T: Stream + Unpin, +{ + type Item = ::Item; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.inner).poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, } } } -impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> +#[cfg(feature = "std")] +impl + AsyncWebsocketClient< + TungsteniteWebsocketStream>, + WebsocketClosed, + > +{ + pub async fn open( + uri: Url, + ) -> Result< + AsyncWebsocketClient< + TungsteniteWebsocketStream>, + WebsocketOpen, + >, + > { + let (websocket_stream, _) = tungstenite_connect_async(uri).await.unwrap(); // TODO: unwrap + + Ok(AsyncWebsocketClient { + inner: websocket_stream, + status: PhantomData::, + }) + } +} + +impl + AsyncWebsocketClient, WebsocketClosed> where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn write(&mut self, request: &R) -> Result<()> { - let request_json = match serde_json::to_string(&request) { - Ok(as_string) => as_string, - Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), - }; - self.inner - .write(request_json.into(), Some(WebsocketSendMessageType::Text)) + pub async fn open<'a, B, E>( + stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), + buffer: &'a mut [u8], + rng: Rng, + websocket_options: &EmbeddedWebsocketOptions<'_>, + ) -> Result< + AsyncWebsocketClient, WebsocketOpen>, + > + where + B: AsRef<[u8]>, + E: Debug, + { + let websocket = EmbeddedWebsocket::::new_client(rng); + let mut framer = EmbeddedWebsocketFramer::new(websocket); + framer + .connect(stream, buffer, websocket_options) .await - } + .unwrap(); // TODO: unwrap - async fn read(&mut self) -> Result>> { - match self.inner.read().await { - None => Ok(None), - Some(Ok(read_result)) => Ok(Some(read_result)), - Some(Err(read_error)) => Err(read_error), - } + Ok(AsyncWebsocketClient { + inner: framer, + status: PhantomData::, + }) } } -impl<'a, T, Rng> WebsocketClose for AsyncWebsocketClient<'a, T, Rng, Open> +impl AsyncWebsocketClient, WebsocketOpen> where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn close(&mut self) -> Result<()> { - self.inner.close().await + pub fn encode( + &mut self, + message_type: EmbeddedWebsocketSendMessageType, + end_of_message: bool, + from: &[u8], + to: &mut [u8], + ) -> Result + where + E: Debug, + { + let len = self + .inner + .encode::(message_type, end_of_message, from, to) + .unwrap(); // TODO: unwrap + + Ok(len) + } + + pub async fn send<'b, E, R: serde::Serialize>( + &mut self, + stream: &mut (impl Sink<&'b [u8], Error = E> + Unpin), + stream_buf: &'b mut [u8], + end_of_message: bool, + frame_buf: R, + ) -> Result<()> + where + E: Debug, + { + self.inner + .write(stream, stream_buf, EmbeddedWebsocketSendMessageType::Binary, end_of_message, serde_json::to_vec(&frame_buf).unwrap().as_slice()) // TODO: unwrap + .await + .unwrap(); // TODO: unwrap + + Ok(()) + } + + pub async fn close<'b, E>( + &mut self, + stream: &mut (impl Sink<&'b [u8], Error = E> + Unpin), + stream_buf: &'b mut [u8], + close_status: EmbeddedWebsocketCloseStatusCode, + status_description: Option<&str>, + ) -> Result<()> + where + E: Debug, + { + self.inner + .close(stream, stream_buf, close_status, status_description) + .await + .unwrap(); // TODO: unwrap + + Ok(()) + } + + pub async fn next<'a, B: Deref, E>( + &'a mut self, + stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), + buffer: &'a mut [u8], + ) -> Option>> // TODO: Change to Response as soon as implemented + where + E: Debug, + { + match self.inner.read(stream, buffer).await { + Some(Ok(read_result)) => Some(Ok(read_result)), + Some(Err(error)) => Some(Err!(XRPLWebsocketException::from(error))), + None => None, + } + } + + pub async fn try_next<'a, B: Deref, E>( + &'a mut self, + stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), + buffer: &'a mut [u8], + ) -> Result>> // TODO: Change to Response as soon as implemented + where + E: Debug, + { + match self.inner.read(stream, buffer).await { + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(error)) => Err!(XRPLWebsocketException::from(error)), + None => Ok(None), + } } } diff --git a/src/asynch/clients/exceptions.rs b/src/asynch/clients/exceptions.rs index f7d212cf..33f1b0fb 100644 --- a/src/asynch/clients/exceptions.rs +++ b/src/asynch/clients/exceptions.rs @@ -1,9 +1,44 @@ +use core::fmt::Debug; +use core::str::Utf8Error; +use embedded_websocket::framer_async::FramerError; use thiserror_no_std::Error; -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum XRPLWebsocketException { +#[derive(Debug, PartialEq, Eq, Error)] +pub enum XRPLWebsocketException { #[error("Websocket is not open")] NotOpen, #[error("Failed to serialize XRPL request to string")] RequestSerializationError, + // FramerError + #[error("I/O error: {0:?}")] + Io(E), + #[error("Frame too large (size: {0:?})")] + FrameTooLarge(usize), + #[error("Failed to interpret u8 to string (error: {0:?})")] + Utf8(Utf8Error), + #[error("Invalid HTTP header")] + HttpHeader, + #[error("Websocket error: {0:?}")] + WebSocket(embedded_websocket::Error), + #[error("Disconnected")] + Disconnected, + #[error("Read buffer is too small (size: {0:?})")] + RxBufferTooSmall(usize), } + +impl From> for XRPLWebsocketException { + fn from(value: FramerError) -> Self { + match value { + FramerError::Io(e) => XRPLWebsocketException::Io(e), + FramerError::FrameTooLarge(e) => XRPLWebsocketException::FrameTooLarge(e), + FramerError::Utf8(e) => XRPLWebsocketException::Utf8(e), + FramerError::HttpHeader(_) => XRPLWebsocketException::HttpHeader, + FramerError::WebSocket(e) => XRPLWebsocketException::WebSocket(e), + FramerError::Disconnected => XRPLWebsocketException::Disconnected, + FramerError::RxBufferTooSmall(e) => XRPLWebsocketException::RxBufferTooSmall(e), + } + } +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLWebsocketException {} diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 6abc376f..8b137891 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -1,38 +1 @@ -//! Traits used for all websocket clients. -use crate::models::Model; -use anyhow::Result; -use em_as_net::client::websocket::ReadResult; -use em_as_net::core::io::{AsyncRead, AsyncWrite}; -use em_as_net::core::tcp::adapters::AdapterConnect; - -use serde::Serialize; - -// A client for interacting with the rippled WebSocket API. -pub trait WebsocketBase { - fn is_open(&self) -> bool; -} - -pub trait WebsocketOpen<'a, A, OpenWS> -where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, -{ - /// Connects to the host. To communicate with a host we need a TCP Socket to - /// send bytes from the client to the host. This socket requires a preferred - /// `adapter` like the `TcpAdapterTokio`. The adapter is the actual socket - /// which must implement `AdapterConnect + AsyncRead + AsyncWrite + Sized + Unpin` - async fn open(self, adapter: A) -> Result; -} - -pub trait WebsocketClose { - /// Closes the websocket stream and disconnects from the host. - async fn close(&mut self) -> Result<()>; -} - -pub trait WebsocketIo { - /// Writes the request to the stream. - async fn write(&mut self, request: &R) -> Result<()>; - - /// Read messages from the stream. - async fn read(&mut self) -> Result>>; -} From dc454745e2c4df8dfdf85623d5ab87d052719059 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 9 Aug 2023 22:40:57 +0200 Subject: [PATCH 13/33] tests for async_websocket_client --- Cargo.toml | 44 ++++++++---- src/asynch/clients/async_websocket_client.rs | 21 ++++-- tests/common/codec.rs | 35 ++++++++++ tests/common/constants.rs | 2 +- tests/common/mod.rs | 42 +++++++++--- tests/integration/clients/mod.rs | 70 +++++++++++++++++--- tests/integration_tests.rs | 1 + 7 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 tests/common/codec.rs diff --git a/Cargo.toml b/Cargo.toml index e5cbdaab..1b8cf46b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,35 +46,40 @@ chrono = { version = "0.4.19", default-features = false, features = [ "clock", ] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } +rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } serde = { version = "1.0.130", default-features = false, features = ["derive"] } serde_json = { version = "1.0.68", default-features = false, features = [ "alloc", ] } -serde_with = "2.3.1" +serde_with = "3.2.0" serde_repr = "0.1" zeroize = "1.5.7" -hashbrown = { version = "0.13.2", default-features = false, features = ["serde"] } +hashbrown = { version = "0.14.0", default-features = false, features = ["serde"] } fnv = { version = "1.0.7", default-features = false } derive-new = { version = "0.5.9", default-features = false } thiserror-no-std = "2.0.2" anyhow = { version ="1.0.69", default-features = false } tokio = { version = "1.28.0", default-features = false, optional = true } +url = { version = "2.2.2", default-features = false, optional = true } +futures = { version = "0.3.28", default-features = false, optional = true } +rand_core = { version = "0.6.3", default-features = false } +tokio-tungstenite = { version = "0.20.0", optional = true } -# Use git version as long as there is no release for em-as-net. -[dependencies.em-as-net] -git = "https://github.com/LimpidCrypto/em-as-net" -package = "em-as-net" +[dependencies.embedded-websocket] +# git version needed to use `framer_async` +git = "https://github.com/ninjasource/embedded-websocket" +version = "0.9.2" +rev = "8d87d46f46fa0c75e099ca8aad37e8d00c8854f8" default-features = false -optional = true [dev-dependencies] -criterion = "0.4.0" +criterion = "0.5.1" cargo-husky = { version = "1.5.0", default-features = false, features = [ "user-hooks", ] } tokio = { version = "1.28.2", features = ["full"] } -rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } +tokio-util = { version = "0.7.7", features = ["codec"] } +bytes = { version = "1.4.0", default-features = false } [[bench]] name = "benchmarks" @@ -88,7 +93,22 @@ requests = ["core", "amounts", "currencies"] ledger = ["core", "amounts", "currencies"] amounts = ["core"] currencies = ["core"] -net = ["em-as-net/core", "em-as-net/client"] +net = ["url", "futures"] core = ["utils"] utils = [] -std = ["em-as-net/std", "tokio/full", "rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] +std = [ + "embedded-websocket/std", + "tokio-tungstenite/native-tls", + "tokio/full", + "futures/std", + "rand/std", + "regex/std", + "chrono/std", + "rand/std_rng", + "hex/std", + "rust_decimal/std", + "bs58/std", + "serde/std", + "indexmap/std", + "secp256k1/std", +] diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index fbb8956c..42fe9083 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -1,5 +1,5 @@ -use crate::Err; use super::exceptions::XRPLWebsocketException; +use crate::Err; use anyhow::Result; use core::{ fmt::{Debug, Display}, @@ -80,7 +80,10 @@ where mut self: core::pin::Pin<&mut Self>, item: I, ) -> core::result::Result<(), Self::Error> { - match Pin::new(&mut self.inner).start_send(TungsteniteMessage::Text(serde_json::to_string(&item).unwrap())) { // TODO: unwrap + match Pin::new(&mut self.inner).start_send(TungsteniteMessage::Text( + serde_json::to_string(&item).unwrap(), + )) { + // TODO: unwrap Ok(()) => Ok(()), Err(error) => Err!(error), } @@ -215,7 +218,13 @@ where E: Debug, { self.inner - .write(stream, stream_buf, EmbeddedWebsocketSendMessageType::Binary, end_of_message, serde_json::to_vec(&frame_buf).unwrap().as_slice()) // TODO: unwrap + .write( + stream, + stream_buf, + EmbeddedWebsocketSendMessageType::Binary, + end_of_message, + serde_json::to_vec(&frame_buf).unwrap().as_slice(), + ) // TODO: unwrap .await .unwrap(); // TODO: unwrap @@ -244,7 +253,8 @@ where &'a mut self, stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), buffer: &'a mut [u8], - ) -> Option>> // TODO: Change to Response as soon as implemented + ) -> Option>> + // TODO: Change to Response as soon as implemented where E: Debug, { @@ -259,7 +269,8 @@ where &'a mut self, stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), buffer: &'a mut [u8], - ) -> Result>> // TODO: Change to Response as soon as implemented + ) -> Result>> + // TODO: Change to Response as soon as implemented where E: Debug, { diff --git a/tests/common/codec.rs b/tests/common/codec.rs new file mode 100644 index 00000000..7e074866 --- /dev/null +++ b/tests/common/codec.rs @@ -0,0 +1,35 @@ +use bytes::{BufMut, BytesMut}; +use std::io; +use tokio_util::codec::{Decoder, Encoder}; + +pub struct Codec {} + +impl Codec { + pub fn new() -> Self { + Codec {} + } +} + +impl Decoder for Codec { + type Item = BytesMut; + type Error = io::Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, io::Error> { + if !buf.is_empty() { + let len = buf.len(); + Ok(Some(buf.split_to(len))) + } else { + Ok(None) + } + } +} + +impl Encoder<&[u8]> for Codec { + type Error = io::Error; + + fn encode(&mut self, data: &[u8], buf: &mut BytesMut) -> Result<(), io::Error> { + buf.reserve(data.len()); + buf.put(data); + Ok(()) + } +} diff --git a/tests/common/constants.rs b/tests/common/constants.rs index ed2ec980..fb33485a 100644 --- a/tests/common/constants.rs +++ b/tests/common/constants.rs @@ -1,2 +1,2 @@ pub const ECHO_WS_SERVER: &'static str = "ws://ws.vi-server.org/mirror/"; -// pub const ECHO_WSS_SERVER: &'static str = "wss://ws.vi-server.org/mirror/"; +pub const ECHO_WSS_SERVER: &'static str = "wss://ws.vi-server.org/mirror/"; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index a622f808..ec69f343 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,19 +1,41 @@ -use rand::rngs::ThreadRng; -use xrpl::asynch::clients::{ - AsyncWebsocketClient, Open, TcpAdapterTokio, TcpSocket, WebsocketBase, WebsocketOpen, +pub mod codec; +use xrpl::asynch::clients::async_websocket_client::{ + AsyncWebsocketClientEmbeddedWebsocketTokio, AsyncWebsocketClientTungstenite, + EmbeddedWebsocketOptions, WebsocketOpen, }; +use tokio::net::TcpStream; +use tokio_util::codec::Framed; + mod constants; pub use constants::*; -pub async fn connect_to_ws_echo<'a>( - buffer: &'a mut [u8], -) -> AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open> { - let websocket = AsyncWebsocketClient::new(ECHO_WS_SERVER.into(), buffer); - let tcp_adapter = TcpAdapterTokio::new(); - - let websocket = websocket.open(tcp_adapter).await.unwrap(); +pub async fn connect_to_wss_tungstinite_echo() -> AsyncWebsocketClientTungstenite { + let websocket = AsyncWebsocketClientTungstenite::open(ECHO_WSS_SERVER.parse().unwrap()) + .await + .unwrap(); assert!(websocket.is_open()); websocket } + +pub async fn connect_to_ws_embedded_websocket_tokio_echo( + stream: &mut Framed, + buffer: &mut [u8], +) -> AsyncWebsocketClientEmbeddedWebsocketTokio { + let rng = rand::thread_rng(); + let websocket_options = EmbeddedWebsocketOptions { + path: "/mirror", + host: "ws.vi-server.org", + origin: "http://ws.vi-server.org:80", + sub_protocols: None, + additional_headers: None, + }; + + let websocket = + AsyncWebsocketClientEmbeddedWebsocketTokio::open(stream, buffer, rng, &websocket_options) + .await + .unwrap(); + + websocket +} diff --git a/tests/integration/clients/mod.rs b/tests/integration/clients/mod.rs index ec57cb21..df96beec 100644 --- a/tests/integration/clients/mod.rs +++ b/tests/integration/clients/mod.rs @@ -1,11 +1,16 @@ -use super::common::connect_to_ws_echo; -use xrpl::asynch::clients::{ReadResult, WebsocketClose, WebsocketIo}; +use super::common::{ + codec::Codec, connect_to_ws_embedded_websocket_tokio_echo, connect_to_wss_tungstinite_echo, +}; +use futures::{SinkExt, TryStreamExt}; +use tokio_util::codec::Framed; +use xrpl::asynch::clients::async_websocket_client::{ + EmbeddedWebsocketReadMessageType, TungsteniteMessage, +}; use xrpl::models::requests::AccountInfo; #[tokio::test] -async fn test_websocket_non_tls() { - let mut buffer = [0u8; 4096]; - let mut websocket = connect_to_ws_echo(&mut buffer).await; +async fn test_websocket_tungstenite_echo() { + let mut websocket = connect_to_wss_tungstinite_echo().await; let account_info = AccountInfo::new( "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", None, @@ -15,12 +20,59 @@ async fn test_websocket_non_tls() { None, None, ); - websocket.write(&account_info).await.unwrap(); - while let Ok(Some(ReadResult::Text(response))) = websocket.read().await { - let account_info_echo: AccountInfo = serde_json::from_str(response).unwrap(); + websocket.send(&account_info).await.unwrap(); + + while let Ok(Some(TungsteniteMessage::Text(response))) = websocket.try_next().await { + let account_info_echo: AccountInfo = serde_json::from_str(response.as_str()).unwrap(); + println!("account_info_echo: {:?}", account_info_echo); assert_eq!(account_info, account_info_echo); - websocket.close().await.unwrap(); + break; + } +} + +#[tokio::test] +async fn test_embedded_websocket_echo() { + let tcp_stream = tokio::net::TcpStream::connect("ws.vi-server.org:80") + .await + .unwrap(); + let mut framed = Framed::new(tcp_stream, Codec::new()); + let mut buffer = [0u8; 4096]; + let mut websocket = connect_to_ws_embedded_websocket_tokio_echo(&mut framed, &mut buffer).await; + let account_info = AccountInfo::new( + "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", + None, + None, + None, + None, + None, + None, + ); + websocket + .send(&mut framed, &mut buffer, false, &account_info) + .await + .unwrap(); + + let mut ping_counter = 0; + loop { + let message = websocket + .try_next(&mut framed, &mut buffer) + .await + .unwrap() + .unwrap(); + match message { + EmbeddedWebsocketReadMessageType::Binary(msg) => { + assert_eq!(serde_json::to_vec(&account_info).unwrap().as_slice(), msg); + break; + } + EmbeddedWebsocketReadMessageType::Ping(_) => { + ping_counter += 1; + if ping_counter > 1 { + panic!("Expected only one ping"); + } + } + _ => panic!("Expected binary message"), + } } } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 797cfdcd..8732ebce 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // Remove eventually mod common; mod integration; From e55644169700337b0bbe798b253b9875a7c758ce Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 9 Aug 2023 22:51:51 +0200 Subject: [PATCH 14/33] run tests on pull requests to the dev branch --- .github/workflows/unit_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 4dfe9a0c..7fd6a512 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -5,6 +5,7 @@ on: pull_request: branches: - main + - dev name: Unit From b2355c5c32ddf67c800e3f611bc1ecc91a275cde Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 13 Apr 2023 18:56:21 +0200 Subject: [PATCH 15/33] get changes from networking branch and use dev branch as base so all commits are signed --- .gitignore | 1 + Cargo.toml | 13 +- src/_anyhow/mod.rs | 2 + src/asynch/clients/async_client.rs | 11 ++ src/asynch/clients/async_json_rpc_client.rs | 2 + src/asynch/clients/async_websocket_client.rs | 164 ++++++++++++++++++ src/asynch/clients/client.rs | 10 ++ src/asynch/clients/exceptions.rs | 9 + src/asynch/clients/json_rpc_base.rs | 2 + src/asynch/clients/mod.rs | 10 ++ src/asynch/clients/websocket_base.rs | 20 +++ src/asynch/mod.rs | 2 + src/lib.rs | 5 + src/models/mod.rs | 1 + src/models/requests/channel_authorize.rs | 2 - src/models/requests/ledger_entry.rs | 2 - src/models/requests/mod.rs | 2 + src/models/requests/responses/mod.rs | 1 + src/models/transactions/account_set.rs | 2 - src/models/transactions/check_cash.rs | 2 - src/models/transactions/deposit_preauth.rs | 2 - src/models/transactions/escrow_create.rs | 2 - src/models/transactions/escrow_finish.rs | 2 - .../transactions/nftoken_accept_offer.rs | 2 - .../transactions/nftoken_cancel_offer.rs | 2 - .../transactions/nftoken_create_offer.rs | 2 - src/models/transactions/nftoken_mint.rs | 2 - src/models/transactions/payment.rs | 1 - src/models/transactions/signer_list_set.rs | 2 - tests/common.rs | 34 +++- 30 files changed, 284 insertions(+), 30 deletions(-) create mode 100644 src/asynch/clients/async_client.rs create mode 100644 src/asynch/clients/async_json_rpc_client.rs create mode 100644 src/asynch/clients/async_websocket_client.rs create mode 100644 src/asynch/clients/client.rs create mode 100644 src/asynch/clients/exceptions.rs create mode 100644 src/asynch/clients/json_rpc_base.rs create mode 100644 src/asynch/clients/mod.rs create mode 100644 src/asynch/clients/websocket_base.rs create mode 100644 src/asynch/mod.rs create mode 100644 src/models/requests/responses/mod.rs diff --git a/.gitignore b/.gitignore index 1ec67b52..0401f97f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ Cargo.lock # VSCode .vscode + .idea # Additional diff --git a/Cargo.toml b/Cargo.toml index 11b261e2..0a0fec7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,14 @@ fnv = { version = "1.0.7", default-features = false } derive-new = { version = "0.5.9", default-features = false } thiserror-no-std = "2.0.2" anyhow = { version ="1.0.69", default-features = false } +tokio = { version = "1.28.0", default-features = false, optional = true } + +# Use git version as long as there is no release for em-as-net. +[dependencies.em-as-net] +git = "https://github.com/LimpidCrypto/em-as-net" +package = "em-as-net" +default-features = false +optional = true [dev-dependencies] criterion = "0.4.0" @@ -71,13 +79,14 @@ name = "benchmarks" harness = false [features] -default = ["std", "core", "models", "utils"] +default = ["std", "core", "models", "utils", "net"] models = ["core", "transactions", "requests", "ledger"] transactions = ["core", "amounts", "currencies"] requests = ["core", "amounts", "currencies"] ledger = ["core", "amounts", "currencies"] amounts = ["core"] currencies = ["core"] +net = ["em-as-net"] core = ["utils"] utils = [] -std = ["rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] +std = ["em-as-net/std", "tokio/full", "rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] diff --git a/src/_anyhow/mod.rs b/src/_anyhow/mod.rs index 345a3a8b..7b4ab3ec 100644 --- a/src/_anyhow/mod.rs +++ b/src/_anyhow/mod.rs @@ -2,6 +2,8 @@ #[macro_export] macro_rules! Err { ($err:expr $(,)?) => {{ + use alloc::string::ToString; + let error = $err.to_string().replace("\"", ""); let boxed_error = ::alloc::boxed::Box::new(error); let leaked_error: &'static str = ::alloc::boxed::Box::leak(boxed_error); diff --git a/src/asynch/clients/async_client.rs b/src/asynch/clients/async_client.rs new file mode 100644 index 00000000..a5449ce5 --- /dev/null +++ b/src/asynch/clients/async_client.rs @@ -0,0 +1,11 @@ +use super::client::Client; +use crate::models::Model; +use anyhow::Result; +use serde::Serialize; + +/// Interface for all async network clients to follow. +pub trait AsyncClient<'a>: Client<'a> { + async fn request(&mut self, request: T) -> Result { + self.request_impl(request).await + } +} diff --git a/src/asynch/clients/async_json_rpc_client.rs b/src/asynch/clients/async_json_rpc_client.rs new file mode 100644 index 00000000..614db2f9 --- /dev/null +++ b/src/asynch/clients/async_json_rpc_client.rs @@ -0,0 +1,2 @@ +// /// An async client for interacting with the rippled JSON RPC. +// pub struct AsyncJsonRpcClient {} diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs new file mode 100644 index 00000000..4c355e24 --- /dev/null +++ b/src/asynch/clients/async_websocket_client.rs @@ -0,0 +1,164 @@ +use crate::asynch::clients::exceptions::XRPLWebsocketException; +use crate::asynch::clients::websocket_base::WebsocketBase; +use crate::models::Model; +use crate::Err; +use anyhow::Result; +use serde::Serialize; + +// exports +#[cfg(feature = "std")] +pub use if_std::AsyncWebsocketClient; + +pub use em_as_net::client::websocket::ReadResult; + +#[cfg(feature = "std")] +mod if_std { + use crate::asynch::clients::async_client::AsyncClient; + use crate::asynch::clients::client::Client; + use crate::asynch::clients::exceptions::XRPLWebsocketException; + use crate::asynch::clients::websocket_base::WebsocketBase; + use crate::models::Model; + use crate::Err; + use alloc::borrow::Cow; + use core::cell::RefCell; + use core::ops::Deref; + + use anyhow::Result; + + use crate::asynch::clients::Websocket; + use em_as_net::client::websocket::{ + ReadResult, WebsocketClient, WebsocketClientIo, WebsocketSendMessageType, + }; + use rand::rngs::ThreadRng; + use serde::Serialize; + use tokio::net; + + /// An async client for interacting with the rippled WebSocket API. + pub struct AsyncWebsocketClient<'a> { + pub uri: Cow<'a, str>, + inner: RefCell>>, + } + + impl<'a> AsyncWebsocketClient<'a> { + pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { + let ws = WebsocketClient::new(uri.clone(), buffer); + Self { + uri, + inner: RefCell::new(Some(ws)), + } + } + } + + impl<'a> Websocket<'a> for AsyncWebsocketClient<'a> {} + + impl<'a> WebsocketBase<'a> for AsyncWebsocketClient<'a> { + fn is_open(&self) -> bool { + if let Some(ws) = self.inner.borrow().deref() { + ws.is_open() + } else { + false + } + } + + async fn do_open(&self) -> Result<()> { + return match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => ws.connect(None).await, + }; + } + + async fn do_close(&self) -> Result<()> { + return match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => ws.close().await, + }; + } + + async fn do_write(&self, request: T) -> Result<()> { + return match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => { + let request_string = match serde_json::to_string(&request) { + Ok(as_string) => as_string, + Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), + }; + ws.write( + Cow::from(request_string), + Some(WebsocketSendMessageType::Text), + ) + .await + } + }; + } + + // TODO: Fix lifetime issue + async fn do_read(&'a mut self) -> Result>> { + return match self.inner.get_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => match ws.read().await { + None => Ok(None), + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(read_error)) => Err(read_error), + }, + }; + } + + async fn do_request_impl(&mut self, _request: T) -> Result { + todo!() + } + } + + impl<'a> AsyncClient<'a> for AsyncWebsocketClient<'a> {} + + impl<'a> Client<'a> for AsyncWebsocketClient<'a> { + async fn request_impl(&mut self, request: T) -> Result { + if ! as WebsocketBase<'_>>::is_open(self) { + return Err!(XRPLWebsocketException::NotOpen); + } + + self.do_request_impl(request).await + } + } +} + +pub trait Websocket<'a>: WebsocketBase<'a> { + async fn open(&mut self) -> Result<()> { + if !self.is_open() { + self.do_open().await + } else { + Ok(()) + } + } + + async fn close(&self) -> Result<()> { + if self.is_open() { + self.do_close().await + } else { + Ok(()) + } + } + + async fn write(&mut self, request: T) -> Result<()> { + if self.is_open() { + self.do_write(request).await + } else { + Err!(XRPLWebsocketException::NotOpen) + } + } + + async fn read(&'a mut self) -> Result>> { + if self.is_open() { + self.do_read().await + } else { + Err!(XRPLWebsocketException::NotOpen) + } + } +} diff --git a/src/asynch/clients/client.rs b/src/asynch/clients/client.rs new file mode 100644 index 00000000..611f7ec2 --- /dev/null +++ b/src/asynch/clients/client.rs @@ -0,0 +1,10 @@ +use crate::models::Model; +use anyhow::Result; +use serde::Serialize; + +/// Interface for all network clients to follow. +// TODO: `T` should implement a trait `Request` +// TODO: `R` should implement a trait `Response` +pub trait Client<'a> { + async fn request_impl(&mut self, request: T) -> Result; +} diff --git a/src/asynch/clients/exceptions.rs b/src/asynch/clients/exceptions.rs new file mode 100644 index 00000000..f7d212cf --- /dev/null +++ b/src/asynch/clients/exceptions.rs @@ -0,0 +1,9 @@ +use thiserror_no_std::Error; + +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum XRPLWebsocketException { + #[error("Websocket is not open")] + NotOpen, + #[error("Failed to serialize XRPL request to string")] + RequestSerializationError, +} diff --git a/src/asynch/clients/json_rpc_base.rs b/src/asynch/clients/json_rpc_base.rs new file mode 100644 index 00000000..b50e9d49 --- /dev/null +++ b/src/asynch/clients/json_rpc_base.rs @@ -0,0 +1,2 @@ +// /// A common interface for JsonRpc requests. +// pub trait JsonRpcBase {} diff --git a/src/asynch/clients/mod.rs b/src/asynch/clients/mod.rs new file mode 100644 index 00000000..80af82fc --- /dev/null +++ b/src/asynch/clients/mod.rs @@ -0,0 +1,10 @@ +mod async_client; +pub mod async_json_rpc_client; +pub mod async_websocket_client; +mod client; +pub mod exceptions; +mod json_rpc_base; +mod websocket_base; + +pub use async_json_rpc_client::*; +pub use async_websocket_client::*; diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs new file mode 100644 index 00000000..20c49799 --- /dev/null +++ b/src/asynch/clients/websocket_base.rs @@ -0,0 +1,20 @@ +use crate::asynch::clients::client::Client; +use crate::models::Model; +use anyhow::Result; +use em_as_net::client::websocket::ReadResult; +use serde::Serialize; + +// A client for interacting with the rippled WebSocket API. +pub trait WebsocketBase<'a>: Client<'a> { + fn is_open(&self) -> bool; + + async fn do_open(&self) -> Result<()>; + + async fn do_close(&self) -> Result<()>; + + async fn do_write(&self, request: T) -> Result<()>; + + async fn do_read(&'a mut self) -> Result>>; + + async fn do_request_impl(&mut self, request: T) -> Result; +} diff --git a/src/asynch/mod.rs b/src/asynch/mod.rs new file mode 100644 index 00000000..286b8f61 --- /dev/null +++ b/src/asynch/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "net")] +pub mod clients; diff --git a/src/lib.rs b/src/lib.rs index 58f3a239..437fbfb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,12 +19,17 @@ //! [XRP Ledger](https://xrpl.org/docs.html). #![no_std] #![allow(dead_code)] // Remove eventually +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] +#![feature(inherent_associated_types)] +#![feature(type_alias_impl_trait)] #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(feature = "std")] extern crate std as alloc; +pub mod asynch; pub mod constants; #[cfg(feature = "core")] pub mod core; diff --git a/src/models/mod.rs b/src/models/mod.rs index b3c6a22d..ff0bc5e9 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -9,6 +9,7 @@ pub mod exceptions; #[cfg(feature = "ledger")] +#[allow(clippy::too_many_arguments)] pub mod ledger; pub mod model; #[cfg(feature = "requests")] diff --git a/src/models/requests/channel_authorize.rs b/src/models/requests/channel_authorize.rs index bd21f346..8024c20a 100644 --- a/src/models/requests/channel_authorize.rs +++ b/src/models/requests/channel_authorize.rs @@ -3,8 +3,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::requests::XRPLChannelAuthorizeException; use crate::{ constants::CryptoAlgorithm, diff --git a/src/models/requests/ledger_entry.rs b/src/models/requests/ledger_entry.rs index a7e851bb..78856383 100644 --- a/src/models/requests/ledger_entry.rs +++ b/src/models/requests/ledger_entry.rs @@ -3,8 +3,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::requests::XRPLLedgerEntryException; use crate::models::{requests::RequestMethod, Model}; diff --git a/src/models/requests/mod.rs b/src/models/requests/mod.rs index 9473949d..1d5d9ca8 100644 --- a/src/models/requests/mod.rs +++ b/src/models/requests/mod.rs @@ -25,6 +25,7 @@ pub mod no_ripple_check; pub mod path_find; pub mod ping; pub mod random; +pub mod responses; pub mod ripple_path_find; pub mod server_info; pub mod server_state; @@ -62,6 +63,7 @@ pub use no_ripple_check::*; pub use path_find::*; pub use ping::*; pub use random::*; +pub use responses::*; pub use ripple_path_find::*; pub use server_info::*; pub use server_state::*; diff --git a/src/models/requests/responses/mod.rs b/src/models/requests/responses/mod.rs new file mode 100644 index 00000000..13671b75 --- /dev/null +++ b/src/models/requests/responses/mod.rs @@ -0,0 +1 @@ +pub trait Response {} diff --git a/src/models/transactions/account_set.rs b/src/models/transactions/account_set.rs index 43f40478..3c9fad90 100644 --- a/src/models/transactions/account_set.rs +++ b/src/models/transactions/account_set.rs @@ -5,8 +5,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_with::skip_serializing_none; use strum_macros::{AsRefStr, Display, EnumIter}; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLAccountSetException; use crate::{ diff --git a/src/models/transactions/check_cash.rs b/src/models/transactions/check_cash.rs index 53816f65..c51951fc 100644 --- a/src/models/transactions/check_cash.rs +++ b/src/models/transactions/check_cash.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLCheckCashException; use crate::models::{ diff --git a/src/models/transactions/deposit_preauth.rs b/src/models/transactions/deposit_preauth.rs index 5e32517b..0376f24c 100644 --- a/src/models/transactions/deposit_preauth.rs +++ b/src/models/transactions/deposit_preauth.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLDepositPreauthException; use crate::models::{ diff --git a/src/models/transactions/escrow_create.rs b/src/models/transactions/escrow_create.rs index d0eacaba..32905e47 100644 --- a/src/models/transactions/escrow_create.rs +++ b/src/models/transactions/escrow_create.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLEscrowCreateException; use crate::models::{ diff --git a/src/models/transactions/escrow_finish.rs b/src/models/transactions/escrow_finish.rs index e049edcd..2ba50e6e 100644 --- a/src/models/transactions/escrow_finish.rs +++ b/src/models/transactions/escrow_finish.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::transactions::XRPLEscrowFinishException; use crate::models::{ amount::XRPAmount, diff --git a/src/models/transactions/nftoken_accept_offer.rs b/src/models/transactions/nftoken_accept_offer.rs index dcfcc532..68f3e5ff 100644 --- a/src/models/transactions/nftoken_accept_offer.rs +++ b/src/models/transactions/nftoken_accept_offer.rs @@ -6,8 +6,6 @@ use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::exceptions::XRPLAmountException; use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLNFTokenAcceptOfferException; diff --git a/src/models/transactions/nftoken_cancel_offer.rs b/src/models/transactions/nftoken_cancel_offer.rs index 3d18160d..205a5312 100644 --- a/src/models/transactions/nftoken_cancel_offer.rs +++ b/src/models/transactions/nftoken_cancel_offer.rs @@ -4,8 +4,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::amount::XRPAmount; use crate::models::transactions::XRPLNFTokenCancelOfferException; use crate::models::{ diff --git a/src/models/transactions/nftoken_create_offer.rs b/src/models/transactions/nftoken_create_offer.rs index 44cc792f..b2b53c5e 100644 --- a/src/models/transactions/nftoken_create_offer.rs +++ b/src/models/transactions/nftoken_create_offer.rs @@ -7,8 +7,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_with::skip_serializing_none; use strum_macros::{AsRefStr, Display, EnumIter}; -use alloc::string::ToString; - use crate::models::{ model::Model, transactions::{Flag, Memo, Signer, Transaction, TransactionType}, diff --git a/src/models/transactions/nftoken_mint.rs b/src/models/transactions/nftoken_mint.rs index dbe29528..b6fbe7b1 100644 --- a/src/models/transactions/nftoken_mint.rs +++ b/src/models/transactions/nftoken_mint.rs @@ -5,8 +5,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_with::skip_serializing_none; use strum_macros::{AsRefStr, Display, EnumIter}; -use alloc::string::ToString; - use crate::{ constants::{MAX_TRANSFER_FEE, MAX_URI_LENGTH}, models::{ diff --git a/src/models/transactions/payment.rs b/src/models/transactions/payment.rs index 6cd3ef0d..3358fc46 100644 --- a/src/models/transactions/payment.rs +++ b/src/models/transactions/payment.rs @@ -11,7 +11,6 @@ use crate::models::{ transactions::{Flag, Memo, Signer, Transaction, TransactionType}, PathStep, }; -use alloc::string::ToString; use crate::Err; use crate::_serde::txn_flags; diff --git a/src/models/transactions/signer_list_set.rs b/src/models/transactions/signer_list_set.rs index 75519cad..961269b9 100644 --- a/src/models/transactions/signer_list_set.rs +++ b/src/models/transactions/signer_list_set.rs @@ -5,8 +5,6 @@ use derive_new::new; use serde::{ser::SerializeMap, Deserialize, Serialize}; use serde_with::skip_serializing_none; -use alloc::string::ToString; - use crate::models::transactions::XRPLSignerListSetException; use crate::models::{ amount::XRPAmount, diff --git a/tests/common.rs b/tests/common.rs index 7db0151d..01978d3c 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,3 +1,31 @@ -/// Setup common testing prerequisites here such as connecting a client -/// to a server or creating required files/directories if needed. -pub fn setup() {} +#[cfg(feature = "std")] +mod std_test { + use xrpl::asynch::clients::{AsyncWebsocketClient, ReadResult, Websocket}; + use xrpl::models::requests::AccountInfo; + + #[tokio::test] + async fn test_async_ws() { + let mut buffer = [0u8; 4096]; + let uri = "ws://limpidcrypto.de:6004"; + let mut ws = AsyncWebsocketClient::new(uri.into(), &mut buffer); + // connect + ws.open().await.unwrap(); + // send request + let account_info = AccountInfo::new( + "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", + None, + None, + None, + None, + None, + None, + ); + ws.write(account_info).await.unwrap(); + // read messages + while let Ok(Some(ReadResult::Text(response))) = ws.read().await { + println!("{:?}", response); + + break; + } + } +} From 0c6c450fb9f3b6f3b541daec8d3b77e1a5291cf0 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 18:09:20 +0200 Subject: [PATCH 16/33] refactor websocket client --- Cargo.toml | 4 +- src/asynch/clients/async_client.rs | 11 - src/asynch/clients/async_websocket_client.rs | 232 +++++++++---------- src/asynch/clients/client.rs | 10 - src/asynch/clients/mod.rs | 2 - src/asynch/clients/websocket_base.rs | 34 ++- 6 files changed, 144 insertions(+), 149 deletions(-) delete mode 100644 src/asynch/clients/async_client.rs delete mode 100644 src/asynch/clients/client.rs diff --git a/Cargo.toml b/Cargo.toml index 0a0fec7f..e5cbdaab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,8 @@ criterion = "0.4.0" cargo-husky = { version = "1.5.0", default-features = false, features = [ "user-hooks", ] } +tokio = { version = "1.28.2", features = ["full"] } +rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } [[bench]] name = "benchmarks" @@ -86,7 +88,7 @@ requests = ["core", "amounts", "currencies"] ledger = ["core", "amounts", "currencies"] amounts = ["core"] currencies = ["core"] -net = ["em-as-net"] +net = ["em-as-net/core", "em-as-net/client"] core = ["utils"] utils = [] std = ["em-as-net/std", "tokio/full", "rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] diff --git a/src/asynch/clients/async_client.rs b/src/asynch/clients/async_client.rs deleted file mode 100644 index a5449ce5..00000000 --- a/src/asynch/clients/async_client.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::client::Client; -use crate::models::Model; -use anyhow::Result; -use serde::Serialize; - -/// Interface for all async network clients to follow. -pub trait AsyncClient<'a>: Client<'a> { - async fn request(&mut self, request: T) -> Result { - self.request_impl(request).await - } -} diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 4c355e24..869ddc1b 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -1,164 +1,162 @@ use crate::asynch::clients::exceptions::XRPLWebsocketException; -use crate::asynch::clients::websocket_base::WebsocketBase; use crate::models::Model; use crate::Err; -use anyhow::Result; -use serde::Serialize; -// exports +// ! EXPORTS +pub use crate::asynch::clients::websocket_base::{ + WebsocketBase, WebsocketClose, WebsocketIo, WebsocketOpen, +}; +pub use em_as_net::client::websocket::ReadResult; +pub use em_as_net::core::io::{AsyncRead, AsyncWrite}; +// AsyncWebSocketClient #[cfg(feature = "std")] -pub use if_std::AsyncWebsocketClient; +pub type AsyncWebsocketClient<'a, T, Rng, Status = Closed> = +if_std::AsyncWebsocketClient<'a, T, Rng, Status>; +pub use em_as_net::core::tcp::TcpSocket; +// TCP Adapters +pub use em_as_net::core::tcp::adapters::TcpAdapterTokio; +// Build your own TCP Socket +pub use em_as_net::core::tcp::TcpConnect; +// Build your own TCP Adapter +pub use em_as_net::core::tcp::adapters::AdapterConnect; +// Websocket statuses +pub struct Open; +pub struct Closed; -pub use em_as_net::client::websocket::ReadResult; +use anyhow::Result; +use em_as_net::client::websocket::{WebsocketClientIo, WebsocketSendMessageType}; +use rand::RngCore; +use serde::Serialize; #[cfg(feature = "std")] mod if_std { - use crate::asynch::clients::async_client::AsyncClient; - use crate::asynch::clients::client::Client; - use crate::asynch::clients::exceptions::XRPLWebsocketException; - use crate::asynch::clients::websocket_base::WebsocketBase; - use crate::models::Model; - use crate::Err; + use super::{AsyncRead, AsyncWrite, Closed, Open, WebsocketBase, WebsocketOpen}; + use alloc::borrow::Cow; use core::cell::RefCell; - use core::ops::Deref; + use core::marker::PhantomData; use anyhow::Result; - - use crate::asynch::clients::Websocket; - use em_as_net::client::websocket::{ - ReadResult, WebsocketClient, WebsocketClientIo, WebsocketSendMessageType, - }; + use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; + use em_as_net::core::tcp::adapters::AdapterConnect; + use em_as_net::core::tcp::{TcpConnect, TcpSocket}; use rand::rngs::ThreadRng; - use serde::Serialize; - use tokio::net; + use rand::{thread_rng, RngCore}; + use crate::asynch::clients::exceptions::XRPLWebsocketException; + use crate::Err; /// An async client for interacting with the rippled WebSocket API. - pub struct AsyncWebsocketClient<'a> { + pub struct AsyncWebsocketClient<'a, T, Rng, Status = Closed> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, + { pub uri: Cow<'a, str>, - inner: RefCell>>, + pub(crate) inner: RefCell>>, + pub(crate) status: PhantomData, } - impl<'a> AsyncWebsocketClient<'a> { + impl<'a, T, Rng, Status> AsyncWebsocketClient<'a, T, Rng, Status> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, + { pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { let ws = WebsocketClient::new(uri.clone(), buffer); Self { uri, inner: RefCell::new(Some(ws)), + status: PhantomData::default(), } } } - impl<'a> Websocket<'a> for AsyncWebsocketClient<'a> {} - - impl<'a> WebsocketBase<'a> for AsyncWebsocketClient<'a> { + impl<'a, T, Rng, Status> WebsocketBase for AsyncWebsocketClient<'a, T, Rng, Status> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, + { fn is_open(&self) -> bool { - if let Some(ws) = self.inner.borrow().deref() { - ws.is_open() - } else { - false - } - } - - async fn do_open(&self) -> Result<()> { - return match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => ws.connect(None).await, - }; + self.status == PhantomData:: } + } - async fn do_close(&self) -> Result<()> { - return match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => ws.close().await, + impl<'a, A> WebsocketOpen<'a, A, AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open>> + for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> + where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, + { + async fn open( + self, + adapter: A, + ) -> Result, ThreadRng, Open>> { + let tcp_socket = TcpSocket::new(adapter); + let mut websocket = match self.inner.take() { + None => { return Err!(XRPLWebsocketException::NotOpen) } + Some(ws) => ws }; + let rng = thread_rng(); + websocket + .connect(tcp_socket, None, rng) + .await + .expect("TODO: panic message"); + + Ok(AsyncWebsocketClient { + uri: self.uri, + inner: RefCell::new(Some(websocket)), + status: PhantomData::, + }) } + } +} - async fn do_write(&self, request: T) -> Result<()> { - return match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => { - let request_string = match serde_json::to_string(&request) { - Ok(as_string) => as_string, - Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), - }; - ws.write( - Cow::from(request_string), - Some(WebsocketSendMessageType::Text), - ) +impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, +{ + async fn write(&self, request: &R) -> Result<()> { + let request_json = match serde_json::to_string(&request) { + Ok(as_string) => as_string, + Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), + }; + match self.inner.borrow_mut().as_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) + } + Some(ws) => { + ws.write(request_json.into(), Some(WebsocketSendMessageType::Text)) .await - } - }; - } - - // TODO: Fix lifetime issue - async fn do_read(&'a mut self) -> Result>> { - return match self.inner.get_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => match ws.read().await { - None => Ok(None), - Some(Ok(read_result)) => Ok(Some(read_result)), - Some(Err(read_error)) => Err(read_error), - }, - }; - } + .expect("TODO: panic message"); - async fn do_request_impl(&mut self, _request: T) -> Result { - todo!() + Ok(()) + } } } - impl<'a> AsyncClient<'a> for AsyncWebsocketClient<'a> {} - - impl<'a> Client<'a> for AsyncWebsocketClient<'a> { - async fn request_impl(&mut self, request: T) -> Result { - if ! as WebsocketBase<'_>>::is_open(self) { - return Err!(XRPLWebsocketException::NotOpen); + async fn read(&mut self) -> Result>> { + return match self.inner.get_mut() { + None => { + Err!(XRPLWebsocketException::NotOpen) } - - self.do_request_impl(request).await - } + Some(ws) => match ws.read().await { + None => Ok(None), + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(read_error)) => Err(read_error), + }, + }; } } -pub trait Websocket<'a>: WebsocketBase<'a> { - async fn open(&mut self) -> Result<()> { - if !self.is_open() { - self.do_open().await - } else { - Ok(()) - } - } - +impl<'a, T, Rng> WebsocketClose for AsyncWebsocketClient<'a, T, Rng, Open> + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, +{ async fn close(&self) -> Result<()> { - if self.is_open() { - self.do_close().await - } else { - Ok(()) - } - } - - async fn write(&mut self, request: T) -> Result<()> { - if self.is_open() { - self.do_write(request).await - } else { - Err!(XRPLWebsocketException::NotOpen) - } - } - - async fn read(&'a mut self) -> Result>> { - if self.is_open() { - self.do_read().await - } else { - Err!(XRPLWebsocketException::NotOpen) + match self.inner.borrow_mut().as_mut() { + None => Err!(XRPLWebsocketException::NotOpen), + Some(ws) => ws.close().await } } } diff --git a/src/asynch/clients/client.rs b/src/asynch/clients/client.rs deleted file mode 100644 index 611f7ec2..00000000 --- a/src/asynch/clients/client.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::models::Model; -use anyhow::Result; -use serde::Serialize; - -/// Interface for all network clients to follow. -// TODO: `T` should implement a trait `Request` -// TODO: `R` should implement a trait `Response` -pub trait Client<'a> { - async fn request_impl(&mut self, request: T) -> Result; -} diff --git a/src/asynch/clients/mod.rs b/src/asynch/clients/mod.rs index 80af82fc..0b80e3bf 100644 --- a/src/asynch/clients/mod.rs +++ b/src/asynch/clients/mod.rs @@ -1,7 +1,5 @@ -mod async_client; pub mod async_json_rpc_client; pub mod async_websocket_client; -mod client; pub mod exceptions; mod json_rpc_base; mod websocket_base; diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 20c49799..1209be01 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -1,20 +1,38 @@ -use crate::asynch::clients::client::Client; +//! Traits used for all websocket clients. + use crate::models::Model; use anyhow::Result; use em_as_net::client::websocket::ReadResult; +use em_as_net::core::io::{AsyncRead, AsyncWrite}; +use em_as_net::core::tcp::adapters::AdapterConnect; + use serde::Serialize; // A client for interacting with the rippled WebSocket API. -pub trait WebsocketBase<'a>: Client<'a> { +pub trait WebsocketBase { fn is_open(&self) -> bool; +} - async fn do_open(&self) -> Result<()>; - - async fn do_close(&self) -> Result<()>; +pub trait WebsocketOpen<'a, A, OpenWS> + where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, +{ + /// Connects to the host. To communicate with a host we need a TCP Socket to + /// send bytes from the client to the host. This socket requires a preferred + /// `adapter` like the `TcpAdapterTokio`. The adapter is the actual socket + /// which must implement `AdapterConnect + AsyncRead + AsyncWrite + Sized + Unpin` + async fn open(self, adapter: A) -> Result; +} - async fn do_write(&self, request: T) -> Result<()>; +pub trait WebsocketClose { + /// Closes the websocket stream and disconnects from the host. + async fn close(&self) -> Result<()>; +} - async fn do_read(&'a mut self) -> Result>>; +pub trait WebsocketIo { + /// Writes the request to the stream. + async fn write(&self, request: &R) -> Result<()>; - async fn do_request_impl(&mut self, request: T) -> Result; + /// Read messages from the stream. + async fn read(&mut self) -> Result>>; } From 412d989bc03e354accbc8b6edd3a42c46f70653a Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 18:09:37 +0200 Subject: [PATCH 17/33] refactor integration websocket client test --- tests/common.rs | 31 ------------------------------- tests/common/constants.rs | 2 ++ tests/common/mod.rs | 19 +++++++++++++++++++ tests/integration/clients/mod.rs | 26 ++++++++++++++++++++++++++ tests/integration/mod.rs | 3 +++ tests/integration_tests.rs | 3 +++ 6 files changed, 53 insertions(+), 31 deletions(-) delete mode 100644 tests/common.rs create mode 100644 tests/common/constants.rs create mode 100644 tests/common/mod.rs create mode 100644 tests/integration/clients/mod.rs create mode 100644 tests/integration/mod.rs create mode 100644 tests/integration_tests.rs diff --git a/tests/common.rs b/tests/common.rs deleted file mode 100644 index 01978d3c..00000000 --- a/tests/common.rs +++ /dev/null @@ -1,31 +0,0 @@ -#[cfg(feature = "std")] -mod std_test { - use xrpl::asynch::clients::{AsyncWebsocketClient, ReadResult, Websocket}; - use xrpl::models::requests::AccountInfo; - - #[tokio::test] - async fn test_async_ws() { - let mut buffer = [0u8; 4096]; - let uri = "ws://limpidcrypto.de:6004"; - let mut ws = AsyncWebsocketClient::new(uri.into(), &mut buffer); - // connect - ws.open().await.unwrap(); - // send request - let account_info = AccountInfo::new( - "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", - None, - None, - None, - None, - None, - None, - ); - ws.write(account_info).await.unwrap(); - // read messages - while let Ok(Some(ReadResult::Text(response))) = ws.read().await { - println!("{:?}", response); - - break; - } - } -} diff --git a/tests/common/constants.rs b/tests/common/constants.rs new file mode 100644 index 00000000..ed2ec980 --- /dev/null +++ b/tests/common/constants.rs @@ -0,0 +1,2 @@ +pub const ECHO_WS_SERVER: &'static str = "ws://ws.vi-server.org/mirror/"; +// pub const ECHO_WSS_SERVER: &'static str = "wss://ws.vi-server.org/mirror/"; diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 00000000..a622f808 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,19 @@ +use rand::rngs::ThreadRng; +use xrpl::asynch::clients::{ + AsyncWebsocketClient, Open, TcpAdapterTokio, TcpSocket, WebsocketBase, WebsocketOpen, +}; + +mod constants; +pub use constants::*; + +pub async fn connect_to_ws_echo<'a>( + buffer: &'a mut [u8], +) -> AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open> { + let websocket = AsyncWebsocketClient::new(ECHO_WS_SERVER.into(), buffer); + let tcp_adapter = TcpAdapterTokio::new(); + + let websocket = websocket.open(tcp_adapter).await.unwrap(); + assert!(websocket.is_open()); + + websocket +} diff --git a/tests/integration/clients/mod.rs b/tests/integration/clients/mod.rs new file mode 100644 index 00000000..ec57cb21 --- /dev/null +++ b/tests/integration/clients/mod.rs @@ -0,0 +1,26 @@ +use super::common::connect_to_ws_echo; +use xrpl::asynch::clients::{ReadResult, WebsocketClose, WebsocketIo}; +use xrpl::models::requests::AccountInfo; + +#[tokio::test] +async fn test_websocket_non_tls() { + let mut buffer = [0u8; 4096]; + let mut websocket = connect_to_ws_echo(&mut buffer).await; + let account_info = AccountInfo::new( + "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", + None, + None, + None, + None, + None, + None, + ); + websocket.write(&account_info).await.unwrap(); + + while let Ok(Some(ReadResult::Text(response))) = websocket.read().await { + let account_info_echo: AccountInfo = serde_json::from_str(response).unwrap(); + assert_eq!(account_info, account_info_echo); + + websocket.close().await.unwrap(); + } +} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs new file mode 100644 index 00000000..d9510731 --- /dev/null +++ b/tests/integration/mod.rs @@ -0,0 +1,3 @@ +use super::common; + +mod clients; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 00000000..797cfdcd --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,3 @@ +mod common; + +mod integration; From 4aa4aa5445bb0faa03c5e995d8597d151361bc7d Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 19:57:03 +0200 Subject: [PATCH 18/33] fix tests --- .github/workflows/unit_test.yml | 4 ++-- .gitignore | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 7fd6a512..b31b08d4 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -25,7 +25,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: build - args: --release --no-default-features --features core,models + args: --release --no-default-features --features core,models,net - uses: actions-rs/cargo@v1 with: command: test @@ -33,4 +33,4 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features --features core,models + args: --no-default-features --features core,models,net diff --git a/.gitignore b/.gitignore index 0401f97f..1ec67b52 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ Cargo.lock # VSCode .vscode - .idea # Additional From e2ec5995fb2d9e27d7cdaf4084d8f9febb6e3e3f Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Fri, 30 Jun 2023 20:20:14 +0200 Subject: [PATCH 19/33] fix tests --- src/asynch/clients/async_websocket_client.rs | 48 ++++++++++---------- src/asynch/clients/websocket_base.rs | 4 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 869ddc1b..8c3d7e10 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -11,7 +11,7 @@ pub use em_as_net::core::io::{AsyncRead, AsyncWrite}; // AsyncWebSocketClient #[cfg(feature = "std")] pub type AsyncWebsocketClient<'a, T, Rng, Status = Closed> = -if_std::AsyncWebsocketClient<'a, T, Rng, Status>; + if_std::AsyncWebsocketClient<'a, T, Rng, Status>; pub use em_as_net::core::tcp::TcpSocket; // TCP Adapters pub use em_as_net::core::tcp::adapters::TcpAdapterTokio; @@ -36,20 +36,20 @@ mod if_std { use core::cell::RefCell; use core::marker::PhantomData; + use crate::asynch::clients::exceptions::XRPLWebsocketException; + use crate::Err; use anyhow::Result; use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; use em_as_net::core::tcp::adapters::AdapterConnect; use em_as_net::core::tcp::{TcpConnect, TcpSocket}; use rand::rngs::ThreadRng; use rand::{thread_rng, RngCore}; - use crate::asynch::clients::exceptions::XRPLWebsocketException; - use crate::Err; /// An async client for interacting with the rippled WebSocket API. pub struct AsyncWebsocketClient<'a, T, Rng, Status = Closed> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, { pub uri: Cow<'a, str>, pub(crate) inner: RefCell>>, @@ -57,9 +57,9 @@ mod if_std { } impl<'a, T, Rng, Status> AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, { pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { let ws = WebsocketClient::new(uri.clone(), buffer); @@ -72,9 +72,9 @@ mod if_std { } impl<'a, T, Rng, Status> WebsocketBase for AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, + where + T: TcpConnect<'a> + AsyncRead + AsyncWrite, + Rng: RngCore, { fn is_open(&self) -> bool { self.status == PhantomData:: @@ -82,9 +82,9 @@ mod if_std { } impl<'a, A> WebsocketOpen<'a, A, AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open>> - for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> - where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, + for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> + where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, { async fn open( self, @@ -92,8 +92,8 @@ mod if_std { ) -> Result, ThreadRng, Open>> { let tcp_socket = TcpSocket::new(adapter); let mut websocket = match self.inner.take() { - None => { return Err!(XRPLWebsocketException::NotOpen) } - Some(ws) => ws + None => return Err!(XRPLWebsocketException::NotOpen), + Some(ws) => ws, }; let rng = thread_rng(); websocket @@ -111,9 +111,9 @@ mod if_std { } impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, - Rng: RngCore, +where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, { async fn write(&self, request: &R) -> Result<()> { let request_json = match serde_json::to_string(&request) { @@ -149,14 +149,14 @@ impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> } impl<'a, T, Rng> WebsocketClose for AsyncWebsocketClient<'a, T, Rng, Open> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, - Rng: RngCore, +where + T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, + Rng: RngCore, { async fn close(&self) -> Result<()> { match self.inner.borrow_mut().as_mut() { None => Err!(XRPLWebsocketException::NotOpen), - Some(ws) => ws.close().await + Some(ws) => ws.close().await, } } } diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 1209be01..9f799787 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -14,8 +14,8 @@ pub trait WebsocketBase { } pub trait WebsocketOpen<'a, A, OpenWS> - where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, +where + A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, { /// Connects to the host. To communicate with a host we need a TCP Socket to /// send bytes from the client to the host. This socket requires a preferred From 8c566e2ab189cfda64794f2f5aa682cf457400ee Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 1 Jul 2023 15:51:28 +0200 Subject: [PATCH 20/33] fix cargo clippy --- src/asynch/clients/async_websocket_client.rs | 59 ++++++-------------- src/asynch/clients/websocket_base.rs | 4 +- src/models/requests/mod.rs | 1 + 3 files changed, 20 insertions(+), 44 deletions(-) diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 8c3d7e10..7250efa0 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -33,11 +33,8 @@ mod if_std { use super::{AsyncRead, AsyncWrite, Closed, Open, WebsocketBase, WebsocketOpen}; use alloc::borrow::Cow; - use core::cell::RefCell; use core::marker::PhantomData; - use crate::asynch::clients::exceptions::XRPLWebsocketException; - use crate::Err; use anyhow::Result; use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; use em_as_net::core::tcp::adapters::AdapterConnect; @@ -52,7 +49,7 @@ mod if_std { Rng: RngCore, { pub uri: Cow<'a, str>, - pub(crate) inner: RefCell>>, + pub(crate) inner: WebsocketClient<'a, T, Rng>, pub(crate) status: PhantomData, } @@ -62,10 +59,9 @@ mod if_std { Rng: RngCore, { pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { - let ws = WebsocketClient::new(uri.clone(), buffer); Self { - uri, - inner: RefCell::new(Some(ws)), + uri: uri.clone(), + inner: WebsocketClient::new(uri.clone(), buffer), status: PhantomData::default(), } } @@ -87,23 +83,19 @@ mod if_std { A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, { async fn open( - self, + mut self, adapter: A, ) -> Result, ThreadRng, Open>> { let tcp_socket = TcpSocket::new(adapter); - let mut websocket = match self.inner.take() { - None => return Err!(XRPLWebsocketException::NotOpen), - Some(ws) => ws, - }; let rng = thread_rng(); - websocket + self.inner .connect(tcp_socket, None, rng) .await .expect("TODO: panic message"); Ok(AsyncWebsocketClient { uri: self.uri, - inner: RefCell::new(Some(websocket)), + inner: self.inner, status: PhantomData::, }) } @@ -115,36 +107,22 @@ where T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn write(&self, request: &R) -> Result<()> { + async fn write(&mut self, request: &R) -> Result<()> { let request_json = match serde_json::to_string(&request) { Ok(as_string) => as_string, Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), }; - match self.inner.borrow_mut().as_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => { - ws.write(request_json.into(), Some(WebsocketSendMessageType::Text)) - .await - .expect("TODO: panic message"); - - Ok(()) - } - } + self.inner + .write(request_json.into(), Some(WebsocketSendMessageType::Text)) + .await } async fn read(&mut self) -> Result>> { - return match self.inner.get_mut() { - None => { - Err!(XRPLWebsocketException::NotOpen) - } - Some(ws) => match ws.read().await { - None => Ok(None), - Some(Ok(read_result)) => Ok(Some(read_result)), - Some(Err(read_error)) => Err(read_error), - }, - }; + match self.inner.read().await { + None => Ok(None), + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(read_error)) => Err(read_error), + } } } @@ -153,10 +131,7 @@ where T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn close(&self) -> Result<()> { - match self.inner.borrow_mut().as_mut() { - None => Err!(XRPLWebsocketException::NotOpen), - Some(ws) => ws.close().await, - } + async fn close(&mut self) -> Result<()> { + self.inner.close().await } } diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 9f799787..6abc376f 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -26,12 +26,12 @@ where pub trait WebsocketClose { /// Closes the websocket stream and disconnects from the host. - async fn close(&self) -> Result<()>; + async fn close(&mut self) -> Result<()>; } pub trait WebsocketIo { /// Writes the request to the stream. - async fn write(&self, request: &R) -> Result<()>; + async fn write(&mut self, request: &R) -> Result<()>; /// Read messages from the stream. async fn read(&mut self) -> Result>>; diff --git a/src/models/requests/mod.rs b/src/models/requests/mod.rs index 1d5d9ca8..297799df 100644 --- a/src/models/requests/mod.rs +++ b/src/models/requests/mod.rs @@ -17,6 +17,7 @@ pub mod ledger; pub mod ledger_closed; pub mod ledger_current; pub mod ledger_data; +#[allow(clippy::result_large_err)] pub mod ledger_entry; pub mod manifest; pub mod nft_buy_offers; From 54947d480c15ae0906c884cf852f507824dfdc50 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 9 Aug 2023 22:40:21 +0200 Subject: [PATCH 21/33] implement new async_websocket_client --- src/asynch/clients/async_websocket_client.rs | 341 +++++++++++++------ src/asynch/clients/exceptions.rs | 39 ++- src/asynch/clients/websocket_base.rs | 37 -- 3 files changed, 275 insertions(+), 142 deletions(-) diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 7250efa0..fbb8956c 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -1,137 +1,272 @@ -use crate::asynch::clients::exceptions::XRPLWebsocketException; -use crate::models::Model; use crate::Err; - -// ! EXPORTS -pub use crate::asynch::clients::websocket_base::{ - WebsocketBase, WebsocketClose, WebsocketIo, WebsocketOpen, +use super::exceptions::XRPLWebsocketException; +use anyhow::Result; +use core::{ + fmt::{Debug, Display}, + marker::PhantomData, + ops::Deref, + pin::Pin, + task::Poll, +}; +use embedded_websocket::{ + framer_async::Framer as EmbeddedWebsocketFramer, Client as EmbeddedWebsocketClient, + WebSocket as EmbeddedWebsocket, }; -pub use em_as_net::client::websocket::ReadResult; -pub use em_as_net::core::io::{AsyncRead, AsyncWrite}; -// AsyncWebSocketClient +use futures::{Sink, Stream}; +use rand_core::RngCore; +use url::Url; + +#[cfg(feature = "std")] +use tokio::net::TcpStream; #[cfg(feature = "std")] -pub type AsyncWebsocketClient<'a, T, Rng, Status = Closed> = - if_std::AsyncWebsocketClient<'a, T, Rng, Status>; -pub use em_as_net::core::tcp::TcpSocket; -// TCP Adapters -pub use em_as_net::core::tcp::adapters::TcpAdapterTokio; -// Build your own TCP Socket -pub use em_as_net::core::tcp::TcpConnect; -// Build your own TCP Adapter -pub use em_as_net::core::tcp::adapters::AdapterConnect; -// Websocket statuses -pub struct Open; -pub struct Closed; +use tokio_tungstenite::{ + connect_async as tungstenite_connect_async, MaybeTlsStream as TungsteniteMaybeTlsStream, + WebSocketStream as TungsteniteWebsocketStream, +}; -use anyhow::Result; -use em_as_net::client::websocket::{WebsocketClientIo, WebsocketSendMessageType}; -use rand::RngCore; -use serde::Serialize; +// Exports +pub use embedded_websocket::{ + framer_async::{ + FramerError as EmbeddedWebsocketFramerError, ReadResult as EmbeddedWebsocketReadMessageType, + }, + Error as EmbeddedWebsocketError, WebSocketCloseStatusCode as EmbeddedWebsocketCloseStatusCode, + WebSocketOptions as EmbeddedWebsocketOptions, + WebSocketSendMessageType as EmbeddedWebsocketSendMessageType, + WebSocketState as EmbeddedWebsocketState, +}; #[cfg(feature = "std")] -mod if_std { - use super::{AsyncRead, AsyncWrite, Closed, Open, WebsocketBase, WebsocketOpen}; +pub type AsyncWebsocketClientTungstenite = + AsyncWebsocketClient>, Status>; +pub type AsyncWebsocketClientEmbeddedWebsocketTokio = + AsyncWebsocketClient, Status>; +#[cfg(feature = "std")] +pub use tokio_tungstenite::tungstenite::Message as TungsteniteMessage; - use alloc::borrow::Cow; - use core::marker::PhantomData; +pub struct WebsocketOpen; +pub struct WebsocketClosed; - use anyhow::Result; - use em_as_net::client::websocket::{WebsocketClient, WebsocketClientConnect}; - use em_as_net::core::tcp::adapters::AdapterConnect; - use em_as_net::core::tcp::{TcpConnect, TcpSocket}; - use rand::rngs::ThreadRng; - use rand::{thread_rng, RngCore}; +pub struct AsyncWebsocketClient { + inner: T, + status: PhantomData, +} - /// An async client for interacting with the rippled WebSocket API. - pub struct AsyncWebsocketClient<'a, T, Rng, Status = Closed> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, - { - pub uri: Cow<'a, str>, - pub(crate) inner: WebsocketClient<'a, T, Rng>, - pub(crate) status: PhantomData, +impl AsyncWebsocketClient { + pub fn is_open(&self) -> bool { + core::any::type_name::() == core::any::type_name::() } +} - impl<'a, T, Rng, Status> AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, - { - pub fn new(uri: Cow<'a, str>, buffer: &'a mut [u8]) -> Self { - Self { - uri: uri.clone(), - inner: WebsocketClient::new(uri.clone(), buffer), - status: PhantomData::default(), - } +impl Sink for AsyncWebsocketClient +where + T: Sink + Unpin, + >::Error: Display, + I: serde::Serialize, +{ + type Error = anyhow::Error; + + fn poll_ready( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + match Pin::new(&mut self.inner).poll_ready(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(error)) => Poll::Ready(Err!(error)), + Poll::Pending => Poll::Pending, } } - impl<'a, T, Rng, Status> WebsocketBase for AsyncWebsocketClient<'a, T, Rng, Status> - where - T: TcpConnect<'a> + AsyncRead + AsyncWrite, - Rng: RngCore, - { - fn is_open(&self) -> bool { - self.status == PhantomData:: + fn start_send( + mut self: core::pin::Pin<&mut Self>, + item: I, + ) -> core::result::Result<(), Self::Error> { + match Pin::new(&mut self.inner).start_send(TungsteniteMessage::Text(serde_json::to_string(&item).unwrap())) { // TODO: unwrap + Ok(()) => Ok(()), + Err(error) => Err!(error), } } - impl<'a, A> WebsocketOpen<'a, A, AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open>> - for AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Closed> - where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, - { - async fn open( - mut self, - adapter: A, - ) -> Result, ThreadRng, Open>> { - let tcp_socket = TcpSocket::new(adapter); - let rng = thread_rng(); - self.inner - .connect(tcp_socket, None, rng) - .await - .expect("TODO: panic message"); - - Ok(AsyncWebsocketClient { - uri: self.uri, - inner: self.inner, - status: PhantomData::, - }) + fn poll_flush( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + match Pin::new(&mut self.inner).poll_flush(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(error)) => Poll::Ready(Err!(error)), + Poll::Pending => Poll::Pending, + } + } + + fn poll_close( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + match Pin::new(&mut self.inner).poll_close(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(error)) => Poll::Ready(Err!(error)), + Poll::Pending => Poll::Pending, + } + } +} + +impl Stream for AsyncWebsocketClient +where + T: Stream + Unpin, +{ + type Item = ::Item; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.inner).poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, } } } -impl<'a, T, Rng> WebsocketIo for AsyncWebsocketClient<'a, T, Rng, Open> +#[cfg(feature = "std")] +impl + AsyncWebsocketClient< + TungsteniteWebsocketStream>, + WebsocketClosed, + > +{ + pub async fn open( + uri: Url, + ) -> Result< + AsyncWebsocketClient< + TungsteniteWebsocketStream>, + WebsocketOpen, + >, + > { + let (websocket_stream, _) = tungstenite_connect_async(uri).await.unwrap(); // TODO: unwrap + + Ok(AsyncWebsocketClient { + inner: websocket_stream, + status: PhantomData::, + }) + } +} + +impl + AsyncWebsocketClient, WebsocketClosed> where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn write(&mut self, request: &R) -> Result<()> { - let request_json = match serde_json::to_string(&request) { - Ok(as_string) => as_string, - Err(_) => return Err!(XRPLWebsocketException::RequestSerializationError), - }; - self.inner - .write(request_json.into(), Some(WebsocketSendMessageType::Text)) + pub async fn open<'a, B, E>( + stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), + buffer: &'a mut [u8], + rng: Rng, + websocket_options: &EmbeddedWebsocketOptions<'_>, + ) -> Result< + AsyncWebsocketClient, WebsocketOpen>, + > + where + B: AsRef<[u8]>, + E: Debug, + { + let websocket = EmbeddedWebsocket::::new_client(rng); + let mut framer = EmbeddedWebsocketFramer::new(websocket); + framer + .connect(stream, buffer, websocket_options) .await - } + .unwrap(); // TODO: unwrap - async fn read(&mut self) -> Result>> { - match self.inner.read().await { - None => Ok(None), - Some(Ok(read_result)) => Ok(Some(read_result)), - Some(Err(read_error)) => Err(read_error), - } + Ok(AsyncWebsocketClient { + inner: framer, + status: PhantomData::, + }) } } -impl<'a, T, Rng> WebsocketClose for AsyncWebsocketClient<'a, T, Rng, Open> +impl AsyncWebsocketClient, WebsocketOpen> where - T: TcpConnect<'a> + AsyncRead + AsyncWrite + Unpin, Rng: RngCore, { - async fn close(&mut self) -> Result<()> { - self.inner.close().await + pub fn encode( + &mut self, + message_type: EmbeddedWebsocketSendMessageType, + end_of_message: bool, + from: &[u8], + to: &mut [u8], + ) -> Result + where + E: Debug, + { + let len = self + .inner + .encode::(message_type, end_of_message, from, to) + .unwrap(); // TODO: unwrap + + Ok(len) + } + + pub async fn send<'b, E, R: serde::Serialize>( + &mut self, + stream: &mut (impl Sink<&'b [u8], Error = E> + Unpin), + stream_buf: &'b mut [u8], + end_of_message: bool, + frame_buf: R, + ) -> Result<()> + where + E: Debug, + { + self.inner + .write(stream, stream_buf, EmbeddedWebsocketSendMessageType::Binary, end_of_message, serde_json::to_vec(&frame_buf).unwrap().as_slice()) // TODO: unwrap + .await + .unwrap(); // TODO: unwrap + + Ok(()) + } + + pub async fn close<'b, E>( + &mut self, + stream: &mut (impl Sink<&'b [u8], Error = E> + Unpin), + stream_buf: &'b mut [u8], + close_status: EmbeddedWebsocketCloseStatusCode, + status_description: Option<&str>, + ) -> Result<()> + where + E: Debug, + { + self.inner + .close(stream, stream_buf, close_status, status_description) + .await + .unwrap(); // TODO: unwrap + + Ok(()) + } + + pub async fn next<'a, B: Deref, E>( + &'a mut self, + stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), + buffer: &'a mut [u8], + ) -> Option>> // TODO: Change to Response as soon as implemented + where + E: Debug, + { + match self.inner.read(stream, buffer).await { + Some(Ok(read_result)) => Some(Ok(read_result)), + Some(Err(error)) => Some(Err!(XRPLWebsocketException::from(error))), + None => None, + } + } + + pub async fn try_next<'a, B: Deref, E>( + &'a mut self, + stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), + buffer: &'a mut [u8], + ) -> Result>> // TODO: Change to Response as soon as implemented + where + E: Debug, + { + match self.inner.read(stream, buffer).await { + Some(Ok(read_result)) => Ok(Some(read_result)), + Some(Err(error)) => Err!(XRPLWebsocketException::from(error)), + None => Ok(None), + } } } diff --git a/src/asynch/clients/exceptions.rs b/src/asynch/clients/exceptions.rs index f7d212cf..33f1b0fb 100644 --- a/src/asynch/clients/exceptions.rs +++ b/src/asynch/clients/exceptions.rs @@ -1,9 +1,44 @@ +use core::fmt::Debug; +use core::str::Utf8Error; +use embedded_websocket::framer_async::FramerError; use thiserror_no_std::Error; -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum XRPLWebsocketException { +#[derive(Debug, PartialEq, Eq, Error)] +pub enum XRPLWebsocketException { #[error("Websocket is not open")] NotOpen, #[error("Failed to serialize XRPL request to string")] RequestSerializationError, + // FramerError + #[error("I/O error: {0:?}")] + Io(E), + #[error("Frame too large (size: {0:?})")] + FrameTooLarge(usize), + #[error("Failed to interpret u8 to string (error: {0:?})")] + Utf8(Utf8Error), + #[error("Invalid HTTP header")] + HttpHeader, + #[error("Websocket error: {0:?}")] + WebSocket(embedded_websocket::Error), + #[error("Disconnected")] + Disconnected, + #[error("Read buffer is too small (size: {0:?})")] + RxBufferTooSmall(usize), } + +impl From> for XRPLWebsocketException { + fn from(value: FramerError) -> Self { + match value { + FramerError::Io(e) => XRPLWebsocketException::Io(e), + FramerError::FrameTooLarge(e) => XRPLWebsocketException::FrameTooLarge(e), + FramerError::Utf8(e) => XRPLWebsocketException::Utf8(e), + FramerError::HttpHeader(_) => XRPLWebsocketException::HttpHeader, + FramerError::WebSocket(e) => XRPLWebsocketException::WebSocket(e), + FramerError::Disconnected => XRPLWebsocketException::Disconnected, + FramerError::RxBufferTooSmall(e) => XRPLWebsocketException::RxBufferTooSmall(e), + } + } +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLWebsocketException {} diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs index 6abc376f..8b137891 100644 --- a/src/asynch/clients/websocket_base.rs +++ b/src/asynch/clients/websocket_base.rs @@ -1,38 +1 @@ -//! Traits used for all websocket clients. -use crate::models::Model; -use anyhow::Result; -use em_as_net::client::websocket::ReadResult; -use em_as_net::core::io::{AsyncRead, AsyncWrite}; -use em_as_net::core::tcp::adapters::AdapterConnect; - -use serde::Serialize; - -// A client for interacting with the rippled WebSocket API. -pub trait WebsocketBase { - fn is_open(&self) -> bool; -} - -pub trait WebsocketOpen<'a, A, OpenWS> -where - A: AdapterConnect<'a> + AsyncRead + AsyncWrite + Sized + Unpin, -{ - /// Connects to the host. To communicate with a host we need a TCP Socket to - /// send bytes from the client to the host. This socket requires a preferred - /// `adapter` like the `TcpAdapterTokio`. The adapter is the actual socket - /// which must implement `AdapterConnect + AsyncRead + AsyncWrite + Sized + Unpin` - async fn open(self, adapter: A) -> Result; -} - -pub trait WebsocketClose { - /// Closes the websocket stream and disconnects from the host. - async fn close(&mut self) -> Result<()>; -} - -pub trait WebsocketIo { - /// Writes the request to the stream. - async fn write(&mut self, request: &R) -> Result<()>; - - /// Read messages from the stream. - async fn read(&mut self) -> Result>>; -} From 6520cc15c91788183d087b2bbdc4495ab8c7cdef Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 9 Aug 2023 22:40:57 +0200 Subject: [PATCH 22/33] tests for async_websocket_client --- Cargo.toml | 44 ++++++++---- src/asynch/clients/async_websocket_client.rs | 21 ++++-- tests/common/codec.rs | 35 ++++++++++ tests/common/constants.rs | 2 +- tests/common/mod.rs | 42 +++++++++--- tests/integration/clients/mod.rs | 70 +++++++++++++++++--- tests/integration_tests.rs | 1 + 7 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 tests/common/codec.rs diff --git a/Cargo.toml b/Cargo.toml index e5cbdaab..1b8cf46b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,35 +46,40 @@ chrono = { version = "0.4.19", default-features = false, features = [ "clock", ] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } +rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } serde = { version = "1.0.130", default-features = false, features = ["derive"] } serde_json = { version = "1.0.68", default-features = false, features = [ "alloc", ] } -serde_with = "2.3.1" +serde_with = "3.2.0" serde_repr = "0.1" zeroize = "1.5.7" -hashbrown = { version = "0.13.2", default-features = false, features = ["serde"] } +hashbrown = { version = "0.14.0", default-features = false, features = ["serde"] } fnv = { version = "1.0.7", default-features = false } derive-new = { version = "0.5.9", default-features = false } thiserror-no-std = "2.0.2" anyhow = { version ="1.0.69", default-features = false } tokio = { version = "1.28.0", default-features = false, optional = true } +url = { version = "2.2.2", default-features = false, optional = true } +futures = { version = "0.3.28", default-features = false, optional = true } +rand_core = { version = "0.6.3", default-features = false } +tokio-tungstenite = { version = "0.20.0", optional = true } -# Use git version as long as there is no release for em-as-net. -[dependencies.em-as-net] -git = "https://github.com/LimpidCrypto/em-as-net" -package = "em-as-net" +[dependencies.embedded-websocket] +# git version needed to use `framer_async` +git = "https://github.com/ninjasource/embedded-websocket" +version = "0.9.2" +rev = "8d87d46f46fa0c75e099ca8aad37e8d00c8854f8" default-features = false -optional = true [dev-dependencies] -criterion = "0.4.0" +criterion = "0.5.1" cargo-husky = { version = "1.5.0", default-features = false, features = [ "user-hooks", ] } tokio = { version = "1.28.2", features = ["full"] } -rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } +tokio-util = { version = "0.7.7", features = ["codec"] } +bytes = { version = "1.4.0", default-features = false } [[bench]] name = "benchmarks" @@ -88,7 +93,22 @@ requests = ["core", "amounts", "currencies"] ledger = ["core", "amounts", "currencies"] amounts = ["core"] currencies = ["core"] -net = ["em-as-net/core", "em-as-net/client"] +net = ["url", "futures"] core = ["utils"] utils = [] -std = ["em-as-net/std", "tokio/full", "rand/std", "regex/std", "chrono/std", "rand/std_rng", "hex/std", "rust_decimal/std", "bs58/std", "serde/std", "indexmap/std", "secp256k1/std"] +std = [ + "embedded-websocket/std", + "tokio-tungstenite/native-tls", + "tokio/full", + "futures/std", + "rand/std", + "regex/std", + "chrono/std", + "rand/std_rng", + "hex/std", + "rust_decimal/std", + "bs58/std", + "serde/std", + "indexmap/std", + "secp256k1/std", +] diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index fbb8956c..42fe9083 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -1,5 +1,5 @@ -use crate::Err; use super::exceptions::XRPLWebsocketException; +use crate::Err; use anyhow::Result; use core::{ fmt::{Debug, Display}, @@ -80,7 +80,10 @@ where mut self: core::pin::Pin<&mut Self>, item: I, ) -> core::result::Result<(), Self::Error> { - match Pin::new(&mut self.inner).start_send(TungsteniteMessage::Text(serde_json::to_string(&item).unwrap())) { // TODO: unwrap + match Pin::new(&mut self.inner).start_send(TungsteniteMessage::Text( + serde_json::to_string(&item).unwrap(), + )) { + // TODO: unwrap Ok(()) => Ok(()), Err(error) => Err!(error), } @@ -215,7 +218,13 @@ where E: Debug, { self.inner - .write(stream, stream_buf, EmbeddedWebsocketSendMessageType::Binary, end_of_message, serde_json::to_vec(&frame_buf).unwrap().as_slice()) // TODO: unwrap + .write( + stream, + stream_buf, + EmbeddedWebsocketSendMessageType::Binary, + end_of_message, + serde_json::to_vec(&frame_buf).unwrap().as_slice(), + ) // TODO: unwrap .await .unwrap(); // TODO: unwrap @@ -244,7 +253,8 @@ where &'a mut self, stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), buffer: &'a mut [u8], - ) -> Option>> // TODO: Change to Response as soon as implemented + ) -> Option>> + // TODO: Change to Response as soon as implemented where E: Debug, { @@ -259,7 +269,8 @@ where &'a mut self, stream: &mut (impl Stream> + Sink<&'a [u8], Error = E> + Unpin), buffer: &'a mut [u8], - ) -> Result>> // TODO: Change to Response as soon as implemented + ) -> Result>> + // TODO: Change to Response as soon as implemented where E: Debug, { diff --git a/tests/common/codec.rs b/tests/common/codec.rs new file mode 100644 index 00000000..7e074866 --- /dev/null +++ b/tests/common/codec.rs @@ -0,0 +1,35 @@ +use bytes::{BufMut, BytesMut}; +use std::io; +use tokio_util::codec::{Decoder, Encoder}; + +pub struct Codec {} + +impl Codec { + pub fn new() -> Self { + Codec {} + } +} + +impl Decoder for Codec { + type Item = BytesMut; + type Error = io::Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, io::Error> { + if !buf.is_empty() { + let len = buf.len(); + Ok(Some(buf.split_to(len))) + } else { + Ok(None) + } + } +} + +impl Encoder<&[u8]> for Codec { + type Error = io::Error; + + fn encode(&mut self, data: &[u8], buf: &mut BytesMut) -> Result<(), io::Error> { + buf.reserve(data.len()); + buf.put(data); + Ok(()) + } +} diff --git a/tests/common/constants.rs b/tests/common/constants.rs index ed2ec980..fb33485a 100644 --- a/tests/common/constants.rs +++ b/tests/common/constants.rs @@ -1,2 +1,2 @@ pub const ECHO_WS_SERVER: &'static str = "ws://ws.vi-server.org/mirror/"; -// pub const ECHO_WSS_SERVER: &'static str = "wss://ws.vi-server.org/mirror/"; +pub const ECHO_WSS_SERVER: &'static str = "wss://ws.vi-server.org/mirror/"; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index a622f808..ec69f343 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,19 +1,41 @@ -use rand::rngs::ThreadRng; -use xrpl::asynch::clients::{ - AsyncWebsocketClient, Open, TcpAdapterTokio, TcpSocket, WebsocketBase, WebsocketOpen, +pub mod codec; +use xrpl::asynch::clients::async_websocket_client::{ + AsyncWebsocketClientEmbeddedWebsocketTokio, AsyncWebsocketClientTungstenite, + EmbeddedWebsocketOptions, WebsocketOpen, }; +use tokio::net::TcpStream; +use tokio_util::codec::Framed; + mod constants; pub use constants::*; -pub async fn connect_to_ws_echo<'a>( - buffer: &'a mut [u8], -) -> AsyncWebsocketClient<'a, TcpSocket, ThreadRng, Open> { - let websocket = AsyncWebsocketClient::new(ECHO_WS_SERVER.into(), buffer); - let tcp_adapter = TcpAdapterTokio::new(); - - let websocket = websocket.open(tcp_adapter).await.unwrap(); +pub async fn connect_to_wss_tungstinite_echo() -> AsyncWebsocketClientTungstenite { + let websocket = AsyncWebsocketClientTungstenite::open(ECHO_WSS_SERVER.parse().unwrap()) + .await + .unwrap(); assert!(websocket.is_open()); websocket } + +pub async fn connect_to_ws_embedded_websocket_tokio_echo( + stream: &mut Framed, + buffer: &mut [u8], +) -> AsyncWebsocketClientEmbeddedWebsocketTokio { + let rng = rand::thread_rng(); + let websocket_options = EmbeddedWebsocketOptions { + path: "/mirror", + host: "ws.vi-server.org", + origin: "http://ws.vi-server.org:80", + sub_protocols: None, + additional_headers: None, + }; + + let websocket = + AsyncWebsocketClientEmbeddedWebsocketTokio::open(stream, buffer, rng, &websocket_options) + .await + .unwrap(); + + websocket +} diff --git a/tests/integration/clients/mod.rs b/tests/integration/clients/mod.rs index ec57cb21..df96beec 100644 --- a/tests/integration/clients/mod.rs +++ b/tests/integration/clients/mod.rs @@ -1,11 +1,16 @@ -use super::common::connect_to_ws_echo; -use xrpl::asynch::clients::{ReadResult, WebsocketClose, WebsocketIo}; +use super::common::{ + codec::Codec, connect_to_ws_embedded_websocket_tokio_echo, connect_to_wss_tungstinite_echo, +}; +use futures::{SinkExt, TryStreamExt}; +use tokio_util::codec::Framed; +use xrpl::asynch::clients::async_websocket_client::{ + EmbeddedWebsocketReadMessageType, TungsteniteMessage, +}; use xrpl::models::requests::AccountInfo; #[tokio::test] -async fn test_websocket_non_tls() { - let mut buffer = [0u8; 4096]; - let mut websocket = connect_to_ws_echo(&mut buffer).await; +async fn test_websocket_tungstenite_echo() { + let mut websocket = connect_to_wss_tungstinite_echo().await; let account_info = AccountInfo::new( "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", None, @@ -15,12 +20,59 @@ async fn test_websocket_non_tls() { None, None, ); - websocket.write(&account_info).await.unwrap(); - while let Ok(Some(ReadResult::Text(response))) = websocket.read().await { - let account_info_echo: AccountInfo = serde_json::from_str(response).unwrap(); + websocket.send(&account_info).await.unwrap(); + + while let Ok(Some(TungsteniteMessage::Text(response))) = websocket.try_next().await { + let account_info_echo: AccountInfo = serde_json::from_str(response.as_str()).unwrap(); + println!("account_info_echo: {:?}", account_info_echo); assert_eq!(account_info, account_info_echo); - websocket.close().await.unwrap(); + break; + } +} + +#[tokio::test] +async fn test_embedded_websocket_echo() { + let tcp_stream = tokio::net::TcpStream::connect("ws.vi-server.org:80") + .await + .unwrap(); + let mut framed = Framed::new(tcp_stream, Codec::new()); + let mut buffer = [0u8; 4096]; + let mut websocket = connect_to_ws_embedded_websocket_tokio_echo(&mut framed, &mut buffer).await; + let account_info = AccountInfo::new( + "rJumr5e1HwiuV543H7bqixhtFreChWTaHH", + None, + None, + None, + None, + None, + None, + ); + websocket + .send(&mut framed, &mut buffer, false, &account_info) + .await + .unwrap(); + + let mut ping_counter = 0; + loop { + let message = websocket + .try_next(&mut framed, &mut buffer) + .await + .unwrap() + .unwrap(); + match message { + EmbeddedWebsocketReadMessageType::Binary(msg) => { + assert_eq!(serde_json::to_vec(&account_info).unwrap().as_slice(), msg); + break; + } + EmbeddedWebsocketReadMessageType::Ping(_) => { + ping_counter += 1; + if ping_counter > 1 { + panic!("Expected only one ping"); + } + } + _ => panic!("Expected binary message"), + } } } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 797cfdcd..8732ebce 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // Remove eventually mod common; mod integration; From b7f8311db7db99c3625cc19320c424d73c8a12cc Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 20:35:11 +0200 Subject: [PATCH 23/33] use nightly to allow unstable features --- .github/workflows/unit_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index b31b08d4..241f0c92 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly - uses: actions-rs/cargo@v1 with: command: build From 57ef4498b6a10a215e40992c6f3ba51067e19e73 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 20:35:40 +0200 Subject: [PATCH 24/33] remove unneeded features --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 437fbfb7..ba014d95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,6 @@ #![allow(dead_code)] // Remove eventually #![allow(incomplete_features)] #![feature(async_fn_in_trait)] -#![feature(inherent_associated_types)] -#![feature(type_alias_impl_trait)] #[cfg(not(feature = "std"))] extern crate alloc; From 15f3bf4c6c809284baf80e4c3a4ca96a0c74c2ef Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 20:36:45 +0200 Subject: [PATCH 25/33] rename AsyncWebsocketClientEmbeddedWebsocketTokio -> AsyncWebsocketClientEmbeddedWebsocket --- src/asynch/clients/async_websocket_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asynch/clients/async_websocket_client.rs b/src/asynch/clients/async_websocket_client.rs index 42fe9083..7505ba00 100644 --- a/src/asynch/clients/async_websocket_client.rs +++ b/src/asynch/clients/async_websocket_client.rs @@ -38,7 +38,7 @@ pub use embedded_websocket::{ #[cfg(feature = "std")] pub type AsyncWebsocketClientTungstenite = AsyncWebsocketClient>, Status>; -pub type AsyncWebsocketClientEmbeddedWebsocketTokio = +pub type AsyncWebsocketClientEmbeddedWebsocket = AsyncWebsocketClient, Status>; #[cfg(feature = "std")] pub use tokio_tungstenite::tungstenite::Message as TungsteniteMessage; From 2452c68cb67115abe9158df09e32927a064f1a6c Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 20:37:10 +0200 Subject: [PATCH 26/33] remove client base files --- src/asynch/clients/json_rpc_base.rs | 2 -- src/asynch/clients/mod.rs | 2 -- src/asynch/clients/websocket_base.rs | 1 - 3 files changed, 5 deletions(-) delete mode 100644 src/asynch/clients/json_rpc_base.rs delete mode 100644 src/asynch/clients/websocket_base.rs diff --git a/src/asynch/clients/json_rpc_base.rs b/src/asynch/clients/json_rpc_base.rs deleted file mode 100644 index b50e9d49..00000000 --- a/src/asynch/clients/json_rpc_base.rs +++ /dev/null @@ -1,2 +0,0 @@ -// /// A common interface for JsonRpc requests. -// pub trait JsonRpcBase {} diff --git a/src/asynch/clients/mod.rs b/src/asynch/clients/mod.rs index 0b80e3bf..50be6176 100644 --- a/src/asynch/clients/mod.rs +++ b/src/asynch/clients/mod.rs @@ -1,8 +1,6 @@ pub mod async_json_rpc_client; pub mod async_websocket_client; pub mod exceptions; -mod json_rpc_base; -mod websocket_base; pub use async_json_rpc_client::*; pub use async_websocket_client::*; diff --git a/src/asynch/clients/websocket_base.rs b/src/asynch/clients/websocket_base.rs deleted file mode 100644 index 8b137891..00000000 --- a/src/asynch/clients/websocket_base.rs +++ /dev/null @@ -1 +0,0 @@ - From b8d8b83ae84b67e9df984bb236d427a1732943d3 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 20:37:23 +0200 Subject: [PATCH 27/33] rename AsyncWebsocketClientEmbeddedWebsocketTokio -> AsyncWebsocketClientEmbeddedWebsocket --- tests/common/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index ec69f343..37d62bd4 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,6 +1,6 @@ pub mod codec; use xrpl::asynch::clients::async_websocket_client::{ - AsyncWebsocketClientEmbeddedWebsocketTokio, AsyncWebsocketClientTungstenite, + AsyncWebsocketClientEmbeddedWebsocket, AsyncWebsocketClientTungstenite, EmbeddedWebsocketOptions, WebsocketOpen, }; @@ -22,7 +22,7 @@ pub async fn connect_to_wss_tungstinite_echo() -> AsyncWebsocketClientTungstenit pub async fn connect_to_ws_embedded_websocket_tokio_echo( stream: &mut Framed, buffer: &mut [u8], -) -> AsyncWebsocketClientEmbeddedWebsocketTokio { +) -> AsyncWebsocketClientEmbeddedWebsocket { let rng = rand::thread_rng(); let websocket_options = EmbeddedWebsocketOptions { path: "/mirror", @@ -33,7 +33,7 @@ pub async fn connect_to_ws_embedded_websocket_tokio_echo( }; let websocket = - AsyncWebsocketClientEmbeddedWebsocketTokio::open(stream, buffer, rng, &websocket_options) + AsyncWebsocketClientEmbeddedWebsocket::open(stream, buffer, rng, &websocket_options) .await .unwrap(); From 557ee6540f7170985b2eb61719bab9fa2c4c691a Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 20:47:00 +0200 Subject: [PATCH 28/33] update dependencies --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1b8cf46b..c5ea1809 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,14 +29,14 @@ ed25519-dalek = "1.0.1" secp256k1 = { version = "0.27.0", default-features = false, features = [ "alloc", ] } -bs58 = { version = "0.4.0", default-features = false, features = [ +bs58 = { version = "0.5.0", default-features = false, features = [ "check", "alloc", ] } -indexmap = { version = "1.7.0", features = ["serde"] } +indexmap = { version = "2.0.0", features = ["serde"] } regex = { version = "1.5.4", default-features = false } -strum = { version = "0.24.1", default-features = false } -strum_macros = { version = "0.24.2", default-features = false } +strum = { version = "0.25.0", default-features = false } +strum_macros = { version = "0.25.2", default-features = false } crypto-bigint = { version = "0.5.1" } rust_decimal = { version = "1.17.0", default-features = false, features = [ "serde", From 30776d300e3aafd89839bab2c347b60ecc90d6ec Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 21:01:30 +0200 Subject: [PATCH 29/33] add dev dependencies --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c5ea1809..475a1faa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,8 @@ cargo-husky = { version = "1.5.0", default-features = false, features = [ tokio = { version = "1.28.2", features = ["full"] } tokio-util = { version = "0.7.7", features = ["codec"] } bytes = { version = "1.4.0", default-features = false } +futures = "0.3.28" +rand = "0.8.5" [[bench]] name = "benchmarks" From e898989690920330942ca1ba0085cf5a3385080b Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 10 Aug 2023 21:03:00 +0200 Subject: [PATCH 30/33] add dev dependencies --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 475a1faa..3dd81c07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ rev = "8d87d46f46fa0c75e099ca8aad37e8d00c8854f8" default-features = false [dev-dependencies] +xrpl-rust = { path = "." } criterion = "0.5.1" cargo-husky = { version = "1.5.0", default-features = false, features = [ "user-hooks", From 8cf41224052c83b6594dcff7b14925d97ca0e7bb Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Mon, 14 Aug 2023 18:49:47 +0200 Subject: [PATCH 31/33] change clients export --- src/asynch/clients/mod.rs | 6 +++--- tests/common/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/asynch/clients/mod.rs b/src/asynch/clients/mod.rs index 50be6176..b5dbb46e 100644 --- a/src/asynch/clients/mod.rs +++ b/src/asynch/clients/mod.rs @@ -1,6 +1,6 @@ -pub mod async_json_rpc_client; -pub mod async_websocket_client; -pub mod exceptions; +mod async_json_rpc_client; +mod async_websocket_client; +mod exceptions; pub use async_json_rpc_client::*; pub use async_websocket_client::*; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 37d62bd4..59ec33cf 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,5 +1,5 @@ pub mod codec; -use xrpl::asynch::clients::async_websocket_client::{ +use xrpl::asynch::clients::{ AsyncWebsocketClientEmbeddedWebsocket, AsyncWebsocketClientTungstenite, EmbeddedWebsocketOptions, WebsocketOpen, }; From 14965928aeabb8aebba5611f579a78610012eac9 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Mon, 14 Aug 2023 18:50:05 +0200 Subject: [PATCH 32/33] change clients export --- tests/integration/clients/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/clients/mod.rs b/tests/integration/clients/mod.rs index df96beec..4d259642 100644 --- a/tests/integration/clients/mod.rs +++ b/tests/integration/clients/mod.rs @@ -3,9 +3,7 @@ use super::common::{ }; use futures::{SinkExt, TryStreamExt}; use tokio_util::codec::Framed; -use xrpl::asynch::clients::async_websocket_client::{ - EmbeddedWebsocketReadMessageType, TungsteniteMessage, -}; +use xrpl::asynch::clients::{EmbeddedWebsocketReadMessageType, TungsteniteMessage}; use xrpl::models::requests::AccountInfo; #[tokio::test] From 3ba3acbea1763d8a947702196b19fe92dfbe1963 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Mon, 14 Aug 2023 18:50:27 +0200 Subject: [PATCH 33/33] add .DS_STORE to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 1ec67b52..338bdf82 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ Cargo.lock # Additional src/main.rs + +.DS_Store