Skip to content

Commit 5d01f60

Browse files
committed
docs(settlement): extend settlement docs
1 parent 1e390f4 commit 5d01f60

File tree

11 files changed

+160
-26
lines changed

11 files changed

+160
-26
lines changed

crates/interledger-settlement/Cargo.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ tokio-retry = { version = "0.2.0", default-features = false }
2626
tokio = { version = "0.2.6", default-features = false, features = ["macros", "rt-core"] }
2727
num-bigint = { version = "0.2.3", default-features = false, features = ["std"] }
2828
num-traits = { version = "0.2.8", default-features = false }
29-
# warp = { version = "0.1.20", default-features = false }
30-
warp = { git = "https://github.com/seanmonstar/warp", default-features = false }
29+
warp = { version = "0.2", default-features = false }
3130
http = "0.2.0"
32-
# redis_crate = { package = "redis", version = "0.13.0", optional = true }
33-
redis_crate = { package = "redis", git = "https://github.com/mitsuhiko/redis-rs", optional = true, features = ["tokio-rt-core"] }
31+
redis_crate = { package = "redis", version = "0.15.1", optional = true, features = ["tokio-rt-core"] }
3432
async-trait = "0.1.22"
3533

3634
[dev-dependencies]

crates/interledger-settlement/src/api/client.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,27 @@ use reqwest::Client;
66
use serde_json::json;
77
use uuid::Uuid;
88

9+
/// Helper struct to execute settlements
910
#[derive(Clone)]
1011
pub struct SettlementClient {
12+
/// Asynchronous reqwest client
1113
http_client: Client,
1214
}
1315

1416
impl SettlementClient {
17+
/// Simple constructor
1518
pub fn new() -> Self {
1619
SettlementClient {
1720
http_client: Client::new(),
1821
}
1922
}
2023

24+
/// Sends a settlement request to the node's settlement engine for the provided account and amount
25+
///
26+
/// # Errors
27+
/// 1. Account has no engine configured
28+
/// 1. HTTP request to engine failed from node side
29+
/// 1. HTTP response from engine was an error
2130
pub async fn send_settlement<A: SettlementAccount + Account>(
2231
&self,
2332
account: A,

crates/interledger-settlement/src/api/message_service.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ use tokio_retry::{strategy::ExponentialBackoff, Retry};
1010

1111
const PEER_FULFILLMENT: [u8; 32] = [0; 32];
1212

13+
/// Service which implements [`IncomingService`](../../interledger_service/trait.IncomingService.html).
14+
/// Responsible for catching incoming requests which are sent to `peer.settle` and forward them to
15+
/// the node's settlement engine via HTTP
1316
#[derive(Clone)]
1417
pub struct SettlementMessageService<I, A> {
18+
/// The next incoming service which requests that don't get caught get sent to
1519
next: I,
20+
/// HTTP client used to notify the engine corresponding to the account about
21+
/// an incoming message from a peer's engine
1622
http_client: Client,
1723
account_type: PhantomData<A>,
1824
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
/// Settlement-related client methods
12
mod client;
3+
/// [`IncomingService`](../../interledger_service/trait.IncomingService.html) which catches
4+
/// incoming requests which are sent to `peer.settle` (the node's settlement engine ILP address)
25
mod message_service;
6+
/// The Warp API exposed by the connector
37
mod node_api;
48

59
#[cfg(test)]
610
mod fixtures;
711
#[cfg(test)]
812
mod test_helpers;
913

10-
// Expose the API creation filter method and the necessary services
1114
pub use client::SettlementClient;
1215
pub use message_service::SettlementMessageService;
1316
pub use node_api::create_settlements_filter;

crates/interledger-settlement/src/api/node_api.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ use std::{
2323
use uuid::Uuid;
2424
use warp::{self, reject::Rejection, Filter};
2525

26+
/// Prepare packet's execution condition as defined in the [RFC](https://interledger.org/rfcs/0038-settlement-engines/#exchanging-messages)
2627
static PEER_PROTOCOL_CONDITION: [u8; 32] = [
2728
102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32, 8, 151, 20, 133,
2829
110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41, 37,
2930
];
3031

32+
/// Makes an idempotent call to [`do_receive_settlement`](./fn.do_receive_settlement.html)
33+
/// Returns Status Code 201
3134
async fn receive_settlement<S, A>(
3235
account_id: String,
3336
idempotency_key: Option<String>,
@@ -65,6 +68,8 @@ where
6568
.unwrap())
6669
}
6770

71+
/// Makes an idempotent call to [`do_send_outgoing_message`](./fn.do_send_outgoing_message.html)
72+
/// Returns Status Code 201
6873
async fn send_message<S, A, O>(
6974
account_id: String,
7075
idempotency_key: Option<String>,
@@ -103,6 +108,11 @@ where
103108
.unwrap())
104109
}
105110

111+
/// Returns a Node Settlement filter which exposes a Warp-compatible
112+
/// idempotent API which
113+
/// 1. receives messages about incoming settlements from the engine
114+
/// 1. sends messages from the connector's engine to the peer's
115+
/// message service which are sent to the peer's engine
106116
pub fn create_settlements_filter<S, O, A>(
107117
store: S,
108118
outgoing_handler: O,
@@ -153,6 +163,10 @@ where
153163
.boxed()
154164
}
155165

166+
/// Receives a settlement message from the connector's engine, proceeds to scale it to the
167+
/// asset scale which corresponds to the account, and finally increases the account's balance
168+
/// by the processed amount. This implements the main functionality by which an account's credit
169+
/// is repaid, allowing them to send out more payments
156170
async fn do_receive_settlement<S, A>(
157171
store: S,
158172
account_id: String,
@@ -270,6 +284,10 @@ where
270284
Ok(ApiResponse::Default)
271285
}
272286

287+
/// Sends a messages via the provided `outgoing_handler` with the `peer.settle`
288+
/// ILP Address as ultimate destination. This messages should get caught by the
289+
/// peer's message service, get forwarded to their engine, and then the response
290+
/// should be communicated back via a Fulfill or Reject packet
273291
async fn do_send_outgoing_message<S, O, A>(
274292
store: S,
275293
outgoing_handler: O,
@@ -337,7 +355,7 @@ where
337355
match packet {
338356
Ok(fulfill) => {
339357
// TODO: Can we avoid copying here?
340-
let data = Bytes::copy_from_slice(fulfill.as_ref());
358+
let data = Bytes::copy_from_slice(fulfill.data());
341359
Ok(ApiResponse::Data(data))
342360
}
343361
Err(reject) => {

crates/interledger-settlement/src/core/backends_common/redis/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,26 @@ use async_trait::async_trait;
2121
#[cfg(test)]
2222
mod test_helpers;
2323

24+
/// Domain separator for leftover amounts
2425
static UNCREDITED_AMOUNT_KEY: &str = "uncredited_engine_settlement_amount";
26+
27+
/// Helper function to get a redis key
2528
fn uncredited_amount_key(account_id: &str) -> String {
2629
format!("{}:{}", UNCREDITED_AMOUNT_KEY, account_id)
2730
}
2831

32+
/// Builder object to create a Redis connection for the engine
2933
pub struct EngineRedisStoreBuilder {
3034
redis_url: ConnectionInfo,
3135
}
3236

3337
impl EngineRedisStoreBuilder {
38+
/// Simple constructor
3439
pub fn new(redis_url: ConnectionInfo) -> Self {
3540
EngineRedisStoreBuilder { redis_url }
3641
}
3742

43+
/// Connects to the provided redis_url and returns a Redis connection for the Settlement Engine
3844
pub async fn connect(&self) -> Result<EngineRedisStore, ()> {
3945
let client = match Client::open(self.redis_url.clone()) {
4046
Ok(c) => c,
@@ -133,6 +139,7 @@ impl IdempotentStore for EngineRedisStore {
133139
}
134140
}
135141

142+
/// Helper datatype for storing and loading quantities of a number with different scales
136143
#[derive(Debug, Clone)]
137144
struct AmountWithScale {
138145
num: BigUint,
@@ -152,11 +159,11 @@ impl ToRedisArgs for AmountWithScale {
152159
}
153160

154161
impl AmountWithScale {
162+
/// Iterates over all values because in this case it's making
163+
/// an lrange call. This returns all the tuple elements in 1 array, and
164+
/// it cannot differentiate between 1 AmountWithScale value or multiple
165+
/// ones. This looks like a limitation of redis.rs
155166
fn parse_multi_values(items: &[Value]) -> Option<Self> {
156-
// We have to iterate over all values because in this case we're making
157-
// an lrange call. This returns all the tuple elements in 1 array, and
158-
// it cannot differentiate between 1 AmountWithScale value or multiple
159-
// ones. This looks like a limitation of redis.rs
160167
let len = items.len();
161168
let mut iter = items.iter();
162169

crates/interledger-settlement/src/core/engines_api.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub struct CreateAccount {
1919
id: String,
2020
}
2121

22+
/// Makes an idempotent call to [`engine.create_account`](../types/trait.SettlementEngine.html#tymethod.create_account)
23+
/// Returns `Status Code 201`
2224
async fn create_engine_account<E, S>(
2325
idempotency_key: Option<String>,
2426
account_id: CreateAccount,
@@ -47,6 +49,8 @@ where
4749
.unwrap())
4850
}
4951

52+
/// Makes an idempotent call to [`engine.delete_account`](../types/trait.SettlementEngine.html#tymethod.delete_account)
53+
/// Returns Status Code `204`.
5054
async fn delete_engine_account<E, S>(
5155
account_id: String,
5256
idempotency_key: Option<String>,
@@ -75,6 +79,8 @@ where
7579
.unwrap())
7680
}
7781

82+
/// Makes an idempotent call to [`engine.send_money`](../types/trait.SettlementEngine.html#tymethod.send_money)
83+
/// Returns Status Code `201`
7884
async fn engine_send_money<E, S>(
7985
id: String,
8086
idempotency_key: Option<String>,
@@ -105,6 +111,8 @@ where
105111
.unwrap())
106112
}
107113

114+
/// Makes an idempotent call to [`engine.receive_message`](../types/trait.SettlementEngine.html#tymethod.receive_message)
115+
/// Returns Status Code `201`
108116
async fn engine_receive_message<E, S>(
109117
id: String,
110118
idempotency_key: Option<String>,

crates/interledger-settlement/src/core/idempotency.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ use http::StatusCode;
77
use interledger_http::error::*;
88
use log::error;
99

10+
/// Data stored for the idempotency features
1011
#[derive(Debug, Clone, PartialEq)]
1112
pub struct IdempotentData {
13+
/// The HTTP Status Code of the API's response
1214
pub status: StatusCode,
15+
/// The body of the API's response
1316
pub body: Bytes,
17+
/// The hash of the serialized input parameters which generated the API's response
1418
pub input_hash: [u8; 32],
1519
}
1620

1721
impl IdempotentData {
22+
/// Simple constructor
1823
pub fn new(status: StatusCode, body: Bytes, input_hash: [u8; 32]) -> Self {
1924
Self {
2025
status,
@@ -24,6 +29,7 @@ impl IdempotentData {
2429
}
2530
}
2631

32+
/// Store trait which should be implemented for idempotency related features
2733
#[async_trait]
2834
pub trait IdempotentStore {
2935
/// Returns the API response that was saved when the idempotency key was used
@@ -34,7 +40,8 @@ pub trait IdempotentStore {
3440
) -> Result<Option<IdempotentData>, ()>;
3541

3642
/// Saves the data that was passed along with the api request for later
37-
/// The store MUST also save a hash of the input, so that it errors out on requests
43+
/// The store also saves the hash of the input, so that it errors out on requests
44+
/// with conflicting input hashes for the same idempotency key
3845
async fn save_idempotent_data(
3946
&self,
4047
idempotency_key: String,
@@ -44,9 +51,9 @@ pub trait IdempotentStore {
4451
) -> Result<(), ()>;
4552
}
4653

47-
// Helper function that returns any idempotent data that corresponds to a
48-
// provided idempotency key. It fails if the hash of the input that
49-
// generated the idempotent data does not match the hash of the provided input.
54+
/// Helper function that returns any idempotent data that corresponds to a
55+
/// provided idempotency key. It fails if the hash of the input that
56+
/// generated the idempotent data does not match the hash of the provided input.
5057
async fn check_idempotency<S>(
5158
store: S,
5259
idempotency_key: String,
@@ -136,11 +143,12 @@ where
136143
}
137144
};
138145

139-
// NOTE: This is bytes 0.4.12. API Response is defined over it.
140146
let data = match ret {
141147
ApiResponse::Default => default_return_value,
142148
ApiResponse::Data(d) => d,
143149
};
150+
// TODO refactor for readability, can unify the 2 idempotency calls, the error and the data
151+
// are both Bytes
144152
store
145153
.save_idempotent_data(
146154
idempotency_key,

crates/interledger-settlement/src/core/mod.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
/// Only exported if the `backends_common` feature flag is enabled
33
#[cfg(feature = "backends_common")]
44
pub mod backends_common;
5-
/// The REST API for the settlement engines
5+
6+
/// Web service which exposes settlement related endpoints as described in the [RFC](https://interledger.org/rfcs/0038-settlement-engines/),
7+
/// All endpoints are idempotent.
68
pub mod engines_api;
9+
710
/// Expose useful utilities for implementing idempotent functionalities
811
pub mod idempotency;
12+
913
/// Expose useful traits
1014
pub mod types;
1115

@@ -14,6 +18,27 @@ use num_traits::Zero;
1418
use ring::digest::{digest, SHA256};
1519
use types::{Convert, ConvertDetails};
1620

21+
/// Converts a number from a precision to another while taking precision loss into account
22+
///
23+
/// # Examples
24+
/// ```rust
25+
/// # use num_bigint::BigUint;
26+
/// # use interledger_settlement::core::scale_with_precision_loss;
27+
/// assert_eq!(
28+
/// scale_with_precision_loss(BigUint::from(905u32), 9, 11),
29+
/// (BigUint::from(9u32), BigUint::from(5u32))
30+
/// );
31+
///
32+
/// assert_eq!(
33+
/// scale_with_precision_loss(BigUint::from(8053u32), 9, 12),
34+
/// (BigUint::from(8u32), BigUint::from(53u32))
35+
/// );
36+
///
37+
/// assert_eq!(
38+
/// scale_with_precision_loss(BigUint::from(1u32), 9, 6),
39+
/// (BigUint::from(1000u32), BigUint::from(0u32))
40+
/// );
41+
/// ```
1742
pub fn scale_with_precision_loss(
1843
amount: BigUint,
1944
local_scale: u8,
@@ -49,9 +74,7 @@ pub fn scale_with_precision_loss(
4974
}
5075
}
5176

52-
// Helper function that returns any idempotent data that corresponds to a
53-
// provided idempotency key. It fails if the hash of the input that
54-
// generated the idempotent data does not match the hash of the provided input.
77+
/// Returns the 32-bytes SHA256 hash of the provided preimage
5578
pub fn get_hash_of(preimage: &[u8]) -> [u8; 32] {
5679
let mut hash = [0; 32];
5780
hash.copy_from_slice(digest(&SHA256, preimage).as_ref());

0 commit comments

Comments
 (0)