allow to definie static fees for transactions Fixes #137
This commit is contained in:
parent
872d55cb4c
commit
27890cfcff
@ -55,7 +55,7 @@ pub use utils::IsDust;
|
|||||||
|
|
||||||
use address_validator::AddressValidator;
|
use address_validator::AddressValidator;
|
||||||
use signer::{Signer, SignerId, SignerOrdering, SignersContainer};
|
use signer::{Signer, SignerId, SignerOrdering, SignersContainer};
|
||||||
use tx_builder::TxBuilder;
|
use tx_builder::{FeePolicy, TxBuilder};
|
||||||
use utils::{After, Older};
|
use utils::{After, Older};
|
||||||
|
|
||||||
use crate::blockchain::{Blockchain, BlockchainMarker, OfflineBlockchain, Progress};
|
use crate::blockchain::{Blockchain, BlockchainMarker, OfflineBlockchain, Progress};
|
||||||
@ -299,7 +299,7 @@ where
|
|||||||
output: vec![],
|
output: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let fee_rate = builder.fee_rate.unwrap_or_default();
|
let fee_rate = get_fee_rate(&builder.fee_policy);
|
||||||
if builder.send_all && builder.recipients.len() != 1 {
|
if builder.send_all && builder.recipients.len() != 1 {
|
||||||
return Err(Error::SendAllMultipleOutputs);
|
return Err(Error::SendAllMultipleOutputs);
|
||||||
}
|
}
|
||||||
@ -393,6 +393,14 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut fee_amount = fee_amount.ceil() as u64;
|
let mut fee_amount = fee_amount.ceil() as u64;
|
||||||
|
|
||||||
|
if builder.has_absolute_fee() {
|
||||||
|
fee_amount = match builder.fee_policy.as_ref().unwrap() {
|
||||||
|
FeePolicy::FeeAmount(amount) => *amount,
|
||||||
|
_ => fee_amount,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let change_val = (selected_amount - outgoing).saturating_sub(fee_amount);
|
let change_val = (selected_amount - outgoing).saturating_sub(fee_amount);
|
||||||
if !builder.send_all && !change_val.is_dust() {
|
if !builder.send_all && !change_val.is_dust() {
|
||||||
let mut change_output = change_output.unwrap();
|
let mut change_output = change_output.unwrap();
|
||||||
@ -485,9 +493,9 @@ where
|
|||||||
// the new tx must "pay for its bandwidth"
|
// the new tx must "pay for its bandwidth"
|
||||||
let vbytes = tx.get_weight() as f32 / 4.0;
|
let vbytes = tx.get_weight() as f32 / 4.0;
|
||||||
let required_feerate = FeeRate::from_sat_per_vb(details.fees as f32 / vbytes + 1.0);
|
let required_feerate = FeeRate::from_sat_per_vb(details.fees as f32 / vbytes + 1.0);
|
||||||
let new_feerate = builder.fee_rate.unwrap_or_default();
|
let new_feerate = get_fee_rate(&builder.fee_policy);
|
||||||
|
|
||||||
if new_feerate < required_feerate {
|
if new_feerate < required_feerate && !builder.has_absolute_fee() {
|
||||||
return Err(Error::FeeRateTooLow {
|
return Err(Error::FeeRateTooLow {
|
||||||
required: required_feerate,
|
required: required_feerate,
|
||||||
});
|
});
|
||||||
@ -623,6 +631,15 @@ where
|
|||||||
|
|
||||||
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);
|
||||||
let initial_fee = tx.get_weight() as f32 / 4.0 * new_feerate.as_sat_vb();
|
let initial_fee = tx.get_weight() as f32 / 4.0 * new_feerate.as_sat_vb();
|
||||||
|
let initial_fee = if builder.has_absolute_fee() {
|
||||||
|
match builder.fee_policy.as_ref().unwrap() {
|
||||||
|
FeePolicy::FeeAmount(amount) => *amount as f32,
|
||||||
|
_ => initial_fee,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initial_fee
|
||||||
|
};
|
||||||
|
|
||||||
let coin_selection::CoinSelectionResult {
|
let coin_selection::CoinSelectionResult {
|
||||||
txin,
|
txin,
|
||||||
selected_amount,
|
selected_amount,
|
||||||
@ -652,6 +669,12 @@ where
|
|||||||
details.sent = selected_amount;
|
details.sent = selected_amount;
|
||||||
|
|
||||||
let mut fee_amount = fee_amount.ceil() as u64;
|
let mut fee_amount = fee_amount.ceil() as u64;
|
||||||
|
if builder.has_absolute_fee() {
|
||||||
|
fee_amount = match builder.fee_policy.as_ref().unwrap() {
|
||||||
|
FeePolicy::FeeAmount(amount) => *amount,
|
||||||
|
_ => fee_amount,
|
||||||
|
}
|
||||||
|
};
|
||||||
let removed_output_fee_cost = (serialize(&removed_updatable_output).len() as f32
|
let removed_output_fee_cost = (serialize(&removed_updatable_output).len() as f32
|
||||||
* new_feerate.as_sat_vb())
|
* new_feerate.as_sat_vb())
|
||||||
.ceil() as u64;
|
.ceil() as u64;
|
||||||
@ -659,14 +682,23 @@ where
|
|||||||
let change_val = selected_amount - amount_needed - fee_amount;
|
let change_val = selected_amount - amount_needed - fee_amount;
|
||||||
let change_val_after_add = change_val.saturating_sub(removed_output_fee_cost);
|
let change_val_after_add = change_val.saturating_sub(removed_output_fee_cost);
|
||||||
if !builder.send_all && !change_val_after_add.is_dust() {
|
if !builder.send_all && !change_val_after_add.is_dust() {
|
||||||
|
if builder.has_absolute_fee() {
|
||||||
|
removed_updatable_output.value = change_val_after_add + removed_output_fee_cost;
|
||||||
|
details.received += change_val_after_add + removed_output_fee_cost;
|
||||||
|
} else {
|
||||||
removed_updatable_output.value = change_val_after_add;
|
removed_updatable_output.value = change_val_after_add;
|
||||||
fee_amount += removed_output_fee_cost;
|
fee_amount += removed_output_fee_cost;
|
||||||
details.received += change_val_after_add;
|
details.received += change_val_after_add;
|
||||||
|
}
|
||||||
|
|
||||||
tx.output.push(removed_updatable_output);
|
tx.output.push(removed_updatable_output);
|
||||||
} else if builder.send_all && !change_val_after_add.is_dust() {
|
} else if builder.send_all && !change_val_after_add.is_dust() {
|
||||||
|
if builder.has_absolute_fee() {
|
||||||
|
removed_updatable_output.value = change_val_after_add + removed_output_fee_cost;
|
||||||
|
} else {
|
||||||
removed_updatable_output.value = change_val_after_add;
|
removed_updatable_output.value = change_val_after_add;
|
||||||
fee_amount += removed_output_fee_cost;
|
fee_amount += removed_output_fee_cost;
|
||||||
|
}
|
||||||
|
|
||||||
// send_all to our address
|
// send_all to our address
|
||||||
if self.is_mine(&removed_updatable_output.script_pubkey)? {
|
if self.is_mine(&removed_updatable_output.script_pubkey)? {
|
||||||
@ -1210,6 +1242,17 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the fee rate if specified or a default
|
||||||
|
fn get_fee_rate(fee_policy: &Option<FeePolicy>) -> FeeRate {
|
||||||
|
if fee_policy.is_none() {
|
||||||
|
return FeeRate::default();
|
||||||
|
}
|
||||||
|
match fee_policy.as_ref().unwrap() {
|
||||||
|
FeePolicy::FeeRate(fr) => *fr,
|
||||||
|
_ => FeeRate::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -1660,6 +1703,21 @@ mod test {
|
|||||||
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
|
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_tx_absolute_fee() {
|
||||||
|
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
|
||||||
|
let addr = wallet.get_new_address().unwrap();
|
||||||
|
let (psbt, details) = wallet
|
||||||
|
.create_tx(
|
||||||
|
TxBuilder::with_recipients(vec![(addr.script_pubkey(), 0)])
|
||||||
|
.fee_absolute(100)
|
||||||
|
.send_all(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(details.fees, 100);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_tx_add_change() {
|
fn test_create_tx_add_change() {
|
||||||
use super::tx_builder::TxOrdering;
|
use super::tx_builder::TxOrdering;
|
||||||
@ -2049,6 +2107,71 @@ mod test {
|
|||||||
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature);
|
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bump_fee_absolute_reduce_change() {
|
||||||
|
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
|
||||||
|
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||||
|
let (psbt, mut original_details) = wallet
|
||||||
|
.create_tx(
|
||||||
|
TxBuilder::with_recipients(vec![(addr.script_pubkey(), 25_000)]).enable_rbf(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut tx = psbt.extract_tx();
|
||||||
|
let txid = tx.txid();
|
||||||
|
// skip saving the new utxos, we know they can't be used anyways
|
||||||
|
for txin in &mut tx.input {
|
||||||
|
txin.witness.push([0x00; 108].to_vec()); // fake signature
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.del_utxo(&txin.previous_output)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
original_details.transaction = Some(tx);
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.set_tx(&original_details)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (psbt, details) = wallet
|
||||||
|
.bump_fee(&txid, TxBuilder::new().fee_absolute(200))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(details.sent, original_details.sent);
|
||||||
|
assert_eq!(
|
||||||
|
details.received + details.fees,
|
||||||
|
original_details.received + original_details.fees
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
details.fees > original_details.fees,
|
||||||
|
"{} > {}",
|
||||||
|
details.fees,
|
||||||
|
original_details.fees
|
||||||
|
);
|
||||||
|
|
||||||
|
let tx = &psbt.global.unsigned_tx;
|
||||||
|
assert_eq!(tx.output.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
tx.output
|
||||||
|
.iter()
|
||||||
|
.find(|txout| txout.script_pubkey == addr.script_pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.value,
|
||||||
|
25_000
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tx.output
|
||||||
|
.iter()
|
||||||
|
.find(|txout| txout.script_pubkey != addr.script_pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.value,
|
||||||
|
details.received
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(details.fees, 200);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bump_fee_reduce_send_all() {
|
fn test_bump_fee_reduce_send_all() {
|
||||||
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
|
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
|
||||||
@ -2096,6 +2219,48 @@ mod test {
|
|||||||
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature);
|
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bump_fee_absolute_reduce_send_all() {
|
||||||
|
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
|
||||||
|
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||||
|
let (psbt, mut original_details) = wallet
|
||||||
|
.create_tx(
|
||||||
|
TxBuilder::with_recipients(vec![(addr.script_pubkey(), 0)])
|
||||||
|
.send_all()
|
||||||
|
.enable_rbf(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut tx = psbt.extract_tx();
|
||||||
|
let txid = tx.txid();
|
||||||
|
for txin in &mut tx.input {
|
||||||
|
txin.witness.push([0x00; 108].to_vec()); // fake signature
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.del_utxo(&txin.previous_output)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
original_details.transaction = Some(tx);
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.set_tx(&original_details)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (psbt, details) = wallet
|
||||||
|
.bump_fee(&txid, TxBuilder::new().send_all().fee_absolute(300))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(details.sent, original_details.sent);
|
||||||
|
assert!(details.fees > original_details.fees);
|
||||||
|
|
||||||
|
let tx = &psbt.global.unsigned_tx;
|
||||||
|
assert_eq!(tx.output.len(), 1);
|
||||||
|
assert_eq!(tx.output[0].value + details.fees, details.sent);
|
||||||
|
|
||||||
|
assert_eq!(details.fees, 300);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "InsufficientFunds")]
|
#[should_panic(expected = "InsufficientFunds")]
|
||||||
fn test_bump_fee_remove_send_all_output() {
|
fn test_bump_fee_remove_send_all_output() {
|
||||||
@ -2211,6 +2376,68 @@ mod test {
|
|||||||
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(50.0), @add_signature);
|
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(50.0), @add_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bump_fee_absolute_add_input() {
|
||||||
|
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
|
||||||
|
wallet.database.borrow_mut().received_tx(
|
||||||
|
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
|
||||||
|
Some(100),
|
||||||
|
);
|
||||||
|
|
||||||
|
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||||
|
let (psbt, mut original_details) = wallet
|
||||||
|
.create_tx(
|
||||||
|
TxBuilder::with_recipients(vec![(addr.script_pubkey(), 45_000)]).enable_rbf(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut tx = psbt.extract_tx();
|
||||||
|
let txid = tx.txid();
|
||||||
|
// skip saving the new utxos, we know they can't be used anyways
|
||||||
|
for txin in &mut tx.input {
|
||||||
|
txin.witness.push([0x00; 108].to_vec()); // fake signature
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.del_utxo(&txin.previous_output)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
original_details.transaction = Some(tx);
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.set_tx(&original_details)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (psbt, details) = wallet
|
||||||
|
.bump_fee(&txid, TxBuilder::new().fee_absolute(6_000))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(details.sent, original_details.sent + 25_000);
|
||||||
|
assert_eq!(details.fees + details.received, 30_000);
|
||||||
|
|
||||||
|
let tx = &psbt.global.unsigned_tx;
|
||||||
|
assert_eq!(tx.input.len(), 2);
|
||||||
|
assert_eq!(tx.output.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
tx.output
|
||||||
|
.iter()
|
||||||
|
.find(|txout| txout.script_pubkey == addr.script_pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.value,
|
||||||
|
45_000
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tx.output
|
||||||
|
.iter()
|
||||||
|
.find(|txout| txout.script_pubkey != addr.script_pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.value,
|
||||||
|
details.received
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(details.fees, 6_000);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bump_fee_no_change_add_input_and_change() {
|
fn test_bump_fee_no_change_add_input_and_change() {
|
||||||
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
|
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
|
||||||
@ -2422,6 +2649,78 @@ mod test {
|
|||||||
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
|
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bump_fee_absolute_force_add_input() {
|
||||||
|
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
|
||||||
|
let incoming_txid = wallet.database.borrow_mut().received_tx(
|
||||||
|
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
|
||||||
|
Some(100),
|
||||||
|
);
|
||||||
|
|
||||||
|
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||||
|
let (psbt, mut original_details) = wallet
|
||||||
|
.create_tx(
|
||||||
|
TxBuilder::with_recipients(vec![(addr.script_pubkey(), 45_000)]).enable_rbf(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut tx = psbt.extract_tx();
|
||||||
|
let txid = tx.txid();
|
||||||
|
// skip saving the new utxos, we know they can't be used anyways
|
||||||
|
for txin in &mut tx.input {
|
||||||
|
txin.witness.push([0x00; 108].to_vec()); // fake signature
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.del_utxo(&txin.previous_output)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
original_details.transaction = Some(tx);
|
||||||
|
wallet
|
||||||
|
.database
|
||||||
|
.borrow_mut()
|
||||||
|
.set_tx(&original_details)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// the new fee_rate is low enough that just reducing the change would be fine, but we force
|
||||||
|
// the addition of an extra input with `add_utxo()`
|
||||||
|
let (psbt, details) = wallet
|
||||||
|
.bump_fee(
|
||||||
|
&txid,
|
||||||
|
TxBuilder::new()
|
||||||
|
.add_utxo(OutPoint {
|
||||||
|
txid: incoming_txid,
|
||||||
|
vout: 0,
|
||||||
|
})
|
||||||
|
.fee_absolute(250),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(details.sent, original_details.sent + 25_000);
|
||||||
|
assert_eq!(details.fees + details.received, 30_000);
|
||||||
|
|
||||||
|
let tx = &psbt.global.unsigned_tx;
|
||||||
|
assert_eq!(tx.input.len(), 2);
|
||||||
|
assert_eq!(tx.output.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
tx.output
|
||||||
|
.iter()
|
||||||
|
.find(|txout| txout.script_pubkey == addr.script_pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.value,
|
||||||
|
45_000
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tx.output
|
||||||
|
.iter()
|
||||||
|
.find(|txout| txout.script_pubkey != addr.script_pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.value,
|
||||||
|
details.received
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(details.fees, 250);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sign_single_xprv() {
|
fn test_sign_single_xprv() {
|
||||||
let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||||
|
@ -60,7 +60,7 @@ use crate::types::{FeeRate, UTXO};
|
|||||||
pub struct TxBuilder<D: Database, Cs: CoinSelectionAlgorithm<D>> {
|
pub struct TxBuilder<D: Database, Cs: CoinSelectionAlgorithm<D>> {
|
||||||
pub(crate) recipients: Vec<(Script, u64)>,
|
pub(crate) recipients: Vec<(Script, u64)>,
|
||||||
pub(crate) send_all: bool,
|
pub(crate) send_all: bool,
|
||||||
pub(crate) fee_rate: Option<FeeRate>,
|
pub(crate) fee_policy: Option<FeePolicy>,
|
||||||
pub(crate) policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
pub(crate) policy_path: Option<BTreeMap<String, Vec<usize>>>,
|
||||||
pub(crate) utxos: Option<Vec<OutPoint>>,
|
pub(crate) utxos: Option<Vec<OutPoint>>,
|
||||||
pub(crate) unspendable: Option<Vec<OutPoint>>,
|
pub(crate) unspendable: Option<Vec<OutPoint>>,
|
||||||
@ -76,6 +76,12 @@ pub struct TxBuilder<D: Database, Cs: CoinSelectionAlgorithm<D>> {
|
|||||||
phantom: PhantomData<D>,
|
phantom: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FeePolicy {
|
||||||
|
FeeRate(FeeRate),
|
||||||
|
FeeAmount(u64),
|
||||||
|
}
|
||||||
|
|
||||||
// Unfortunately derive doesn't work with `PhantomData`: https://github.com/rust-lang/rust/issues/26925
|
// Unfortunately derive doesn't work with `PhantomData`: https://github.com/rust-lang/rust/issues/26925
|
||||||
impl<D: Database, Cs: CoinSelectionAlgorithm<D>> Default for TxBuilder<D, Cs>
|
impl<D: Database, Cs: CoinSelectionAlgorithm<D>> Default for TxBuilder<D, Cs>
|
||||||
where
|
where
|
||||||
@ -85,7 +91,7 @@ where
|
|||||||
TxBuilder {
|
TxBuilder {
|
||||||
recipients: Default::default(),
|
recipients: Default::default(),
|
||||||
send_all: Default::default(),
|
send_all: Default::default(),
|
||||||
fee_rate: Default::default(),
|
fee_policy: Default::default(),
|
||||||
policy_path: Default::default(),
|
policy_path: Default::default(),
|
||||||
utxos: Default::default(),
|
utxos: Default::default(),
|
||||||
unspendable: Default::default(),
|
unspendable: Default::default(),
|
||||||
@ -140,7 +146,13 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs> {
|
|||||||
|
|
||||||
/// Set a custom fee rate
|
/// Set a custom fee rate
|
||||||
pub fn fee_rate(mut self, fee_rate: FeeRate) -> Self {
|
pub fn fee_rate(mut self, fee_rate: FeeRate) -> Self {
|
||||||
self.fee_rate = Some(fee_rate);
|
self.fee_policy = Some(FeePolicy::FeeRate(fee_rate));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an absolute fee
|
||||||
|
pub fn fee_absolute(mut self, fee_amount: u64) -> Self {
|
||||||
|
self.fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +299,7 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs> {
|
|||||||
TxBuilder {
|
TxBuilder {
|
||||||
recipients: self.recipients,
|
recipients: self.recipients,
|
||||||
send_all: self.send_all,
|
send_all: self.send_all,
|
||||||
fee_rate: self.fee_rate,
|
fee_policy: self.fee_policy,
|
||||||
policy_path: self.policy_path,
|
policy_path: self.policy_path,
|
||||||
utxos: self.utxos,
|
utxos: self.utxos,
|
||||||
unspendable: self.unspendable,
|
unspendable: self.unspendable,
|
||||||
@ -303,6 +315,17 @@ impl<D: Database, Cs: CoinSelectionAlgorithm<D>> TxBuilder<D, Cs> {
|
|||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if an absolute fee was specified
|
||||||
|
pub fn has_absolute_fee(&self) -> bool {
|
||||||
|
if self.fee_policy.is_none() {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
match self.fee_policy.as_ref().unwrap() {
|
||||||
|
FeePolicy::FeeAmount(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ordering of the transaction's inputs and outputs
|
/// Ordering of the transaction's inputs and outputs
|
||||||
|
Loading…
x
Reference in New Issue
Block a user