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

Upgrade bitcoin dependency to v0.29.0 #450

Merged
merged 3 commits into from Sep 20, 2022

Conversation

tcharding
Copy link
Member

@tcharding tcharding commented Jul 25, 2022

This is going to need a pretty careful review by @sanket1729 and/or @apoelstra to double check all the sequence/lock_time stuff. Even with the new APIs this stuff is non-trivial. This patch changes the current behaviour around logic for these two types. I am unable to work out if current behaviour has bugs in it or if I just don't fully understand it.

Note

Please ignore the branch name, it stale now since this PR upgrades to the newly released bitcoin v0.29.1

@tcharding tcharding changed the title Depend on "possible" bitcoin 0.29.0 Depend on bitcoin 0.29.0 release tracking PR Jul 27, 2022
Copy link

@Kixunil Kixunil left a comment

Choose a reason for hiding this comment

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

I found a few mistakes. Disclaimer: didn't do super-deep review.

@@ -257,6 +257,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
TaprootBuilderError::EmptyTree => {
unreachable!("Taptree is a well formed tree with atleast 1 element")
}
_ => unreachable!("non_exhaustive catchall"),
Copy link

Choose a reason for hiding this comment

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

This is actually wrong, a new variant could be added for any reason. To correctly handle the error it needs to be returned.

If this is too much hassle maybe we should improve the API or perhaps commit to never changing TaprootBuilderError and remove #[non_exhaustive]

Copy link
Member Author

Choose a reason for hiding this comment

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

This whole error match is strange, every error variant has an unreachable!. To be honest I was mainly just interested in checking that rust-bitcoin worked so I just hacked this up to make it build.

Copy link

Choose a reason for hiding this comment

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

They have explanation why it should be OK, so I think it's fine. But it makes me question the whole API. Maybe we should've used typestate instead?

Copy link
Member

Choose a reason for hiding this comment

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

I will make a cleanup PR to upstream to cleanup this API

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

This is fixed now, please check my use of a single unreachable statement @sanket1729

src/interpreter/error.rs Outdated Show resolved Hide resolved
src/interpreter/error.rs Outdated Show resolved Hide resolved
},
///Absolute Timelock for CLTV.
AbsoluteTimelock {
/// The value of Absolute timelock
time: u32,
n: LockTime,
Copy link

Choose a reason for hiding this comment

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

time was definitely a better name than n.

Copy link
Member Author

Choose a reason for hiding this comment

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

Its a block height or a block time. n is from n OP_CLTV and nLockTime - that's why I used n.

Copy link

Choose a reason for hiding this comment

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

n in nLockTime literally means number. It's meaningless. I do see that time is confusing, so lock_time would be better because most people already know it can be height.

Copy link
Member Author

Choose a reason for hiding this comment

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

Used lock_time for this field and similarly sequence for SatisfiedConstraint::RelativeTimelock field.

fn index(&self, index: I) -> &Self::Output {
&self.0[index]
}
}
Copy link

Choose a reason for hiding this comment

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

Doesn't Hash impl Deref? if it does this shouldn't be needed at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

I added this because of the trait bounds on Hash trait (from bitcoin_consensus). Deref doesn't help with that, right? Or am I missing something?

Copy link

Choose a reason for hiding this comment

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

I don't see any Hash trait in bitcoin_consensus, I guess you mean bitcoin_hashes. You're right that Deref doesn't help but it's a question if that bound should be there since it's quite burdensome. I'd have to look into it deeper. The first check is will anything break if we remove it?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll investigate this, cheers.

Copy link
Member Author

Choose a reason for hiding this comment

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

@@ -820,16 +821,16 @@ impl Property for Type {
// Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
// number on the stack would be a 5 bytes signed integer but Miniscript's B type
// only consumes 4 bytes from the stack.
if t == 0 || (t & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0 {
if t == PackedLockTime::ZERO {
Copy link

Choose a reason for hiding this comment

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

Did the code originally attempt to check if lock times are actually enabled or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

Originally both sequnece and locktime (After and Older) where handled together. When I split them apart I kept the disable check even though it seemed wrong, finally I got the confidence to remove it :)

Copy link
Member

Choose a reason for hiding this comment

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

It's not just after vs older -- the original code would look at a u32 which might contain a value that didn't encode any locktime at all.

It's not clear to me that this check can be removed.

Copy link
Member Author

Choose a reason for hiding this comment

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

No worries, since this PR is not a merge candidate this bridge doesn't need crossing now. I imagine between @apoelstra and @sanket1729 this will have to be worked out when miniscript upgrades to use rust-bitcoin 0.29.0.

Copy link
Member Author

Choose a reason for hiding this comment

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

Flagging this thread, this will need resolving before we can consider upgrading to the newly release version of rust-bitcoin. Sorry to be a drag but I think this is kind of subtle and will need input by @sanket1729 and/or @apoelstra to work it out. I can code up the solution.

Copy link
Member

Choose a reason for hiding this comment

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

I feel confident that removing this check is correct. This was just checking something else entirely which was wrong.

This was checking a check on sequence values in script which has no constraints. The only constraint we have is on transaction's sequence, which is different from checking the number encoded in script.

Copy link
Member

Choose a reason for hiding this comment

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

@apoelstra are you convinced that this is resolved?

Copy link
Member

Choose a reason for hiding this comment

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

Let me re-read this PR.

@tcharding
Copy link
Member Author

Thanks for the review @Kixunil!

sanket1729 added a commit to rust-bitcoin/rust-bitcoin that referenced this pull request Aug 1, 2022
870ad59 Rename is_finalized to is_finalizable (sanket1729)
aaadd25 Add breaking test that allowed incomplete builders to be created (sanket1729)
0b88051 Update TaprootBuilder::finalize (sanket1729)
5813ec7 Return EmptyTree instead of OverCompleteTree when there are no scripts to add (sanket1729)

Pull request description:

  Found while reviewing rust-bitcoin/rust-miniscript#450 . There is also a BUG fix in the second commit that would have let users spendinfo from incomplete trees.

  The bug was introduced in #936 which I am responsible for ACKing

ACKs for top commit:
  apoelstra:
    ACK 870ad59
  Kixunil:
    ACK 870ad59
  tcharding:
    ACK 870ad59

Tree-SHA512: 61442bd95df6912865cbecdc207f330b241e7fbb88b5e915243b2b48a756bea9eb29cb28d8c8249647a0a2ac514df4737bddab695f63075bd55284be5be228ff
ChallengeDev210 pushed a commit to ChallengeDev210/rust-bitcoin that referenced this pull request Aug 1, 2022
870ad59 Rename is_finalized to is_finalizable (sanket1729)
aaadd25 Add breaking test that allowed incomplete builders to be created (sanket1729)
0b88051 Update TaprootBuilder::finalize (sanket1729)
5813ec7 Return EmptyTree instead of OverCompleteTree when there are no scripts to add (sanket1729)

Pull request description:

  Found while reviewing rust-bitcoin/rust-miniscript#450 . There is also a BUG fix in the second commit that would have let users spendinfo from incomplete trees.

  The bug was introduced in #936 which I am responsible for ACKing

ACKs for top commit:
  apoelstra:
    ACK 870ad59
  Kixunil:
    ACK 870ad59
  tcharding:
    ACK 870ad59

Tree-SHA512: 61442bd95df6912865cbecdc207f330b241e7fbb88b5e915243b2b48a756bea9eb29cb28d8c8249647a0a2ac514df4737bddab695f63075bd55284be5be228ff
apoelstra added a commit to rust-bitcoin/rust-bitcoin that referenced this pull request Aug 8, 2022
110b5d8 Bump version to v0.29.0 (Tobin C. Harding)

Pull request description:

  Add changelog notes and bump the version number to v0.29.0.

  ## Notes

  I attempted to go through all the PRs since last release, please sing out if you had a PR merged that is not mentioned and you would like it mentioned.

  The changelog notes can be changed or improved, please do not take me writing them to imply I know exactly what goes on round here - I just made an effort to save others having to do it.

  ## TODO
  - [x]  merge all 'required' PRs
    - #1131
    - #1137
    - #1129
    - #1151
    - #1165 (add release notes still)
  - [x] Ensure all green from the CI run on: rust-bitcoin/rust-miniscript#450
  - [ ] Carry out (and improve) the #1106

ACKs for top commit:
  tcharding:
    ACK 110b5d8
  Kixunil:
    ACK 110b5d8
  apoelstra:
    ACK 110b5d8
  sanket1729:
    reACK 110b5d8

Tree-SHA512: d00c70534476794c01cd694ea9a23affef947c4f913b14344405272bc99cc00763f1ac755cc677e7afbd3dbef573d786251c9093d5dbafd76ee0cf86ca3b0ebd
@tcharding tcharding changed the title Depend on bitcoin 0.29.0 release tracking PR Upgrade bitcoin dependency to v0.29.0 Aug 9, 2022
Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

Mostly ACK. did a round of careful review. Just waiting on reply for comparison FIXME comment

src/interpreter/mod.rs Show resolved Hide resolved
@@ -620,9 +621,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
.push_slice(&Pk::hash_to_hash160(hash)[..])
.push_opcode(opcodes::all::OP_EQUALVERIFY),
Terminal::After(t) => builder
.push_int(t as i64)
.push_int(t.0.into())
Copy link
Member

Choose a reason for hiding this comment

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

nit(here and elsewhere): slightly prefer to use t.to_u32() over t.0

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I changed them all.

@@ -437,16 +438,16 @@ pub trait Property: Sized {
// Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
// number on the stack would be a 5 bytes signed integer but Miniscript's B type
// only consumes 4 bytes from the stack.
if t == 0 || (t & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0 {
if t == PackedLockTime::ZERO {
Copy link
Member

Choose a reason for hiding this comment

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

Just making sure that this was a bug that was fixed here

Copy link
Member Author

@tcharding tcharding Aug 29, 2022

Choose a reason for hiding this comment

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

I believe so but that belief is based on the meaningless-ness of comparing a LockTime to the disable bit of sequence.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm still at a loss as to exactly what the comment means. It should be removed now though, right?

@@ -820,16 +821,16 @@ impl Property for Type {
// Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
// number on the stack would be a 5 bytes signed integer but Miniscript's B type
// only consumes 4 bytes from the stack.
if t == 0 || (t & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0 {
if t == PackedLockTime::ZERO {
Copy link
Member

Choose a reason for hiding this comment

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

I feel confident that removing this check is correct. This was just checking something else entirely which was wrong.

This was checking a check on sequence values in script which has no constraints. The only constraint we have is on transaction's sequence, which is different from checking the number encoded in script.

self = match self {
Policy::After(t) => {
if !timelock::absolute_timelocks_are_same_unit(t, n) {
// FIXME: If the point of the LockTime type is to make it hard to make mistakes then
// this code shows it fails at that since we _must_ check is_same_unit before comparing.
Copy link
Member

Choose a reason for hiding this comment

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

Indeed, sorry I was absent during the review of the initial code. I get it that there was some hesitance in implementing PartialOrd, but why not have a method that returns Optioncmp::Ordering?

We should really not need to check that they are same units here.

Copy link
Member

Choose a reason for hiding this comment

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

PartialOrd gives us a method that returns Option<cmp::Ordering>. It's Ord that we couldn't implement, because there is no natural total ordering on timelocks, but which we wanted to implement so that you could use Vec::sort and derive Ord on structures that contain timelocks.

Copy link

Choose a reason for hiding this comment

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

I think @sanket1729 means that PartialOrd still allows < and > operators which are foot guns. Maybe this should be a clippy lint.

src/psbt/mod.rs Show resolved Hide resolved
@tcharding
Copy link
Member Author

Thanks for the review @sanket1729, I'll get back to this soonish, no rush because AFAIU there are a few other crates to upgrade first. Feel free to take this work and use it if you don't want to wait for me.

if self.is_height_locked() && n.is_height_locked()
|| self.is_time_locked() && n.is_time_locked()
{
n.to_consensus_u32() <= self.to_consensus_u32()
Copy link
Contributor

Choose a reason for hiding this comment

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

The previous implementation masked the sequence before doing the comparison. How can we justify not doing this here? What if one has the RBF disabled and the other doesn't for example (disabling RBF will always be the highest value unless you mask out that bit).

PS methods on Sequence like is_satisfied_by_height and is_satisfied_by_time would be super useful here.

Copy link
Member Author

Choose a reason for hiding this comment

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

How can we justify not doing this here?

Thanks man, you are 100% correct, this code is not the same as the code it replaces - that's my bug.

PS methods on Sequence like is_satisfied_by_height and is_satisfied_by_time would be super useful here.

A relative lock time type is meant to be in the works (not sure if anyone is actively working on it yet) and that type would have these methods you suggest. When Sequence was added relative lock time methods were explicitly out of scope.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've reverted these changes, so we now have

        // We need a relative lock time type in rust-bitcoin to clean this up.

        /* If nSequence encodes a relative lock-time, this mask is
         * applied to extract that lock-time from the sequence field. */
        const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff;
        const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 0x00400000;

        let mask = SEQUENCE_LOCKTIME_MASK | SEQUENCE_LOCKTIME_TYPE_FLAG;
        let masked_n = n.to_consensus_u32() & mask;
        let masked_seq = self.to_consensus_u32() & mask;
        if masked_n < SEQUENCE_LOCKTIME_TYPE_FLAG && masked_seq >= SEQUENCE_LOCKTIME_TYPE_FLAG {
            false
        } else {
            masked_n <= masked_seq
        }

Copy link
Member Author

Choose a reason for hiding this comment

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

Please see #455 which implements this using a new relative::LockTime API.

@@ -109,36 +106,35 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
}

/// Assert whether an relative locktime is satisfied
fn check_older(&self, _: u32) -> bool {
fn check_older(&self, _: Sequence) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you introduce a relative locktime type then it should probably be passed in here instead of Sequence.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks man, I've done that now in the demo PR.

@sanket1729
Copy link
Member

I don't think we should wait for relative lock time PR in bitcoin 0.30.0. We would need a version of rust-miniscript that supports bitcoin 0.29.0

@tcharding tcharding force-pushed the pre-release-bitcoin-0.29.0 branch 2 times, most recently from fd48ef3 to c51595b Compare September 1, 2022 02:12
@tcharding tcharding marked this pull request as ready for review September 1, 2022 02:56
Policy::Unsatisfiable
} else if t > n {
} else if t.to_consensus_u32() > n.to_consensus_u32() {
Copy link
Member

Choose a reason for hiding this comment

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

This is okay, but I would have expected an API like

let cmp_res = t.is_greater_than(n) ; // Any other name.  

match(cmp_res) {
	Some(true),
	Some(false),
	None,
}

Rust miniscript/or any other downstream project should need to know that they should check !t.is_same_unit() before comparing.

Perhaps, something worth looking at 0.30.0

Copy link
Member Author

Choose a reason for hiding this comment

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

We could add a method is_satisfied_by_lock in the same manner as relative::LockTime.

Copy link
Member Author

Choose a reason for hiding this comment

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

Which would give us the much nicer:

            Policy::After(t) => {
                let t = LockTime::from(t);
                } else if !t.is_satisfied_by_lock(n) {
                    Policy::Unsatisfiable
                } else {
                    Policy::After(t.into())
                }
            }

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Face palm, we actually provide a patter for this in the rustdocs (double face palm, I even wrote the rustdocs and still forgot to use the correct pattern)

I've updated the PR to be

            Policy::After(t) => {
                let t = LockTime::from(t);
                let is_satisfied_by = match (t, n) {
                    (Blocks(t), Blocks(n)) => t <= n,
                    (Seconds(t), Seconds(n)) => t <= n,
                    _ => false
                };
                if !is_satisfied_by {
                    Policy::Unsatisfiable
                } else {
                    Policy::After(t.into())
                }
            }

@tcharding
Copy link
Member Author

Hey @sanket1729, seems @josediegorobles is pretty keen to use the upcoming release, is there anything else that needs doing before we can consider this PR and get a release out?

Cargo already uses an env var to configure the toolchain
`RUSTUP_TOOLCHAIN`, there is no need for our custom `TOOLCHAIN`
environment variable.
A recent release of `form_urlencoded` breaks our dev build when using
1.41.1 toolchain.

Pin the `url` and `form_urlencoded` dependencies when on 1.41.1
toolchain.
Depend on the newly released bitcoin 0.29.0
@sanket1729
Copy link
Member

Hi @tcharding, there is nothing that needs to be done in particular here. I have a few cleanups in mind, but those can wait for 8.1.0 . I will look into preparing a new release this week.

@josediegorobles
Copy link

Hi, @sanket1729 by suggestion of @tcharding i started a PR for the release: tcharding#1

What version must be, 7.1.0 or 8.0.0? And what other things must be on the changelog?

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

ACK 24a2498. Reviewed by range-diff from c51595b .

I am waiting on @apoelstra to respond to the comment issue he raised. #450 (comment)

@sanket1729 sanket1729 added this to the 8.0.0 milestone Sep 19, 2022
@sanket1729 sanket1729 mentioned this pull request Sep 19, 2022
}),
("older", 1) => expression::terminal(&top.args[0], |x| {
expression::parse_num(x).map(Policy::Older)
expression::parse_num(x).map(|x| Policy::older(x))
Copy link
Member

Choose a reason for hiding this comment

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

nit: there was no need for the |x| here

Copy link
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 24a2498

@sanket1729 sanket1729 merged commit cddc22d into rust-bitcoin:master Sep 20, 2022
@apoelstra
Copy link
Member

@sanket1729 in future before merging can you edit PR descriptions to remove the @ signs before people's names? Otherwise github creates spurious notifications for them.

@sanket1729
Copy link
Member

@apoelstra, will take care from next time

@tcharding
Copy link
Member Author

Should I just not put mentions in the PR description? I thought it was only in the commit log (on the actual patch) that mattered?

@apoelstra
Copy link
Member

@tcharding no, it matters both on the commits and in the PR description. (Though the PR description is really easy to edit, for anyone with merge permissions, while the commits are a pain.)

@tcharding
Copy link
Member Author

My bad, duly schooled :)

@Kixunil
Copy link

Kixunil commented Sep 21, 2022

@apoelstra are you sure? I was never spuriously notified from PRs. Creation doesn't count as spurious - I consider mentioning explicitly wanting to notify someone.

@apoelstra
Copy link
Member

@Kixunil yes, though I believe the reason is that our merge script copies the entire PR description into the merge commit message.

@Kixunil
Copy link

Kixunil commented Sep 21, 2022

Ah, good point! Could the merge script be modified to replace ([:whitespace:])@([a-zA-Z0-9_-]+[:whitespace]]) with \1\2 in PR description?

@apoelstra
Copy link
Member

I don't know -- could you open an issue on https://github.com/bitcoin-core/bitcoin-maintainer-tools/ ?

Note that it does currently display an angry warning if there are @s in the PR description, which I always respond to by just editing the PR description and trying again ... maybe they don't want to modify PRs by default but it'd be nice if there was at least a gitconfig flag or something to do it.

apoelstra added a commit that referenced this pull request Oct 19, 2022
3f451db Release 8.0.0 (sanket1729)

Pull request description:

  Draft release PR. Still blocked on reviews of

  1) #418
  2) #450
  3) #461

ACKs for top commit:
  apoelstra:
    ACK 3f451db

Tree-SHA512: aa455fd57a894b1edb49b6273126a05c9d0897ca50ee572fa8a59b4d2a96e2013f2ed9ec4b96cd5e449ba5b5ee6bd32aa57da110df69bd35b8a970ccb1440002
sanket1729 added a commit to sanket1729/elements-miniscript that referenced this pull request Oct 21, 2022
…022_10

cddc22d Merge rust-bitcoin/rust-miniscript#450: Upgrade bitcoin dependency to v0.29.0 (Change satisfier locktime)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants