-
Notifications
You must be signed in to change notification settings - Fork 628
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
Converting LeafVersion into an enum #718
Conversation
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: perhaps you want to name this odd
?
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.
Note that the rustdoc says 'even' as well.
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.
Nice catch, the doc-comment should have said odd
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.
Also the parameter name needs to be updated in the docs.
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.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for spotting! Addressed with the new force-pushed version.
I actually agree to switch back into |
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.
Concept ACK
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should just always serialize in hex.
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.
Hmm, version is the kind of thing that I consider naturally decimal.
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.
That's fair.
Ok, "no opinion" on what format it should have :)
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 1ab9282
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. |
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.
ACK, but one last thing
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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 |
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.
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
LeafVersion(TAPROOT_LEAF_TAPSCRIPT) | ||
} | ||
/// Future leaf version | ||
Future(u8) |
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 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 ( |
} | ||
} | ||
|
||
impl fmt::Display for FutureLeafVersion { |
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.
@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 match
ing over LeafVersion
and thus will be rarely print directly.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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.
impl Into<u8>
doesn't seem right, so not ACKing yet.
src/util/taproot.rs
Outdated
where | ||
E: serde::de::Error, | ||
{ | ||
LeafVersion::from_consensus(value).map_err(|err| E::custom(err)) |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E::unexpected_value
requires to list all the expected values, which makes this looking ugly
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.
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 |
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.
ACK a3642e8
Just non-critical style comments, can be addressed later or ignored.
src/util/taproot.rs
Outdated
} | ||
} | ||
|
||
impl fmt::UpperHex for FutureLeafVersion { |
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.
doc comments for these impls saying it's in consensus encoding would be nice but not going to block this important PR.
/// # Errors | ||
/// - If the last bit of the `version` is odd. | ||
/// - If the `version` is 0x50 ([`TAPROOT_ANNEX_PREFIX`]). | ||
/// - If the `version` is not a valid [`LeafVersion`] byte. |
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.
Nice!
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.
Not nice anymore: we do not error on invalid LeafVersion bytes other than annex. Will provide a fix for this doc
(LeafVersion::TapScript, true) => fmt::Display::fmt(&TAPROOT_LEAF_TAPSCRIPT, f), | ||
(LeafVersion::Future(version), false) => write!(f, "future_script_{:#02x}", version.0), | ||
(LeafVersion::Future(version), true) => fmt::Display::fmt(version, f), | ||
} |
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.
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.
match (self, f.alternate()) { | ||
(LeafVersion::TapScript, false) => f.write_str("tapscript"), | ||
(LeafVersion::TapScript, true) => fmt::Display::fmt(&TAPROOT_LEAF_TAPSCRIPT, f), | ||
(LeafVersion::Future(version), false) => write!(f, "future_script_{:#02x}", version.0), |
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.
Super-unimortant: .0
could be dropped here.
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.
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
} | ||
} | ||
|
||
impl fmt::UpperHex for LeafVersion { |
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.
Comment on these (consensus encoding) may also be helpful.
|
||
#[cfg(feature = "serde")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] | ||
impl<'de> ::serde::Deserialize<'de> for LeafVersion { |
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.
Shame you forgot about doc comment. Not critical.
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.
TODO: create good first issue
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.
Hm, which kind of doc comment can be put here?
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.
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
0xE0 | 0xE2 | 0xE4 | 0xE6 | 0xE8 | 0xEA | 0xEC | 0xEE | | ||
0xF0 | 0xF2 | 0xF4 | 0xF6 | 0xF8 | 0xFA | 0xFC | 0xFE | | ||
// and also 0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe | ||
0x66 | 0x7E | 0x80 | 0x84 | 0x96 | 0x98 | 0xBA | 0xBC | 0xBE => Ok(FutureLeafVersion(version)), |
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.
The above values are merely a recommendation and not a consensus rule. I think we should be checking for only even values here.
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.
Fixed
src/util/taproot.rs
Outdated
@@ -790,16 +827,16 @@ impl LeafVersion { | |||
match version { | |||
TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVersion::TapScript), | |||
TAPROOT_ANNEX_PREFIX => Err(TaprootError::InvalidTaprootLeafVersion(TAPROOT_ANNEX_PREFIX)), | |||
odd if odd & TAPROOT_LEAF_MASK != odd => Err(TaprootError::InvalidTaprootLeafVersion(odd)), | |||
future => Ok(LeafVersion::Future(future)), | |||
0xC1 => Err(TaprootError::InvalidTaprootLeafVersion(0xC1)), |
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.
Any special reason why this needs a special line? I think this would be handled by the future
match arm in the next line.
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.
Fixed
src/util/taproot.rs
Outdated
@@ -1059,6 +1096,8 @@ mod test { | |||
fn control_block_verify() { | |||
let secp = Secp256k1::verification_only(); | |||
// test vectors obtained from printing values in feature_taproot.py from bitcoin core | |||
// excluding four test cases using invalid leaf version in the control block | |||
// see details in <https://github.com/rust-bitcoin/rust-bitcoin/pull/718#issuecomment-1007596228> |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to classify all values which are not 0xC0
, LeafVersion::Future
?
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.
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).
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.
done
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.
Even though I'm not happy about inverted alternate()
proceeding is better.
ACK ef8a3a8
/// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: create good first issue for rewriting to `u8`
and [`LeafVersion`]
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.
Sorry, I am not following what do you mean here
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.
Putting backticks and square brackets around them :)
Created #763
} | ||
} | ||
|
||
impl fmt::Display for FutureLeafVersion { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
(LeafVersion::TapScript, true) => fmt::Display::fmt(&TAPROOT_LEAF_TAPSCRIPT, f), | ||
(LeafVersion::Future(version), false) => write!(f, "future_script_{:#02x}", version.0), | ||
(LeafVersion::Future(version), true) => fmt::Display::fmt(version, f), | ||
} |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
#[cfg(feature = "serde")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] | ||
impl<'de> ::serde::Deserialize<'de> for LeafVersion { |
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.
TODO: create good first issue
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.
crACK ef8a3a8. Waiting a day to let others complete review before merging.
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.
ACK ef8a3a8
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
LeafVersion
implementation 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, whereLeafVersion
may operate as a context object provided toScript
to specify interpretation rules for particular op codes.