<span id="1488">1488</span>
<span id="1489">1489</span>
<span id="1490">1490</span>
+<span id="1491">1491</span>
+<span id="1492">1492</span>
+<span id="1493">1493</span>
+<span id="1494">1494</span>
+<span id="1495">1495</span>
+<span id="1496">1496</span>
+<span id="1497">1497</span>
+<span id="1498">1498</span>
+<span id="1499">1499</span>
+<span id="1500">1500</span>
+<span id="1501">1501</span>
+<span id="1502">1502</span>
+<span id="1503">1503</span>
+<span id="1504">1504</span>
+<span id="1505">1505</span>
+<span id="1506">1506</span>
+<span id="1507">1507</span>
+<span id="1508">1508</span>
+<span id="1509">1509</span>
+<span id="1510">1510</span>
+<span id="1511">1511</span>
+<span id="1512">1512</span>
+<span id="1513">1513</span>
+<span id="1514">1514</span>
+<span id="1515">1515</span>
+<span id="1516">1516</span>
+<span id="1517">1517</span>
+<span id="1518">1518</span>
+<span id="1519">1519</span>
+<span id="1520">1520</span>
+<span id="1521">1521</span>
+<span id="1522">1522</span>
+<span id="1523">1523</span>
+<span id="1524">1524</span>
+<span id="1525">1525</span>
+<span id="1526">1526</span>
+<span id="1527">1527</span>
+<span id="1528">1528</span>
+<span id="1529">1529</span>
+<span id="1530">1530</span>
+<span id="1531">1531</span>
+<span id="1532">1532</span>
+<span id="1533">1533</span>
+<span id="1534">1534</span>
+<span id="1535">1535</span>
+<span id="1536">1536</span>
+<span id="1537">1537</span>
+<span id="1538">1538</span>
+<span id="1539">1539</span>
+<span id="1540">1540</span>
+<span id="1541">1541</span>
+<span id="1542">1542</span>
+<span id="1543">1543</span>
+<span id="1544">1544</span>
+<span id="1545">1545</span>
+<span id="1546">1546</span>
+<span id="1547">1547</span>
+<span id="1548">1548</span>
+<span id="1549">1549</span>
+<span id="1550">1550</span>
+<span id="1551">1551</span>
+<span id="1552">1552</span>
+<span id="1553">1553</span>
+<span id="1554">1554</span>
+<span id="1555">1555</span>
+<span id="1556">1556</span>
+<span id="1557">1557</span>
+<span id="1558">1558</span>
+<span id="1559">1559</span>
+<span id="1560">1560</span>
+<span id="1561">1561</span>
+<span id="1562">1562</span>
+<span id="1563">1563</span>
+<span id="1564">1564</span>
+<span id="1565">1565</span>
+<span id="1566">1566</span>
+<span id="1567">1567</span>
+<span id="1568">1568</span>
+<span id="1569">1569</span>
+<span id="1570">1570</span>
+<span id="1571">1571</span>
+<span id="1572">1572</span>
+<span id="1573">1573</span>
+<span id="1574">1574</span>
+<span id="1575">1575</span>
+<span id="1576">1576</span>
+<span id="1577">1577</span>
+<span id="1578">1578</span>
+<span id="1579">1579</span>
+<span id="1580">1580</span>
+<span id="1581">1581</span>
+<span id="1582">1582</span>
+<span id="1583">1583</span>
+<span id="1584">1584</span>
+<span id="1585">1585</span>
+<span id="1586">1586</span>
+<span id="1587">1587</span>
+<span id="1588">1588</span>
+<span id="1589">1589</span>
+<span id="1590">1590</span>
+<span id="1591">1591</span>
+<span id="1592">1592</span>
+<span id="1593">1593</span>
+<span id="1594">1594</span>
+<span id="1595">1595</span>
+<span id="1596">1596</span>
</pre><pre class="rust"><code><span class="comment">// Bitcoin Dev Kit</span>
<span class="comment">// Written in 2020 by Alekos Filini <alekos.filini@gmail.com></span>
<span class="comment">//</span>
.<span class="ident">map</span>(<span class="op">|</span><span class="ident">u</span><span class="op">|</span> <span class="ident">OutputGroup::new</span>(<span class="ident">u</span>, <span class="ident">fee_rate</span>))
.<span class="ident">collect</span>();
- <span class="comment">// Mapping every (UTXO, usize) to an output group.</span>
+ <span class="comment">// Mapping every (UTXO, usize) to an output group, filtering UTXOs with a negative</span>
+ <span class="comment">// effective value</span>
<span class="kw">let</span> <span class="ident">optional_utxos</span>: <span class="ident">Vec</span><span class="op"><</span><span class="ident">OutputGroup</span><span class="op">></span> <span class="op">=</span> <span class="ident">optional_utxos</span>
.<span class="ident">into_iter</span>()
.<span class="ident">map</span>(<span class="op">|</span><span class="ident">u</span><span class="op">|</span> <span class="ident">OutputGroup::new</span>(<span class="ident">u</span>, <span class="ident">fee_rate</span>))
+ .<span class="ident">filter</span>(<span class="op">|</span><span class="ident">u</span><span class="op">|</span> <span class="ident">u</span>.<span class="ident">effective_value</span>.<span class="ident">is_positive</span>())
.<span class="ident">collect</span>();
<span class="kw">let</span> <span class="ident">curr_value</span> <span class="op">=</span> <span class="ident">required_utxos</span>
<span class="kw">let</span> <span class="ident">cost_of_change</span> <span class="op">=</span> <span class="self">self</span>.<span class="ident">size_of_change</span> <span class="kw">as</span> <span class="ident">f32</span> <span class="op">*</span> <span class="ident">fee_rate</span>.<span class="ident">as_sat_vb</span>();
- <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> (<span class="ident">curr_available_value</span> <span class="op">+</span> <span class="ident">curr_value</span>)
- .<span class="ident">try_into</span>()
- .<span class="ident">map_err</span>(<span class="op">|</span><span class="kw">_</span><span class="op">|</span> {
- <span class="ident">Error::Generic</span>(<span class="string">"Sum of UTXO spendable values does not fit into u64"</span>.<span class="ident">to_string</span>())
- })<span class="question-mark">?</span>;
-
- <span class="kw">if</span> <span class="ident">expected</span> <span class="op"><</span> <span class="ident">target_amount</span> {
- <span class="kw">return</span> <span class="prelude-val">Err</span>(<span class="ident">Error::InsufficientFunds</span> {
- <span class="ident">needed</span>: <span class="ident">target_amount</span>,
- <span class="ident">available</span>: <span class="ident">expected</span>,
- });
+ <span class="comment">// `curr_value` and `curr_available_value` are both the sum of *effective_values* of</span>
+ <span class="comment">// the UTXOs. For the optional UTXOs (curr_available_value) we filter out UTXOs with</span>
+ <span class="comment">// negative effective value, so it will always be positive.</span>
+ <span class="comment">//</span>
+ <span class="comment">// Since we are required to spend the required UTXOs (curr_value) we have to consider</span>
+ <span class="comment">// all their effective values, even when negative, which means that curr_value could</span>
+ <span class="comment">// be negative as well.</span>
+ <span class="comment">//</span>
+ <span class="comment">// If the sum of curr_value and curr_available_value is negative or lower than our target,</span>
+ <span class="comment">// we can immediately exit with an error, as it's guaranteed we will never find a solution</span>
+ <span class="comment">// if we actually run the BnB.</span>
+ <span class="kw">let</span> <span class="ident">total_value</span>: <span class="prelude-ty">Result</span><span class="op"><</span><span class="ident">u64</span>, <span class="kw">_</span><span class="op">></span> <span class="op">=</span> (<span class="ident">curr_available_value</span> <span class="op">+</span> <span class="ident">curr_value</span>).<span class="ident">try_into</span>();
+ <span class="kw">match</span> <span class="ident">total_value</span> {
+ <span class="prelude-val">Ok</span>(<span class="ident">v</span>) <span class="kw">if</span> <span class="ident">v</span> <span class="op">></span><span class="op">=</span> <span class="ident">target_amount</span> => {}
+ <span class="kw">_</span> => {
+ <span class="comment">// Assume we spend all the UTXOs we can (all the required + all the optional with</span>
+ <span class="comment">// positive effective value), sum their value and their fee cost.</span>
+ <span class="kw">let</span> (<span class="ident">utxo_fees</span>, <span class="ident">utxo_value</span>) <span class="op">=</span> <span class="ident">required_utxos</span>
+ .<span class="ident">iter</span>()
+ .<span class="ident">chain</span>(<span class="ident">optional_utxos</span>.<span class="ident">iter</span>())
+ .<span class="ident">fold</span>((<span class="number">0</span>, <span class="number">0</span>), <span class="op">|</span>(<span class="kw-2">mut</span> <span class="ident">fees</span>, <span class="kw-2">mut</span> <span class="ident">value</span>), <span class="ident">utxo</span><span class="op">|</span> {
+ <span class="ident">fees</span> <span class="op">+</span><span class="op">=</span> <span class="ident">utxo</span>.<span class="ident">fee</span>;
+ <span class="ident">value</span> <span class="op">+</span><span class="op">=</span> <span class="ident">utxo</span>.<span class="ident">weighted_utxo</span>.<span class="ident">utxo</span>.<span class="ident">txout</span>().<span class="ident">value</span>;
+
+ (<span class="ident">fees</span>, <span class="ident">value</span>)
+ });
+
+ <span class="comment">// Add to the target the fee cost of the UTXOs</span>
+ <span class="kw">return</span> <span class="prelude-val">Err</span>(<span class="ident">Error::InsufficientFunds</span> {
+ <span class="ident">needed</span>: <span class="ident">target_amount</span> <span class="op">+</span> <span class="ident">utxo_fees</span>,
+ <span class="ident">available</span>: <span class="ident">utxo_value</span>,
+ });
+ }
}
<span class="kw">let</span> <span class="ident">target_amount</span> <span class="op">=</span> <span class="ident">target_amount</span>
)
.<span class="ident">unwrap</span>();
- <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected</span>.<span class="ident">len</span>(), <span class="number">3</span>);
- <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected_amount</span>(), <span class="number">300010</span>);
- <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">fee_amount</span>, <span class="number">204</span>);
+ <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected</span>.<span class="ident">len</span>(), <span class="number">2</span>);
+ <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected_amount</span>(), <span class="number">300000</span>);
+ <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">fee_amount</span>, <span class="number">136</span>);
}
<span class="attribute">#[<span class="ident">test</span>]</span>
)
.<span class="ident">unwrap</span>();
- <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected</span>.<span class="ident">len</span>(), <span class="number">3</span>);
- <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected_amount</span>(), <span class="number">300_010</span>);
- <span class="macro">assert!</span>((<span class="ident">result</span>.<span class="ident">fee_amount</span> <span class="kw">as</span> <span class="ident">f32</span> <span class="op">-</span> <span class="number">204.0</span>).<span class="ident">abs</span>() <span class="op"><</span> <span class="ident">f32::EPSILON</span>);
+ <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected</span>.<span class="ident">len</span>(), <span class="number">2</span>);
+ <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">selected_amount</span>(), <span class="number">300_000</span>);
+ <span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">fee_amount</span>, <span class="number">136</span>);
}
<span class="attribute">#[<span class="ident">test</span>]</span>
<span class="macro">assert!</span>(<span class="ident">result</span>.<span class="ident">selected_amount</span>() <span class="op">></span> <span class="ident">target_amount</span>);
<span class="macro">assert_eq!</span>(<span class="ident">result</span>.<span class="ident">fee_amount</span>, (<span class="ident">result</span>.<span class="ident">selected</span>.<span class="ident">len</span>() <span class="op">*</span> <span class="number">68</span>) <span class="kw">as</span> <span class="ident">u64</span>);
}
+
+ <span class="attribute">#[<span class="ident">test</span>]</span>
+ <span class="kw">fn</span> <span class="ident">test_bnb_exclude_negative_effective_value</span>() {
+ <span class="kw">let</span> <span class="ident">utxos</span> <span class="op">=</span> <span class="ident">get_test_utxos</span>();
+ <span class="kw">let</span> <span class="ident">database</span> <span class="op">=</span> <span class="ident">MemoryDatabase::default</span>();
+ <span class="kw">let</span> <span class="ident">drain_script</span> <span class="op">=</span> <span class="ident">Script::default</span>();
+
+ <span class="kw">let</span> <span class="ident">err</span> <span class="op">=</span> <span class="ident">BranchAndBoundCoinSelection::default</span>()
+ .<span class="ident">coin_select</span>(
+ <span class="kw-2">&</span><span class="ident">database</span>,
+ <span class="macro">vec!</span>[],
+ <span class="ident">utxos</span>,
+ <span class="ident">FeeRate::from_sat_per_vb</span>(<span class="number">10.0</span>),
+ <span class="number">500_000</span>,
+ <span class="kw-2">&</span><span class="ident">drain_script</span>,
+ )
+ .<span class="ident">unwrap_err</span>();
+
+ <span class="macro">assert!</span>(<span class="macro">matches!</span>(
+ <span class="ident">err</span>,
+ <span class="ident">Error::InsufficientFunds</span> {
+ <span class="ident">available</span>: <span class="number">300_000</span>,
+ ..
+ }
+ ));
+ }
+
+ <span class="attribute">#[<span class="ident">test</span>]</span>
+ <span class="kw">fn</span> <span class="ident">test_bnb_include_negative_effective_value_when_required</span>() {
+ <span class="kw">let</span> <span class="ident">utxos</span> <span class="op">=</span> <span class="ident">get_test_utxos</span>();
+ <span class="kw">let</span> <span class="ident">database</span> <span class="op">=</span> <span class="ident">MemoryDatabase::default</span>();
+ <span class="kw">let</span> <span class="ident">drain_script</span> <span class="op">=</span> <span class="ident">Script::default</span>();
+
+ <span class="kw">let</span> (<span class="ident">required</span>, <span class="ident">optional</span>) <span class="op">=</span> <span class="ident">utxos</span>
+ .<span class="ident">into_iter</span>()
+ .<span class="ident">partition</span>(<span class="op">|</span><span class="ident">u</span><span class="op">|</span> <span class="macro">matches!</span>(<span class="ident">u</span>, <span class="ident">WeightedUtxo</span> { <span class="ident">utxo</span>, .. } <span class="kw">if</span> <span class="ident">utxo</span>.<span class="ident">txout</span>().<span class="ident">value</span> <span class="op"><</span> <span class="number">1000</span>));
+
+ <span class="kw">let</span> <span class="ident">err</span> <span class="op">=</span> <span class="ident">BranchAndBoundCoinSelection::default</span>()
+ .<span class="ident">coin_select</span>(
+ <span class="kw-2">&</span><span class="ident">database</span>,
+ <span class="ident">required</span>,
+ <span class="ident">optional</span>,
+ <span class="ident">FeeRate::from_sat_per_vb</span>(<span class="number">10.0</span>),
+ <span class="number">500_000</span>,
+ <span class="kw-2">&</span><span class="ident">drain_script</span>,
+ )
+ .<span class="ident">unwrap_err</span>();
+
+ <span class="macro">assert!</span>(<span class="macro">matches!</span>(
+ <span class="ident">err</span>,
+ <span class="ident">Error::InsufficientFunds</span> {
+ <span class="ident">available</span>: <span class="number">300_010</span>,
+ ..
+ }
+ ));
+ }
+
+ <span class="attribute">#[<span class="ident">test</span>]</span>
+ <span class="kw">fn</span> <span class="ident">test_bnb_sum_of_effective_value_negative</span>() {
+ <span class="kw">let</span> <span class="ident">utxos</span> <span class="op">=</span> <span class="ident">get_test_utxos</span>();
+ <span class="kw">let</span> <span class="ident">database</span> <span class="op">=</span> <span class="ident">MemoryDatabase::default</span>();
+ <span class="kw">let</span> <span class="ident">drain_script</span> <span class="op">=</span> <span class="ident">Script::default</span>();
+
+ <span class="kw">let</span> <span class="ident">err</span> <span class="op">=</span> <span class="ident">BranchAndBoundCoinSelection::default</span>()
+ .<span class="ident">coin_select</span>(
+ <span class="kw-2">&</span><span class="ident">database</span>,
+ <span class="ident">utxos</span>,
+ <span class="macro">vec!</span>[],
+ <span class="ident">FeeRate::from_sat_per_vb</span>(<span class="number">10_000.0</span>),
+ <span class="number">500_000</span>,
+ <span class="kw-2">&</span><span class="ident">drain_script</span>,
+ )
+ .<span class="ident">unwrap_err</span>();
+
+ <span class="macro">assert!</span>(<span class="macro">matches!</span>(
+ <span class="ident">err</span>,
+ <span class="ident">Error::InsufficientFunds</span> {
+ <span class="ident">available</span>: <span class="number">300_010</span>,
+ ..
+ }
+ ));
+ }
}
</code></pre></div>
</section><section id="search" class="content hidden"></section></div></main><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="bdk" data-themes="ayu,dark,light" data-resource-suffix="" data-rustdoc-version="1.60.0-nightly (51126be1b 2022-01-24)" ></div>