Skip to content

Add missing docs #589

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

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 7 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ jobs:
command: printf "[profile.dev]\ncodegen-units = 1\n" >> Cargo.toml
- run:
name: Build
command: cargo build --all-features --all-targets
command: cargo build # --all-features --all-targets
- run:
name: Test
# Note the timeout is included to make sure that they
# do not run for more than 10 minutes under any circumstances
# (We have had issues with bugs causing the tests to "run"
# for 5 hours, wasting a ton of compute credits)
command: timeout 10m cargo test --all --all-features
command: timeout 10m cargo test # --all --all-features
environment:
- RUST_LOG: "interledger=trace"
- RUST_BACKTRACE: "full"
Expand All @@ -44,7 +44,11 @@ jobs:
cargo clippy --all-targets --all-features -- -D warnings
- run:
name: Audit Dependencies
command: cargo audit
# Disable:
# 1. lazy_static advisory: https://github.com/interledger-rs/interledger-rs/issues/588
# 2. http/hyper advisory: outdated http due to yup-oauth2 3.1.1, tungstenite 0.9.2
command: cargo audit --ignore RUSTSEC-2019-0033 --ignore RUSTSEC-2019-0034 --ignore RUSTSEC-2019-0031

test-md:
docker:
- image: circleci/rust
Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@
[![crates.io](https://img.shields.io/crates/v/interledger.svg)](https://crates.io/crates/interledger)
[![Interledger.rs Documentation](https://docs.rs/interledger/badge.svg)](https://docs.rs/interledger)
[![CircleCI](https://circleci.com/gh/interledger-rs/interledger-rs.svg?style=shield)](https://circleci.com/gh/interledger-rs/interledger-rs)
![Rust Version](https://img.shields.io/badge/rust-stable-Success)
[![Docker Image](https://img.shields.io/docker/pulls/interledgerrs/node.svg?maxAge=2592000)](https://hub.docker.com/r/interledgerrs/node/)
![rustc](https://img.shields.io/badge/rustc-1.39+-red.svg)
![Rust](https://img.shields.io/badge/rust-stable-Success)
Copy link
Collaborator

Choose a reason for hiding this comment

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

image

This looks like Rust itself is stable (passing CI or something). What about this?

![rustc](https://img.shields.io/badge/Rust-1.39%2B%20stable-Success)

rustc

[![Docker Image](https://img.shields.io/docker/pulls/interledgerrs/ilp-node.svg?maxAge=2592000)](https://hub.docker.com/r/interledgerrs/ilp-node/)

## Requirements

All crates require Rust 2018 edition and are tested on the following channels:

- `1.39.0` (minimum supported)
- `stable`

## Connecting to the Testnet

Expand All @@ -34,7 +42,7 @@ To run the Interledger.rs components by themselves (rather than the `testnet-bun
#### Install

```bash #
docker pull interledgerrs/node
docker pull interledgerrs/ilp-node
docker pull interledgerrs/ilp-cli
docker pull interledgerrs/ilp-settlement-ethereum
```
Expand All @@ -43,7 +51,7 @@ docker pull interledgerrs/ilp-settlement-ethereum

```bash #
# This runs the sender / receiver / router bundle
docker run -it interledgerrs/node
docker run -it interledgerrs/ilp-node

# This is a simple CLI for interacting with the node's HTTP API
docker run -it --rm interledgerrs/ilp-cli
Expand Down
61 changes: 58 additions & 3 deletions crates/interledger-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,34 +75,45 @@ where
pub trait NodeStore: AddressStore + Clone + Send + Sync + 'static {
type Account: Account;

/// Inserts an account to the store. Generates a UUID and returns the full Account object.
async fn insert_account(&self, account: AccountDetails) -> Result<Self::Account, ()>;

/// Deletes the account corresponding to the provided id and returns it
async fn delete_account(&self, id: Uuid) -> Result<Self::Account, ()>;

/// Overwrites the account corresponding to the provided id with the provided details
async fn update_account(&self, id: Uuid, account: AccountDetails) -> Result<Self::Account, ()>;

/// Modifies the account corresponding to the provided id with the provided settings
async fn modify_account_settings(
&self,
id: Uuid,
settings: AccountSettings,
) -> Result<Self::Account, ()>;

// TODO limit the number of results and page through them
/// Gets all stored accounts
async fn get_all_accounts(&self) -> Result<Vec<Self::Account>, ()>;

/// Sets the static routes for routing
async fn set_static_routes<R>(&self, routes: R) -> Result<(), ()>
where
R: IntoIterator<Item = (String, Uuid)> + Send + 'async_trait;

/// Sets a single static route
async fn set_static_route(&self, prefix: String, account_id: Uuid) -> Result<(), ()>;

/// Sets the default route ("") to be the provided account id
/// (acts as a catch-all route if all other routes don't match)
async fn set_default_route(&self, account_id: Uuid) -> Result<(), ()>;

/// Sets the default settlement engines to be used for the provided asset codes
async fn set_settlement_engines(
&self,
asset_to_url_map: impl IntoIterator<Item = (String, Url)> + Send + 'async_trait,
) -> Result<(), ()>;

/// Gets the default settlement engine for the provided asset code
async fn get_asset_settlement_engine(&self, asset_code: &str) -> Result<Option<Url>, ()>;
}

Expand All @@ -118,17 +129,25 @@ pub struct ExchangeRates(
/// their HTTP/BTP endpoints, since they may change their network configuration.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AccountSettings {
/// The account's incoming ILP over HTTP token.
pub ilp_over_http_incoming_token: Option<SecretString>,
/// The account's incoming ILP over BTP token.
pub ilp_over_btp_incoming_token: Option<SecretString>,
/// The account's outgoing ILP over HTTP token
pub ilp_over_http_outgoing_token: Option<SecretString>,
/// The account's API and incoming ILP over HTTP token.
pub ilp_over_btp_outgoing_token: Option<SecretString>,
/// The account's ILP over HTTP URL (this is where packets are sent over HTTP from your node)
pub ilp_over_http_url: Option<String>,
/// The account's ILP over BTP URL (this is where packets are sent over WebSockets from your node)
pub ilp_over_btp_url: Option<String>,
/// The threshold after which the balance service will trigger a settlement
#[serde(default, deserialize_with = "optional_number_or_string")]
pub settle_threshold: Option<i64>,
// Note that this is intentionally an unsigned integer because users should
// not be able to set the settle_to value to be negative (meaning the node
// would pre-fund with the user)
/// The amount which the balance service will attempt to settle down to.
/// Note that this is intentionally an unsigned integer because users should
/// not be able to set the settle_to value to be negative (meaning the node
/// would pre-fund with the user)
#[serde(default, deserialize_with = "optional_number_or_string")]
pub settle_to: Option<u64>,
}
Expand All @@ -146,45 +165,76 @@ pub struct EncryptedAccountSettings {
pub ilp_over_http_url: Option<String>,
pub ilp_over_btp_url: Option<String>,
#[serde(default, deserialize_with = "optional_number_or_string")]
/// The threshold after which the balance service will trigger a settlement
pub settle_threshold: Option<i64>,
#[serde(default, deserialize_with = "optional_number_or_string")]
/// The amount which the balance service will attempt to settle down to
pub settle_to: Option<u64>,
}

/// The Account type for the RedisStore.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountDetails {
/// The account's Interledger Protocol address.
/// If none is provided, the node should generate one
pub ilp_address: Option<Address>,
/// The account's username
pub username: Username,
/// The account's currency
pub asset_code: String,
#[serde(deserialize_with = "number_or_string")]
/// The account's asset scale
pub asset_scale: u8,
#[serde(default = "u64::max_value", deserialize_with = "number_or_string")]
/// The max amount per packet which can be routed for this account
pub max_packet_amount: u64,
/// The minimum balance this account can have (consider this as a credit/trust limit)
#[serde(default, deserialize_with = "optional_number_or_string")]
pub min_balance: Option<i64>,
/// The account's ILP over HTTP URL (this is where packets are sent over HTTP from your node)
pub ilp_over_http_url: Option<String>,
/// The account's API and incoming ILP over HTTP token.
/// This must match the ILP over HTTP outgoing token on the peer's node if receiving
/// packets from that peer
// TODO: The incoming token is used for both ILP over HTTP, and for authorizing actions from the HTTP API.
// Should we add 1 more token, for more granular permissioning?
pub ilp_over_http_incoming_token: Option<SecretString>,
/// The account's outgoing ILP over HTTP token
/// This must match the ILP over HTTP incoming token on the peer's node if receiving
/// packets from that peer
pub ilp_over_http_outgoing_token: Option<SecretString>,
pub ilp_over_btp_url: Option<String>,
pub ilp_over_btp_outgoing_token: Option<SecretString>,
/// The account's incoming ILP over BTP token.
pub ilp_over_btp_incoming_token: Option<SecretString>,
/// The threshold after which the balance service will trigger a settlement
#[serde(default, deserialize_with = "optional_number_or_string")]
pub settle_threshold: Option<i64>,
/// The amount which the balance service will attempt to settle down to
#[serde(default, deserialize_with = "optional_number_or_string")]
pub settle_to: Option<i64>,
/// The routing relation of the account
pub routing_relation: Option<String>,
/// The round trip time of the account (should be set depending on how
/// well the network connectivity of the account and the node is)
#[serde(default, deserialize_with = "optional_number_or_string")]
pub round_trip_time: Option<u32>,
/// The maximum amount the account can send per minute
#[serde(default, deserialize_with = "optional_number_or_string")]
pub amount_per_minute_limit: Option<u64>,
/// The limit of packets the account can send per minute
#[serde(default, deserialize_with = "optional_number_or_string")]
pub packets_per_minute_limit: Option<u32>,
/// The account's settlement engine URL. If a global engine url is configured
/// for the account's asset code, that will be used instead (even if the account is
/// configured with a specific one)
pub settlement_engine_url: Option<String>,
}

pub struct NodeApi<S, I, O, B, A: Account> {
store: S,
/// The admin's API token, used to make admin-only changes
// TODO: Make this a SecretString
admin_api_token: String,
default_spsp_account: Option<Username>,
incoming_handler: I,
Expand All @@ -194,6 +244,7 @@ pub struct NodeApi<S, I, O, B, A: Account> {
// The BTP service is included here so that we can add a new client
// connection when an account is added with BTP details
btp: BtpOutgoingService<B, A>,
/// Server secret used to instantiate SPSP/Stream connections
server_secret: Bytes,
node_version: Option<String>,
}
Expand Down Expand Up @@ -240,16 +291,19 @@ where
}
}

/// Sets the default SPSP account. This also enables the /.well_known/pay endpoint
pub fn default_spsp_account(&mut self, username: Username) -> &mut Self {
self.default_spsp_account = Some(username);
self
}

/// Sets the node version
pub fn node_version(&mut self, version: String) -> &mut Self {
self.node_version = Some(version);
self
}

/// Returns a Warp Filter which exposes the accounts and admin APIs
pub fn into_warp_filter(self) -> warp::filters::BoxedFilter<(impl warp::Reply,)> {
routes::accounts_api(
self.server_secret,
Expand All @@ -268,6 +322,7 @@ where
.boxed()
}

/// Serves the API at the provided address
pub async fn bind(self, addr: SocketAddr) {
warp::serve(self.into_warp_filter()).bind(addr).await
}
Expand Down
15 changes: 5 additions & 10 deletions crates/interledger-btp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,6 @@ use log::{debug, error, trace};
use rand::random;
use tokio_tungstenite::connect_async;
use tungstenite::Message;
use url::{ParseError, Url};

pub fn parse_btp_url(uri: &str) -> Result<Url, ParseError> {
let uri = if uri.starts_with("btp+") {
uri.split_at(4).1
} else {
uri
};
Url::parse(uri)
}

/// Create a BtpOutgoingService wrapping BTP connections to the accounts specified.
/// Calling `handle_incoming` with an `IncomingService` will turn the returned
Expand Down Expand Up @@ -46,6 +36,11 @@ where
Ok(service)
}

/// Initiates a BTP connection with the specified account and saves it to the list of connections
/// maintained by the provided service. This is done in the following steps:
/// 1. Initialize a WebSocket connection at the BTP account's URL
/// 2. Send a BTP authorization packet to the peer
/// 3. If successful, consider the BTP connection established and add it to the service
pub async fn connect_to_service_account<O, A>(
account: A,
error_on_unavailable: bool,
Expand Down
6 changes: 5 additions & 1 deletion crates/interledger-btp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ mod server;
mod service;
mod wrapped_ws;

pub use self::client::{connect_client, connect_to_service_account, parse_btp_url};
pub use self::client::{connect_client, connect_to_service_account};
pub use self::server::btp_service_as_filter; // This is consumed only by the node.
pub use self::service::{BtpOutgoingService, BtpService};

/// Extention trait for [Account](../interledger_service/trait.Account.html) with [ILP over BTP](https://interledger.org/rfcs/0023-bilateral-transfer-protocol/) related information
pub trait BtpAccount: Account {
/// Returns the BTP Websockets URL corresponding to this account
fn get_ilp_over_btp_url(&self) -> Option<&Url>;
/// Returns the BTP authentication token which is used when initiating a BTP connection
/// with a peer
fn get_ilp_over_btp_outgoing_token(&self) -> Option<&[u8]>;
}

Expand Down
16 changes: 6 additions & 10 deletions crates/interledger-btp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use super::{service::BtpOutgoingService, wrapped_ws::WsWrap};
use futures::{FutureExt, Sink, Stream};
use futures::{SinkExt, StreamExt, TryFutureExt};
use interledger_service::*;
use log::{debug, warn};
use log::{debug, error, warn};
use secrecy::{ExposeSecret, SecretString};
// use std::time::Duration;
use warp::{
Expand All @@ -17,14 +17,7 @@ use warp::{
// const WEBSOCKET_TIMEOUT: Duration = Duration::from_secs(10);
const MAX_MESSAGE_SIZE: usize = 40000;

/// Returns a BtpOutgoingService and a warp Filter.
///
/// The BtpOutgoingService wraps all BTP/WebSocket connections that come
/// in on the given address. Calling `handle_incoming` with an `IncomingService` will
/// turn the returned BtpOutgoingService into a bidirectional handler.
/// The separation is designed to enable the returned BtpOutgoingService to be passed
/// to another service like the Router, and _then_ for the Router to be passed as the
/// IncomingService to the BTP server.
/// Returns a Warp Filter instantiated for the provided BtpOutgoingService service.
///
/// The warp filter handles the websocket upgrades and adds incoming connections
/// to the BTP service so that it will handle each of the messages.
Expand Down Expand Up @@ -126,7 +119,10 @@ where
.to_bytes(),
);

let _ = connection.send(auth_response).await;
connection
.send(auth_response)
.map_err(|_| error!("warp::Error sending auth response"))
.await?;

Ok((account, connection))
}
Expand Down
26 changes: 16 additions & 10 deletions crates/interledger-btp/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ lazy_static! {
type IlpResultChannel = oneshot::Sender<Result<Fulfill, Reject>>;
type IncomingRequestBuffer<A> = UnboundedReceiver<(A, u32, Prepare)>;

/// A container for BTP/WebSocket connections that implements OutgoingService
/// for sending outgoing ILP Prepare packets over one of the connected BTP connections.
/// The BtpOutgoingService wraps all BTP/WebSocket connections that come
/// in on the given address. It implements OutgoingService for sending
/// outgoing ILP Prepare packets over one of the connected BTP connections.
/// Calling `handle_incoming` with an `IncomingService` will turn the returned
/// BtpOutgoingService into a bidirectional handler.
/// The separation is designed to enable the returned BtpOutgoingService to be passed
/// to another service like the Router, and _then_ for the Router to be passed as the
/// IncomingService to the BTP server.
#[derive(Clone)]
pub struct BtpOutgoingService<O, A: Account> {
ilp_address: Address,
Expand All @@ -47,14 +53,14 @@ pub struct BtpOutgoingService<O, A: Account> {
stream_valve: Arc<Valve>,
}

// Handle the packets based on whether they are an incoming request or a response to something we sent.
// a. If it's a prepare packet, it gets buffered in the incoming_sender channel which will get consumed
// once an incoming handler is added
// b. If it's a Fulfill/Reject packet, it gets added to the pending_outgoing hashmap which gets consumed
// by the outgoing service implementation immediately
// incoming_sender.unbounded_send basically sends data to the self.incoming_receiver
// to be consumed when we setup the incoming handler
// Set up a listener to handle incoming packets from the WebSocket connection
/// Handle the packets based on whether they are an incoming request or a response to something we sent.
/// a. If it's a prepare packet, it gets buffered in the incoming_sender channel which will get consumed
/// once an incoming handler is added
/// b. If it's a Fulfill/Reject packet, it gets added to the pending_outgoing hashmap which gets consumed
/// by the outgoing service implementation immediately
/// incoming_sender.unbounded_send basically sends data to the self.incoming_receiver
/// to be consumed when we setup the incoming handler
/// Set up a listener to handle incoming packets from the WebSocket connection
async fn handle_message<A: BtpAccount>(
message: Message,
tx_clone: UnboundedSender<Message>,
Expand Down
1 change: 0 additions & 1 deletion crates/interledger-ccp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ lazy_static = { version = "1.4.0", default-features = false }
log = { version = "0.4.8", default-features = false }
parking_lot = { version = "0.9.0", default-features = false }
ring = { version = "0.16.9", default-features = false }
tokio-executor = { version = "0.1.8", default-features = false }
uuid = { version = "0.8.1", default-features = false, features = ["v4"]}
serde = { version = "1.0.101", default-features = false, features = ["derive"] }
async-trait = "0.1.22"
Expand Down
Loading