Converting LeafVersion into an enum#718
Converting LeafVersion into an enum#718apoelstra merged 5 commits intorust-bitcoin:masterfrom BP-WG:taproot/leafver
Conversation
sanket1729
left a comment
There was a problem hiding this comment.
concept ACK. I really like from_u8 over from_byte, but waiting to see what others think of it.
src/util/taproot.rs
Outdated
| match version { | ||
| TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVersion::TapScript), | ||
| TAPROOT_ANNEX_PREFIX => Err(TaprootError::InvalidTaprootLeafVersion(TAPROOT_ANNEX_PREFIX)), | ||
| even if even & TAPROOT_LEAF_MASK != even => Err(TaprootError::InvalidTaprootLeafVersion(even)), |
There was a problem hiding this comment.
nit: perhaps you want to name this odd?
There was a problem hiding this comment.
Note that the rustdoc says 'even' as well.
There was a problem hiding this comment.
Nice catch, the doc-comment should have said odd
There was a problem hiding this comment.
Also the parameter name needs to be updated in the docs.
There was a problem hiding this comment.
@dr-orlovsky, can you please address this. I am good with the rest of the PR.
- Update the docs here (change to odd instead of even)
- The code variable name should be odd instead of even
There was a problem hiding this comment.
Thank you for spotting! Addressed with the new force-pushed version.
|
I actually agree to switch back into |
src/util/taproot.rs
Outdated
| (LeafVersion::TapScript, true) => fmt::Display::fmt(&TAPROOT_LEAF_TAPSCRIPT, f), | ||
| (LeafVersion::Future(version), false) => { | ||
| f.write_str("future(")?; | ||
| fmt::Display::fmt(version, f)?; |
There was a problem hiding this comment.
I don't think formatter parameters should affect version number. E.g. "{:10}" would display future(.........1) (spaces instead of dots, GH is broken) instead of .future(1)
However, getting parameters right is quite annoying. :(
There was a problem hiding this comment.
I think we should just always serialize in hex.
There was a problem hiding this comment.
Hmm, version is the kind of thing that I consider naturally decimal.
There was a problem hiding this comment.
That's fair.
Ok, "no opinion" on what format it should have :)
There was a problem hiding this comment.
I agree with @apoelstra that the script "version", which is not ordinal, is better to be formatted as hex: it will be odd to see the next post-tapscript version as future(82) than future(0x52). Decimals will do a bad job here.
I agree with @sanket1729 that we should not use formatting when we use version "number" (actually version id) as a part of other string.
I fixed both of the problems and also changed from future(x) to future_script_0xXX so it will be more readable. When the version is printed without any other string in alternate representation, I pass the formatting flags to allow users to format it as they like (decimal, prefixed with zeros etc).
|
concept ACK. I also prefer |
|
@apoelstra @sanket1729 @Kixunil rebased and changed to |
There was a problem hiding this comment.
ACK 1ab928272cdd15f32bc603cd852fcfe3fc6ecb44
I am a little tempted to say that the two error conditions should be separate variants, but I think this is fine.
Edit: removed a leading 5 from my hash -- my 5 key and paste-key are right next to each other.
|
Concept ACK In general, we do well to lean further into Rust's excellent enum system. |
src/util/taproot.rs
Outdated
| match version { | ||
| TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVersion::TapScript), | ||
| TAPROOT_ANNEX_PREFIX => Err(TaprootError::InvalidTaprootLeafVersion(TAPROOT_ANNEX_PREFIX)), | ||
| even if even & TAPROOT_LEAF_MASK != even => Err(TaprootError::InvalidTaprootLeafVersion(even)), |
There was a problem hiding this comment.
@dr-orlovsky, can you please address this. I am good with the rest of the PR.
- Update the docs here (change to odd instead of even)
- The code variable name should be odd instead of even
|
Note: let's not forget to add this to |
|
Addressed all comments. @Kixunil added to non-exhaustive todo (as a part of your comment #510 (comment)). @apoelstra needs re-ACK |
Kixunil
left a comment
There was a problem hiding this comment.
I think serialization issue is serious enough that I'd like at least some good argument why keep it.
I'd also really, really like to avoid weird states by using FutureLeafVersion.
The potential confusion related to from_u8 is not great although not strictly related to this PR, so willing to accept in followup PR.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
This is essentially a public field that allows creating LeafVersion::Future(TAPROOT_LEAF_TAPSCRIPT) which could cause breakages/confusion.
I suggest:
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct FutureLeafVersion(u8);
// IMPORTANT: NO PUBLIC CONSTRUCTOR!
// The only way to construct this is by converting u8 to LeafVersion and then extracting it
impl FutureLeafVersion> {
pub fn to_consensus(self) -> u8 {
self.0
}
}
// How to represent it though?
impl Display for FutureLeafVersion { ... }Maybe also perform conversion from consensus encoding to cardinal representation so that we can use NonZeroU8
|
@Kixunil I did as you have suggested. Interestingly enough disallowing invalid leaf versions breaks unit tests, since they include deserialization of invalid control blocks. I assume this must be allowed, otherwise taproot would be a hard fork and not a soft fork. Basically, we need to allow processing (but not constructing?) invalid leaf versions?! I propose to add third variant ( |
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
@Kixunil I think this (unlike LeafVersion) should be allowed to be displayed as a simple number with whatever formatting options were used by the caller.
This is an internal type which can be obtained only from matching over LeafVersion and thus will be rarely print directly.
There was a problem hiding this comment.
Sure, I don't feel strongly about the representation. But maybe also implement hex traits?
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
I clearly remember that I was writing one here. Probably commit got lost through multiple rebases...
|
I don't understand why rejecting an invalid control block would be hard fork and not soft fork. Anyway, this is outside of my knowledge. If accepting invalid versions is truly needed then I'm not against having another variant/type but not currently entirely sure it's the best way. |
Kixunil
left a comment
There was a problem hiding this comment.
impl Into<u8> doesn't seem right, so not ACKing yet.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
I think E::unexpected_value would be nicer but wouldn't hold up the urgent PR. Doing it later is completely OK.
There was a problem hiding this comment.
E::unexpected_value requires to list all the expected values, which makes this looking ugly
There was a problem hiding this comment.
Not really, you can describe it with a string "consensus-encoded leaf version as u8" or something similar
|
If our tests assume you can deserialize invalid control blocks then the tests are wrong. There is no reason to support this. And no, disallowing invalid data does not make Taproot a hardfork. |
|
@apoelstra from what I understand, pre-taproot block may contain witness v1 output spent by anybody with arbitrary script (before taproot activation) including use of invalid control block. The test which is failing is taken from bitcoin core, so probably we should double-check what it is... I will try to figure this out |
|
@dr-orlovsky maybe they shouldn't be parsed as control blocks pre-taproot activation? |
|
We don't need to support pre-Taproot Taproot-like spends in this library. |
|
From README:
and
Suggest that it's up to PR author to decide. IMO it'd be faster to just skip it now, releasing Taproot is more important. |
|
Ok, rebased & removed failed tests with invalid control blocks. Ready for re-review |
Kixunil
left a comment
There was a problem hiding this comment.
ACK a3642e8977ac3b19994644bb25c1c48e464f115e
Just non-critical style comments, can be addressed later or ignored.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
doc comments for these impls saying it's in consensus encoding would be nice but not going to block this important PR.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
Not nice anymore: we do not error on invalid LeafVersion bytes other than annex. Will provide a fix for this doc
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
Very nice that you implement alternate formatting, however the convention is that alternate true means more human-friendly/verbose representation, so it appears it's inverted here.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
Super-unimortant: .0 could be dropped here.
There was a problem hiding this comment.
Better not to: we may change the way FutureLeafVersion is displayed in a future and this will break formatting here
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
Comment on these (consensus encoding) may also be helpful.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
Shame you forgot about doc comment. Not critical.
There was a problem hiding this comment.
TODO: create good first issue
There was a problem hiding this comment.
Hm, which kind of doc comment can be put here?
There was a problem hiding this comment.
Something like "(De)serializes LeafVersion as u8 using consensus encoding."
@dr-orlovsky @apoelstra The current implementation is master correct. As I mentioned in the review, I think the futureleafversion is incorrect. I had those test vectors from cross-testing with bitcoin core. All these control blocks were accpeted by bitcoin core in regtest. So, if we cannot parse them, it is our bug! From BIP 341:
There can be an update to use leaf version, 0x02, but BIP341 does not recommend doing it. But an update would be consensus accepted. Thus, we must parse those leaf versions correctly. |
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
The above values are merely a recommendation and not a consensus rule. I think we should be checking for only even values here.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
Any special reason why this needs a special line? I think this would be handled by the future match arm in the next line.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
No need to exclude these tests, if you change the above, the tests can then be uncommented. See #718 (comment)
There was a problem hiding this comment.
Regardless they should definitely not be called invalid if they are not actually invalid.
Sorry for contributing to confusion instead of just checking the BIP myself.
There was a problem hiding this comment.
@apoelstra sorry, now I do not understand at all what should be done with these tests or LeafVersion type.
There was a problem hiding this comment.
@dr-orlovsky the tests should be restored. We should only reject odd values or 0x50, as the BIP says in footnote 7. Nothing else should be rejected.
There was a problem hiding this comment.
How to classify all values which are not 0xC0, LeafVersion::Future?
There was a problem hiding this comment.
Everything that isn't known or invalid is Future (even though the real-life future will not include BIPs that ignore the advice in BIP 341).
| /// Inner type representing future (non-tapscript) leaf versions. See [`LeafVersion::Future`]. | ||
| /// | ||
| /// NB: NO PUBLIC CONSTRUCTOR! | ||
| /// The only way to construct this is by converting u8 to LeafVersion and then extracting it. |
There was a problem hiding this comment.
TODO: create good first issue for rewriting to `u8` and [`LeafVersion`]
There was a problem hiding this comment.
Sorry, I am not following what do you mean here
There was a problem hiding this comment.
Putting backticks and square brackets around them :)
Created #763
src/util/taproot.rs
Outdated
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
src/util/taproot.rs
Outdated
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
src/util/taproot.rs
Outdated
There was a problem hiding this comment.
TODO: create good first issue
sanket1729
left a comment
There was a problem hiding this comment.
crACK ef8a3a8. Waiting a day to let others complete review before merging.
|
Addresses all your addressable requests in #761 |
7f06e91 LowerHex and UpperHex implementations for LeafVersion (Dr Maxim Orlovsky) 6a3f3aa Inverse alternative formatting for LeafVersion type (Dr Maxim Orlovsky) bec6694 Fix docs on error conditions in LeafVersion::from_consensus (Dr Maxim Orlovsky) 7c28b47 LowerHex and UpperHex implementations for FutureLeafVersion (Dr Maxim Orlovsky) Pull request description: Trivial post-merge fixups from review comments in #718 ACKs for top commit: Kixunil: ACK 7f06e91 sanket1729: ACK 7f06e91 Tree-SHA512: d94c4bd3d0b466287c8965103f74ecaba185d14c13b6c3f37d9fbe194343b3fc902fd2c7716554ad01fe28ff89cda933df199b7e8388a3fa6097028caf62522b
The original
LeafVersionimplementation was just a newtype aroundu8. I think that having enum explicitly listing consensus script implementation rules may be more beneficial in terms of both code readibility and future use of multiple script types, whereLeafVersionmay operate as a context object provided toScriptto specify interpretation rules for particular op codes.