Skip to content

Commit

Permalink
feat!: use u64 for weights instead of u32
Browse files Browse the repository at this point in the history
Also change tests and examples to use `v0.32` of the `bitcoin` crate.
  • Loading branch information
evanlinjin committed Sep 7, 2024
1 parent 434c6e8 commit 2ca0375
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ std = []
[dev-dependencies]
rand = "0.8"
proptest = "1.4"
bitcoin = "0.30"
bitcoin = "0.32"
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@
```rust
use std::str::FromStr;
use bdk_coin_select::{ CoinSelector, Candidate, TR_KEYSPEND_TXIN_WEIGHT, Drain, FeeRate, Target, ChangePolicy, TargetOutputs, TargetFee, DrainWeights};
use bitcoin::{ Address, Network, Transaction, TxIn, TxOut };
use bitcoin::{ Amount, Address, Network, Transaction, TxIn, TxOut };

let recipient_addr =
Address::from_str("tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46").unwrap();
let recipient_addr: Address = "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
.parse::<Address<_>>()
.unwrap()
.assume_checked();

let outputs = vec![TxOut {
value: 3_500_000,
script_pubkey: recipient_addr.payload.script_pubkey(),
value: Amount::from_sat(3_500_000),
script_pubkey: recipient_addr.script_pubkey(),
}];

let target = Target {
outputs: TargetOutputs::fund_outputs(outputs.iter().map(|output| (output.weight() as u32, output.value))),
outputs: TargetOutputs::fund_outputs(outputs.iter().map(|output| (output.weight().to_wu(), output.value.to_sat()))),
fee: TargetFee::from_feerate(FeeRate::from_sat_per_vb(42.0))
};

Expand Down Expand Up @@ -87,14 +89,16 @@ metric by implementing the [`BnbMetric`] yourself but we don't recommend this.
use std::str::FromStr;
use bdk_coin_select::{ Candidate, CoinSelector, FeeRate, Target, TargetFee, TargetOutputs, ChangePolicy, TR_KEYSPEND_TXIN_WEIGHT, TR_DUST_RELAY_MIN_VALUE};
use bdk_coin_select::metrics::LowestFee;
use bitcoin::{ Address, Network, Transaction, TxIn, TxOut };
use bitcoin::{ Address, Amount, Network, Transaction, TxIn, TxOut };

let recipient_addr =
Address::from_str("tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46").unwrap();
let recipient_addr: Address = "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
.parse::<Address<_>>()
.unwrap()
.assume_checked();

let outputs = vec![TxOut {
value: 210_000,
script_pubkey: recipient_addr.payload.script_pubkey(),
value: Amount::from_sat(210_000),
script_pubkey: recipient_addr.script_pubkey(),
}];

let candidates = [
Expand Down Expand Up @@ -125,7 +129,7 @@ let mut coin_selector = CoinSelector::new(&candidates);

let target = Target {
fee: TargetFee::from_feerate(FeeRate::from_sat_per_vb(15.0)),
outputs: TargetOutputs::fund_outputs(outputs.iter().map(|output| (output.weight() as u32, output.value))),
outputs: TargetOutputs::fund_outputs(outputs.iter().map(|output| (output.weight().to_wu(), output.value.to_sat()))),
};

// The change output must be at least this size to be relayed.
Expand Down
12 changes: 6 additions & 6 deletions src/coin_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,14 @@ impl<'a> CoinSelector<'a> {

/// The weight of the inputs including the witness header and the varint for the number of
/// inputs.
pub fn input_weight(&self) -> u32 {
pub fn input_weight(&self) -> u64 {
let is_segwit_tx = self.selected().any(|(_, wv)| wv.is_segwit);
let witness_header_extra_weight = is_segwit_tx as u32 * 2;
let witness_header_extra_weight = is_segwit_tx as u64 * 2;

let input_count = self.selected().map(|(_, wv)| wv.input_count).sum::<usize>();
let input_varint_weight = varint_size(input_count) * 4;

let selected_weight: u32 = self
let selected_weight: u64 = self
.selected()
.map(|(_, candidate)| {
let mut weight = candidate.weight;
Expand Down Expand Up @@ -166,7 +166,7 @@ impl<'a> CoinSelector<'a> {
///
/// If you don't have any drain outputs (only target outputs) just set drain_weights to
/// [`DrainWeights::NONE`].
pub fn weight(&self, target_ouputs: TargetOutputs, drain_weight: DrainWeights) -> u32 {
pub fn weight(&self, target_ouputs: TargetOutputs, drain_weight: DrainWeights) -> u64 {
TX_FIXED_FIELD_WEIGHT
+ self.input_weight()
+ target_ouputs.output_weight_with_drain(drain_weight)
Expand Down Expand Up @@ -642,7 +642,7 @@ pub struct Candidate {
/// Total weight of including this/these UTXO(s).
/// `txin` fields: `prevout`, `nSequence`, `scriptSigLen`, `scriptSig`, `scriptWitnessLen`,
/// `scriptWitness` should all be included.
pub weight: u32,
pub weight: u64,
/// Total number of inputs; so we can calculate extra `varint` weight due to `vin` len changes.
pub input_count: usize,
/// Whether this [`Candidate`] contains at least one segwit spend.
Expand All @@ -660,7 +660,7 @@ impl Candidate {
///
/// `satisfaction_weight` is the weight of `scriptSigLen + scriptSig + scriptWitnessLen +
/// scriptWitness`.
pub fn new(value: u64, satisfaction_weight: u32, is_segwit: bool) -> Candidate {
pub fn new(value: u64, satisfaction_weight: u64, is_segwit: bool) -> Candidate {
let weight = TXIN_BASE_WEIGHT + satisfaction_weight;
Candidate {
value,
Expand Down
4 changes: 2 additions & 2 deletions src/drain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ pub struct DrainWeights {
/// The weight of including this drain output.
///
/// This must not take into account any weight change from varint output count.
pub output_weight: u32,
pub output_weight: u64,
/// The weight of spending this drain output (in the future).
pub spend_weight: u32,
pub spend_weight: u64,
/// The total number of outputs that the drain will use
pub n_outputs: usize,
}
Expand Down
20 changes: 10 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,35 @@ pub use drain::*;

/// Txin "base" fields include `outpoint` (32+4) and `nSequence` (4) and 1 byte for the scriptSig
/// length.
pub const TXIN_BASE_WEIGHT: u32 = (32 + 4 + 4 + 1) * 4;
pub const TXIN_BASE_WEIGHT: u64 = (32 + 4 + 4 + 1) * 4;

/// The weight of a TXOUT with a zero length `scriptPubKey`
#[allow(clippy::identity_op)]
pub const TXOUT_BASE_WEIGHT: u32 =
pub const TXOUT_BASE_WEIGHT: u64 =
// The value
4 * core::mem::size_of::<u64>() as u32
4 * core::mem::size_of::<u64>() as u64
// The spk length
+ (4 * 1);

/// The weight of the `nVersion` and `nLockTime` transaction fields
pub const TX_FIXED_FIELD_WEIGHT: u32 = (4 /* nVersion */ + 4/* nLockTime */) * 4;
pub const TX_FIXED_FIELD_WEIGHT: u64 = (4 /* nVersion */ + 4/* nLockTime */) * 4;

/// The weight of a taproot keyspend witness
pub const TR_KEYSPEND_SATISFACTION_WEIGHT: u32 = 1 /*witness_len*/ + 1 /*item len*/ + 64 /*signature*/;
pub const TR_KEYSPEND_SATISFACTION_WEIGHT: u64 = 1 /*witness_len*/ + 1 /*item len*/ + 64 /*signature*/;

/// The weight of a segwit `v1` (taproot) script pubkey in an output. This does not include the weight of
/// the `TxOut` itself or the script pubkey length field.
pub const TR_SPK_WEIGHT: u32 = (1 + 1 + 32) * 4; // version + push + key
pub const TR_SPK_WEIGHT: u64 = (1 + 1 + 32) * 4; // version + push + key

/// The weight of a taproot TxIn with witness
pub const TR_KEYSPEND_TXIN_WEIGHT: u32 = TXIN_BASE_WEIGHT + TR_KEYSPEND_SATISFACTION_WEIGHT;
pub const TR_KEYSPEND_TXIN_WEIGHT: u64 = TXIN_BASE_WEIGHT + TR_KEYSPEND_SATISFACTION_WEIGHT;

/// The minimum value a taproot output can have to be relayed with Bitcoin core's default dust relay
/// fee
pub const TR_DUST_RELAY_MIN_VALUE: u64 = 330;

/// Helper to calculate varint size. `v` is the value the varint represents.
const fn varint_size(v: usize) -> u32 {
const fn varint_size(v: usize) -> u64 {
if v <= 0xfc {
return 1;
}
Expand All @@ -71,6 +71,6 @@ const fn varint_size(v: usize) -> u32 {
}

#[allow(unused)]
fn txout_weight_from_spk_len(spk_len: usize) -> u32 {
(TXOUT_BASE_WEIGHT + varint_size(spk_len) + (spk_len as u32)) * 4
fn txout_weight_from_spk_len(spk_len: usize) -> u64 {
(TXOUT_BASE_WEIGHT + varint_size(spk_len) + (spk_len as u64)) * 4
}
10 changes: 5 additions & 5 deletions src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ pub struct TargetOutputs {
/// The sum of the values of the individual `TxOuts`s.
pub value_sum: u64,
/// The sum of the weights of the individual `TxOut`s.
pub weight_sum: u32,
pub weight_sum: u64,
/// The total number of outputs
pub n_outputs: usize,
}

impl TargetOutputs {
/// The output weight of the outptus we're trying to fund
pub fn output_weight(&self) -> u32 {
pub fn output_weight(&self) -> u64 {
self.weight_sum + varint_size(self.n_outputs) * 4
}

Expand All @@ -42,13 +42,13 @@ impl TargetOutputs {
/// adding the drain weights might add an extra vbyte for the length of the varint.
///
/// [`output_weight`]: Self::output_weight
pub fn output_weight_with_drain(&self, drain_weight: DrainWeights) -> u32 {
pub fn output_weight_with_drain(&self, drain_weight: DrainWeights) -> u64 {
let n_outputs = drain_weight.n_outputs + self.n_outputs;
varint_size(n_outputs) * 4 + drain_weight.output_weight + self.weight_sum
}

/// Creates a `TargetOutputs` from a list of outputs represented as `(weight, value)` pairs.
pub fn fund_outputs(outputs: impl IntoIterator<Item = (u32, u64)>) -> Self {
pub fn fund_outputs(outputs: impl IntoIterator<Item = (u64, u64)>) -> Self {
let mut n_outputs = 0;
let mut weight_sum = 0;
let mut value_sum = 0;
Expand Down Expand Up @@ -135,7 +135,7 @@ impl Replace {
/// This is defined by [RBF rule 4].
///
/// [RBF rule 4]: https://github.com/bitcoin/bitcoin/blob/master/doc/policy/mempool-replacements.md#current-replace-by-fee-policy
pub fn min_fee_to_do_replacement(&self, replacing_tx_weight: u32) -> u64 {
pub fn min_fee_to_do_replacement(&self, replacing_tx_weight: u64) -> u64 {
let min_fee_increment =
(replacing_tx_weight as f32 * self.incremental_relay_feerate.spwu()).ceil() as u64;
self.fee + min_fee_increment
Expand Down
6 changes: 3 additions & 3 deletions tests/changeless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ proptest! {
let mut rng = TestRng::deterministic_rng(RngAlgorithm::ChaCha);
let feerate = FeeRate::from_sat_per_vb(feerate);
let drain_weights = DrainWeights {
output_weight: drain_weight,
spend_weight: drain_spend_weight,
output_weight: drain_weight as u64,
spend_weight: drain_spend_weight as u64,
n_outputs: n_drain_outputs,
};

Expand All @@ -61,7 +61,7 @@ proptest! {
outputs: TargetOutputs {
n_outputs: n_target_outputs,
value_sum: target_value,
weight_sum: target_weight,
weight_sum: target_weight as u64,
},
fee: TargetFee {
rate: feerate,
Expand Down
6 changes: 3 additions & 3 deletions tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl StrategyParams {
},
outputs: TargetOutputs {
value_sum: self.target_value,
weight_sum: self.target_weight,
weight_sum: self.target_weight as u64,
n_outputs: self.n_target_outputs,
},
}
Expand All @@ -233,8 +233,8 @@ impl StrategyParams {

pub fn drain_weights(&self) -> DrainWeights {
DrainWeights {
output_weight: self.drain_weight,
spend_weight: self.drain_spend_weight,
output_weight: self.drain_weight as u64,
spend_weight: self.drain_spend_weight as u64,
n_outputs: self.n_drain_outputs,
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/lowest_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn combined_changeless_metric() {
let params = common::StrategyParams {
n_candidates: 100,
target_value: 100_000,
target_weight: 1000 - TX_FIXED_FIELD_WEIGHT - 1,
target_weight: 1000 - TX_FIXED_FIELD_WEIGHT as u32 - 1,
replace: None,
feerate: 5.0,
feerate_lt_diff: -4.0,
Expand Down
2 changes: 1 addition & 1 deletion tests/rbf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn run_bitcoin_core_rbf_tests() {
fn pays_for_rbf(
original_fees: u64,
replacement_fees: u64,
replacement_vsize: u32,
replacement_vsize: u64,
relay_fee: FeeRate,
) -> bool {
let min_fee = Replace {
Expand Down
Loading

0 comments on commit 2ca0375

Please sign in to comment.