43
43
namespace ledger {
44
44
namespace core {
45
45
46
- BitcoinLikeStrategyUtxoPicker::BitcoinLikeStrategyUtxoPicker (const std::shared_ptr<api::ExecutionContext> &context,
47
- const api::Currency ¤cy) : BitcoinLikeUtxoPicker(context, currency) {
46
+ std::optional<bool > compareConfirmation (const BitcoinLikeUtxo &u1, const BitcoinLikeUtxo &u2) {
47
+ bool isU1Confirmed = u1.blockHeight .hasValue ();
48
+ bool isU2Confirmed = u2.blockHeight .hasValue ();
49
+
50
+ if ((isU1Confirmed && isU2Confirmed) || (!isU1Confirmed && !isU2Confirmed)) {
51
+ // ignore the case where confirmation status is similar
52
+ return {};
53
+ }
54
+
55
+ // otherwise confirmed utxo should be first
56
+ return isU1Confirmed;
48
57
}
49
58
59
+ BitcoinLikeStrategyUtxoPicker::BitcoinLikeStrategyUtxoPicker (const std::shared_ptr<api::ExecutionContext> &context,
60
+ const api::Currency ¤cy,
61
+ bool useConfirmedFirst)
62
+ : BitcoinLikeUtxoPicker(context, currency), _useConfirmedFirst{useConfirmedFirst} {}
63
+
50
64
Future<std::vector<BitcoinLikeUtxo>>
51
65
BitcoinLikeStrategyUtxoPicker::filterInputs (const std::shared_ptr<BitcoinLikeUtxoPicker::Buddy> &buddy) {
52
66
return computeAggregatedAmount (buddy).flatMap <std::vector<BitcoinLikeUtxo>>(getContext (), [=](BigInt const &amount) {
@@ -71,9 +85,9 @@ namespace ledger {
71
85
case api::BitcoinLikePickingStrategy::DEEP_OUTPUTS_FIRST:
72
86
return filterWithDeepFirst (buddy, utxos, amount, getCurrency ());
73
87
case api::BitcoinLikePickingStrategy::OPTIMIZE_SIZE:
74
- return filterWithOptimizeSize (buddy, utxos, amount, getCurrency ());
88
+ return filterWithOptimizeSize (buddy, utxos, amount, getCurrency (), _useConfirmedFirst );
75
89
case api::BitcoinLikePickingStrategy::MERGE_OUTPUTS:
76
- return filterWithMergeOutputs (buddy, utxos, amount, getCurrency ());
90
+ return filterWithMergeOutputs (buddy, utxos, amount, getCurrency (), _useConfirmedFirst );
77
91
}
78
92
79
93
throw make_exception (api::ErrorCode::ILLEGAL_ARGUMENT, " Unknown UTXO picking strategy." );
@@ -173,7 +187,8 @@ namespace ledger {
173
187
BitcoinLikeStrategyUtxoPicker::filterWithOptimizeSize (const std::shared_ptr<BitcoinLikeUtxoPicker::Buddy> &buddy,
174
188
const std::vector<BitcoinLikeUtxo> &utxos,
175
189
const BigInt &aggregatedAmount,
176
- const api::Currency ¤cy) {
190
+ const api::Currency ¤cy,
191
+ bool useConfirmedFirst) {
177
192
// NOTE: why are we using buddy->outputAmount here instead of aggregatedAmount ?
178
193
// Don't use this strategy for wipe mode (we have more performent strategies for this use case)
179
194
if (buddy->request .wipe ) {
@@ -272,7 +287,13 @@ namespace ledger {
272
287
throw make_exception (api::ErrorCode::NOT_ENOUGH_FUNDS, " Cannot gather enough funds." );
273
288
}
274
289
275
- auto descendingEffectiveValue = [](const EffectiveUtxo &lhs, const EffectiveUtxo &rhs) -> bool {
290
+ auto descendingEffectiveValue = [useConfirmedFirst](const EffectiveUtxo &lhs, const EffectiveUtxo &rhs) -> bool {
291
+ if (useConfirmedFirst) {
292
+ std::optional<bool > comp = compareConfirmation (*lhs.utxo , *rhs.utxo );
293
+ if (comp.has_value ()) {
294
+ return comp.value ();
295
+ }
296
+ }
276
297
return lhs.effectiveValue > rhs.effectiveValue ;
277
298
};
278
299
@@ -347,7 +368,7 @@ namespace ledger {
347
368
// If no selection found fallback on filterWithDeepFirst
348
369
if (bestSelection.empty ()) {
349
370
buddy->logger ->debug (" No best selection found, fallback on filterWithKnapsackSolver coin selection" );
350
- return filterWithKnapsackSolver (buddy, utxos, aggregatedAmount, currency);
371
+ return filterWithKnapsackSolver (buddy, utxos, aggregatedAmount, currency, useConfirmedFirst );
351
372
}
352
373
353
374
// Prepare result
@@ -414,7 +435,8 @@ namespace ledger {
414
435
const std::shared_ptr<BitcoinLikeUtxoPicker::Buddy> &buddy,
415
436
const std::vector<BitcoinLikeUtxo> &utxos,
416
437
const BigInt &aggregatedAmount,
417
- const api::Currency ¤cy) {
438
+ const api::Currency ¤cy,
439
+ bool useConfirmedFirst) {
418
440
// Tx fixed size
419
441
auto const fixedSize = BitcoinLikeTransactionApi::estimateSize (0 ,
420
442
0 ,
@@ -473,12 +495,18 @@ namespace ledger {
473
495
std::vector<BitcoinLikeUtxo> out;
474
496
475
497
// Random shuffle utxos
476
- std::vector<size_t > indexes (utxos.size ());
477
- std::iota (indexes.begin (), indexes.end (), 0 );
478
-
498
+ const auto &sz = utxos.size ();
499
+ std::vector<size_t > indexes (sz, 0 );
479
500
auto const seed = std::chrono::system_clock::now ().time_since_epoch ().count ();
501
+ std::iota (indexes.begin (), indexes.end (), 0 );
480
502
std::shuffle (indexes.begin (), indexes.end (), std::default_random_engine (seed));
481
503
504
+ if (useConfirmedFirst) {
505
+ std::stable_sort (indexes.begin (), indexes.end (), [&utxos](auto id1, auto id2) {
506
+ return compareConfirmation (utxos[id1], utxos[id2]).value_or (true );
507
+ });
508
+ }
509
+
482
510
// Add fees for a signed input to amount
483
511
for (auto index : indexes) {
484
512
auto &utxo = utxos[index];
@@ -516,7 +544,13 @@ namespace ledger {
516
544
}
517
545
518
546
// Sort vUTXOs descending
519
- std::sort (vUTXOs.begin (), vUTXOs.end (), [](auto const &lhs, auto const &rhs) {
547
+ std::sort (vUTXOs.begin (), vUTXOs.end (), [useConfirmedFirst](auto const &lhs, auto const &rhs) {
548
+ if (useConfirmedFirst) {
549
+ std::optional<bool > comp = compareConfirmation (lhs, rhs);
550
+ if (comp.has_value ()) {
551
+ return comp.value ();
552
+ }
553
+ }
520
554
return lhs.value .toLong () > rhs.value .toLong ();
521
555
});
522
556
@@ -586,10 +620,18 @@ namespace ledger {
586
620
const std::shared_ptr<BitcoinLikeUtxoPicker::Buddy> &buddy,
587
621
const std::vector<BitcoinLikeUtxo> &utxos,
588
622
const BigInt &aggregatedAmount,
589
- const api::Currency ¤cy) {
623
+ const api::Currency ¤cy,
624
+ bool useConfirmedFirst) {
590
625
buddy->logger ->debug (" Start filterWithMergeOutputs" );
591
626
592
- return filterWithSort (buddy, utxos, aggregatedAmount, currency, [](auto &lhs, auto &rhs) {
627
+ return filterWithSort (buddy, utxos, aggregatedAmount, currency, [useConfirmedFirst](auto &lhs, auto &rhs) {
628
+ if (useConfirmedFirst) {
629
+ std::optional<bool > comp = compareConfirmation (lhs, rhs);
630
+ if (comp.has_value ()) {
631
+ return comp.value ();
632
+ }
633
+ }
634
+
593
635
return lhs.value .toLong () < rhs.value .toLong ();
594
636
});
595
637
}
@@ -631,5 +673,6 @@ namespace ledger {
631
673
632
674
return pickedUtxos;
633
675
}
676
+
634
677
} // namespace core
635
678
} // namespace ledger
0 commit comments