diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index cd81c8f..90bdc87 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -24,12 +24,13 @@ use bitcoin_hashes::{sha256d, Hash}; use util; use util::Error::{SpvBadTarget, SpvBadProofOfWork}; -use util::hash::BitcoinHash; +use util::hash::{BitcoinHash, MerkleRoot, bitcoin_merkle_root}; use util::uint::Uint256; -use consensus::encode::VarInt; +use consensus::encode::{VarInt, Encodable}; use network::constants::Network; use blockdata::transaction::Transaction; use blockdata::constants::max_target; +use bitcoin_hashes::HashEngine; /// A block header, which contains all the block's information except /// the actual transactions @@ -60,6 +61,61 @@ pub struct Block { pub txdata: Vec } +impl Block { + /// check if merkle root of header matches merkle root of the transaction list + pub fn check_merkle_root (&self) -> bool { + self.header.merkle_root == self.merkle_root() + } + + /// check if witness commitment in coinbase is matching the transaction list + pub fn check_witness_commitment(&self) -> bool { + + // witness commitment is optional if there are no transactions using SegWit in the block + if self.txdata.iter().all(|t| t.input.iter().all(|i| i.witness.is_empty())) { + return true; + } + if self.txdata.len() > 0 { + let coinbase = &self.txdata[0]; + if coinbase.is_coin_base() { + // commitment is in the last output that starts with below magic + if let Some(pos) = coinbase.output.iter() + .rposition(|o| { + o.script_pubkey.len () >= 38 && + o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) { + let commitment = sha256d::Hash::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap(); + // witness reserved value is in coinbase input witness + if coinbase.input[0].witness.len() == 1 && coinbase.input[0].witness[0].len() == 32 { + let witness_root = self.witness_root(); + return commitment == Self::compute_witness_commitment(&witness_root, coinbase.input[0].witness[0].as_slice()) + } + } + } + } + false + } + + /// compute witness commitment for the transaction list + pub fn compute_witness_commitment (witness_root: &sha256d::Hash, witness_reserved_value: &[u8]) -> sha256d::Hash { + let mut encoder = sha256d::Hash::engine(); + witness_root.consensus_encode(&mut encoder).unwrap(); + encoder.input(witness_reserved_value); + sha256d::Hash::from_engine(encoder) + } + + /// Merkle root of transactions hashed for witness + pub fn witness_root(&self) -> sha256d::Hash { + let mut txhashes = vec!(sha256d::Hash::default()); + txhashes.extend(self.txdata.iter().skip(1).map(|t|t.bitcoin_hash())); + bitcoin_merkle_root(txhashes) + } +} + +impl MerkleRoot for Block { + fn merkle_root(&self) -> sha256d::Hash { + bitcoin_merkle_root(self.txdata.iter().map(|obj| obj.txid()).collect()) + } +} + /// A block header with txcount attached, which is given in the `headers` /// network message. #[derive(PartialEq, Eq, Clone, Debug)] @@ -170,6 +226,7 @@ mod tests { use blockdata::block::{Block, BlockHeader}; use consensus::encode::{deserialize, serialize}; + use util::hash::MerkleRoot; #[test] fn block_test() { @@ -187,13 +244,16 @@ mod tests { let real_decode = decode.unwrap(); assert_eq!(real_decode.header.version, 1); assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash); - // [test] TODO: actually compute the merkle root + assert_eq!(real_decode.header.merkle_root, real_decode.merkle_root()); assert_eq!(serialize(&real_decode.header.merkle_root), merkle); assert_eq!(real_decode.header.time, 1231965655); assert_eq!(real_decode.header.bits, 486604799); assert_eq!(real_decode.header.nonce, 2067413810); // [test] TODO: check the transaction data - + + // should be also ok for a non-witness block as commitment is optional in that case + assert!(real_decode.check_witness_commitment()); + assert_eq!(serialize(&real_decode), some_block); } @@ -212,11 +272,14 @@ mod tests { assert_eq!(real_decode.header.version, 0x20000000); // 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.merkle_root()); assert_eq!(real_decode.header.time, 1472004949); assert_eq!(real_decode.header.bits, 0x1a06d450); assert_eq!(real_decode.header.nonce, 1879759182); // [test] TODO: check the transaction data + assert!(real_decode.check_witness_commitment()); + assert_eq!(serialize(&real_decode), segwit_block); } diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index 76f040b..8d86b49 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -27,7 +27,6 @@ use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn}; use blockdata::block::{Block, BlockHeader}; use network::constants::Network; use util::misc::hex_bytes; -use util::hash::MerkleRoot; use util::uint::Uint256; /// The maximum allowable sequence number @@ -98,7 +97,7 @@ pub fn genesis_block(network: Network) -> Block { header: BlockHeader { version: 1, prev_blockhash: Default::default(), - merkle_root: txdata.merkle_root(), + merkle_root: txdata[0].txid(), time: 1231006505, bits: 0x1d00ffff, nonce: 2083236893 @@ -112,7 +111,7 @@ pub fn genesis_block(network: Network) -> Block { header: BlockHeader { version: 1, prev_blockhash: Default::default(), - merkle_root: txdata.merkle_root(), + merkle_root: txdata[0].txid(), time: 1296688602, bits: 0x1d00ffff, nonce: 414098458 @@ -126,7 +125,7 @@ pub fn genesis_block(network: Network) -> Block { header: BlockHeader { version: 1, prev_blockhash: Default::default(), - merkle_root: txdata.merkle_root(), + merkle_root: txdata[0].txid(), time: 1296688602, bits: 0x207fffff, nonce: 2 diff --git a/src/util/hash.rs b/src/util/hash.rs index 1de0f1b..60e801a 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -51,18 +51,6 @@ pub fn bitcoin_merkle_root(data: Vec) -> sha256d::Hash { bitcoin_merkle_root(next) } -impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] { - fn merkle_root(&self) -> sha256d::Hash { - bitcoin_merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect()) - } -} - -impl MerkleRoot for Vec { - fn merkle_root(&self) -> sha256d::Hash { - (&self[..]).merkle_root() - } -} - /// Objects which are referred to by hash pub trait BitcoinHash { /// Produces a Sha256dHash which can be used to refer to the object