Skip to content

Commit 57a53ab

Browse files
committed
Introduce CoinFilterParams
This begins work on the `wallet::coin_control` module. The idea is to filter coins, and determine which coins are fit for coin selection. `Wallet::avaliable_utxos` is introduced to use `CoinFilterParams`.
1 parent 4776644 commit 57a53ab

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

src/wallet/coin_control.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Bitcoin Dev Kit
2+
// Written in 2020 by Alekos Filini <[email protected]>
3+
//
4+
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5+
//
6+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9+
// You may not use this file except in accordance with one or both of these
10+
// licenses.
11+
12+
//! Coin control
13+
//!
14+
//! This module defines how coins are to be sorted and/or grouped before coin selection.
15+
16+
use std::collections::HashMap;
17+
18+
use bitcoin::OutPoint;
19+
20+
use crate::TransactionDetails;
21+
22+
use super::COINBASE_MATURITY;
23+
24+
/// Parameters to determine which coins/outputs to filter.
25+
pub struct CoinFilterParams {
26+
/// Outpoints to manually keep (`true`) or skip (`false`). This overrides all other parameters.
27+
pub manual: HashMap<OutPoint, bool>,
28+
29+
/// Whether we should filter out unconfirmed transactions.
30+
/// TODO: Use minimum confirmations instead.
31+
pub filter_unconfirmed: bool,
32+
33+
/// Whether we should filter out immature coinbase outputs.
34+
/// Coinbase transaction outputs need to be at least 100 blocks deep before being spendable.
35+
/// If this is set, but `current_height == None`, all coinbase outputs will be filtered out.
36+
pub filter_immature_coinbase: bool,
37+
38+
/// Current block height.
39+
pub current_height: Option<u32>,
40+
}
41+
42+
impl CoinFilterParams {
43+
/// Returns true if coin is to be kept, false if coin is to be filtered out.
44+
pub(crate) fn keep(&self, tx: &TransactionDetails, outpoint: &OutPoint) -> bool {
45+
let raw_tx = tx.transaction.as_ref().expect("failed to obtain raw tx");
46+
47+
if let Some(&keep) = self.manual.get(outpoint) {
48+
if keep {
49+
return true;
50+
} else {
51+
return false;
52+
}
53+
}
54+
55+
if self.filter_unconfirmed && tx.confirmation_time.is_none() {
56+
return false;
57+
}
58+
59+
// https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
60+
if self.filter_immature_coinbase
61+
&& raw_tx.is_coin_base()
62+
&& !matches!((self.current_height, &tx.confirmation_time), (Some(tip), Some(conf)) if tip.saturating_sub(conf.height) >= COINBASE_MATURITY)
63+
{
64+
return false;
65+
}
66+
67+
return true;
68+
}
69+
}

src/wallet/mod.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use miniscript::ToPublicKey;
3838
use log::{debug, error, info, trace};
3939

4040
pub mod address_validator;
41+
pub mod coin_control;
4142
pub mod coin_selection;
4243
pub mod export;
4344
pub mod signer;
@@ -79,6 +80,8 @@ use crate::testutils;
7980
use crate::types::*;
8081
use crate::wallet::coin_selection::Excess::{Change, NoChange};
8182

83+
use self::coin_control::CoinFilterParams;
84+
8285
const CACHE_ADDR_BATCH_SIZE: u32 = 100;
8386
const COINBASE_MATURITY: u32 = 100;
8487

@@ -1454,6 +1457,22 @@ where
14541457
.collect())
14551458
}
14561459

1460+
/// Yes, we can!
1461+
pub fn available_utxos(
1462+
&self,
1463+
params: CoinFilterParams,
1464+
) -> Result<impl Iterator<Item = LocalUtxo> + '_, Error> {
1465+
let db = self.database.borrow();
1466+
1467+
Ok(self.iter_unspent()?.filter(move |utxo| {
1468+
if let Ok(Some(tx)) = db.get_tx(&utxo.outpoint.txid, true) {
1469+
params.keep(&tx, &utxo.outpoint)
1470+
} else {
1471+
false
1472+
}
1473+
}))
1474+
}
1475+
14571476
/// Given the options returns the list of utxos that must be used to form the
14581477
/// transaction and any further that may be used if needed.
14591478
#[allow(clippy::type_complexity)]
@@ -1464,7 +1483,7 @@ where
14641483
unspendable: &HashSet<OutPoint>,
14651484
manually_selected: Vec<WeightedUtxo>,
14661485
must_use_all_available: bool,
1467-
manual_only: bool,
1486+
manual_only: bool, // We can just manually call `CoinSelector::select`, then `CoinSelector::finish`
14681487
must_only_use_confirmed_tx: bool,
14691488
current_height: Option<u32>,
14701489
) -> Result<(Vec<WeightedUtxo>, Vec<WeightedUtxo>), Error> {

0 commit comments

Comments
 (0)