Skip to content

Commit

Permalink
Add ChainHash type
Browse files Browse the repository at this point in the history
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.
Chain hash is now being used by the DLC folks, as such it is useful to
have it implemented in rust-bitcoin.

One method of calculating a chain hash is by hashing the genesis block
for the respective network.

Add a `ChainHash` type that can be used to get the unique identifier of
each of the 4 Bitcoin networks we support. Add a method that calculates
the chain hash for a network using the double sha256 of the genesis
block. Do so using hard coded consts and add unit
tests (regression/sanity) that show these hard code hex strings match
the hash of the data we return for the genesis block for the respective
network.

The chain hash for the main Bitcoin network can be verified from LN
docs (BOLT 0), add a link to this document.
  • Loading branch information
tcharding committed Mar 20, 2022
1 parent d2615ed commit 03d5dff
Showing 1 changed file with 70 additions and 2 deletions.
72 changes: 70 additions & 2 deletions src/blockdata/constants.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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 using_genesis_block(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::*;
Expand Down Expand Up @@ -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::using_genesis_block(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);
}
}

0 comments on commit 03d5dff

Please sign in to comment.