-
Notifications
You must be signed in to change notification settings - Fork 349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor: Migrate bdk::FeeRate to bitcoin::FeeRate #1141
Conversation
@nondiremanuel I think this PR and the associated issue #1136 deserves to be added to the alpha. We can discuss more on the next team call, but I think it needs to be done so we don't duplicate functionality with our |
Thanks for taking this on @junderw ! looks good so far. |
@@ -276,7 +276,7 @@ impl CoinSelectionAlgorithm for OldestFirstCoinSelection { | |||
pub fn decide_change(remaining_amount: u64, fee_rate: FeeRate, drain_script: &Script) -> Excess { | |||
// drain_output_len = size(len(script_pubkey)) + len(script_pubkey) + size(output_value) | |||
let drain_output_len = serialize(drain_script).len() + 8usize; | |||
let change_fee = fee_rate.fee_vb(drain_output_len); | |||
let change_fee = (fee_rate * Weight::from_wu((drain_output_len * 4) as u64)).to_sat(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we hardcoding 4 here?
Might be worth a comment...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let change_fee = (fee_rate * Weight::from_vb_unchecked(drain_output_len as u64)).to_sat();
builder.fee_rate(FeeRate::from_sat_per_vb(2.5)).enable_rbf(); | ||
builder | ||
.fee_rate(FeeRate::from_sat_per_vb_unchecked(3)) | ||
.enable_rbf(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the logic of the test. We are now testing an integer instead of a float.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, using kwu here to test 2.5 (which would be 625) would probably be better.
assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature); | ||
assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_kwu(751), @add_signature); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change from vBytes to satoshis per 1000 weight units?
let fee_rate = FeeRate::from_sat_per_vb(2.01); | ||
let fee_rate = FeeRate::from_sat_per_kwu(522); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change from vBytes to satoshis per 1000 weight units?
assert_eq!(psbt.inputs.len(), 1); | ||
assert_eq!(psbt.inputs.len(), 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems odd to me.
From my understanding you are not changing the PSBT inputs in this test.
Hence, why this change?
Is this test broken? Was this passing before your change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since coin selection is affected by fees, likely the switch to a non-float FeeRate had some effect on the outcome of selected coins.
This will need investigation.
let fee_rate = FeeRate::from_sat_per_vb(1.0); | ||
let fee_rate = FeeRate::from_sat_per_kwu(251); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change from vBytes to satoshis per 1000 weight units?
let fee_rate = FeeRate::from_sat_per_vb(1.0); | ||
let fee_rate = FeeRate::from_sat_per_kwu(251); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change from vBytes to satoshis per 1000 weight units?
To answer all of these questions: Because FeeRate now only deals with integers, so in order to get more granularity, using sat/kwu allows us to perform <1 sat/vbyte. As for all the other places: Because that change is what was necessary to make the tests pass. 250 sat/kwu = 1 sat/vbyte, so technically, this is also changing the value to 1.004 sat/vbyte I have not investigated into why this change was needed to make the tests pass. The next steps in this PR will be uncovering why each of these test changes was necessary and considering what that means for the library. |
I edited the body of the PR:
|
let weight = self.clone().extract_tx().weight(); | ||
FeeRate::from_wu(fee, weight) | ||
Some(FeeRate::from_sat_per_kwu( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fee_amount.map(|fee| {
Amount::from_sat(fee) / weight
})
@@ -489,7 +491,7 @@ impl CoinSelectionAlgorithm for BranchAndBoundCoinSelection { | |||
curr_value, | |||
curr_available_value, | |||
target_amount, | |||
cost_of_change, | |||
cost_of_change as f32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the signature of BranchAndBoundCoinSelection::bnb
change to accept cost_of_change: u64
as parameter ?
@@ -574,7 +574,7 @@ impl<D> Wallet<D> { | |||
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> { | |||
self.calculate_fee(tx).map(|fee| { | |||
let weight = tx.weight(); | |||
FeeRate::from_wu(fee, weight) | |||
FeeRate::from_sat_per_kwu(fee.saturating_mul(1000) / weight.to_wu()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.calculate_fee(tx)
.map(|fee| Amount::from_sat(fee) / tx.weight())
@@ -1350,7 +1352,7 @@ impl<D> Wallet<D> { | |||
utxos: original_utxos, | |||
bumping_fee: Some(tx_builder::PreviousFee { | |||
absolute: fee, | |||
rate: fee_rate.as_sat_per_vb(), | |||
rate: fee_rate.to_sat_per_vb_ceil() as f32, | |||
}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be rate: fee_rate
if the type of rate
in PreviousFee
is changed to bitcoin::FeeRate
assert!(finalized_fee_rate.as_sat_per_vb() >= expected_fee_rate); | ||
assert!(finalized_fee_rate.as_sat_per_vb() < unfinalized_fee_rate.as_sat_per_vb()); | ||
assert!(finalized_fee_rate.to_sat_per_vb_ceil() as f32 >= expected_fee_rate); | ||
assert!(finalized_fee_rate.to_sat_per_vb_ceil() <= unfinalized_fee_rate.to_sat_per_vb_ceil()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we provided a method for creating a FeeRate
from a float with something like
fn from_sat_per_vbf(sat_vb: f64) -> FeeRate {...}
then the above can be simplified
let expected_fee_rate = FeeRate::from_sat_per_vbf(1.2345);
builder.fee_rate(expected_fee_rate);
...
assert!(finalized_fee_rate >= expected_fee_rate);
assert!(finalized_fee_rate < unfinalized_fee_rate);
// fee rate (sats per vbyte) = fee / vbytes = 1000 / 113 = 8.8495575221 rounded to 8.849558 | ||
assert_eq!(tx_fee_rate.as_sat_per_vb(), 8.849558); | ||
// fee rate (sats per vbyte) = fee / vbytes = 1000 / 113 = 8.8495575221 rounded up to 9 | ||
assert_eq!(tx_fee_rate.to_sat_per_vb_ceil(), 9); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we lose precision with to_sat_per_vb_ceil
, we should assert the value in sat/kwu also.
We don't need to implement the |
Of course! |
475a772 refactor(bdk)!: Remove trait Vbytes (vmammal) 0d64beb chore: organize some imports (vmammal) 89608dd refactor(bdk): display CreateTxError::FeeRateTooLow in sat/vb (vmammal) 09bd86e test(bdk): initialize all feerates from `u64` (vmammal) 004957d refactor(bdk)!: drop FeeRate from bdk::types (vmammal) Pull request description: ### Description This follows a similar approach to #1141 namely to remove `FeeRate` from `bdk::types` and instead defer to the upstream implementation for all fee rates. The idea is that making the switch allows BDK to benefit from a higher level abstraction, leaving the implementation details largely hidden. As noted in #774, we should avoid extraneous conversions that can result in deviations in estimated transaction size and calculated fee amounts, etc. This would happen for example whenever calling a method like `FeeRate::to_sat_per_vb_ceil`. The only exception I would make is if we must return a fee rate error to the user, we might prefer to display it in the more familiar sats/vb, but this would only be useful if the rate can be expressed as a float. ### Notes to the reviewers `bitcoin::FeeRate` is an integer whose native unit is sats per kilo-weight unit. In order to facilitate the change, a helper method `feerate_unchecked` is added and used only in wallet tests and psbt tests as necessary to convert existing fee rates to the new type. It's "unchecked" in the sense that we're not checking for integer overflow, because it's assumed we're passing a valid fee rate in a unit test. Potential follow-ups can include: - [x] Constructing a proper `FeeRate` from a `u64` in all unit tests, and thus obviating the need for the helper `feerate_unchecked` going forward. - [x] Remove trait `Vbytes`. - Consider adding an extra check that the argument to `TxBuilder::drain_to` is within "standard" size limits. - Consider refactoring `coin_selection::select_sorted_utxos` to be efficient and readable. closes #1136 ### Changelog notice - Removed `FeeRate` type. All fee rates are now rust-bitcoin [`FeeRate`](https://docs.rs/bitcoin/latest/bitcoin/blockdata/fee_rate/struct.FeeRate.html). - Removed trait `Vbytes`. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing ACKs for top commit: evanlinjin: ACK 475a772 Tree-SHA512: 511dab8aa7a65d2b15b160cb4feb96964e8401bb04cda4ef0f0244524bf23a575b3739783a14b90d2dccc984b3f30f5dabfb0a890ffe7c897c2dc23ba301bcaf
Closes #1136
Description
This PR will eventually migrate FeeRate to rust-bitcoin's FeeRate.
Notes to the reviewers
Changelog notice
Changed
Checklists
All Submissions:
cargo fmt
andcargo clippy
before committing