spend: don't use database's Coin type

We could use a trait but instead there is just a couple fields we need so simply copy them over.
This commit is contained in:
Antoine Poinsot 2023-11-30 14:18:36 +01:00
parent 08ce0ad1d7
commit c63a120794
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
2 changed files with 44 additions and 34 deletions

View File

@ -202,6 +202,21 @@ fn unsigned_tx_max_vbytes(tx: &bitcoin::Transaction, max_sat_weight: u64) -> u64
.unwrap()
}
fn coin_to_candidate(
coin: &Coin,
must_select: bool,
sequence: Option<bitcoin::Sequence>,
) -> CandidateCoin {
CandidateCoin {
outpoint: coin.outpoint,
amount: coin.amount,
deriv_index: coin.derivation_index,
is_change: coin.is_change,
must_select,
sequence,
}
}
impl DaemonControl {
// Get the derived descriptor for this coin
fn derived_desc(&self, coin: &Coin) -> descriptors::DerivedSinglePathLianaDesc {
@ -472,10 +487,8 @@ impl DaemonControl {
db_conn
.coins(&[CoinStatus::Confirmed], &[])
.into_values()
.map(|c| CandidateCoin {
coin: c,
must_select: false, // No coin is mandatory.
sequence: None, // No specific nSequence is required.
.map(|c| {
coin_to_candidate(&c, /*must_select=*/ false, /*sequence=*/ None)
})
.collect()
} else {
@ -492,11 +505,7 @@ impl DaemonControl {
}
coins
.into_values()
.map(|c| CandidateCoin {
coin: c,
must_select: true, // All coins must be selected.
sequence: None, // No specific nSequence is required.
})
.map(|c| coin_to_candidate(&c, /*must_select=*/ true, /*sequence=*/ None))
.collect()
};
@ -792,10 +801,8 @@ impl DaemonControl {
// transaction in this way, then we set candidates in the same way as for the `!is_cancel` case.
let mut candidate_coins: Vec<CandidateCoin> = prev_coins
.values()
.map(|c| CandidateCoin {
coin: *c,
must_select: !is_cancel,
sequence: None, // No specific nSequence is required.
.map(|c| {
coin_to_candidate(c, /*must_select=*/ !is_cancel, /*sequence=*/ None)
})
.collect();
let confirmed_cands: Vec<CandidateCoin> = db_conn
@ -805,11 +812,9 @@ impl DaemonControl {
// Make sure we don't have duplicate candidates in case any of the coins are not
// currently set as spending in the DB (and are therefore still confirmed).
if !prev_coins.contains_key(&c.outpoint) {
Some(CandidateCoin {
coin: c,
must_select: false,
sequence: None, // No specific nSequence is required.
})
Some(coin_to_candidate(
&c, /*must_select=*/ false, /*sequence=*/ None,
))
} else {
None
}
@ -992,11 +997,11 @@ impl DaemonControl {
.map(|b| current_height + 1 >= b.height + height_delta)
.unwrap_or(false)
{
Some(CandidateCoin {
coin: c,
must_select: true, // All coins must be selected.
sequence: Some(bitcoin::Sequence::from_height(timelock)),
})
Some(coin_to_candidate(
&c,
/*must_select=*/ true,
/*sequence=*/ Some(bitcoin::Sequence::from_height(timelock)),
))
} else {
None
}

View File

@ -1,4 +1,4 @@
use crate::{database::Coin, descriptors};
use crate::descriptors;
use std::{collections::BTreeMap, convert::TryInto, fmt};
@ -166,8 +166,14 @@ fn sanity_check_psbt(
/// A candidate for coin selection when creating a transaction.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CandidateCoin {
/// The candidate coin.
pub coin: Coin,
/// Unique identifier of this coin.
pub outpoint: bitcoin::OutPoint,
/// The value of this coin.
pub amount: bitcoin::Amount,
/// The derivation index used to generate the scriptpubkey of this coin.
pub deriv_index: bip32::ChildNumber,
/// Whether this coin pays to a scriptpubkey derived from the internal keychain.
pub is_change: bool,
/// Whether or not this coin must be selected by the coin selection algorithm.
pub must_select: bool,
/// The nSequence field to set for an input spending this coin.
@ -254,7 +260,7 @@ fn select_coins_for_spend(
.iter()
.map(|cand| Candidate {
input_count: 1,
value: cand.coin.amount.to_sat(),
value: cand.amount.to_sat(),
weight: max_input_weight,
is_segwit: true, // We only support receiving on Segwit scripts.
})
@ -351,14 +357,14 @@ fn select_coins_for_spend(
fn derived_desc(
secp: &secp256k1::Secp256k1<secp256k1::VerifyOnly>,
desc: &descriptors::LianaDescriptor,
coin: &Coin,
coin: &CandidateCoin,
) -> descriptors::DerivedSinglePathLianaDesc {
let desc = if coin.is_change {
desc.change_descriptor()
} else {
desc.receive_descriptor()
};
desc.derive(coin.derivation_index, secp)
desc.derive(coin.deriv_index, secp)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -547,25 +553,24 @@ pub fn create_spend(
// Iterate through selected coins and add necessary information to the PSBT inputs.
let mut psbt_ins = Vec::with_capacity(selected_coins.len());
for cand in &selected_coins {
let coin = &cand.coin;
let sequence = cand
.sequence
.unwrap_or(bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME);
tx.input.push(bitcoin::TxIn {
previous_output: coin.outpoint,
previous_output: cand.outpoint,
sequence,
// TODO: once we move to Taproot, anti-fee-sniping using nSequence
..bitcoin::TxIn::default()
});
// Populate the PSBT input with the information needed by signers.
let coin_desc = derived_desc(secp, main_descriptor, coin);
let coin_desc = derived_desc(secp, main_descriptor, cand);
let witness_script = Some(coin_desc.witness_script());
let witness_utxo = Some(bitcoin::TxOut {
value: coin.amount.to_sat(),
value: cand.amount.to_sat(),
script_pubkey: coin_desc.script_pubkey(),
});
let non_witness_utxo = tx_getter.get_tx(&coin.outpoint.txid);
let non_witness_utxo = tx_getter.get_tx(&cand.outpoint.txid);
let bip32_derivation = coin_desc.bip32_derivations();
psbt_ins.push(PsbtIn {
witness_script,