Correct input/output selection

Payments weren't properly selected, as it'd drain a sequential series 
instead of the specified set, and inputs had a memory condition Rust 
couldn't prove was safe.
This commit is contained in:
Luke Parker 2022-06-09 04:34:15 -04:00
parent d611300adb
commit 75fb9b3198
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6

View file

@ -207,20 +207,23 @@ impl<D: CoinDb, C: Coin> Wallet<D, C> {
while outputs.len() != 0 { while outputs.len() != 0 {
// Select the maximum amount of outputs possible // Select the maximum amount of outputs possible
let mut inputs = &outputs[0 .. C::MAX_INPUTS.min(outputs.len())]; let mut input_bound = C::MAX_INPUTS.min(outputs.len());
// Calculate their sum value, minus the fee needed to spend them // Calculate their sum value, minus the fee needed to spend them
let mut sum = inputs.iter().map(|input| input.amount()).sum::<u64>(); let mut sum = outputs[0 .. input_bound].iter().map(|input| input.amount()).sum::<u64>();
// sum -= C::MAX_FEE; // TODO // sum -= C::MAX_FEE; // TODO
// Grab the payments this will successfully fund // Grab the payments this will successfully fund
let mut these_payments = vec![]; let mut these_payments = vec![];
for payment in &payments { let mut p = 0;
if sum > payment.1 { while p < payments.len() {
these_payments.push(payment); if sum >= payments[p].1 {
sum -= payment.1; sum -= payments[p].1;
} these_payments.push(payments.remove(p));
} else {
// Doesn't break in this else case as a smaller payment may still fit // Doesn't break in this else case as a smaller payment may still fit
p += 1;
}
} }
// Move to the next set of keys if none of these outputs remain significant // Move to the next set of keys if none of these outputs remain significant
@ -228,16 +231,17 @@ impl<D: CoinDb, C: Coin> Wallet<D, C> {
break; break;
} }
// Drop any uneeded outputs // Drop any uneeded inputs
while sum > inputs[inputs.len() - 1].amount() { while sum > outputs[input_bound - 1].amount() {
sum -= inputs[inputs.len() - 1].amount(); sum -= outputs[input_bound - 1].amount();
inputs = &inputs[.. (inputs.len() - 1)]; input_bound -= 1;
} }
// TODO: Replace any high value inputs with low value inputs, if we can
// We now have a minimal effective outputs/payments set // We now have a minimal effective outputs/payments set
// Take ownership while removing these candidates from the provided list // Take ownership while removing these candidates from the provided list
let inputs = outputs.drain(.. inputs.len()).collect(); let inputs = outputs.drain(.. input_bound).collect();
let payments = payments.drain(.. these_payments.len()).collect::<Vec<_>>();
let mut transcript = Transcript::new(b"Serai Processor Wallet Send"); let mut transcript = Transcript::new(b"Serai Processor Wallet Send");
transcript.append_message( transcript.append_message(
@ -257,7 +261,7 @@ impl<D: CoinDb, C: Coin> Wallet<D, C> {
transcript, transcript,
acknowledged_height, acknowledged_height,
inputs, inputs,
&payments &these_payments
).await?; ).await?;
// self.db.save_tx(tx) // TODO // self.db.save_tx(tx) // TODO
txs.push(tx); txs.push(tx);