Skip to content

Commit e892efa

Browse files
committed
feature(engines): Add trait for saving leftover values
Implement the trait for the eth engine
1 parent 6cf0782 commit e892efa

File tree

5 files changed

+104
-5
lines changed

5 files changed

+104
-5
lines changed

crates/interledger-settlement-engines/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ clap = "2.32.0"
3535
clarity = { git = "https://github.com/gakonst/clarity" }
3636
sha3 = "0.8.2"
3737
num-bigint = "0.2.2"
38+
num-traits = "0.2.8"
3839

3940
[dev-dependencies]
4041
lazy_static = "1.3"

crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use hyper::StatusCode;
1717
use interledger_store_redis::RedisStoreBuilder;
1818
use log::info;
1919
use num_bigint::BigUint;
20+
use num_traits::Zero;
2021
use redis::IntoConnectionInfo;
2122
use reqwest::r#async::{Client, Response as HttpResponse};
2223
use ring::{digest, hmac};
@@ -489,7 +490,11 @@ where
489490
.push("settlements");
490491
let client = Client::new();
491492
debug!("Making POST to {:?} {:?} about {:?}", url, amount, tx_hash);
493+
let self_clone = self.clone();
492494
let action = move || {
495+
// need to make 2 clones, one to own the variables in the function
496+
// and one for the retry closure..
497+
let self_clone = self_clone.clone();
493498
let account_id = account_id.clone();
494499
client
495500
.post(url.clone())
@@ -504,11 +509,7 @@ where
504509
})
505510
.and_then(move |response| {
506511
trace!("Accounting system responded with {:?}", response);
507-
if response.status().is_success() {
508-
Ok(())
509-
} else {
510-
Err(())
511-
}
512+
self_clone.process_connector_response(account_id_clone2, response, amount)
512513
})
513514
};
514515
Retry::spawn(
@@ -520,6 +521,68 @@ where
520521
})
521522
}
522523

524+
fn process_connector_response(
525+
&self,
526+
account_id: String,
527+
response: HttpResponse,
528+
amount: U256,
529+
) -> impl Future<Item = (), Error = ()> {
530+
let engine_scale = self.asset_scale;
531+
let store = self.store.clone();
532+
if !response.status().is_success() {
533+
return Either::A(err(()));
534+
}
535+
Either::B(
536+
response
537+
.into_body()
538+
.concat2()
539+
.map_err(|err| {
540+
let err = format!("Couldn't retrieve body {:?}", err);
541+
error!("{}", err);
542+
})
543+
.and_then(move |body| {
544+
// Get the amount stored by the connector and
545+
// check for any precision loss / overflow
546+
serde_json::from_slice::<Quantity>(&body).map_err(|err| {
547+
let err = format!("Couldn't parse body {:?} into Quantity {:?}", body, err);
548+
error!("{}", err);
549+
})
550+
})
551+
.and_then(move |quantity: Quantity| {
552+
// Convert both to BigUInts so we can do arithmetic
553+
join_all(vec![
554+
result(BigUint::from_str(&quantity.amount)),
555+
result(BigUint::from_str(&amount.to_string())),
556+
])
557+
.map_err(|err| {
558+
let error_msg = format!("Error converting to BigUints {:?}", err);
559+
error!("{:?}", error_msg);
560+
})
561+
.and_then(move |amounts: Vec<BigUint>| {
562+
let connector_amount = amounts[0].clone();
563+
let engine_amount = amounts[1].clone();
564+
// Scale the amount settled by the
565+
// connector back up to our scale
566+
result(connector_amount.normalize_scale(ConvertDetails {
567+
from: quantity.scale,
568+
to: engine_scale,
569+
}))
570+
.and_then(move |scaled_connector_amount| {
571+
let diff: BigUint = engine_amount - scaled_connector_amount;
572+
if diff > Zero::zero() {
573+
// connector settled less than we
574+
// instructed it to, so we must save
575+
// the difference for the leftovers
576+
Either::A(store.save_leftovers(account_id, diff))
577+
} else {
578+
Either::B(ok(()))
579+
}
580+
})
581+
})
582+
}),
583+
)
584+
}
585+
523586
/// Helper function which submits an Ethereum ledger transaction to `to` for `amount`.
524587
/// If called with `token_address`, it makes an ERC20 transaction instead.
525588
/// Due to the lack of an API to create and sign the transaction

crates/interledger-settlement-engines/src/engines/ethereum_ledger/test_helpers.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@ pub struct TestStore {
6161
pub last_observed_block: Arc<RwLock<U256>>,
6262
pub saved_hashes: Arc<RwLock<HashMap<H256, bool>>>,
6363
pub cache_hits: Arc<RwLock<u64>>,
64+
pub leftovers: Arc<RwLock<HashMap<String, String>>>,
65+
}
66+
67+
use crate::stores::LeftoversStore;
68+
use num_bigint::BigUint;
69+
70+
impl LeftoversStore for TestStore {
71+
fn save_leftovers(
72+
&self,
73+
account_id: String,
74+
leftovers: BigUint,
75+
) -> Box<Future<Item = (), Error = ()> + Send> {
76+
let mut guard = self.leftovers.write();
77+
(*guard).insert(account_id, leftovers.to_string());
78+
Box::new(ok(()))
79+
}
6480
}
6581

6682
impl EthereumStore for TestStore {

crates/interledger-settlement-engines/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![type_length_limit = "1739269"]
2+
13
use clap::{value_t, App, Arg, SubCommand};
24
use hex;
35
use std::str::FromStr;

crates/interledger-settlement-engines/src/stores/redis_ethereum_ledger/store.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ impl EthereumLedgerRedisStore {
105105
}
106106
}
107107

108+
impl LeftoversStore for EthereumLedgerRedisStore {
109+
fn save_leftovers(
110+
&self,
111+
account_id: String,
112+
leftovers: BigUint,
113+
) -> Box<dyn Future<Item = (), Error = ()> + Send> {
114+
let mut pipe = redis::pipe();
115+
pipe.set(format!("leftovers:{}", account_id), leftovers.to_string())
116+
.ignore();
117+
Box::new(
118+
pipe.query_async(self.connection.clone())
119+
.map_err(move |err| error!("Error saving leftovers {:?}: {:?}", leftovers, err))
120+
.and_then(move |(_conn, _ret): (_, Value)| Ok(())),
121+
)
122+
}
123+
}
124+
108125
impl EthereumStore for EthereumLedgerRedisStore {
109126
type Account = Account;
110127

0 commit comments

Comments
 (0)