You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In src/services/escrow.rs, EscrowService::create validates the escrow amount by parsing it as f64:
let amount:f64 = req
.amount.parse().map_err(|_| AppError::Validation("amount must be a valid decimal string".into()))?;if amount <= 0.0{returnErr(AppError::Validation("amount must be positive".into()));}
The amount is stored as a string (good — avoids float storage), but validation itself goes through f64, which cannot exactly represent most decimal fractions. This means:
A value like "0.1" or amounts with more than ~15-17 significant decimal digits can silently round during the <= 0.0 positivity check, in edge cases producing incorrect accept/reject decisions (e.g. a value that should be treated as zero/negative passing the check, or a tiny positive value being treated as zero due to float underflow after arithmetic elsewhere that reuses this pattern).
Stellar/Soroban amounts are fixed-point (7 decimal places for classic Stellar assets), so f64 parsing is the wrong validation primitive: it doesn't reject amounts with excess precision (e.g. "1.123456789" parses fine as f64 but is not a valid Stellar amount).
This is a money-transfer backend. Any amount-validation logic that doesn't use a fixed-point/decimal type is a correctness risk for a financial system — even rare rounding edge cases can mean an invalid amount is submitted to a Soroban contract or an escrow is created with an amount that doesn't match what the client believes was validated.
Proposed fix
Introduce a shared parse_stellar_amount(&str) -> AppResult<i128> (or use a decimal crate, e.g. rust_decimal) that parses to a fixed-point representation with at most 7 decimal places, matching Stellar's Int128Parts/i64 stroop representation, and rejects excess precision explicitly.
Replace the f64 parse/compare in EscrowService::create and BatchPaymentService::execute_batch with this shared validator.
Add unit tests for boundary cases: "0.0000001" (min valid stroop), "0.00000001" (too precise, should reject), "0", "-1", huge amounts near i64::MAX stroops, and strings with locale-style separators ("1,000") which should be rejected, not silently parsed as 1.0.
Additional Notes
Edge cases
Scientific notation strings ("1e10") parse fine as f64 but are almost certainly not intended as valid user input — a decimal-based validator should reject them outright.
NaN/Infinity: "nan".parse::<f64>() succeeds and produces f64::NAN, which fails <= 0.0 (false) and would incorrectly pass validation as "positive". This is a real, exploitable gap: submitting amount: "nan" currently bypasses the positivity check.
Locale formatting (thousands separators, comma decimal points) should be explicitly rejected rather than silently mis-parsed.
Testing strategy
Property-based test (e.g. proptest) generating random decimal strings and asserting the validator never accepts values with >7 fractional digits or non-finite results.
Regression test specifically for the "nan"/"inf" bypass described above.
Cross-references
Same pattern in src/services/batch.rs (BatchPaymentService::execute_batch) — see the batch payment crash-consistency issue filed alongside this one for related durability concerns in that file.
Problem
In
src/services/escrow.rs,EscrowService::createvalidates the escrow amount by parsing it asf64:The amount is stored as a string (good — avoids float storage), but validation itself goes through
f64, which cannot exactly represent most decimal fractions. This means:"0.1"or amounts with more than ~15-17 significant decimal digits can silently round during the<= 0.0positivity check, in edge cases producing incorrect accept/reject decisions (e.g. a value that should be treated as zero/negative passing the check, or a tiny positive value being treated as zero due to float underflow after arithmetic elsewhere that reuses this pattern)."1.123456789"parses fine as f64 but is not a valid Stellar amount).src/services/batch.rs::execute_batch(let amount: f64 = leg.amount.parse()...), so a fix here should be applied consistently across both call sites (see EscrowService uses f64 for amount validation, risking precision loss on financial values #29, filed alongside this one, for the batch-specific consistency issue).Why it matters
This is a money-transfer backend. Any amount-validation logic that doesn't use a fixed-point/decimal type is a correctness risk for a financial system — even rare rounding edge cases can mean an invalid amount is submitted to a Soroban contract or an escrow is created with an amount that doesn't match what the client believes was validated.
Proposed fix
parse_stellar_amount(&str) -> AppResult<i128>(or use a decimal crate, e.g.rust_decimal) that parses to a fixed-point representation with at most 7 decimal places, matching Stellar'sInt128Parts/i64stroop representation, and rejects excess precision explicitly.f64parse/compare inEscrowService::createandBatchPaymentService::execute_batchwith this shared validator."0.0000001"(min valid stroop),"0.00000001"(too precise, should reject),"0","-1", huge amounts near i64::MAX stroops, and strings with locale-style separators ("1,000") which should be rejected, not silently parsed as1.0.Additional Notes
Edge cases
"1e10") parse fine as f64 but are almost certainly not intended as valid user input — a decimal-based validator should reject them outright."nan".parse::<f64>()succeeds and producesf64::NAN, which fails<= 0.0(false) and would incorrectly pass validation as "positive". This is a real, exploitable gap: submittingamount: "nan"currently bypasses the positivity check.Testing strategy
proptest) generating random decimal strings and asserting the validator never accepts values with >7 fractional digits or non-finite results."nan"/"inf"bypass described above.Cross-references
src/services/batch.rs(BatchPaymentService::execute_batch) — see the batch payment crash-consistency issue filed alongside this one for related durability concerns in that file.