Skip to content

Commit 754c764

Browse files
committed
Merge branch 'gakonst-asset-scale' into gakonst-precision-loss
2 parents 85a07d8 + d227963 commit 754c764

File tree

4 files changed

+196
-72
lines changed

4 files changed

+196
-72
lines changed

crates/interledger-service-util/src/exchange_rates_service.rs

+43-8
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,52 @@ where
9090
.build()));
9191
};
9292

93-
// Note that this is not an overflow safe operation. Given realistic
94-
// assumptions, the asset scale will be <=18 and >=1.
95-
// It is doubtful that the exchange rate between two assets,
96-
// multiplied by 18 would exceed std::f64::MAX
97-
let scaled_rate = rate.normalize_scale(ConvertDetails {
93+
// Can we overflow here?
94+
let outgoing_amount = (request.prepare.amount() as f64) * rate;
95+
let outgoing_amount = outgoing_amount.normalize_scale(ConvertDetails {
9896
from: request.from.asset_scale(),
9997
to: request.to.asset_scale(),
10098
});
101-
let outgoing_amount = (request.prepare.amount() as f64) * scaled_rate;
102-
request.prepare.set_amount(outgoing_amount as u64);
103-
trace!("Converted incoming amount of: {} {} (scale {}) from account {} to outgoing amount of: {} {} (scale {}) for account {}", request.original_amount, request.from.asset_code(), request.from.asset_scale(), request.from.id(), outgoing_amount, request.to.asset_code(), request.to.asset_scale(), request.to.id());
99+
100+
match outgoing_amount {
101+
Ok(outgoing_amount) => {
102+
// f64 which cannot fit in u64 gets cast as 0
103+
if outgoing_amount != 0.0 && outgoing_amount as u64 == 0 {
104+
return Box::new(err(RejectBuilder {
105+
code: ErrorCode::F08_AMOUNT_TOO_LARGE,
106+
message: format!(
107+
"Could not cast outgoing amount to u64 {}",
108+
outgoing_amount,
109+
)
110+
.as_bytes(),
111+
triggered_by: Some(&self.ilp_address),
112+
data: &[],
113+
}
114+
.build()));
115+
}
116+
request.prepare.set_amount(outgoing_amount as u64);
117+
trace!("Converted incoming amount of: {} {} (scale {}) from account {} to outgoing amount of: {} {} (scale {}) for account {}",
118+
request.original_amount, request.from.asset_code(), request.from.asset_scale(), request.from.id(),
119+
outgoing_amount, request.to.asset_code(), request.to.asset_scale(), request.to.id());
120+
}
121+
Err(_) => {
122+
return Box::new(err(RejectBuilder {
123+
code: ErrorCode::F08_AMOUNT_TOO_LARGE,
124+
message: format!(
125+
"Could not convert exchange rate from {}:{} to: {}:{}. Got incoming amount: {}",
126+
request.from.asset_code(),
127+
request.from.asset_scale(),
128+
request.to.asset_code(),
129+
request.to.asset_scale(),
130+
request.prepare.amount(),
131+
)
132+
.as_bytes(),
133+
triggered_by: Some(&self.ilp_address),
134+
data: &[],
135+
}
136+
.build()));
137+
}
138+
}
104139
}
105140

106141
Box::new(self.next.send_request(request))

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

+16-8
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ where
820820
let engine_scale = self.asset_scale;
821821
Box::new(
822822
result(BigUint::from_str(&body.amount).map_err(move |err| {
823-
let error_msg = format!("Error converting to U256 {:?}", err);
823+
let error_msg = format!("Error converting to BigUint {:?}", err);
824824
error!("{:?}", error_msg);
825825
(StatusCode::from_u16(400).unwrap(), error_msg)
826826
}))
@@ -832,13 +832,21 @@ where
832832
to: engine_scale,
833833
});
834834

835-
// Typecast from num_bigint::BigUInt because we're using
836-
// ethereum_types::U256 for all rust-web3 related functionality
837-
result(U256::from_dec_str(&amount.to_string()).map_err(move |err| {
838-
let error_msg = format!("Error converting to U256 {:?}", err);
839-
error!("{:?}", error_msg);
840-
(StatusCode::from_u16(400).unwrap(), error_msg)
841-
}))
835+
result(amount)
836+
.map_err(move |err| {
837+
let error_msg = format!("Error scaling amount: {:?}", err);
838+
error!("{:?}", error_msg);
839+
(StatusCode::from_u16(400).unwrap(), error_msg)
840+
})
841+
.and_then(move |amount| {
842+
// Typecast from num_bigint::BigUInt because we're using
843+
// ethereum_types::U256 for all rust-web3 related functionality
844+
result(U256::from_dec_str(&amount.to_string()).map_err(move |err| {
845+
let error_msg = format!("Error converting to U256 {:?}", err);
846+
error!("{:?}", error_msg);
847+
(StatusCode::from_u16(400).unwrap(), error_msg)
848+
}))
849+
})
842850
})
843851
.and_then(move |amount| {
844852
self_clone

crates/interledger-settlement/src/api.rs

+21-13
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,30 @@ impl_web! {
191191
from: engine_scale,
192192
to: account.asset_scale(),
193193
});
194-
// If we'd overflow, settle for the maximum u64 value
195-
let safe_amount = if let Some(amount_u64) = amount.to_u64() {
196-
amount_u64
197-
} else {
198-
debug!("Amount settled from engine overflowed during conversion to connector scale: {}. Settling for u64::MAX", amount);
199-
std::u64::MAX
200-
};
201-
store.update_balance_for_incoming_settlement(account_id, safe_amount, idempotency_key)
194+
result(amount.clone())
202195
.map_err(move |_| {
203-
let error_msg = format!("Error updating balance of account: {} for incoming settlement of amount: {}", account_id, amount);
196+
let error_msg = format!("Could not convert amount: {:?}", amount);
204197
error!("{}", error_msg);
205198
(StatusCode::from_u16(500).unwrap(), error_msg)
206199
})
207-
.and_then(move |_| {
208-
let ret = json!(Quantity::new(safe_amount, account.asset_scale()));
209-
Ok((StatusCode::OK, ret.to_string().into()))
200+
.and_then(move |amount| {
201+
// If we'd overflow, settle for the maximum u64 value
202+
let amount = if let Some(amount_u64) = amount.to_u64() {
203+
amount_u64
204+
} else {
205+
debug!("Amount settled from engine overflowed during conversion to connector scale: {:?}. Settling for u64::MAX", amount);
206+
std::u64::MAX
207+
};
208+
store.update_balance_for_incoming_settlement(account_id, amount, idempotency_key)
209+
.map_err(move |_| {
210+
let error_msg = format!("Error updating balance of account: {} for incoming settlement of amount: {}", account_id, amount);
211+
error!("{}", error_msg);
212+
(StatusCode::from_u16(500).unwrap(), error_msg)
213+
})
214+
.and_then(move |_| {
215+
let ret = json!(Quantity::new(amount, account.asset_scale()));
216+
Ok((StatusCode::OK, ret.to_string().into()))
217+
})
210218
})
211219
})
212220
}))
@@ -346,7 +354,7 @@ mod tests {
346354
let q: Quantity = serde_json::from_slice(ret.body()).unwrap();
347355
assert_eq!(ret.status(), 200);
348356
assert_eq!(q.amount, 1.to_string());
349-
let leftovers = 123 - 1.normalize_scale(ConvertDetails { from: 9, to: 11 });
357+
let leftovers = 123 - 1u64.normalize_scale(ConvertDetails { from: 9, to: 11 }).unwrap();
350358
assert_eq!(leftovers, 23);
351359
assert_eq!(q.scale, 9);
352360
}

0 commit comments

Comments
 (0)