Skip to content

Commit 5328a28

Browse files
authored
Merge pull request #253 from Emurgo/ruslan/coin-selection-fix-using-all-inputs
RandomImprove : panicked at 'cannot sample empty range'
2 parents e875161 + fbe4b9a commit 5328a28

File tree

1 file changed

+43
-16
lines changed

1 file changed

+43
-16
lines changed

rust/src/tx_builder.rs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -220,21 +220,23 @@ impl TransactionBuilder {
220220
associated_inputs.entry(output.clone()).or_default().push(input);
221221
}
222222
}
223-
// Phase 2: Improvement
224-
for output in outputs.iter_mut() {
225-
let associated = associated_inputs.get_mut(output).unwrap();
226-
for input in associated.iter_mut() {
227-
let random_index = rng.gen_range(0..available_inputs.len());
228-
let new_input = available_inputs.get_mut(random_index).unwrap();
229-
let cur = from_bignum(&input.output.amount.coin);
230-
let new = from_bignum(&new_input.output.amount.coin);
231-
let min = from_bignum(&output.amount.coin);
232-
let ideal = 2 * min;
233-
let max = 3 * min;
234-
let move_closer = (ideal as i128 - new as i128).abs() < (ideal as i128 - cur as i128).abs();
235-
let not_exceed_max = new < max;
236-
if move_closer && not_exceed_max {
237-
std::mem::swap(input, new_input);
223+
if !available_inputs.is_empty() {
224+
// Phase 2: Improvement
225+
for output in outputs.iter_mut() {
226+
let associated = associated_inputs.get_mut(output).unwrap();
227+
for input in associated.iter_mut() {
228+
let random_index = rng.gen_range(0..available_inputs.len());
229+
let new_input = available_inputs.get_mut(random_index).unwrap();
230+
let cur = from_bignum(&input.output.amount.coin);
231+
let new = from_bignum(&new_input.output.amount.coin);
232+
let min = from_bignum(&output.amount.coin);
233+
let ideal = 2 * min;
234+
let max = 3 * min;
235+
let move_closer = (ideal as i128 - new as i128).abs() < (ideal as i128 - cur as i128).abs();
236+
let not_exceed_max = new < max;
237+
if move_closer && not_exceed_max {
238+
std::mem::swap(input, new_input);
239+
}
238240
}
239241
}
240242
}
@@ -2064,7 +2066,6 @@ mod tests {
20642066
assert_eq!(3u8, tx.inputs().get(1).transaction_id().0[0]);
20652067
}
20662068

2067-
20682069
#[test]
20692070
fn tx_builder_cip2_random_improve() {
20702071
// we have a = 1 to test increasing fees when more inputs are added
@@ -2117,6 +2118,31 @@ mod tests {
21172118
assert!(input_total >= Value::new(&tx_builder.min_fee().unwrap().checked_add(&to_bignum(COST)).unwrap()));
21182119
}
21192120

2121+
#[test]
2122+
fn tx_builder_cip2_random_improve_when_using_all_available_inputs() {
2123+
// we have a = 1 to test increasing fees when more inputs are added
2124+
let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0));
2125+
let mut tx_builder = TransactionBuilder::new(
2126+
&linear_fee,
2127+
&Coin::zero(),
2128+
&to_bignum(0),
2129+
9999,
2130+
9999,
2131+
&to_bignum(0),
2132+
);
2133+
const COST: u64 = 1000;
2134+
tx_builder.add_output(&TransactionOutput::new(
2135+
&Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z").unwrap(),
2136+
&Value::new(&to_bignum(COST))
2137+
)).unwrap();
2138+
let mut available_inputs = TransactionUnspentOutputs::new();
2139+
available_inputs.add(&make_input(1u8, Value::new(&to_bignum(800))));
2140+
available_inputs.add(&make_input(2u8, Value::new(&to_bignum(800))));
2141+
let add_inputs_res =
2142+
tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove);
2143+
assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
2144+
}
2145+
21202146
fn build_tx_pay_to_multisig() {
21212147
let linear_fee = LinearFee::new(&to_bignum(10), &to_bignum(2));
21222148
let mut tx_builder =
@@ -2322,3 +2348,4 @@ mod tests {
23222348
assert_eq!(_deser_t.body().auxiliary_data_hash.unwrap(), utils::hash_auxiliary_data(&auxiliary_data));
23232349
}
23242350
}
2351+

0 commit comments

Comments
 (0)