Skip to content
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

Add support for custom sorting and deprecate BIP69 #556

Closed
wants to merge 2 commits into from

Conversation

Yureien
Copy link
Contributor

@Yureien Yureien commented Feb 27, 2022

Description

Resolves #534.

This PR adds a way to add custom sorting functions for both inputs and outputs. BIP69 is permanently deprecated.

Notes to the reviewers

Fn was used instead of FnMut to reduce complexity, since it's inside an Arc. We can try supporting FnMut but that would also require us to use a Mutex and I'm not sure whether it's worth the added complexity.

Additionally, I cannot decide what kind of test will be good for this. Should I implement just a simple sort (like a.value.cmp(&b.value)) and test if it's in proper order?

All Submissions:

  • I've signed all my commits
  • I followed the contribution guidelines
  • I ran cargo fmt and cargo clippy before committing

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature
  • I've updated CHANGELOG.md

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR

Copy link
Contributor

@rajarshimaitra rajarshimaitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial Concept ACK.. But I don't recall if we have decided to depreciate BIP69.. It feel its still useful to keep it around?? Use it just as another option aTxOrdering? Some user might want to use lexicographic..

On the test, what comes to mind is use the lexicoraphic function itself as custom fn, and compare against standard test vectors.. You should get test vecs in reference impls of bip69.. maybe rust-bitcoin have some.. Or else cook up your simple custom sort and test against some self generated vectors.. In that case its better to hardcode the derived vecs in the test code..

@afilini
Copy link
Member

afilini commented Mar 10, 2022

But I don't recall if we have decided to depreciate BIP69

Yes, the idea is to deprecate it, but never remove it. It's just to make the users aware of its issues, they will be shown a warning and they can decide if they want to keep using it or not.

pub enum TxOrdering {
/// Randomized (default)
Shuffle,
/// Unchanged
Untouched,
/// BIP69 / Lexicographic
#[deprecated = "BIP69 does not improve privacy as was the intention of the BIP"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we can, but if possible I would add a link to the discussion here on github. Maybe you could make a multiline warning, first line stays the same, and in a new line something like "To know more read the discussion at: ...".

Similar to what the rust compiler does when you have an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@afilini Can you give me an example in the rust lang? I can just look at their source code and implement the same way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant something like:

warning: the feature `native_link_modifiers` is incomplete and may not be safe to use and/or cause compiler crashes
 --> library/unwind/src/lib.rs:8:12
  |
8 | #![feature(native_link_modifiers)]
  |            ^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(incomplete_features)]` on by default
  = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information

I like that "note: see issue ..." at the end

/// Provide custom comparison functions for sorting
Custom {
/// Transaction inputs sort function
input_sort: Arc<dyn Fn(&TxIn, &TxIn) -> Ordering>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor nit, but this confused me for a second: I would just import std::cmp and then here use cmp::Ordering.

For a second I confused Ordering with TxOrdering and I couldn't figure out what was going on.

Bip69Lexicographic,
/// Provide custom comparison functions for sorting
Custom {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may have to add some sort of "state" here, something that the two functions could look at to know what to do essentially. It doesn't have to be mutable, that would mess a lot of this up.

If we don't wanna add generic types we could maybe add a Box<dyn std::any::Any>, which you can then downcast to whatever you need in the functions. Obviously you would also have to add that as an extra argument to your functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC @LLFourn who proposed sorting with a shared secret. I think you would not be able to do that, unless you introduce this state.

I'm not sure what happens if you try borrowing from local variables in the two Fns, I'm guessing you are not allowed to do that because of lifetime issues. Maybe we should test this first, actually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fadedcoder can you write a doc comment or a test which maybe demos how to use a secret to hash the input/output to produce the key?

Given the awkward Arc thing it might be better to have a convenience method like set_custom_ordering(i_f: impl Fn(&Txin...), o_f: impl Fn(&TxOut,..)) which would wrap the Arcs around them.

I think we may have to add some sort of "state" here, something that the two functions could look at to know what to do essentially. It doesn't have to be mutable, that would mess a lot of this up.

Just move in what you need?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just move in what you need?

For some reason I always instinctively think that move will only work with FnOnce because I feel like the value would be consumed at the first iteration.

So yeah, move probably works, but as you said it would be nice to have an example and more tests to verify this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sure, I'll add some tests or examples soon!

Copy link
Contributor

@LLFourn LLFourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. LGTM but needs a test.

output_sort,
} => {
tx.input.sort_unstable_by(|a, b| input_sort(a, b));
tx.output.sort_unstable_by(|a, b| output_sort(a, b));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 I think you want sort_by_cached_key here since you might do some expensive computation why computing the key e.g. hashing.

Bip69Lexicographic,
/// Provide custom comparison functions for sorting
Custom {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fadedcoder can you write a doc comment or a test which maybe demos how to use a secret to hash the input/output to produce the key?

Given the awkward Arc thing it might be better to have a convenience method like set_custom_ordering(i_f: impl Fn(&Txin...), o_f: impl Fn(&TxOut,..)) which would wrap the Arcs around them.

I think we may have to add some sort of "state" here, something that the two functions could look at to know what to do essentially. It doesn't have to be mutable, that would mess a lot of this up.

Just move in what you need?

@notmandatory
Copy link
Member

Hi, please rebase to pickup changes in #596. Thanks!

@danielabrozzoni
Copy link
Member

Hey @Yureien, are you still working on this?

@danielabrozzoni
Copy link
Member

I'm closing this PR as the author hasn't been working on it for a while. If that's not the case, and instead you wish to keep working on this, feel free to re-open it :)

notmandatory added a commit to notmandatory/bdk that referenced this pull request Jul 5, 2024
…e BIP69

3bee563 refactor(wallet)!: remove TxOrdering::Bip69Lexicographic (nymius)
e5cb7b2 feat(wallet): add TxOrdering::Custom (FadedCoder)

Pull request description:

  <!-- You can erase any parts of this template not applicable to your Pull Request. -->

  ### Description

  Resolves bitcoindevkit#534.

  Resumes from the work in bitcoindevkit#556.

  Add custom sorting function for inputs and outputs through `TxOrdering::Custom` and deprecates `TxOrdering::Bip69Lexicographic`.

  <!-- Describe the purpose of this PR, what's being adding and/or fixed -->

  ### Notes to the reviewers

  I tried consider all discussions in bitcoindevkit#534 while implementing some changes to the original PR. I created a summary of the considerations I had while implementing this:

  ##### Why use smart pointers?
  The size of enums and structs should be known at compilation time. A struct whose fields implements some kind of trait cannot be specified without using a smart pointer because the size of the implementations of the trait cannot be known beforehand.

  ##### Why `Arc` or `Rc` instead of `Box`?
  The majority of the useful smart pointers that I know (`Arc`, `Box`, `Rc`) for this case implement `Drop` which rules out the implementation of `Copy`, making harder to manipulate a simple enum like `TxOrdering`. `Clone` can be used instead, implemented by `Arc` and `Rc`, but not implemented by `Box`.

  #####  Why `Arc` instead of `Rc`?
  Multi threading I guess.

  ##### Why using a type alias like `TxVecSort`?
  cargo-clippy was accusing a too complex type if using the whole type inlined in the struct inside the enum.

  ##### Why `Fn` and not `FnMut`?
  `FnMut` is not allowed inside `Arc`. I think this is due to the `&mut self` ocupies the first parameter of the `call` method when desugared (https://rustyyato.github.io/rust/syntactic/sugar/2019/01/17/Closures-Magic-Functions.html), which doesn't respects `Arc` limitation of not having mutable references to data stored inside `Arc`:
  Quoting the [docs](https://doc.rust-lang.org/std/sync/struct.Arc.html):
  > you cannot generally obtain a mutable reference to something inside an `Arc`.

  `FnOnce` > `FnMut` > `Fn`, where `>` stands for "is supertrait of", so, `Fn` can be used everywhere `FnMut` is expected.

  ##### Why not `&'a dyn FnMut`?
  It needs to include a lifetime parameter in `TxOrdering`, which will force the addition of a lifetime parameter in `TxParams`, which will require the addition of a lifetime parameter in a lot of places more. **Which one is preferable?**

  <!-- In this section you can include notes directed to the reviewers, like explaining why some parts
  of the PR were done in a specific way -->

  ### Changelog notice

  - Adds new `TxOrdering` variant: `TxOrdering::Custom`. A structure that stores the ordering functions to sort the inputs and outputs of a transaction.
  - Deprecates `TxOrdering::Bip69Lexicographic`.

  <!-- Notice the release manager should include in the release tag message changelog -->
  <!-- See https://keepachangelog.com/en/1.0.0/ for examples -->

  ### Checklists

  #### All Submissions:

  * [ ] 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

  #### New Features:

  * [x] I've added tests for the new feature
  * [ ] I've added docs for the new feature

Top commit has no ACKs.

Tree-SHA512: 0d3e3ea9aee3a6c9e9d5e1ae93215be84bd1bd99907a319976517819aeda768a7166860a48a8d24abb30c516e0129decb6a6aebd8f24783ea2230143e6dcd72a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Remove BIP 69 sorting
6 participants