From 3ec5eff56eaceb6bbac48224d705795de68daa76 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 12 Mar 2024 15:00:05 +1100 Subject: [PATCH 1/2] Add Magic::from_params Currently `Magic` has per network consts but no way to dynamically get the magic bytes for a network. Note also that we are currently trying to reduce the usage of `Network` in the public API. Add a public constructor to the `Magic` type that accepts a `Params` parameter to determine the network to use. --- bitcoin/src/p2p/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bitcoin/src/p2p/mod.rs b/bitcoin/src/p2p/mod.rs index 3d8709fa..8a3147c7 100644 --- a/bitcoin/src/p2p/mod.rs +++ b/bitcoin/src/p2p/mod.rs @@ -28,6 +28,7 @@ use internals::{debug_from_display, write_err}; use io::{BufRead, Write}; use crate::consensus::encode::{self, Decodable, Encodable}; +use crate::consensus::Params; use crate::prelude::*; use crate::Network; @@ -226,6 +227,11 @@ impl Magic { /// Get network magic bytes. pub fn to_bytes(self) -> [u8; 4] { self.0 } + + /// Returns the magic bytes for the network defined by `params`. + pub fn from_params(params: impl AsRef) -> Self { + params.as_ref().network.into() + } } impl FromStr for Magic { From f6467ac98d3648ff77c35142f98612ecdf67a809 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 14 Mar 2024 11:26:09 +1100 Subject: [PATCH 2/2] Minimize usage of Network in public API A release or so ago we added `non_exhaustive` to the `Network` enum, this turned out to make usage of the enum un-ergonomic for downstream users. After much debate we decided that a way forward was to just minimize the usage of the enum in the public API by instead use `AsRef` so that downstream could define their own network enum based on the networks they support. Minimize usage of `Network` by using `AsRef` as a parameter type instead. "minimize" because the `Network` still appears in some places. --- bitcoin/src/address/mod.rs | 7 +++-- bitcoin/src/blockdata/constants.rs | 40 ++++++++++++++++++++++------ bitcoin/src/blockdata/transaction.rs | 3 ++- bitcoin/src/consensus/params.rs | 26 ++++++++++++++++++ bitcoin/src/lib.rs | 1 + bitcoin/src/network.rs | 2 +- 6 files changed, 67 insertions(+), 12 deletions(-) diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 1af1580c..3ec7fac3 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -43,6 +43,7 @@ use crate::blockdata::constants::{ use crate::blockdata::script::witness_program::WitnessProgram; use crate::blockdata::script::witness_version::WitnessVersion; use crate::blockdata::script::{self, Script, ScriptBuf, ScriptHash}; +use crate::consensus::Params; use crate::crypto::key::{ CompressedPublicKey, PubkeyHash, PublicKey, TweakedPublicKey, UntweakedPublicKey, }; @@ -505,7 +506,8 @@ impl Address { pub fn is_spend_standard(&self) -> bool { self.address_type().is_some() } /// Constructs an [`Address`] from an output script (`scriptPubkey`). - pub fn from_script(script: &Script, network: Network) -> Result { + pub fn from_script(script: &Script, params: impl AsRef) -> Result { + let network = params.as_ref().network; if script.is_p2pkh() { let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long"); let hash = PubkeyHash::from_byte_array(bytes); @@ -820,6 +822,7 @@ mod tests { use hex_lit::hex; use super::*; + use crate::consensus::params; use crate::network::Network::{Bitcoin, Testnet}; fn roundtrips(addr: &Address, network: Network) { @@ -1281,7 +1284,7 @@ mod tests { assert_eq!(Address::from_script(&bad_p2wpkh, Network::Bitcoin), expected); assert_eq!(Address::from_script(&bad_p2wsh, Network::Bitcoin), expected); assert_eq!( - Address::from_script(&invalid_segwitv0_script, Network::Bitcoin), + Address::from_script(&invalid_segwitv0_script, ¶ms::MAINNET), Err(FromScriptError::WitnessProgram(witness_program::Error::InvalidSegwitV0Length(17))) ); } diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs index 1f8de58b..d59072b7 100644 --- a/bitcoin/src/blockdata/constants.rs +++ b/bitcoin/src/blockdata/constants.rs @@ -17,6 +17,7 @@ use crate::blockdata::opcodes::all::*; use crate::blockdata::script; use crate::blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut}; use crate::blockdata::witness::Witness; +use crate::consensus::Params; use crate::internal_macros::impl_bytes_newtype; use crate::network::Network; use crate::pow::CompactTarget; @@ -84,11 +85,11 @@ fn bitcoin_genesis_tx() -> Transaction { } /// Constructs and returns the genesis block. -pub fn genesis_block(network: Network) -> Block { +pub fn genesis_block(params: impl AsRef) -> Block { let txdata = vec![bitcoin_genesis_tx()]; let hash: sha256d::Hash = txdata[0].compute_txid().into(); let merkle_root = hash.into(); - match network { + match params.as_ref().network { Network::Bitcoin => Block { header: block::Header { version: block::Version::ONE, @@ -169,7 +170,17 @@ impl ChainHash { /// /// See [BOLT 0](https://github.com/lightning/bolts/blob/ffeece3dab1c52efdb9b53ae476539320fa44938/00-introduction.md#chain_hash) /// for specification. - pub const fn using_genesis_block(network: Network) -> Self { + pub fn using_genesis_block(params: impl AsRef) -> Self { + let network = params.as_ref().network; + let hashes = [Self::BITCOIN, Self::TESTNET, Self::SIGNET, Self::REGTEST]; + hashes[network as usize] + } + + /// Returns the hash of the `network` genesis block for use as a chain hash. + /// + /// See [BOLT 0](https://github.com/lightning/bolts/blob/ffeece3dab1c52efdb9b53ae476539320fa44938/00-introduction.md#chain_hash) + /// for specification. + pub const fn using_genesis_block_const(network: Network) -> Self { let hashes = [Self::BITCOIN, Self::TESTNET, Self::SIGNET, Self::REGTEST]; hashes[network as usize] } @@ -187,6 +198,7 @@ mod test { use hex::test_hex_unwrap as hex; use super::*; + use crate::consensus::params; use crate::consensus::encode::serialize; #[test] @@ -213,9 +225,21 @@ mod test { ); } + #[test] + fn bitcoin_genesis_block_calling_convention() { + // This is the best. + let _ = genesis_block(¶ms::MAINNET); + // this works and is ok too. + let _ = genesis_block(&Network::Bitcoin); + let _ = genesis_block(Network::Bitcoin); + // This works too, but is suboptimal because it inlines the const. + let _ = genesis_block(Params::MAINNET); + let _ = genesis_block(&Params::MAINNET); + } + #[test] fn bitcoin_genesis_full_block() { - let gen = genesis_block(Network::Bitcoin); + let gen = genesis_block(¶ms::MAINNET); assert_eq!(gen.header.version, block::Version::ONE); assert_eq!(gen.header.prev_blockhash, Hash::all_zeros()); @@ -235,7 +259,7 @@ mod test { #[test] fn testnet_genesis_full_block() { - let gen = genesis_block(Network::Testnet); + let gen = genesis_block(¶ms::TESTNET); assert_eq!(gen.header.version, block::Version::ONE); assert_eq!(gen.header.prev_blockhash, Hash::all_zeros()); assert_eq!( @@ -253,7 +277,7 @@ mod test { #[test] fn signet_genesis_full_block() { - let gen = genesis_block(Network::Signet); + let gen = genesis_block(¶ms::SIGNET); assert_eq!(gen.header.version, block::Version::ONE); assert_eq!(gen.header.prev_blockhash, Hash::all_zeros()); assert_eq!( @@ -280,7 +304,7 @@ mod test { let hash = sha256::Hash::from_slice(genesis_hash.as_byte_array()).unwrap(); let want = format!("{:02x}", hash); - let chain_hash = ChainHash::using_genesis_block(network); + let chain_hash = ChainHash::using_genesis_block_const(network); let got = format!("{:02x}", chain_hash); // Compare strings because the spec specifically states how the chain hash must encode to hex. @@ -317,7 +341,7 @@ mod test { // Test vector taken from: https://github.com/lightning/bolts/blob/master/00-introduction.md #[test] fn mainnet_chain_hash_test_vector() { - let got = ChainHash::using_genesis_block(Network::Bitcoin).to_string(); + let got = ChainHash::using_genesis_block_const(Network::Bitcoin).to_string(); let want = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"; assert_eq!(got, want); } diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index 3dd985b5..b4633ed6 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -91,10 +91,11 @@ impl OutPoint { /// # Examples /// /// ```rust + /// use bitcoin::consensus::params; /// use bitcoin::constants::genesis_block; /// use bitcoin::Network; /// - /// let block = genesis_block(Network::Bitcoin); + /// let block = genesis_block(¶ms::MAINNET); /// let tx = &block.txdata[0]; /// /// // Coinbase transactions don't have any previous output. diff --git a/bitcoin/src/consensus/params.rs b/bitcoin/src/consensus/params.rs index 19522134..a6863ca3 100644 --- a/bitcoin/src/consensus/params.rs +++ b/bitcoin/src/consensus/params.rs @@ -56,6 +56,21 @@ pub struct Params { pub no_pow_retargeting: bool, } +/// The mainnet parameters. +/// +/// Use this for a static reference e.g., `¶ms::MAINNET`. +/// +/// For more on static vs const see The Rust Reference [using-statics-or-consts] section. +/// +/// [using-statics-or-consts]: +pub static MAINNET: Params = Params::MAINNET; +/// The testnet parameters. +pub static TESTNET: Params = Params::TESTNET; +/// The signet parameters. +pub static SIGNET: Params = Params::SIGNET; +/// The regtest parameters. +pub static REGTEST: Params = Params::REGTEST; + impl Params { /// The mainnet parameters (alias for `Params::MAINNET`). pub const BITCOIN: Params = Params::MAINNET; @@ -163,3 +178,14 @@ impl From<&Network> for &'static Params { impl AsRef for Params { fn as_ref(&self) -> &Params { self } } + +impl AsRef for Network { + fn as_ref(&self) -> &Params { + match *self { + Network::Bitcoin => &MAINNET, + Network::Testnet => &TESTNET, + Network::Signet => &SIGNET, + Network::Regtest => ®TEST, + } + } +} diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index c5c73737..3d10ba65 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -129,6 +129,7 @@ pub use crate::{ blockdata::weight::Weight, blockdata::witness::{self, Witness}, consensus::encode::VarInt, + consensus::params, crypto::ecdsa, crypto::key::{self, PrivateKey, PubkeyHash, PublicKey, CompressedPublicKey, WPubkeyHash, XOnlyPublicKey}, crypto::sighash::{self, LegacySighash, SegwitV0Sighash, TapSighash, TapSighashTag}, diff --git a/bitcoin/src/network.rs b/bitcoin/src/network.rs index 4483bb8e..10fae609 100644 --- a/bitcoin/src/network.rs +++ b/bitcoin/src/network.rs @@ -154,7 +154,7 @@ impl Network { /// let network = Network::Bitcoin; /// assert_eq!(network.chain_hash(), ChainHash::BITCOIN); /// ``` - pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block(self) } + pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block_const(self) } /// Creates a `Network` from the chain hash (genesis block hash). ///