From 182fe2e829abcf6b0a7bdd47c62adc8705ddabf9 Mon Sep 17 00:00:00 2001 From: Tobin Harding Date: Wed, 16 Mar 2022 12:47:08 +1100 Subject: [PATCH] Add ChainHash type The Lightning network defines a type called 'chain hash' that is used to uniquely represent the various Bitcoin networks as a 32 byte hash value. It is calculated by hashing the genesis block for the respective network. Chain hash is now being used by the DLC folks, as such it is useful to have it implemented in rust-bitcoin. Add a `ChainHash` type that can be used to get the unique identifier of each of the 4 Bitcoin networks we support. --- src/blockdata/constants.rs | 72 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index 931f1538d5..09c8f3e0f4 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -23,8 +23,8 @@ use prelude::*; use core::default::Default; -use hashes::hex::{HexIterator, Error as HexError}; -use hashes::sha256d; +use hashes::hex::{HexIterator, Error as HexError, FromHex}; +use hashes::{Hash, sha256d}; use blockdata::opcodes; use blockdata::script; use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn}; @@ -176,6 +176,38 @@ pub fn genesis_block(network: Network) -> Block { } } +// Mainnet value can be verified at https://github.com/lightning/bolts/blob/master/00-introduction.md +const CHAIN_HASH_BITCOIN: &str = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"; +const CHAIN_HASH_TESTNET: &str = "43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000"; +const CHAIN_HASH_SIGNET: &str = "f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000"; +const CHAIN_HASH_REGTEST: &str = "06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"; + + +/// The uniquely identifying hash of the target blockchain. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ChainHash([u8; 32]); +impl_array_newtype!(ChainHash, u8, 32); +impl_bytes_newtype!(ChainHash, 32); + +impl ChainHash { + /// 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 fn for_network(network: Network) -> Self { + let s = match network { + Network::Bitcoin => CHAIN_HASH_BITCOIN, + Network::Testnet => CHAIN_HASH_TESTNET, + Network::Signet => CHAIN_HASH_SIGNET, + Network::Regtest => CHAIN_HASH_REGTEST, + }; + + // Chain hash is the block hash of the genesis block and `BlockHash` is a sha256d. + let hash = sha256d::Hash::from_hex(s).expect("valid static hex string"); + ChainHash(hash.into_inner()) + } +} + #[cfg(test)] mod test { use super::*; @@ -250,5 +282,41 @@ mod test { assert_eq!(format!("{:x}", gen.header.block_hash()), "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6".to_string()); } + + // The *_chain_hash tests are sanity/regression tests, they verify that the const hex string + // representing the genesis block is the same as the string created by hashing the genesis block. + fn chain_hash_and_genesis_block(network: Network) -> (String, String) { + let chain_hash = ChainHash::for_network(network); + let genesis = genesis_block(network).block_hash(); + + let got = format!("{:x}", chain_hash); + let want = format!("{:x}", genesis); + + (got, want) + } + + #[test] + fn mainnet_chain_hash() { + let (got, want) = chain_hash_and_genesis_block(Network::Bitcoin); + assert_eq!(got, want); + } + + #[test] + fn testnet_chain_hash() { + let (got, want) = chain_hash_and_genesis_block(Network::Testnet); + assert_eq!(got, want); + } + + #[test] + fn signet_chain_hash() { + let (got, want) = chain_hash_and_genesis_block(Network::Signet); + assert_eq!(got, want); + } + + #[test] + fn regtest_chain_hash() { + let (got, want) = chain_hash_and_genesis_block(Network::Regtest); + assert_eq!(got, want); + } }