From 819d8d72e8c6e263c8fa77b30a64bee3d4e9c6e8 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 2 Oct 2024 09:34:04 +1000 Subject: [PATCH 1/2] Stop using private Version constructor In preparation for moving the `block::Version` type over to `primitives` stop using the private constructor and inner field. Use the public getter/setter instead (`to_consensus`and `from_consensus` respectively). --- bitcoin/src/blockdata/block.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index c5ddf11fa..98ee605ad 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -190,13 +190,13 @@ impl Default for Version { impl Encodable for Version { fn consensus_encode(&self, w: &mut W) -> Result { - self.0.consensus_encode(w) + self.to_consensus().consensus_encode(w) } } impl Decodable for Version { fn consensus_decode(r: &mut R) -> Result { - Decodable::consensus_decode(r).map(Version) + Decodable::consensus_decode(r).map(Version::from_consensus) } } @@ -515,7 +515,7 @@ mod tests { assert!(decode.is_ok()); assert!(bad_decode.is_err()); let real_decode = decode.unwrap(); - assert_eq!(real_decode.header.version, Version(1)); + assert_eq!(real_decode.header.version, Version::from_consensus(1)); assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash); assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap()); assert_eq!(serialize(&real_decode.header.merkle_root), merkle); @@ -557,7 +557,7 @@ mod tests { assert!(decode.is_ok()); let real_decode = decode.unwrap(); - assert_eq!(real_decode.header.version, Version(Version::USE_VERSION_BITS as i32)); // VERSIONBITS but no bits set + assert_eq!(real_decode.header.version, Version::from_consensus(0x2000_0000)); // VERSIONBITS but no bits set assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash); assert_eq!(serialize(&real_decode.header.merkle_root), merkle); assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap()); @@ -587,13 +587,13 @@ mod tests { let decode: Result = deserialize(&block); assert!(decode.is_ok()); let real_decode = decode.unwrap(); - assert_eq!(real_decode.header.version, Version(2147483647)); + assert_eq!(real_decode.header.version, Version::from_consensus(2147483647)); let block2 = hex!("000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); let decode2: Result = deserialize(&block2); assert!(decode2.is_ok()); let real_decode2 = decode2.unwrap(); - assert_eq!(real_decode2.header.version, Version(-2147483648)); + assert_eq!(real_decode2.header.version, Version::from_consensus(-2147483648)); } #[test] @@ -614,7 +614,7 @@ mod tests { // test with modified header let mut invalid_header: Header = some_header; - invalid_header.version.0 += 1; + invalid_header.version = Version::from_consensus(invalid_header.version.to_consensus() + 1); match invalid_header.validate_pow(invalid_header.target()) { Err(ValidationError::BadProofOfWork) => (), _ => panic!("unexpected result from validate_pow"), @@ -635,7 +635,7 @@ mod tests { fn soft_fork_signalling() { for i in 0..31 { let version_int = (0x20000000u32 ^ 1 << i) as i32; - let version = Version(version_int); + let version = Version::from_consensus(version_int); if i < 29 { assert!(version.is_signalling_soft_fork(i)); } else { @@ -643,7 +643,7 @@ mod tests { } } - let segwit_signal = Version(0x20000000 ^ 1 << 1); + let segwit_signal = Version::from_consensus(0x20000000 ^ 1 << 1); assert!(!segwit_signal.is_signalling_soft_fork(0)); assert!(segwit_signal.is_signalling_soft_fork(1)); assert!(!segwit_signal.is_signalling_soft_fork(2)); From 70264bfcece6594a491ea71830a13c2369d09e44 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 2 Oct 2024 09:26:39 +1000 Subject: [PATCH 2/2] Move block::Version to primitives This is a straight up move of the whole type because there are only three methods, a getter, a setter, and `is_signalling_soft_fork`. If we use an extension trait for `is_signalling_soft_fork` then we have to make the two private associated consts public which is in my opinion worse. --- bitcoin/src/blockdata/block.rs | 70 ---------------------------------- primitives/src/block.rs | 70 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index 98ee605ad..cfd800a85 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -118,76 +118,6 @@ impl fmt::Debug for Header { } } -/// Bitcoin block version number. -/// -/// Originally used as a protocol version, but repurposed for soft-fork signaling. -/// -/// The inner value is a signed integer in Bitcoin Core for historical reasons, if version bits is -/// being used the top three bits must be 001, this gives us a useful range of [0x20000000...0x3FFFFFFF]. -/// -/// > When a block nVersion does not have top bits 001, it is treated as if all bits are 0 for the purposes of deployments. -/// -/// ### Relevant BIPs -/// -/// * [BIP9 - Version bits with timeout and delay](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki) (current usage) -/// * [BIP34 - Block v2, Height in Coinbase](https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki) -#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Version(i32); - -impl Version { - /// The original Bitcoin Block v1. - pub const ONE: Self = Self(1); - - /// BIP-34 Block v2. - pub const TWO: Self = Self(2); - - /// BIP-9 compatible version number that does not signal for any softforks. - pub const NO_SOFT_FORK_SIGNALLING: Self = Self(Self::USE_VERSION_BITS as i32); - - /// BIP-9 soft fork signal bits mask. - const VERSION_BITS_MASK: u32 = 0x1FFF_FFFF; - - /// 32bit value starting with `001` to use version bits. - /// - /// The value has the top three bits `001` which enables the use of version bits to signal for soft forks. - const USE_VERSION_BITS: u32 = 0x2000_0000; - - /// Creates a [`Version`] from a signed 32 bit integer value. - /// - /// This is the data type used in consensus code in Bitcoin Core. - #[inline] - pub const fn from_consensus(v: i32) -> Self { Version(v) } - - /// Returns the inner `i32` value. - /// - /// This is the data type used in consensus code in Bitcoin Core. - pub fn to_consensus(self) -> i32 { self.0 } - - /// Checks whether the version number is signalling a soft fork at the given bit. - /// - /// A block is signalling for a soft fork under BIP-9 if the first 3 bits are `001` and - /// the version bit for the specific soft fork is toggled on. - pub fn is_signalling_soft_fork(&self, bit: u8) -> bool { - // Only bits [0, 28] inclusive are used for signalling. - if bit > 28 { - return false; - } - - // To signal using version bits, the first three bits must be `001`. - if (self.0 as u32) & !Self::VERSION_BITS_MASK != Self::USE_VERSION_BITS { - return false; - } - - // The bit is set if signalling a soft fork. - (self.0 as u32 & Self::VERSION_BITS_MASK) & (1 << bit) > 0 - } -} - -impl Default for Version { - fn default() -> Version { Self::NO_SOFT_FORK_SIGNALLING } -} - impl Encodable for Version { fn consensus_encode(&self, w: &mut W) -> Result { self.to_consensus().consensus_encode(w) diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 3ddf01034..ca992a730 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -9,6 +9,76 @@ use hashes::sha256d; +/// Bitcoin block version number. +/// +/// Originally used as a protocol version, but repurposed for soft-fork signaling. +/// +/// The inner value is a signed integer in Bitcoin Core for historical reasons, if version bits is +/// being used the top three bits must be 001, this gives us a useful range of [0x20000000...0x3FFFFFFF]. +/// +/// > When a block nVersion does not have top bits 001, it is treated as if all bits are 0 for the purposes of deployments. +/// +/// ### Relevant BIPs +/// +/// * [BIP9 - Version bits with timeout and delay](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki) (current usage) +/// * [BIP34 - Block v2, Height in Coinbase](https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki) +#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Version(i32); + +impl Version { + /// The original Bitcoin Block v1. + pub const ONE: Self = Self(1); + + /// BIP-34 Block v2. + pub const TWO: Self = Self(2); + + /// BIP-9 compatible version number that does not signal for any softforks. + pub const NO_SOFT_FORK_SIGNALLING: Self = Self(Self::USE_VERSION_BITS as i32); + + /// BIP-9 soft fork signal bits mask. + const VERSION_BITS_MASK: u32 = 0x1FFF_FFFF; + + /// 32bit value starting with `001` to use version bits. + /// + /// The value has the top three bits `001` which enables the use of version bits to signal for soft forks. + const USE_VERSION_BITS: u32 = 0x2000_0000; + + /// Creates a [`Version`] from a signed 32 bit integer value. + /// + /// This is the data type used in consensus code in Bitcoin Core. + #[inline] + pub const fn from_consensus(v: i32) -> Self { Version(v) } + + /// Returns the inner `i32` value. + /// + /// This is the data type used in consensus code in Bitcoin Core. + pub fn to_consensus(self) -> i32 { self.0 } + + /// Checks whether the version number is signalling a soft fork at the given bit. + /// + /// A block is signalling for a soft fork under BIP-9 if the first 3 bits are `001` and + /// the version bit for the specific soft fork is toggled on. + pub fn is_signalling_soft_fork(&self, bit: u8) -> bool { + // Only bits [0, 28] inclusive are used for signalling. + if bit > 28 { + return false; + } + + // To signal using version bits, the first three bits must be `001`. + if (self.0 as u32) & !Self::VERSION_BITS_MASK != Self::USE_VERSION_BITS { + return false; + } + + // The bit is set if signalling a soft fork. + (self.0 as u32 & Self::VERSION_BITS_MASK) & (1 << bit) > 0 + } +} + +impl Default for Version { + fn default() -> Version { Self::NO_SOFT_FORK_SIGNALLING } +} + hashes::hash_newtype! { /// A bitcoin block hash. pub struct BlockHash(sha256d::Hash);