Incorporate RBF rules into utxo selection function
This commit is contained in:
parent
a6b70af2fb
commit
8d65581825
@ -45,7 +45,6 @@ use log::{debug, error, info, trace};
|
|||||||
pub mod address_validator;
|
pub mod address_validator;
|
||||||
pub mod coin_selection;
|
pub mod coin_selection;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
mod rbf;
|
|
||||||
pub mod signer;
|
pub mod signer;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod tx_builder;
|
pub mod tx_builder;
|
||||||
@ -355,6 +354,7 @@ where
|
|||||||
&builder.utxos,
|
&builder.utxos,
|
||||||
builder.send_all,
|
builder.send_all,
|
||||||
builder.manually_selected_only,
|
builder.manually_selected_only,
|
||||||
|
false, // we don't mind using unconfirmed outputs here, hopefully coin selection will sort this out?
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let coin_selection::CoinSelectionResult {
|
let coin_selection::CoinSelectionResult {
|
||||||
@ -604,18 +604,15 @@ where
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let (must_use_utxos, may_use_utxos) = self.get_must_may_use_utxos(
|
let (mut must_use_utxos, may_use_utxos) = self.get_must_may_use_utxos(
|
||||||
builder.change_policy,
|
builder.change_policy,
|
||||||
&builder.unspendable,
|
&builder.unspendable,
|
||||||
&builder_extra_utxos[..],
|
&builder_extra_utxos[..],
|
||||||
false, // when doing bump_fee `send_all` does not mean use all available utxos
|
false, // when doing bump_fee `send_all` does not mean use all available utxos
|
||||||
builder.manually_selected_only,
|
builder.manually_selected_only,
|
||||||
|
true, // we only want confirmed transactions for RBF
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut must_use_utxos =
|
|
||||||
rbf::filter_available(self.database.borrow().deref(), must_use_utxos.into_iter())?;
|
|
||||||
let may_use_utxos =
|
|
||||||
rbf::filter_available(self.database.borrow().deref(), may_use_utxos.into_iter())?;
|
|
||||||
must_use_utxos.append(&mut original_utxos);
|
must_use_utxos.append(&mut original_utxos);
|
||||||
|
|
||||||
let amount_needed = tx.output.iter().fold(0, |acc, out| acc + out.value);
|
let amount_needed = tx.output.iter().fold(0, |acc, out| acc + out.value);
|
||||||
@ -995,6 +992,7 @@ where
|
|||||||
manually_selected: &[OutPoint],
|
manually_selected: &[OutPoint],
|
||||||
must_use_all_available: bool,
|
must_use_all_available: bool,
|
||||||
manual_only: bool,
|
manual_only: bool,
|
||||||
|
must_only_use_confirmed_tx: bool,
|
||||||
) -> Result<(Vec<(UTXO, usize)>, Vec<(UTXO, usize)>), Error> {
|
) -> Result<(Vec<(UTXO, usize)>, Vec<(UTXO, usize)>), Error> {
|
||||||
// must_spend <- manually selected utxos
|
// must_spend <- manually selected utxos
|
||||||
// may_spend <- all other available utxos
|
// may_spend <- all other available utxos
|
||||||
@ -1022,8 +1020,31 @@ where
|
|||||||
return Ok((must_spend, vec![]));
|
return Ok((must_spend, vec![]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let satisfies_confirmed = match must_only_use_confirmed_tx {
|
||||||
|
true => {
|
||||||
|
let database = self.database.borrow_mut();
|
||||||
|
may_spend
|
||||||
|
.iter()
|
||||||
|
.map(|u| {
|
||||||
|
database
|
||||||
|
.get_tx(&u.0.outpoint.txid, true)
|
||||||
|
.map(|tx| match tx {
|
||||||
|
None => false,
|
||||||
|
Some(tx) => tx.height.is_some(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
|
}
|
||||||
|
false => vec![true; may_spend.len()],
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
may_spend.retain(|u| {
|
may_spend.retain(|u| {
|
||||||
change_policy.is_satisfied_by(&u.0) && !unspendable.contains(&u.0.outpoint)
|
let retain = change_policy.is_satisfied_by(&u.0)
|
||||||
|
&& !unspendable.contains(&u.0.outpoint)
|
||||||
|
&& satisfies_confirmed[i];
|
||||||
|
i += 1;
|
||||||
|
retain
|
||||||
});
|
});
|
||||||
|
|
||||||
if must_use_all_available {
|
if must_use_all_available {
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
// Magical Bitcoin Library
|
|
||||||
// Written in 2020 by
|
|
||||||
// Alekos Filini <alekos.filini@gmail.com>
|
|
||||||
//
|
|
||||||
// Copyright (c) 2020 Magical Bitcoin
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
use crate::database::Database;
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::types::*;
|
|
||||||
|
|
||||||
/// Filters unspent utxos
|
|
||||||
pub(super) fn filter_available<I: Iterator<Item = (UTXO, usize)>, D: Database>(
|
|
||||||
database: &D,
|
|
||||||
iter: I,
|
|
||||||
) -> Result<Vec<(UTXO, usize)>, Error> {
|
|
||||||
Ok(iter
|
|
||||||
.map(|(utxo, weight)| {
|
|
||||||
Ok(match database.get_tx(&utxo.outpoint.txid, true)? {
|
|
||||||
None => None,
|
|
||||||
Some(tx) if tx.height.is_none() => None,
|
|
||||||
Some(_) => Some((utxo, weight)),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, Error>>()?
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|x| x)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use bitcoin::{OutPoint, Transaction, TxIn, TxOut, Txid};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::database::{BatchOperations, MemoryDatabase};
|
|
||||||
|
|
||||||
fn add_transaction(
|
|
||||||
database: &mut MemoryDatabase,
|
|
||||||
spend: Vec<OutPoint>,
|
|
||||||
outputs: Vec<u64>,
|
|
||||||
) -> Txid {
|
|
||||||
let tx = Transaction {
|
|
||||||
version: 1,
|
|
||||||
lock_time: 0,
|
|
||||||
input: spend
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|previous_output| TxIn {
|
|
||||||
previous_output,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
output: outputs
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|value| TxOut {
|
|
||||||
value,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
let txid = tx.txid();
|
|
||||||
|
|
||||||
for input in &spend {
|
|
||||||
database.del_utxo(input).unwrap();
|
|
||||||
}
|
|
||||||
for vout in 0..outputs.len() {
|
|
||||||
database
|
|
||||||
.set_utxo(&UTXO {
|
|
||||||
txout: tx.output[vout].clone(),
|
|
||||||
outpoint: OutPoint {
|
|
||||||
txid,
|
|
||||||
vout: vout as u32,
|
|
||||||
},
|
|
||||||
is_internal: true,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
database
|
|
||||||
.set_tx(&TransactionDetails {
|
|
||||||
txid,
|
|
||||||
transaction: Some(tx),
|
|
||||||
height: None,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
txid
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_filter_available() {
|
|
||||||
let mut database = MemoryDatabase::new();
|
|
||||||
add_transaction(
|
|
||||||
&mut database,
|
|
||||||
vec![OutPoint::from_str(
|
|
||||||
"aad194c72fd5cfd16d23da9462930ca91e35df1cfee05242b62f4034f50c3d41:5",
|
|
||||||
)
|
|
||||||
.unwrap()],
|
|
||||||
vec![50_000],
|
|
||||||
);
|
|
||||||
|
|
||||||
let filtered = filter_available(
|
|
||||||
&database,
|
|
||||||
database
|
|
||||||
.iter_utxos()
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|utxo| (utxo, 0)),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(filtered, &[]);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user