Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DO NOT MERGE - Async networking #67

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7acf7dc
get changes from networking branch and use dev branch as base so all …
Apr 13, 2023
bd7d44b
make GitHub workflows work with dev branch instead of develop
May 28, 2023
5d777c7
refactor websocket client
Jun 30, 2023
6406bbc
refactor integration websocket client test
Jun 30, 2023
b60613e
fix tests
Jun 30, 2023
464e776
fix tests
Jun 30, 2023
316a122
fix cargo clippy
Jul 1, 2023
3ae950e
add code examples
Aug 3, 2023
0d7f9e9
fix model constructor to public; wallet scope public
Aug 3, 2023
e68ae4e
add changes to changelog
Aug 3, 2023
aacb322
add changes to changelog
Aug 3, 2023
f651a0f
implement new async_websocket_client
Aug 9, 2023
dc45474
tests for async_websocket_client
Aug 9, 2023
1e134c9
Add Examples
LimpidCrypto Aug 9, 2023
e556441
run tests on pull requests to the dev branch
Aug 9, 2023
b2355c5
get changes from networking branch and use dev branch as base so all …
Apr 13, 2023
0c6c450
refactor websocket client
Jun 30, 2023
412d989
refactor integration websocket client test
Jun 30, 2023
4aa4aa5
fix tests
Jun 30, 2023
e2ec599
fix tests
Jun 30, 2023
8c566e2
fix cargo clippy
Jul 1, 2023
54947d4
implement new async_websocket_client
Aug 9, 2023
6520cc1
tests for async_websocket_client
Aug 9, 2023
54aa509
Merge remote-tracking branch 'origin/async-net' into async-net
Aug 10, 2023
b7f8311
use nightly to allow unstable features
Aug 10, 2023
57ef449
remove unneeded features
Aug 10, 2023
15f3bf4
rename AsyncWebsocketClientEmbeddedWebsocketTokio -> AsyncWebsocketCl…
Aug 10, 2023
2452c68
remove client base files
Aug 10, 2023
b8d8b83
rename AsyncWebsocketClientEmbeddedWebsocketTokio -> AsyncWebsocketCl…
Aug 10, 2023
557ee65
update dependencies
Aug 10, 2023
30776d3
add dev dependencies
Aug 10, 2023
e898989
add dev dependencies
Aug 10, 2023
8cf4122
change clients export
Aug 14, 2023
1496592
change clients export
Aug 14, 2023
3ba3acb
add .DS_STORE to gitignore
Aug 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Cargo.lock

# VSCode
.vscode

LimpidCrypto marked this conversation as resolved.
Show resolved Hide resolved
.idea

# Additional
Expand Down
13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
LimpidCrypto marked this conversation as resolved.
Show resolved Hide resolved

[dev-dependencies]
criterion = "0.4.0"
Expand All @@ -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"]
2 changes: 2 additions & 0 deletions src/_anyhow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 11 additions & 0 deletions src/asynch/clients/async_client.rs
Original file line number Diff line number Diff line change
@@ -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<T: Model + Serialize, R>(&mut self, request: T) -> Result<R> {
self.request_impl(request).await
}
}
2 changes: 2 additions & 0 deletions src/asynch/clients/async_json_rpc_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// /// An async client for interacting with the rippled JSON RPC.
// pub struct AsyncJsonRpcClient {}
164 changes: 164 additions & 0 deletions src/asynch/clients/async_websocket_client.rs
Original file line number Diff line number Diff line change
@@ -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<Option<WebsocketClient<'a, net::TcpStream, ThreadRng>>>,
}

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<T: Model + Serialize>(&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<Option<ReadResult<'a>>> {
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<T: Model + Serialize, R>(&mut self, _request: T) -> Result<R> {
todo!()
}
}

impl<'a> AsyncClient<'a> for AsyncWebsocketClient<'a> {}

impl<'a> Client<'a> for AsyncWebsocketClient<'a> {
async fn request_impl<T: Model + Serialize, R>(&mut self, request: T) -> Result<R> {
if !<AsyncWebsocketClient<'a> 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<T: Model + Serialize>(&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<Option<ReadResult<'a>>> {
if self.is_open() {
self.do_read().await
} else {
Err!(XRPLWebsocketException::NotOpen)
}
}
}
10 changes: 10 additions & 0 deletions src/asynch/clients/client.rs
Original file line number Diff line number Diff line change
@@ -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<T: Model + Serialize, R>(&mut self, request: T) -> Result<R>;
}
9 changes: 9 additions & 0 deletions src/asynch/clients/exceptions.rs
Original file line number Diff line number Diff line change
@@ -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,
}
2 changes: 2 additions & 0 deletions src/asynch/clients/json_rpc_base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// /// A common interface for JsonRpc requests.
// pub trait JsonRpcBase {}
10 changes: 10 additions & 0 deletions src/asynch/clients/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
20 changes: 20 additions & 0 deletions src/asynch/clients/websocket_base.rs
Original file line number Diff line number Diff line change
@@ -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<T: Model + Serialize>(&self, request: T) -> Result<()>;

async fn do_read(&'a mut self) -> Result<Option<ReadResult<'a>>>;

async fn do_request_impl<T: Model + Serialize, R>(&mut self, request: T) -> Result<R>;
LimpidCrypto marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 2 additions & 0 deletions src/asynch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(feature = "net")]
pub mod clients;
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

pub mod exceptions;
#[cfg(feature = "ledger")]
#[allow(clippy::too_many_arguments)]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we allow these exceptions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I allowed too_many_arguments because the new methods have a lot of parameters. What would be your suggestion to resolve the clippy warning?

pub mod ledger;
pub mod model;
#[cfg(feature = "requests")]
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_currencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
7 changes: 6 additions & 1 deletion src/models/requests/account_nfts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>, marker: Option<u32>) -> Self {
pub fn new(
account: &'a str,
id: Option<&'a str>,
limit: Option<u32>,
marker: Option<u32>,
) -> Self {
Self {
account,
id,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_offers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/account_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/requests/book_offers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
Loading