Merge pull request #218 from tamasblummer/merkle_root_fix
Merkle root calculation and witness commitment check for Block
This commit is contained in:
commit
084703cba9
|
@ -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<Transaction>
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -51,18 +51,6 @@ pub fn bitcoin_merkle_root(data: Vec<sha256d::Hash>) -> 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 <T: BitcoinHash> MerkleRoot for Vec<T> {
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue