Merge pull request #349 from pandoracore/hashtypes
Hash new types as specified in #284
This commit is contained in:
commit
50f3a60712
|
@ -22,7 +22,7 @@ use-serde = ["hex", "serde", "bitcoin_hashes/serde", "secp256k1/serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bech32 = "0.7.1"
|
bech32 = "0.7.1"
|
||||||
bitcoin_hashes = "0.7"
|
bitcoin_hashes = "0.7.3"
|
||||||
bitcoinconsensus = { version = "0.17", optional = true }
|
bitcoinconsensus = { version = "0.17", optional = true }
|
||||||
serde = { version = "1", optional = true }
|
serde = { version = "1", optional = true }
|
||||||
hex = { version = "=0.3.2", optional = true }
|
hex = { version = "=0.3.2", optional = true }
|
||||||
|
|
|
@ -20,17 +20,16 @@
|
||||||
//! these blocks and the blockchain.
|
//! these blocks and the blockchain.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use hashes::{sha256d, Hash};
|
|
||||||
|
|
||||||
use util;
|
use util;
|
||||||
use util::Error::{BlockBadTarget, BlockBadProofOfWork};
|
use util::Error::{BlockBadTarget, BlockBadProofOfWork};
|
||||||
use util::hash::{BitcoinHash, MerkleRoot, bitcoin_merkle_root};
|
use util::hash::{BitcoinHash, bitcoin_merkle_root};
|
||||||
|
use hashes::{Hash, HashEngine};
|
||||||
|
use hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
|
||||||
use util::uint::Uint256;
|
use util::uint::Uint256;
|
||||||
use consensus::encode::Encodable;
|
use consensus::encode::Encodable;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use blockdata::transaction::Transaction;
|
use blockdata::transaction::Transaction;
|
||||||
use blockdata::constants::max_target;
|
use blockdata::constants::max_target;
|
||||||
use hashes::HashEngine;
|
|
||||||
|
|
||||||
/// A block header, which contains all the block's information except
|
/// A block header, which contains all the block's information except
|
||||||
/// the actual transactions
|
/// the actual transactions
|
||||||
|
@ -39,9 +38,9 @@ pub struct BlockHeader {
|
||||||
/// The protocol version. Should always be 1.
|
/// The protocol version. Should always be 1.
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// Reference to the previous block in the chain
|
/// Reference to the previous block in the chain
|
||||||
pub prev_blockhash: sha256d::Hash,
|
pub prev_blockhash: BlockHash,
|
||||||
/// The root hash of the merkle tree of transactions in the block
|
/// The root hash of the merkle tree of transactions in the block
|
||||||
pub merkle_root: sha256d::Hash,
|
pub merkle_root: TxMerkleNode,
|
||||||
/// The timestamp of the block, as claimed by the miner
|
/// The timestamp of the block, as claimed by the miner
|
||||||
pub time: u32,
|
pub time: u32,
|
||||||
/// The target value below which the blockhash must lie, encoded as a
|
/// The target value below which the blockhash must lie, encoded as a
|
||||||
|
@ -82,7 +81,7 @@ impl Block {
|
||||||
.rposition(|o| {
|
.rposition(|o| {
|
||||||
o.script_pubkey.len () >= 38 &&
|
o.script_pubkey.len () >= 38 &&
|
||||||
o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) {
|
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();
|
let commitment = WitnessCommitment::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
|
||||||
// witness reserved value is in coinbase input witness
|
// witness reserved value is in coinbase input witness
|
||||||
if coinbase.input[0].witness.len() == 1 && coinbase.input[0].witness[0].len() == 32 {
|
if coinbase.input[0].witness.len() == 1 && coinbase.input[0].witness[0].len() == 32 {
|
||||||
let witness_root = self.witness_root();
|
let witness_root = self.witness_root();
|
||||||
|
@ -94,25 +93,31 @@ impl Block {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate the transaction merkle root.
|
||||||
|
pub fn merkle_root(&self) -> TxMerkleNode {
|
||||||
|
let hashes = self.txdata.iter().map(|obj| obj.txid().as_hash());
|
||||||
|
bitcoin_merkle_root(hashes).into()
|
||||||
|
}
|
||||||
|
|
||||||
/// compute witness commitment for the transaction list
|
/// compute witness commitment for the transaction list
|
||||||
pub fn compute_witness_commitment (witness_root: &sha256d::Hash, witness_reserved_value: &[u8]) -> sha256d::Hash {
|
pub fn compute_witness_commitment (witness_root: &WitnessMerkleNode, witness_reserved_value: &[u8]) -> WitnessCommitment {
|
||||||
let mut encoder = sha256d::Hash::engine();
|
let mut encoder = WitnessCommitment::engine();
|
||||||
witness_root.consensus_encode(&mut encoder).unwrap();
|
witness_root.consensus_encode(&mut encoder).unwrap();
|
||||||
encoder.input(witness_reserved_value);
|
encoder.input(witness_reserved_value);
|
||||||
sha256d::Hash::from_engine(encoder)
|
WitnessCommitment::from_engine(encoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merkle root of transactions hashed for witness
|
/// Merkle root of transactions hashed for witness
|
||||||
pub fn witness_root(&self) -> sha256d::Hash {
|
pub fn witness_root(&self) -> WitnessMerkleNode {
|
||||||
let mut txhashes = vec!(sha256d::Hash::default());
|
let hashes = self.txdata.iter().enumerate().map(|(i, t)|
|
||||||
txhashes.extend(self.txdata.iter().skip(1).map(|t|t.bitcoin_hash()));
|
if i == 0 {
|
||||||
bitcoin_merkle_root(txhashes)
|
// Replace the first hash with zeroes.
|
||||||
|
Wtxid::default().as_hash()
|
||||||
|
} else {
|
||||||
|
t.wtxid().as_hash()
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
|
bitcoin_merkle_root(hashes).into()
|
||||||
impl MerkleRoot for Block {
|
|
||||||
fn merkle_root(&self) -> sha256d::Hash {
|
|
||||||
bitcoin_merkle_root(self.txdata.iter().map(|obj| obj.txid()).collect())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,15 +193,15 @@ impl BlockHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for BlockHeader {
|
impl BitcoinHash<BlockHash> for BlockHeader {
|
||||||
fn bitcoin_hash(&self) -> sha256d::Hash {
|
fn bitcoin_hash(&self) -> BlockHash {
|
||||||
use consensus::encode::serialize;
|
use consensus::encode::serialize;
|
||||||
sha256d::Hash::hash(&serialize(self))
|
BlockHash::hash(&serialize(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for Block {
|
impl BitcoinHash<BlockHash> for Block {
|
||||||
fn bitcoin_hash(&self) -> sha256d::Hash {
|
fn bitcoin_hash(&self) -> BlockHash {
|
||||||
self.header.bitcoin_hash()
|
self.header.bitcoin_hash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +217,6 @@ mod tests {
|
||||||
|
|
||||||
use blockdata::block::{Block, BlockHeader};
|
use blockdata::block::{Block, BlockHeader};
|
||||||
use consensus::encode::{deserialize, serialize};
|
use consensus::encode::{deserialize, serialize};
|
||||||
use util::hash::MerkleRoot;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test() {
|
fn block_test() {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
|
use hashes::sha256d;
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use blockdata::script;
|
use blockdata::script;
|
||||||
use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
|
use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
|
||||||
|
@ -96,14 +96,16 @@ fn bitcoin_genesis_tx() -> Transaction {
|
||||||
|
|
||||||
/// Constructs and returns the genesis block
|
/// Constructs and returns the genesis block
|
||||||
pub fn genesis_block(network: Network) -> Block {
|
pub fn genesis_block(network: Network) -> Block {
|
||||||
|
let txdata = vec![bitcoin_genesis_tx()];
|
||||||
|
let hash: sha256d::Hash = txdata[0].txid().into();
|
||||||
|
let merkle_root = hash.into();
|
||||||
match network {
|
match network {
|
||||||
Network::Bitcoin => {
|
Network::Bitcoin => {
|
||||||
let txdata = vec![bitcoin_genesis_tx()];
|
|
||||||
Block {
|
Block {
|
||||||
header: BlockHeader {
|
header: BlockHeader {
|
||||||
version: 1,
|
version: 1,
|
||||||
prev_blockhash: Default::default(),
|
prev_blockhash: Default::default(),
|
||||||
merkle_root: txdata[0].txid(),
|
merkle_root,
|
||||||
time: 1231006505,
|
time: 1231006505,
|
||||||
bits: 0x1d00ffff,
|
bits: 0x1d00ffff,
|
||||||
nonce: 2083236893
|
nonce: 2083236893
|
||||||
|
@ -112,12 +114,11 @@ pub fn genesis_block(network: Network) -> Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Network::Testnet => {
|
Network::Testnet => {
|
||||||
let txdata = vec![bitcoin_genesis_tx()];
|
|
||||||
Block {
|
Block {
|
||||||
header: BlockHeader {
|
header: BlockHeader {
|
||||||
version: 1,
|
version: 1,
|
||||||
prev_blockhash: Default::default(),
|
prev_blockhash: Default::default(),
|
||||||
merkle_root: txdata[0].txid(),
|
merkle_root,
|
||||||
time: 1296688602,
|
time: 1296688602,
|
||||||
bits: 0x1d00ffff,
|
bits: 0x1d00ffff,
|
||||||
nonce: 414098458
|
nonce: 414098458
|
||||||
|
@ -126,12 +127,11 @@ pub fn genesis_block(network: Network) -> Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Network::Regtest => {
|
Network::Regtest => {
|
||||||
let txdata = vec![bitcoin_genesis_tx()];
|
|
||||||
Block {
|
Block {
|
||||||
header: BlockHeader {
|
header: BlockHeader {
|
||||||
version: 1,
|
version: 1,
|
||||||
prev_blockhash: Default::default(),
|
prev_blockhash: Default::default(),
|
||||||
merkle_root: txdata[0].txid(),
|
merkle_root,
|
||||||
time: 1296688602,
|
time: 1296688602,
|
||||||
bits: 0x207fffff,
|
bits: 0x207fffff,
|
||||||
nonce: 2
|
nonce: 2
|
||||||
|
@ -171,7 +171,7 @@ mod test {
|
||||||
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||||
assert_eq!(gen.lock_time, 0);
|
assert_eq!(gen.lock_time, 0);
|
||||||
|
|
||||||
assert_eq!(format!("{:x}", gen.bitcoin_hash()),
|
assert_eq!(format!("{:x}", gen.wtxid()),
|
||||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ use std::{error, fmt, io};
|
||||||
|
|
||||||
#[cfg(feature = "serde")] use serde;
|
#[cfg(feature = "serde")] use serde;
|
||||||
|
|
||||||
|
use hash_types::{ScriptHash, WScriptHash};
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use consensus::{encode, Decodable, Encodable};
|
use consensus::{encode, Decodable, Encodable};
|
||||||
use hashes::{hash160, sha256, Hash};
|
use hashes::Hash;
|
||||||
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
|
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
|
||||||
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
||||||
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
||||||
|
@ -233,7 +234,7 @@ impl Script {
|
||||||
/// Compute the P2SH output corresponding to this redeem script
|
/// Compute the P2SH output corresponding to this redeem script
|
||||||
pub fn to_p2sh(&self) -> Script {
|
pub fn to_p2sh(&self) -> Script {
|
||||||
Builder::new().push_opcode(opcodes::all::OP_HASH160)
|
Builder::new().push_opcode(opcodes::all::OP_HASH160)
|
||||||
.push_slice(&hash160::Hash::hash(&self.0)[..])
|
.push_slice(&ScriptHash::hash(&self.0)[..])
|
||||||
.push_opcode(opcodes::all::OP_EQUAL)
|
.push_opcode(opcodes::all::OP_EQUAL)
|
||||||
.into_script()
|
.into_script()
|
||||||
}
|
}
|
||||||
|
@ -242,7 +243,7 @@ impl Script {
|
||||||
/// script")
|
/// script")
|
||||||
pub fn to_v0_p2wsh(&self) -> Script {
|
pub fn to_v0_p2wsh(&self) -> Script {
|
||||||
Builder::new().push_int(0)
|
Builder::new().push_int(0)
|
||||||
.push_slice(&sha256::Hash::hash(&self.0)[..])
|
.push_slice(&WScriptHash::hash(&self.0)[..])
|
||||||
.into_script()
|
.into_script()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,21 +26,21 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
use hashes::{self, sha256d, Hash};
|
use hashes::{self, Hash, sha256d};
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
|
|
||||||
use util::endian;
|
use util::endian;
|
||||||
use util::hash::BitcoinHash;
|
|
||||||
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
|
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use consensus::{encode, serialize, Decodable, Encodable};
|
use consensus::{encode, serialize, Decodable, Encodable};
|
||||||
|
use hash_types::*;
|
||||||
use VarInt;
|
use VarInt;
|
||||||
|
|
||||||
/// A reference to a transaction output
|
/// A reference to a transaction output
|
||||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct OutPoint {
|
pub struct OutPoint {
|
||||||
/// The referenced transaction's txid
|
/// The referenced transaction's txid
|
||||||
pub txid: sha256d::Hash,
|
pub txid: Txid,
|
||||||
/// The index of the referenced output in its transaction's vout
|
/// The index of the referenced output in its transaction's vout
|
||||||
pub vout: u32,
|
pub vout: u32,
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ serde_struct_human_string_impl!(OutPoint, "an OutPoint", txid, vout);
|
||||||
impl OutPoint {
|
impl OutPoint {
|
||||||
/// Create a new [OutPoint].
|
/// Create a new [OutPoint].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(txid: sha256d::Hash, vout: u32) -> OutPoint {
|
pub fn new(txid: Txid, vout: u32) -> OutPoint {
|
||||||
OutPoint {
|
OutPoint {
|
||||||
txid: txid,
|
txid: txid,
|
||||||
vout: vout,
|
vout: vout,
|
||||||
|
@ -175,7 +175,7 @@ impl ::std::str::FromStr for OutPoint {
|
||||||
return Err(ParseOutPointError::Format);
|
return Err(ParseOutPointError::Format);
|
||||||
}
|
}
|
||||||
Ok(OutPoint {
|
Ok(OutPoint {
|
||||||
txid: sha256d::Hash::from_hex(&s[..colon]).map_err(ParseOutPointError::Txid)?,
|
txid: Txid::from_hex(&s[..colon]).map_err(ParseOutPointError::Txid)?,
|
||||||
vout: parse_vout(&s[colon+1..])?,
|
vout: parse_vout(&s[colon+1..])?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -286,20 +286,29 @@ impl Transaction {
|
||||||
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), witness: vec![], .. *txin }).collect(),
|
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), witness: vec![], .. *txin }).collect(),
|
||||||
output: self.output.clone(),
|
output: self.output.clone(),
|
||||||
};
|
};
|
||||||
cloned_tx.bitcoin_hash()
|
cloned_tx.txid().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the txid. For non-segwit transactions this will be identical
|
/// Computes the txid. For non-segwit transactions this will be identical
|
||||||
/// to the output of `BitcoinHash::bitcoin_hash()`, but for segwit transactions,
|
/// to the output of `wtxid()`, but for segwit transactions,
|
||||||
/// this will give the correct txid (not including witnesses) while `bitcoin_hash`
|
/// this will give the correct txid (not including witnesses) while `wtxid`
|
||||||
/// will also hash witnesses.
|
/// will also hash witnesses.
|
||||||
pub fn txid(&self) -> sha256d::Hash {
|
pub fn txid(&self) -> Txid {
|
||||||
let mut enc = sha256d::Hash::engine();
|
let mut enc = Txid::engine();
|
||||||
self.version.consensus_encode(&mut enc).unwrap();
|
self.version.consensus_encode(&mut enc).unwrap();
|
||||||
self.input.consensus_encode(&mut enc).unwrap();
|
self.input.consensus_encode(&mut enc).unwrap();
|
||||||
self.output.consensus_encode(&mut enc).unwrap();
|
self.output.consensus_encode(&mut enc).unwrap();
|
||||||
self.lock_time.consensus_encode(&mut enc).unwrap();
|
self.lock_time.consensus_encode(&mut enc).unwrap();
|
||||||
sha256d::Hash::from_engine(enc)
|
Txid::from_engine(enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes SegWit-version of the transaction id (wtxid). For transaction with the witness
|
||||||
|
/// data this hash includes witness, for pre-witness transaction it is equal to the normal
|
||||||
|
/// value returned by txid() function.
|
||||||
|
pub fn wtxid(&self) -> Wtxid {
|
||||||
|
let mut enc = Wtxid::engine();
|
||||||
|
self.consensus_encode(&mut enc).unwrap();
|
||||||
|
Wtxid::from_engine(enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a signature hash for a given input index with a given sighash flag.
|
/// Computes a signature hash for a given input index with a given sighash flag.
|
||||||
|
@ -315,14 +324,14 @@ impl Transaction {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if `input_index` is greater than or equal to `self.input.len()`
|
/// Panics if `input_index` is greater than or equal to `self.input.len()`
|
||||||
///
|
///
|
||||||
pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighash_u32: u32) -> sha256d::Hash {
|
pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighash_u32: u32) -> SigHash {
|
||||||
assert!(input_index < self.input.len()); // Panic on OOB
|
assert!(input_index < self.input.len()); // Panic on OOB
|
||||||
|
|
||||||
let (sighash, anyone_can_pay) = SigHashType::from_u32(sighash_u32).split_anyonecanpay_flag();
|
let (sighash, anyone_can_pay) = SigHashType::from_u32(sighash_u32).split_anyonecanpay_flag();
|
||||||
|
|
||||||
// Special-case sighash_single bug because this is easy enough.
|
// Special-case sighash_single bug because this is easy enough.
|
||||||
if sighash == SigHashType::Single && input_index >= self.output.len() {
|
if sighash == SigHashType::Single && input_index >= self.output.len() {
|
||||||
return sha256d::Hash::from_slice(&[1, 0, 0, 0, 0, 0, 0, 0,
|
return SigHash::from_slice(&[1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0]).unwrap();
|
0, 0, 0, 0, 0, 0, 0, 0]).unwrap();
|
||||||
|
@ -370,7 +379,7 @@ impl Transaction {
|
||||||
// hash the result
|
// hash the result
|
||||||
let mut raw_vec = serialize(&tx);
|
let mut raw_vec = serialize(&tx);
|
||||||
raw_vec.extend_from_slice(&endian::u32_to_array_le(sighash_u32));
|
raw_vec.extend_from_slice(&endian::u32_to_array_le(sighash_u32));
|
||||||
sha256d::Hash::hash(&raw_vec)
|
SigHash::hash(&raw_vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty
|
/// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty
|
||||||
|
@ -437,14 +446,6 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for Transaction {
|
|
||||||
fn bitcoin_hash(&self) -> sha256d::Hash {
|
|
||||||
let mut enc = sha256d::Hash::engine();
|
|
||||||
self.consensus_encode(&mut enc).unwrap();
|
|
||||||
sha256d::Hash::from_engine(enc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_consensus_encoding!(TxOut, value, script_pubkey);
|
impl_consensus_encoding!(TxOut, value, script_pubkey);
|
||||||
|
|
||||||
impl Encodable for OutPoint {
|
impl Encodable for OutPoint {
|
||||||
|
@ -625,11 +626,12 @@ mod tests {
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use consensus::encode::serialize;
|
use consensus::encode::serialize;
|
||||||
use consensus::encode::deserialize;
|
use consensus::encode::deserialize;
|
||||||
use util::hash::BitcoinHash;
|
|
||||||
|
|
||||||
use hashes::{sha256d, Hash};
|
use hashes::Hash;
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
|
|
||||||
|
use hash_types::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_outpoint() {
|
fn test_outpoint() {
|
||||||
assert_eq!(OutPoint::from_str("i don't care"),
|
assert_eq!(OutPoint::from_str("i don't care"),
|
||||||
|
@ -645,20 +647,20 @@ mod tests {
|
||||||
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:+42"),
|
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:+42"),
|
||||||
Err(ParseOutPointError::VoutNotCanonical));
|
Err(ParseOutPointError::VoutNotCanonical));
|
||||||
assert_eq!(OutPoint::from_str("i don't care:1"),
|
assert_eq!(OutPoint::from_str("i don't care:1"),
|
||||||
Err(ParseOutPointError::Txid(sha256d::Hash::from_hex("i don't care").unwrap_err())));
|
Err(ParseOutPointError::Txid(Txid::from_hex("i don't care").unwrap_err())));
|
||||||
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X:1"),
|
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X:1"),
|
||||||
Err(ParseOutPointError::Txid(sha256d::Hash::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X").unwrap_err())));
|
Err(ParseOutPointError::Txid(Txid::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X").unwrap_err())));
|
||||||
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:lol"),
|
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:lol"),
|
||||||
Err(ParseOutPointError::Vout(u32::from_str("lol").unwrap_err())));
|
Err(ParseOutPointError::Vout(u32::from_str("lol").unwrap_err())));
|
||||||
|
|
||||||
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:42"),
|
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:42"),
|
||||||
Ok(OutPoint{
|
Ok(OutPoint{
|
||||||
txid: sha256d::Hash::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
|
txid: Txid::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
|
||||||
vout: 42,
|
vout: 42,
|
||||||
}));
|
}));
|
||||||
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0"),
|
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0"),
|
||||||
Ok(OutPoint{
|
Ok(OutPoint{
|
||||||
txid: sha256d::Hash::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
|
txid: Txid::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
|
||||||
vout: 0,
|
vout: 0,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -692,7 +694,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transaction() {
|
fn test_nonsegwit_transaction() {
|
||||||
let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||||
let tx: Result<Transaction, _> = deserialize(&hex_tx);
|
let tx: Result<Transaction, _> = deserialize(&hex_tx);
|
||||||
assert!(tx.is_ok());
|
assert!(tx.is_ok());
|
||||||
|
@ -709,11 +711,44 @@ mod tests {
|
||||||
assert_eq!(realtx.output.len(), 1);
|
assert_eq!(realtx.output.len(), 1);
|
||||||
assert_eq!(realtx.lock_time, 0);
|
assert_eq!(realtx.lock_time, 0);
|
||||||
|
|
||||||
assert_eq!(format!("{:x}", realtx.bitcoin_hash()),
|
assert_eq!(format!("{:x}", realtx.txid()),
|
||||||
|
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||||
|
assert_eq!(format!("{:x}", realtx.wtxid()),
|
||||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||||
assert_eq!(realtx.get_weight(), 193*4);
|
assert_eq!(realtx.get_weight(), 193*4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_segwit_transaction() {
|
||||||
|
let hex_tx = Vec::<u8>::from_hex(
|
||||||
|
"02000000000101595895ea20179de87052b4046dfe6fd515860505d6511a9004cf12a1f93cac7c01000000\
|
||||||
|
00ffffffff01deb807000000000017a9140f3444e271620c736808aa7b33e370bd87cb5a078702483045022\
|
||||||
|
100fb60dad8df4af2841adc0346638c16d0b8035f5e3f3753b88db122e70c79f9370220756e6633b17fd271\
|
||||||
|
0e626347d28d60b0a2d6cbb41de51740644b9fb3ba7751040121028fa937ca8cba2197a37c007176ed89410\
|
||||||
|
55d3bcb8627d085e94553e62f057dcc00000000"
|
||||||
|
).unwrap();
|
||||||
|
let tx: Result<Transaction, _> = deserialize(&hex_tx);
|
||||||
|
assert!(tx.is_ok());
|
||||||
|
let realtx = tx.unwrap();
|
||||||
|
// All these tests aren't really needed because if they fail, the hash check at the end
|
||||||
|
// will also fail. But these will show you where the failure is so I'll leave them in.
|
||||||
|
assert_eq!(realtx.version, 2);
|
||||||
|
assert_eq!(realtx.input.len(), 1);
|
||||||
|
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
|
||||||
|
// as little-endian 256-bit numbers rather than as data strings.
|
||||||
|
assert_eq!(format!("{:x}", realtx.input[0].previous_output.txid),
|
||||||
|
"7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string());
|
||||||
|
assert_eq!(realtx.input[0].previous_output.vout, 1);
|
||||||
|
assert_eq!(realtx.output.len(), 1);
|
||||||
|
assert_eq!(realtx.lock_time, 0);
|
||||||
|
|
||||||
|
assert_eq!(format!("{:x}", realtx.txid()),
|
||||||
|
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());
|
||||||
|
assert_eq!(format!("{:x}", realtx.wtxid()),
|
||||||
|
"80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string());
|
||||||
|
assert_eq!(realtx.get_weight(), 442);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tx_no_input_deserialization() {
|
fn tx_no_input_deserialization() {
|
||||||
let hex_tx = Vec::<u8>::from_hex(
|
let hex_tx = Vec::<u8>::from_hex(
|
||||||
|
@ -778,7 +813,7 @@ mod tests {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
let tx: Transaction = deserialize(&hex_tx).unwrap();
|
let tx: Transaction = deserialize(&hex_tx).unwrap();
|
||||||
|
|
||||||
assert_eq!(format!("{:x}", tx.bitcoin_hash()), "d6ac4a5e61657c4c604dcde855a1db74ec6b3e54f32695d72c5e11c7761ea1b4");
|
assert_eq!(format!("{:x}", tx.wtxid()), "d6ac4a5e61657c4c604dcde855a1db74ec6b3e54f32695d72c5e11c7761ea1b4");
|
||||||
assert_eq!(format!("{:x}", tx.txid()), "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec");
|
assert_eq!(format!("{:x}", tx.txid()), "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec");
|
||||||
assert_eq!(tx.get_weight(), 2718);
|
assert_eq!(tx.get_weight(), 2718);
|
||||||
|
|
||||||
|
@ -793,7 +828,7 @@ mod tests {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
let tx: Transaction = deserialize(&hex_tx).unwrap();
|
let tx: Transaction = deserialize(&hex_tx).unwrap();
|
||||||
|
|
||||||
assert_eq!(format!("{:x}", tx.bitcoin_hash()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
|
assert_eq!(format!("{:x}", tx.wtxid()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
|
||||||
assert_eq!(format!("{:x}", tx.txid()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
|
assert_eq!(format!("{:x}", tx.txid()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,7 +845,7 @@ mod tests {
|
||||||
let script = Script::from(Vec::<u8>::from_hex(script).unwrap());
|
let script = Script::from(Vec::<u8>::from_hex(script).unwrap());
|
||||||
let mut raw_expected = Vec::<u8>::from_hex(expected_result).unwrap();
|
let mut raw_expected = Vec::<u8>::from_hex(expected_result).unwrap();
|
||||||
raw_expected.reverse();
|
raw_expected.reverse();
|
||||||
let expected_result = sha256d::Hash::from_slice(&raw_expected[..]).unwrap();
|
let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap();
|
||||||
|
|
||||||
let actual_result = tx.signature_hash(input_index, &script, hash_type as u32);
|
let actual_result = tx.signature_hash(input_index, &script, hash_type as u32);
|
||||||
assert_eq!(actual_result, expected_result);
|
assert_eq!(actual_result, expected_result);
|
||||||
|
|
|
@ -34,7 +34,8 @@ use std::borrow::Cow;
|
||||||
use std::io::{Cursor, Read, Write};
|
use std::io::{Cursor, Read, Write};
|
||||||
use hashes::hex::ToHex;
|
use hashes::hex::ToHex;
|
||||||
|
|
||||||
use hashes::{sha256d, Hash as HashTrait};
|
use hashes::{sha256d, Hash};
|
||||||
|
use hash_types::{BlockHash, FilterHash, TxMerkleNode};
|
||||||
|
|
||||||
use util::endian;
|
use util::endian;
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
|
@ -81,6 +82,8 @@ pub enum Error {
|
||||||
UnsupportedSegwitFlag(u8),
|
UnsupportedSegwitFlag(u8),
|
||||||
/// Unrecognized network command
|
/// Unrecognized network command
|
||||||
UnrecognizedNetworkCommand(String),
|
UnrecognizedNetworkCommand(String),
|
||||||
|
/// Invalid Inventory type
|
||||||
|
UnknownInventoryType(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -101,6 +104,7 @@ impl fmt::Display for Error {
|
||||||
"unsupported segwit version: {}", swflag),
|
"unsupported segwit version: {}", swflag),
|
||||||
Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f,
|
Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f,
|
||||||
"unrecognized network command: {}", nwcmd),
|
"unrecognized network command: {}", nwcmd),
|
||||||
|
Error::UnknownInventoryType(ref tp) => write!(f, "Unknown Inventory type: {}", tp),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +121,8 @@ impl error::Error for Error {
|
||||||
| Error::UnknownNetworkMagic(..)
|
| Error::UnknownNetworkMagic(..)
|
||||||
| Error::ParseFailed(..)
|
| Error::ParseFailed(..)
|
||||||
| Error::UnsupportedSegwitFlag(..)
|
| Error::UnsupportedSegwitFlag(..)
|
||||||
| Error::UnrecognizedNetworkCommand(..) => None,
|
| Error::UnrecognizedNetworkCommand(..)
|
||||||
|
| Error::UnknownInventoryType(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -590,7 +595,9 @@ macro_rules! impl_vec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl_vec!(sha256d::Hash);
|
impl_vec!(BlockHash);
|
||||||
|
impl_vec!(FilterHash);
|
||||||
|
impl_vec!(TxMerkleNode);
|
||||||
impl_vec!(Transaction);
|
impl_vec!(Transaction);
|
||||||
impl_vec!(TxOut);
|
impl_vec!(TxOut);
|
||||||
impl_vec!(TxIn);
|
impl_vec!(TxIn);
|
||||||
|
@ -649,7 +656,7 @@ impl Decodable for Box<[u8]> {
|
||||||
|
|
||||||
/// Do a double-SHA256 on some data and return the first 4 bytes
|
/// Do a double-SHA256 on some data and return the first 4 bytes
|
||||||
fn sha2_checksum(data: &[u8]) -> [u8; 4] {
|
fn sha2_checksum(data: &[u8]) -> [u8; 4] {
|
||||||
let checksum = <sha256d::Hash as HashTrait>::hash(data);
|
let checksum = <sha256d::Hash as Hash>::hash(data);
|
||||||
[checksum[0], checksum[1], checksum[2], checksum[3]]
|
[checksum[0], checksum[1], checksum[2], checksum[3]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,8 +737,7 @@ impl Encodable for sha256d::Hash {
|
||||||
|
|
||||||
impl Decodable for sha256d::Hash {
|
impl Decodable for sha256d::Hash {
|
||||||
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
||||||
let inner = <[u8; 32]>::consensus_decode(d)?;
|
Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(d)?))
|
||||||
Ok(sha256d::Hash::from_slice(&inner).unwrap())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Rust Bitcoin Library
|
||||||
|
// Written in 2014 by
|
||||||
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
|
//
|
||||||
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
|
// copyright and related and neighboring rights to this software to
|
||||||
|
// the public domain worldwide. This software is distributed without
|
||||||
|
// any warranty.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
// along with this software.
|
||||||
|
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! File defines types for hashes used throughout the library. These types are needed in order
|
||||||
|
//! to avoid mixing data of the same hash format (like SHA256d) but of different meaning
|
||||||
|
//! (transaction id, block hash etc).
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use consensus::encode::{Encodable, Decodable, Error};
|
||||||
|
use hashes::{sha256, sha256d, hash160, Hash};
|
||||||
|
use hashes::hex::{ToHex, FromHex};
|
||||||
|
|
||||||
|
macro_rules! impl_hashencode {
|
||||||
|
($hashtype:ident) => {
|
||||||
|
impl Encodable for $hashtype {
|
||||||
|
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
|
||||||
|
self.0.consensus_encode(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for $hashtype {
|
||||||
|
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
||||||
|
Ok(Self::from_inner(<<$hashtype as Hash>::Inner>::consensus_decode(d)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_newtype!(Txid, sha256d::Hash, 32, doc="A bitcoin transaction hash/transaction ID.");
|
||||||
|
hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID.");
|
||||||
|
hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash.");
|
||||||
|
hash_newtype!(SigHash, sha256d::Hash, 32, doc="Hash of the transaction according to the signature algorithm");
|
||||||
|
|
||||||
|
hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key.");
|
||||||
|
hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode.");
|
||||||
|
hash_newtype!(WPubkeyHash, hash160::Hash, 20, doc="SegWit version of a public key hash.");
|
||||||
|
hash_newtype!(WScriptHash, sha256::Hash, 32, doc="SegWit version of a Bitcoin Script bytecode hash.");
|
||||||
|
|
||||||
|
hash_newtype!(TxMerkleNode, sha256d::Hash, 32, doc="A hash of the Merkle tree branch or root for transactions");
|
||||||
|
hash_newtype!(WitnessMerkleNode, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for witness data");
|
||||||
|
hash_newtype!(WitnessCommitment, sha256d::Hash, 32, doc="A hash corresponding to the witness structure commitment in the coinbase transaction");
|
||||||
|
hash_newtype!(XpubIdentifier, hash160::Hash, 20, doc="XpubIdentifier as defined in BIP-32.");
|
||||||
|
|
||||||
|
hash_newtype!(FilterHash, sha256d::Hash, 32, doc="Bloom filter souble-SHA256 locator hash, as defined in BIP-168");
|
||||||
|
|
||||||
|
|
||||||
|
impl_hashencode!(Txid);
|
||||||
|
impl_hashencode!(Wtxid);
|
||||||
|
impl_hashencode!(SigHash);
|
||||||
|
impl_hashencode!(BlockHash);
|
||||||
|
impl_hashencode!(TxMerkleNode);
|
||||||
|
impl_hashencode!(WitnessMerkleNode);
|
||||||
|
impl_hashencode!(FilterHash);
|
|
@ -227,7 +227,7 @@ macro_rules! display_from_debug {
|
||||||
macro_rules! hex_script (($s:expr) => (::blockdata::script::Script::from(::hex::decode($s).unwrap())));
|
macro_rules! hex_script (($s:expr) => (::blockdata::script::Script::from(::hex::decode($s).unwrap())));
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
macro_rules! hex_hash (($s:expr) => (::hashes::sha256d::Hash::from_slice(&::hex::decode($s).unwrap()).unwrap()));
|
macro_rules! hex_hash (($h:ident, $s:expr) => ($h::from_slice(&::hex::decode($s).unwrap()).unwrap()));
|
||||||
|
|
||||||
macro_rules! serde_struct_impl {
|
macro_rules! serde_struct_impl {
|
||||||
($name:ident, $($fe:ident),*) => (
|
($name:ident, $($fe:ident),*) => (
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
#![allow(ellipsis_inclusive_range_patterns)]
|
#![allow(ellipsis_inclusive_range_patterns)]
|
||||||
|
|
||||||
// Re-exported dependencies.
|
// Re-exported dependencies.
|
||||||
pub extern crate bitcoin_hashes as hashes;
|
#[macro_use] pub extern crate bitcoin_hashes as hashes;
|
||||||
pub extern crate secp256k1;
|
pub extern crate secp256k1;
|
||||||
pub extern crate bech32;
|
pub extern crate bech32;
|
||||||
|
|
||||||
|
@ -77,7 +77,11 @@ pub mod network;
|
||||||
pub mod blockdata;
|
pub mod blockdata;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod consensus;
|
pub mod consensus;
|
||||||
|
// Do not remove: required in order to get hash types implementation macros to work correctly
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub mod hash_types;
|
||||||
|
|
||||||
|
pub use hash_types::*;
|
||||||
pub use blockdata::block::Block;
|
pub use blockdata::block::Block;
|
||||||
pub use blockdata::block::BlockHeader;
|
pub use blockdata::block::BlockHeader;
|
||||||
pub use blockdata::script::Script;
|
pub use blockdata::script::Script;
|
||||||
|
|
|
@ -341,7 +341,7 @@ mod test {
|
||||||
use hashes::Hash as HashTrait;
|
use hashes::Hash as HashTrait;
|
||||||
use network::address::Address;
|
use network::address::Address;
|
||||||
use super::message_network::{Reject, RejectReason, VersionMessage};
|
use super::message_network::{Reject, RejectReason, VersionMessage};
|
||||||
use network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage, InvType};
|
use network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage};
|
||||||
use blockdata::block::{Block, BlockHeader};
|
use blockdata::block::{Block, BlockHeader};
|
||||||
use network::message_filter::{GetCFilters, CFilter, GetCFHeaders, CFHeaders, GetCFCheckpt, CFCheckpt};
|
use network::message_filter::{GetCFilters, CFilter, GetCFHeaders, CFHeaders, GetCFCheckpt, CFCheckpt};
|
||||||
use blockdata::transaction::Transaction;
|
use blockdata::transaction::Transaction;
|
||||||
|
@ -362,11 +362,11 @@ mod test {
|
||||||
NetworkMessage::Version(version_msg),
|
NetworkMessage::Version(version_msg),
|
||||||
NetworkMessage::Verack,
|
NetworkMessage::Verack,
|
||||||
NetworkMessage::Addr(vec![(45, Address::new(&([123,255,000,100], 833).into(), ServiceFlags::NETWORK))]),
|
NetworkMessage::Addr(vec![(45, Address::new(&([123,255,000,100], 833).into(), ServiceFlags::NETWORK))]),
|
||||||
NetworkMessage::Inv(vec![Inventory{inv_type: InvType::Block, hash: hash([8u8; 32])}]),
|
NetworkMessage::Inv(vec![Inventory::Block(hash([8u8; 32]).into())]),
|
||||||
NetworkMessage::GetData(vec![Inventory{inv_type: InvType::Transaction, hash: hash([45u8; 32])}]),
|
NetworkMessage::GetData(vec![Inventory::Transaction(hash([45u8; 32]).into())]),
|
||||||
NetworkMessage::NotFound(vec![Inventory{inv_type: InvType::Error, hash: hash([45u8; 32])}]),
|
NetworkMessage::NotFound(vec![Inventory::Error]),
|
||||||
NetworkMessage::GetBlocks(GetBlocksMessage::new(vec![hash([1u8; 32]), hash([4u8; 32])], hash([5u8; 32]))),
|
NetworkMessage::GetBlocks(GetBlocksMessage::new(vec![hash([1u8; 32]).into(), hash([4u8; 32]).into()], hash([5u8; 32]).into())),
|
||||||
NetworkMessage::GetHeaders(GetHeadersMessage::new(vec![hash([10u8; 32]), hash([40u8; 32])], hash([50u8; 32]))),
|
NetworkMessage::GetHeaders(GetHeadersMessage::new(vec![hash([10u8; 32]).into(), hash([40u8; 32]).into()], hash([50u8; 32]).into())),
|
||||||
NetworkMessage::MemPool,
|
NetworkMessage::MemPool,
|
||||||
NetworkMessage::Tx(tx),
|
NetworkMessage::Tx(tx),
|
||||||
NetworkMessage::Block(block),
|
NetworkMessage::Block(block),
|
||||||
|
@ -375,12 +375,12 @@ mod test {
|
||||||
NetworkMessage::GetAddr,
|
NetworkMessage::GetAddr,
|
||||||
NetworkMessage::Ping(15),
|
NetworkMessage::Ping(15),
|
||||||
NetworkMessage::Pong(23),
|
NetworkMessage::Pong(23),
|
||||||
NetworkMessage::GetCFilters(GetCFilters{filter_type: 2, start_height: 52, stop_hash: hash([42u8; 32])}),
|
NetworkMessage::GetCFilters(GetCFilters{filter_type: 2, start_height: 52, stop_hash: hash([42u8; 32]).into()}),
|
||||||
NetworkMessage::CFilter(CFilter{filter_type: 7, block_hash: hash([25u8; 32]), filter: vec![1,2,3]}),
|
NetworkMessage::CFilter(CFilter{filter_type: 7, block_hash: hash([25u8; 32]).into(), filter: vec![1,2,3]}),
|
||||||
NetworkMessage::GetCFHeaders(GetCFHeaders{filter_type: 4, start_height: 102, stop_hash: hash([47u8; 32])}),
|
NetworkMessage::GetCFHeaders(GetCFHeaders{filter_type: 4, start_height: 102, stop_hash: hash([47u8; 32]).into()}),
|
||||||
NetworkMessage::CFHeaders(CFHeaders{filter_type: 13, stop_hash: hash([53u8; 32]), previous_filter: hash([12u8; 32]), filter_hashes: vec![hash([4u8; 32]), hash([12u8; 32])]}),
|
NetworkMessage::CFHeaders(CFHeaders{filter_type: 13, stop_hash: hash([53u8; 32]).into(), previous_filter: hash([12u8; 32]).into(), filter_hashes: vec![hash([4u8; 32]).into(), hash([12u8; 32]).into()]}),
|
||||||
NetworkMessage::GetCFCheckpt(GetCFCheckpt{filter_type: 17, stop_hash: hash([25u8; 32])}),
|
NetworkMessage::GetCFCheckpt(GetCFCheckpt{filter_type: 17, stop_hash: hash([25u8; 32]).into()}),
|
||||||
NetworkMessage::CFCheckpt(CFCheckpt{filter_type: 27, stop_hash: hash([77u8; 32]), filter_headers: vec![hash([3u8; 32]), hash([99u8; 32])]}),
|
NetworkMessage::CFCheckpt(CFCheckpt{filter_type: 27, stop_hash: hash([77u8; 32]).into(), filter_headers: vec![hash([3u8; 32]).into(), hash([99u8; 32]).into()]}),
|
||||||
NetworkMessage::Alert(vec![45,66,3,2,6,8,9,12,3,130]),
|
NetworkMessage::Alert(vec![45,66,3,2,6,8,9,12,3,130]),
|
||||||
NetworkMessage::Reject(Reject{message: "Test reject".into(), ccode: RejectReason::Duplicate, reason: "Cause".into(), hash: hash([255u8; 32])}),
|
NetworkMessage::Reject(Reject{message: "Test reject".into(), ccode: RejectReason::Duplicate, reason: "Cause".into(), hash: hash([255u8; 32])}),
|
||||||
];
|
];
|
||||||
|
|
|
@ -18,25 +18,64 @@
|
||||||
//! Bitcoin data (blocks and transactions) around.
|
//! Bitcoin data (blocks and transactions) around.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use network::constants;
|
|
||||||
use consensus::encode::{self, Decodable, Encodable};
|
|
||||||
use hashes::sha256d;
|
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
|
use hashes::sha256d;
|
||||||
/// The type of an inventory object
|
|
||||||
pub enum InvType {
|
use network::constants;
|
||||||
|
use consensus::encode::{self, Decodable, Encodable};
|
||||||
|
use hash_types::{BlockHash, Txid, Wtxid};
|
||||||
|
|
||||||
|
/// An inventory item.
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
|
||||||
|
pub enum Inventory {
|
||||||
/// Error --- these inventories can be ignored
|
/// Error --- these inventories can be ignored
|
||||||
Error,
|
Error,
|
||||||
/// Transaction
|
/// Transaction
|
||||||
Transaction,
|
Transaction(Txid),
|
||||||
/// Block
|
/// Block
|
||||||
Block,
|
Block(BlockHash),
|
||||||
/// Witness Block
|
|
||||||
WitnessBlock,
|
|
||||||
/// Witness Transaction
|
/// Witness Transaction
|
||||||
WitnessTransaction
|
WitnessTransaction(Wtxid),
|
||||||
|
/// Witness Block
|
||||||
|
WitnessBlock(BlockHash),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for Inventory {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_encode<S: io::Write>(
|
||||||
|
&self,
|
||||||
|
mut s: S,
|
||||||
|
) -> Result<usize, encode::Error> {
|
||||||
|
macro_rules! encode_inv {
|
||||||
|
($code:expr, $item:expr) => {
|
||||||
|
u32::consensus_encode(&$code, &mut s)? +
|
||||||
|
$item.consensus_encode(&mut s)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(match *self {
|
||||||
|
Inventory::Error => encode_inv!(0, sha256d::Hash::default()),
|
||||||
|
Inventory::Transaction(ref t) => encode_inv!(1, t),
|
||||||
|
Inventory::Block(ref b) => encode_inv!(2, b),
|
||||||
|
Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t),
|
||||||
|
Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for Inventory {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||||
|
let inv_type: u32 = Decodable::consensus_decode(&mut d)?;
|
||||||
|
Ok(match inv_type {
|
||||||
|
0 => Inventory::Error,
|
||||||
|
1 => Inventory::Transaction(Decodable::consensus_decode(&mut d)?),
|
||||||
|
2 => Inventory::Block(Decodable::consensus_decode(&mut d)?),
|
||||||
|
0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?),
|
||||||
|
0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?),
|
||||||
|
tp => return Err(encode::Error::UnknownInventoryType(tp)),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some simple messages
|
// Some simple messages
|
||||||
|
@ -49,9 +88,9 @@ pub struct GetBlocksMessage {
|
||||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||||
/// reply with its longest known chain, starting from a locator hash
|
/// reply with its longest known chain, starting from a locator hash
|
||||||
/// if possible and block 1 otherwise.
|
/// if possible and block 1 otherwise.
|
||||||
pub locator_hashes: Vec<sha256d::Hash>,
|
pub locator_hashes: Vec<BlockHash>,
|
||||||
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
||||||
pub stop_hash: sha256d::Hash,
|
pub stop_hash: BlockHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `getheaders` message
|
/// The `getheaders` message
|
||||||
|
@ -62,29 +101,14 @@ pub struct GetHeadersMessage {
|
||||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||||
/// reply with its longest known chain, starting from a locator hash
|
/// reply with its longest known chain, starting from a locator hash
|
||||||
/// if possible and block 1 otherwise.
|
/// if possible and block 1 otherwise.
|
||||||
pub locator_hashes: Vec<sha256d::Hash>,
|
pub locator_hashes: Vec<BlockHash>,
|
||||||
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||||
pub stop_hash: sha256d::Hash
|
pub stop_hash: BlockHash
|
||||||
}
|
|
||||||
|
|
||||||
/// An inventory object --- a reference to a Bitcoin object
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
||||||
pub struct Inventory {
|
|
||||||
/// The type of object that is referenced
|
|
||||||
pub inv_type: InvType,
|
|
||||||
/// The object's hash
|
|
||||||
pub hash: sha256d::Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::hash::Hash for Inventory {
|
|
||||||
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.hash.hash(state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetBlocksMessage {
|
impl GetBlocksMessage {
|
||||||
/// Construct a new `getblocks` message
|
/// Construct a new `getblocks` message
|
||||||
pub fn new(locator_hashes: Vec<sha256d::Hash>, stop_hash: sha256d::Hash) -> GetBlocksMessage {
|
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
|
||||||
GetBlocksMessage {
|
GetBlocksMessage {
|
||||||
version: constants::PROTOCOL_VERSION,
|
version: constants::PROTOCOL_VERSION,
|
||||||
locator_hashes: locator_hashes.clone(),
|
locator_hashes: locator_hashes.clone(),
|
||||||
|
@ -97,7 +121,7 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
|
||||||
|
|
||||||
impl GetHeadersMessage {
|
impl GetHeadersMessage {
|
||||||
/// Construct a new `getheaders` message
|
/// Construct a new `getheaders` message
|
||||||
pub fn new(locator_hashes: Vec<sha256d::Hash>, stop_hash: sha256d::Hash) -> GetHeadersMessage {
|
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
|
||||||
GetHeadersMessage {
|
GetHeadersMessage {
|
||||||
version: constants::PROTOCOL_VERSION,
|
version: constants::PROTOCOL_VERSION,
|
||||||
locator_hashes: locator_hashes,
|
locator_hashes: locator_hashes,
|
||||||
|
@ -108,40 +132,6 @@ impl GetHeadersMessage {
|
||||||
|
|
||||||
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
||||||
|
|
||||||
impl Encodable for Inventory {
|
|
||||||
#[inline]
|
|
||||||
fn consensus_encode<S: io::Write>(
|
|
||||||
&self,
|
|
||||||
mut s: S,
|
|
||||||
) -> Result<usize, encode::Error> {
|
|
||||||
let inv_len = match self.inv_type {
|
|
||||||
InvType::Error => 0u32,
|
|
||||||
InvType::Transaction => 1,
|
|
||||||
InvType::Block => 2,
|
|
||||||
InvType::WitnessBlock => 0x40000002,
|
|
||||||
InvType::WitnessTransaction => 0x40000001
|
|
||||||
}.consensus_encode(&mut s)?;
|
|
||||||
Ok(inv_len + self.hash.consensus_encode(&mut s)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decodable for Inventory {
|
|
||||||
#[inline]
|
|
||||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
|
||||||
let int_type: u32 = Decodable::consensus_decode(&mut d)?;
|
|
||||||
Ok(Inventory {
|
|
||||||
inv_type: match int_type {
|
|
||||||
0 => InvType::Error,
|
|
||||||
1 => InvType::Transaction,
|
|
||||||
2 => InvType::Block,
|
|
||||||
// TODO do not fail here
|
|
||||||
_ => { panic!("bad inventory type field") }
|
|
||||||
},
|
|
||||||
hash: Decodable::consensus_decode(d)?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
//!
|
//!
|
||||||
//! BIP157 Client Side Block Filtering network messages
|
//! BIP157 Client Side Block Filtering network messages
|
||||||
//!
|
//!
|
||||||
use hashes::sha256d;
|
|
||||||
|
use hash_types::{BlockHash, FilterHash};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
/// getcfilters message
|
/// getcfilters message
|
||||||
|
@ -11,7 +12,7 @@ pub struct GetCFilters {
|
||||||
/// The height of the first block in the requested range
|
/// The height of the first block in the requested range
|
||||||
pub start_height: u32,
|
pub start_height: u32,
|
||||||
/// The hash of the last block in the requested range
|
/// The hash of the last block in the requested range
|
||||||
pub stop_hash: sha256d::Hash,
|
pub stop_hash: BlockHash,
|
||||||
}
|
}
|
||||||
impl_consensus_encoding!(GetCFilters, filter_type, start_height, stop_hash);
|
impl_consensus_encoding!(GetCFilters, filter_type, start_height, stop_hash);
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ pub struct CFilter {
|
||||||
/// Byte identifying the type of filter being returned
|
/// Byte identifying the type of filter being returned
|
||||||
pub filter_type: u8,
|
pub filter_type: u8,
|
||||||
/// Block hash of the Bitcoin block for which the filter is being returned
|
/// Block hash of the Bitcoin block for which the filter is being returned
|
||||||
pub block_hash: sha256d::Hash,
|
pub block_hash: BlockHash,
|
||||||
/// The serialized compact filter for this block
|
/// The serialized compact filter for this block
|
||||||
pub filter: Vec<u8>,
|
pub filter: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
@ -35,7 +36,7 @@ pub struct GetCFHeaders {
|
||||||
/// The height of the first block in the requested range
|
/// The height of the first block in the requested range
|
||||||
pub start_height: u32,
|
pub start_height: u32,
|
||||||
/// The hash of the last block in the requested range
|
/// The hash of the last block in the requested range
|
||||||
pub stop_hash: sha256d::Hash,
|
pub stop_hash: BlockHash,
|
||||||
}
|
}
|
||||||
impl_consensus_encoding!(GetCFHeaders, filter_type, start_height, stop_hash);
|
impl_consensus_encoding!(GetCFHeaders, filter_type, start_height, stop_hash);
|
||||||
|
|
||||||
|
@ -45,11 +46,11 @@ pub struct CFHeaders {
|
||||||
/// Filter type for which headers are requested
|
/// Filter type for which headers are requested
|
||||||
pub filter_type: u8,
|
pub filter_type: u8,
|
||||||
/// The hash of the last block in the requested range
|
/// The hash of the last block in the requested range
|
||||||
pub stop_hash: sha256d::Hash,
|
pub stop_hash: BlockHash,
|
||||||
/// The filter header preceding the first block in the requested range
|
/// The filter header preceding the first block in the requested range
|
||||||
pub previous_filter: sha256d::Hash,
|
pub previous_filter: FilterHash,
|
||||||
/// The filter hashes for each block in the requested range
|
/// The filter hashes for each block in the requested range
|
||||||
pub filter_hashes: Vec<sha256d::Hash>,
|
pub filter_hashes: Vec<FilterHash>,
|
||||||
}
|
}
|
||||||
impl_consensus_encoding!(CFHeaders, filter_type, stop_hash, previous_filter, filter_hashes);
|
impl_consensus_encoding!(CFHeaders, filter_type, stop_hash, previous_filter, filter_hashes);
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ pub struct GetCFCheckpt {
|
||||||
/// Filter type for which headers are requested
|
/// Filter type for which headers are requested
|
||||||
pub filter_type: u8,
|
pub filter_type: u8,
|
||||||
/// The hash of the last block in the requested range
|
/// The hash of the last block in the requested range
|
||||||
pub stop_hash: sha256d::Hash,
|
pub stop_hash: BlockHash,
|
||||||
}
|
}
|
||||||
impl_consensus_encoding!(GetCFCheckpt, filter_type, stop_hash);
|
impl_consensus_encoding!(GetCFCheckpt, filter_type, stop_hash);
|
||||||
|
|
||||||
|
@ -69,8 +70,8 @@ pub struct CFCheckpt {
|
||||||
/// Filter type for which headers are requested
|
/// Filter type for which headers are requested
|
||||||
pub filter_type: u8,
|
pub filter_type: u8,
|
||||||
/// The hash of the last block in the requested range
|
/// The hash of the last block in the requested range
|
||||||
pub stop_hash: sha256d::Hash,
|
pub stop_hash: BlockHash,
|
||||||
/// The filter headers at intervals of 1,000
|
/// The filter headers at intervals of 1,000
|
||||||
pub filter_headers: Vec<sha256d::Hash>,
|
pub filter_headers: Vec<FilterHash>,
|
||||||
}
|
}
|
||||||
impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers);
|
impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers);
|
|
@ -44,8 +44,9 @@ use std::fmt::{self, Display, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use bech32;
|
use bech32;
|
||||||
use hashes::{hash160, sha256, Hash};
|
use hashes::Hash;
|
||||||
|
|
||||||
|
use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use blockdata::script;
|
use blockdata::script;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
|
@ -159,9 +160,9 @@ impl FromStr for AddressType {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Payload {
|
pub enum Payload {
|
||||||
/// pay-to-pkhash address
|
/// pay-to-pkhash address
|
||||||
PubkeyHash(hash160::Hash),
|
PubkeyHash(PubkeyHash),
|
||||||
/// P2SH address
|
/// P2SH address
|
||||||
ScriptHash(hash160::Hash),
|
ScriptHash(ScriptHash),
|
||||||
/// Segwit address
|
/// Segwit address
|
||||||
WitnessProgram {
|
WitnessProgram {
|
||||||
/// The witness program version
|
/// The witness program version
|
||||||
|
@ -175,9 +176,9 @@ impl Payload {
|
||||||
/// Get a [Payload] from an output script (scriptPubkey).
|
/// Get a [Payload] from an output script (scriptPubkey).
|
||||||
pub fn from_script(script: &script::Script) -> Option<Payload> {
|
pub fn from_script(script: &script::Script) -> Option<Payload> {
|
||||||
Some(if script.is_p2pkh() {
|
Some(if script.is_p2pkh() {
|
||||||
Payload::PubkeyHash(Hash::from_slice(&script.as_bytes()[3..23]).unwrap())
|
Payload::PubkeyHash(PubkeyHash::from_slice(&script.as_bytes()[3..23]).unwrap())
|
||||||
} else if script.is_p2sh() {
|
} else if script.is_p2sh() {
|
||||||
Payload::ScriptHash(Hash::from_slice(&script.as_bytes()[2..22]).unwrap())
|
Payload::ScriptHash(ScriptHash::from_slice(&script.as_bytes()[2..22]).unwrap())
|
||||||
} else if script.is_witness_program() {
|
} else if script.is_witness_program() {
|
||||||
// We can unwrap the u5 check and assume script length
|
// We can unwrap the u5 check and assume script length
|
||||||
// because [Script::is_witness_program] makes sure of this.
|
// because [Script::is_witness_program] makes sure of this.
|
||||||
|
@ -242,12 +243,12 @@ impl Address {
|
||||||
/// This is the preferred non-witness type address
|
/// This is the preferred non-witness type address
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn p2pkh(pk: &key::PublicKey, network: Network) -> Address {
|
pub fn p2pkh(pk: &key::PublicKey, network: Network) -> Address {
|
||||||
let mut hash_engine = hash160::Hash::engine();
|
let mut hash_engine = PubkeyHash::engine();
|
||||||
pk.write_into(&mut hash_engine);
|
pk.write_into(&mut hash_engine);
|
||||||
|
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::PubkeyHash(hash160::Hash::from_engine(hash_engine)),
|
payload: Payload::PubkeyHash(PubkeyHash::from_engine(hash_engine)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,21 +258,21 @@ impl Address {
|
||||||
pub fn p2sh(script: &script::Script, network: Network) -> Address {
|
pub fn p2sh(script: &script::Script, network: Network) -> Address {
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::ScriptHash(hash160::Hash::hash(&script[..])),
|
payload: Payload::ScriptHash(ScriptHash::hash(&script[..])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a witness pay to public key address from a public key
|
/// Create a witness pay to public key address from a public key
|
||||||
/// This is the native segwit address type for an output redeemable with a single signature
|
/// This is the native segwit address type for an output redeemable with a single signature
|
||||||
pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Address {
|
pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Address {
|
||||||
let mut hash_engine = hash160::Hash::engine();
|
let mut hash_engine = WPubkeyHash::engine();
|
||||||
pk.write_into(&mut hash_engine);
|
pk.write_into(&mut hash_engine);
|
||||||
|
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::WitnessProgram {
|
payload: Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
||||||
program: hash160::Hash::from_engine(hash_engine)[..].to_vec(),
|
program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,16 +280,16 @@ impl Address {
|
||||||
/// Create a pay to script address that embeds a witness pay to public key
|
/// Create a pay to script address that embeds a witness pay to public key
|
||||||
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients
|
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients
|
||||||
pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Address {
|
pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Address {
|
||||||
let mut hash_engine = hash160::Hash::engine();
|
let mut hash_engine = WPubkeyHash::engine();
|
||||||
pk.write_into(&mut hash_engine);
|
pk.write_into(&mut hash_engine);
|
||||||
|
|
||||||
let builder = script::Builder::new()
|
let builder = script::Builder::new()
|
||||||
.push_int(0)
|
.push_int(0)
|
||||||
.push_slice(&hash160::Hash::from_engine(hash_engine)[..]);
|
.push_slice(&WPubkeyHash::from_engine(hash_engine)[..]);
|
||||||
|
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::ScriptHash(hash160::Hash::hash(builder.into_script().as_bytes())),
|
payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +299,7 @@ impl Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::WitnessProgram {
|
payload: Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
||||||
program: sha256::Hash::hash(&script[..])[..].to_vec(),
|
program: WScriptHash::hash(&script[..])[..].to_vec(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,12 +309,12 @@ impl Address {
|
||||||
pub fn p2shwsh(script: &script::Script, network: Network) -> Address {
|
pub fn p2shwsh(script: &script::Script, network: Network) -> Address {
|
||||||
let ws = script::Builder::new()
|
let ws = script::Builder::new()
|
||||||
.push_int(0)
|
.push_int(0)
|
||||||
.push_slice(&sha256::Hash::hash(&script[..])[..])
|
.push_slice(&WScriptHash::hash(&script[..])[..])
|
||||||
.into_script();
|
.into_script();
|
||||||
|
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::ScriptHash(hash160::Hash::hash(&ws[..])),
|
payload: Payload::ScriptHash(ScriptHash::hash(&ws[..])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,19 +471,19 @@ impl FromStr for Address {
|
||||||
let (network, payload) = match data[0] {
|
let (network, payload) = match data[0] {
|
||||||
0 => (
|
0 => (
|
||||||
Network::Bitcoin,
|
Network::Bitcoin,
|
||||||
Payload::PubkeyHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
|
Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap()),
|
||||||
),
|
),
|
||||||
5 => (
|
5 => (
|
||||||
Network::Bitcoin,
|
Network::Bitcoin,
|
||||||
Payload::ScriptHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
|
Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap()),
|
||||||
),
|
),
|
||||||
111 => (
|
111 => (
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
Payload::PubkeyHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
|
Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap()),
|
||||||
),
|
),
|
||||||
196 => (
|
196 => (
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
Payload::ScriptHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
|
Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap()),
|
||||||
),
|
),
|
||||||
x => return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))),
|
x => return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))),
|
||||||
};
|
};
|
||||||
|
@ -505,7 +506,7 @@ mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
use hashes::{hash160, Hash};
|
use hashes::Hash;
|
||||||
use hex::{decode as hex_decode, encode as hex_encode};
|
use hex::{decode as hex_decode, encode as hex_encode};
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
|
@ -517,7 +518,8 @@ mod tests {
|
||||||
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
|
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
|
||||||
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
|
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
|
||||||
macro_rules! hex_script (($hex:expr) => (Script::from(hex!($hex))));
|
macro_rules! hex_script (($hex:expr) => (Script::from(hex!($hex))));
|
||||||
macro_rules! hex_hash160 (($hex:expr) => (hash160::Hash::from_slice(&hex!($hex)).unwrap()));
|
macro_rules! hex_pubkeyhash (($hex:expr) => (PubkeyHash::from_slice(&hex!($hex)).unwrap()));
|
||||||
|
macro_rules! hex_scripthash (($hex:expr) => (ScriptHash::from_slice(&hex!($hex)).unwrap()));
|
||||||
|
|
||||||
fn roundtrips(addr: &Address) {
|
fn roundtrips(addr: &Address) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -539,7 +541,7 @@ mod tests {
|
||||||
fn test_p2pkh_address_58() {
|
fn test_p2pkh_address_58() {
|
||||||
let addr = Address {
|
let addr = Address {
|
||||||
network: Bitcoin,
|
network: Bitcoin,
|
||||||
payload: Payload::PubkeyHash(hex_hash160!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
|
payload: Payload::PubkeyHash(hex_pubkeyhash!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -568,7 +570,7 @@ mod tests {
|
||||||
fn test_p2sh_address_58() {
|
fn test_p2sh_address_58() {
|
||||||
let addr = Address {
|
let addr = Address {
|
||||||
network: Bitcoin,
|
network: Bitcoin,
|
||||||
payload: Payload::ScriptHash(hex_hash160!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
|
payload: Payload::ScriptHash(hex_scripthash!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
//! signatures, which are placed in the scriptSig.
|
//! signatures, which are placed in the scriptSig.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use hashes::{sha256d, Hash};
|
use hashes::Hash;
|
||||||
|
use hash_types::SigHash;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use blockdata::transaction::{Transaction, TxIn};
|
use blockdata::transaction::{Transaction, TxIn};
|
||||||
use consensus::encode::Encodable;
|
use consensus::encode::Encodable;
|
||||||
|
@ -32,11 +32,11 @@ pub struct SighashComponents {
|
||||||
tx_version: u32,
|
tx_version: u32,
|
||||||
tx_locktime: u32,
|
tx_locktime: u32,
|
||||||
/// Hash of all the previous outputs
|
/// Hash of all the previous outputs
|
||||||
pub hash_prevouts: sha256d::Hash,
|
pub hash_prevouts: SigHash,
|
||||||
/// Hash of all the input sequence nos
|
/// Hash of all the input sequence nos
|
||||||
pub hash_sequence: sha256d::Hash,
|
pub hash_sequence: SigHash,
|
||||||
/// Hash of all the outputs in this transaction
|
/// Hash of all the outputs in this transaction
|
||||||
pub hash_outputs: sha256d::Hash,
|
pub hash_outputs: SigHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SighashComponents {
|
impl SighashComponents {
|
||||||
|
@ -46,27 +46,27 @@ impl SighashComponents {
|
||||||
/// script_sig and witnesses.
|
/// script_sig and witnesses.
|
||||||
pub fn new(tx: &Transaction) -> SighashComponents {
|
pub fn new(tx: &Transaction) -> SighashComponents {
|
||||||
let hash_prevouts = {
|
let hash_prevouts = {
|
||||||
let mut enc = sha256d::Hash::engine();
|
let mut enc = SigHash::engine();
|
||||||
for txin in &tx.input {
|
for txin in &tx.input {
|
||||||
txin.previous_output.consensus_encode(&mut enc).unwrap();
|
txin.previous_output.consensus_encode(&mut enc).unwrap();
|
||||||
}
|
}
|
||||||
sha256d::Hash::from_engine(enc)
|
SigHash::from_engine(enc)
|
||||||
};
|
};
|
||||||
|
|
||||||
let hash_sequence = {
|
let hash_sequence = {
|
||||||
let mut enc = sha256d::Hash::engine();
|
let mut enc = SigHash::engine();
|
||||||
for txin in &tx.input {
|
for txin in &tx.input {
|
||||||
txin.sequence.consensus_encode(&mut enc).unwrap();
|
txin.sequence.consensus_encode(&mut enc).unwrap();
|
||||||
}
|
}
|
||||||
sha256d::Hash::from_engine(enc)
|
SigHash::from_engine(enc)
|
||||||
};
|
};
|
||||||
|
|
||||||
let hash_outputs = {
|
let hash_outputs = {
|
||||||
let mut enc = sha256d::Hash::engine();
|
let mut enc = SigHash::engine();
|
||||||
for txout in &tx.output {
|
for txout in &tx.output {
|
||||||
txout.consensus_encode(&mut enc).unwrap();
|
txout.consensus_encode(&mut enc).unwrap();
|
||||||
}
|
}
|
||||||
sha256d::Hash::from_engine(enc)
|
SigHash::from_engine(enc)
|
||||||
};
|
};
|
||||||
|
|
||||||
SighashComponents {
|
SighashComponents {
|
||||||
|
@ -80,8 +80,8 @@ impl SighashComponents {
|
||||||
|
|
||||||
/// Compute the BIP143 sighash for a `SIGHASH_ALL` signature for the given
|
/// Compute the BIP143 sighash for a `SIGHASH_ALL` signature for the given
|
||||||
/// input.
|
/// input.
|
||||||
pub fn sighash_all(&self, txin: &TxIn, script_code: &Script, value: u64) -> sha256d::Hash {
|
pub fn sighash_all(&self, txin: &TxIn, script_code: &Script, value: u64) -> SigHash {
|
||||||
let mut enc = sha256d::Hash::engine();
|
let mut enc = SigHash::engine();
|
||||||
self.tx_version.consensus_encode(&mut enc).unwrap();
|
self.tx_version.consensus_encode(&mut enc).unwrap();
|
||||||
self.hash_prevouts.consensus_encode(&mut enc).unwrap();
|
self.hash_prevouts.consensus_encode(&mut enc).unwrap();
|
||||||
self.hash_sequence.consensus_encode(&mut enc).unwrap();
|
self.hash_sequence.consensus_encode(&mut enc).unwrap();
|
||||||
|
@ -95,12 +95,13 @@ impl SighashComponents {
|
||||||
self.hash_outputs.consensus_encode(&mut enc).unwrap();
|
self.hash_outputs.consensus_encode(&mut enc).unwrap();
|
||||||
self.tx_locktime.consensus_encode(&mut enc).unwrap();
|
self.tx_locktime.consensus_encode(&mut enc).unwrap();
|
||||||
1u32.consensus_encode(&mut enc).unwrap(); // hashtype
|
1u32.consensus_encode(&mut enc).unwrap(); // hashtype
|
||||||
sha256d::Hash::from_engine(enc)
|
SigHash::from_engine(enc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use hash_types::SigHash;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use blockdata::transaction::Transaction;
|
use blockdata::transaction::Transaction;
|
||||||
use consensus::encode::deserialize;
|
use consensus::encode::deserialize;
|
||||||
|
@ -140,20 +141,20 @@ mod tests {
|
||||||
tx_version: 1,
|
tx_version: 1,
|
||||||
tx_locktime: 17,
|
tx_locktime: 17,
|
||||||
hash_prevouts: hex_hash!(
|
hash_prevouts: hex_hash!(
|
||||||
"96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
|
SigHash, "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
|
||||||
),
|
),
|
||||||
hash_sequence: hex_hash!(
|
hash_sequence: hex_hash!(
|
||||||
"52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b"
|
SigHash, "52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b"
|
||||||
),
|
),
|
||||||
hash_outputs: hex_hash!(
|
hash_outputs: hex_hash!(
|
||||||
"863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5"
|
SigHash, "863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
comp.sighash_all(&tx.input[1], &witness_script, value),
|
comp.sighash_all(&tx.input[1], &witness_script, value),
|
||||||
hex_hash!("c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670")
|
hex_hash!(SigHash, "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,20 +178,20 @@ mod tests {
|
||||||
tx_version: 1,
|
tx_version: 1,
|
||||||
tx_locktime: 1170,
|
tx_locktime: 1170,
|
||||||
hash_prevouts: hex_hash!(
|
hash_prevouts: hex_hash!(
|
||||||
"b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
|
SigHash, "b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
|
||||||
),
|
),
|
||||||
hash_sequence: hex_hash!(
|
hash_sequence: hex_hash!(
|
||||||
"18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198"
|
SigHash, "18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198"
|
||||||
),
|
),
|
||||||
hash_outputs: hex_hash!(
|
hash_outputs: hex_hash!(
|
||||||
"de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83"
|
SigHash, "de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
comp.sighash_all(&tx.input[0], &witness_script, value),
|
comp.sighash_all(&tx.input[0], &witness_script, value),
|
||||||
hex_hash!("64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6")
|
hex_hash!(SigHash, "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,20 +221,20 @@ mod tests {
|
||||||
tx_version: 1,
|
tx_version: 1,
|
||||||
tx_locktime: 0,
|
tx_locktime: 0,
|
||||||
hash_prevouts: hex_hash!(
|
hash_prevouts: hex_hash!(
|
||||||
"74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
|
SigHash, "74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
|
||||||
),
|
),
|
||||||
hash_sequence: hex_hash!(
|
hash_sequence: hex_hash!(
|
||||||
"3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044"
|
SigHash, "3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044"
|
||||||
),
|
),
|
||||||
hash_outputs: hex_hash!(
|
hash_outputs: hex_hash!(
|
||||||
"bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc"
|
SigHash, "bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
comp.sighash_all(&tx.input[0], &witness_script, value),
|
comp.sighash_all(&tx.input[0], &witness_script, value),
|
||||||
hex_hash!("185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c")
|
hex_hash!(SigHash, "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,8 @@ use std::error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use hashes::{Hash, sha256d, siphash24};
|
use hashes::{Hash, siphash24};
|
||||||
|
use hash_types::{BlockHash, FilterHash};
|
||||||
|
|
||||||
use blockdata::block::Block;
|
use blockdata::block::Block;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
|
@ -106,12 +107,12 @@ pub struct BlockFilter {
|
||||||
|
|
||||||
impl BlockFilter {
|
impl BlockFilter {
|
||||||
/// compute this filter's id in a chain of filters
|
/// compute this filter's id in a chain of filters
|
||||||
pub fn filter_id(&self, previous_filter_id: &sha256d::Hash) -> sha256d::Hash {
|
pub fn filter_id(&self, previous_filter_id: &FilterHash) -> FilterHash {
|
||||||
let filter_hash = sha256d::Hash::hash(self.content.as_slice());
|
let filter_hash = FilterHash::hash(self.content.as_slice());
|
||||||
let mut header_data = [0u8; 64];
|
let mut header_data = [0u8; 64];
|
||||||
header_data[0..32].copy_from_slice(&filter_hash[..]);
|
header_data[0..32].copy_from_slice(&filter_hash[..]);
|
||||||
header_data[32..64].copy_from_slice(&previous_filter_id[..]);
|
header_data[32..64].copy_from_slice(&previous_filter_id[..]);
|
||||||
sha256d::Hash::hash(&header_data)
|
FilterHash::hash(&header_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// create a new filter from pre-computed data
|
/// create a new filter from pre-computed data
|
||||||
|
@ -133,13 +134,13 @@ impl BlockFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// match any query pattern
|
/// match any query pattern
|
||||||
pub fn match_any(&self, block_hash: &sha256d::Hash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
|
pub fn match_any(&self, block_hash: &BlockHash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
|
||||||
let filter_reader = BlockFilterReader::new(block_hash);
|
let filter_reader = BlockFilterReader::new(block_hash);
|
||||||
filter_reader.match_any(&mut Cursor::new(self.content.as_slice()), query)
|
filter_reader.match_any(&mut Cursor::new(self.content.as_slice()), query)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// match all query pattern
|
/// match all query pattern
|
||||||
pub fn match_all(&self, block_hash: &sha256d::Hash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
|
pub fn match_all(&self, block_hash: &BlockHash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
|
||||||
let filter_reader = BlockFilterReader::new(block_hash);
|
let filter_reader = BlockFilterReader::new(block_hash);
|
||||||
filter_reader.match_all(&mut Cursor::new(self.content.as_slice()), query)
|
filter_reader.match_all(&mut Cursor::new(self.content.as_slice()), query)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +207,7 @@ pub struct BlockFilterReader {
|
||||||
|
|
||||||
impl BlockFilterReader {
|
impl BlockFilterReader {
|
||||||
/// Create a block filter reader
|
/// Create a block filter reader
|
||||||
pub fn new(block_hash: &sha256d::Hash) -> BlockFilterReader {
|
pub fn new(block_hash: &BlockHash) -> BlockFilterReader {
|
||||||
let block_hash_as_int = block_hash.into_inner();
|
let block_hash_as_int = block_hash.into_inner();
|
||||||
let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]);
|
let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]);
|
||||||
let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]);
|
let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]);
|
||||||
|
@ -523,6 +524,7 @@ mod test {
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use hash_types::BlockHash;
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -555,13 +557,13 @@ mod test {
|
||||||
|
|
||||||
let testdata = serde_json::from_str::<Value>(data).unwrap().as_array().unwrap().clone();
|
let testdata = serde_json::from_str::<Value>(data).unwrap().as_array().unwrap().clone();
|
||||||
for t in testdata.iter().skip(1) {
|
for t in testdata.iter().skip(1) {
|
||||||
let block_hash = sha256d::Hash::from_hex(&t.get(1).unwrap().as_str().unwrap()).unwrap();
|
let block_hash = BlockHash::from_hex(&t.get(1).unwrap().as_str().unwrap()).unwrap();
|
||||||
let block: Block = deserialize(hex::decode(&t.get(2).unwrap().as_str().unwrap().as_bytes()).unwrap().as_slice()).unwrap();
|
let block: Block = deserialize(hex::decode(&t.get(2).unwrap().as_str().unwrap().as_bytes()).unwrap().as_slice()).unwrap();
|
||||||
assert_eq!(block.bitcoin_hash(), block_hash);
|
assert_eq!(block.bitcoin_hash(), block_hash);
|
||||||
let scripts = t.get(3).unwrap().as_array().unwrap();
|
let scripts = t.get(3).unwrap().as_array().unwrap();
|
||||||
let previous_filter_id = sha256d::Hash::from_hex(&t.get(4).unwrap().as_str().unwrap()).unwrap();
|
let previous_filter_id = FilterHash::from_hex(&t.get(4).unwrap().as_str().unwrap()).unwrap();
|
||||||
let filter_content = hex::decode(&t.get(5).unwrap().as_str().unwrap().as_bytes()).unwrap();
|
let filter_content = hex::decode(&t.get(5).unwrap().as_str().unwrap().as_bytes()).unwrap();
|
||||||
let filter_id = sha256d::Hash::from_hex(&t.get(6).unwrap().as_str().unwrap()).unwrap();
|
let filter_id = FilterHash::from_hex(&t.get(6).unwrap().as_str().unwrap()).unwrap();
|
||||||
|
|
||||||
let mut txmap = HashMap::new();
|
let mut txmap = HashMap::new();
|
||||||
let mut si = scripts.iter();
|
let mut si = scripts.iter();
|
||||||
|
@ -583,7 +585,7 @@ mod test {
|
||||||
assert_eq!(test_filter.content, filter.content);
|
assert_eq!(test_filter.content, filter.content);
|
||||||
|
|
||||||
let block_hash = &block.header.bitcoin_hash();
|
let block_hash = &block.header.bitcoin_hash();
|
||||||
assert!(filter.match_all(&block_hash, &mut txmap.iter()
|
assert!(filter.match_all(block_hash, &mut txmap.iter()
|
||||||
.filter_map(|(_, s)| if !s.is_empty() { Some(s.as_bytes()) } else { None })).unwrap());
|
.filter_map(|(_, s)| if !s.is_empty() { Some(s.as_bytes()) } else { None })).unwrap());
|
||||||
|
|
||||||
for (_, script) in &txmap {
|
for (_, script) in &txmap {
|
||||||
|
|
|
@ -21,7 +21,8 @@ use std::{error, fmt};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
#[cfg(feature = "serde")] use serde;
|
#[cfg(feature = "serde")] use serde;
|
||||||
|
|
||||||
use hashes::{hex, hash160, sha512, Hash, HashEngine, Hmac, HmacEngine};
|
use hash_types::XpubIdentifier;
|
||||||
|
use hashes::{hex, sha512, Hash, HashEngine, Hmac, HmacEngine};
|
||||||
use secp256k1::{self, Secp256k1};
|
use secp256k1::{self, Secp256k1};
|
||||||
|
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
|
@ -478,7 +479,7 @@ impl ExtendedPrivKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the HASH160 of the chaincode
|
/// Returns the HASH160 of the chaincode
|
||||||
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> hash160::Hash {
|
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XpubIdentifier {
|
||||||
ExtendedPubKey::from_private(secp, self).identifier()
|
ExtendedPubKey::from_private(secp, self).identifier()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,10 +562,10 @@ impl ExtendedPubKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the HASH160 of the chaincode
|
/// Returns the HASH160 of the chaincode
|
||||||
pub fn identifier(&self) -> hash160::Hash {
|
pub fn identifier(&self) -> XpubIdentifier {
|
||||||
let mut engine = hash160::Hash::engine();
|
let mut engine = XpubIdentifier::engine();
|
||||||
self.public_key.write_into(&mut engine);
|
self.public_key.write_into(&mut engine);
|
||||||
hash160::Hash::from_engine(engine)
|
XpubIdentifier::from_engine(engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the first four bytes of the identifier
|
/// Returns the first four bytes of the identifier
|
||||||
|
|
|
@ -21,11 +21,12 @@
|
||||||
use secp256k1::{self, Secp256k1};
|
use secp256k1::{self, Secp256k1};
|
||||||
use PrivateKey;
|
use PrivateKey;
|
||||||
use PublicKey;
|
use PublicKey;
|
||||||
use hashes::{hash160, sha256, Hash, HashEngine, Hmac, HmacEngine};
|
use hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
|
||||||
use blockdata::{opcodes, script};
|
use blockdata::{opcodes, script};
|
||||||
|
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
|
use hash_types::ScriptHash;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::address;
|
use util::address;
|
||||||
|
|
||||||
|
@ -208,7 +209,7 @@ pub fn create_address<C: secp256k1::Verification>(secp: &Secp256k1<C>,
|
||||||
Ok(address::Address {
|
Ok(address::Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: address::Payload::ScriptHash(
|
payload: address::Payload::ScriptHash(
|
||||||
hash160::Hash::hash(&script[..])
|
ScriptHash::hash(&script[..])
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,43 +16,68 @@
|
||||||
//! Utility functions related to hashing data, including merkleization
|
//! Utility functions related to hashing data, including merkleization
|
||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::default::Default;
|
use std::io;
|
||||||
|
|
||||||
use hashes::{sha256d, Hash};
|
|
||||||
|
|
||||||
|
use hashes::Hash;
|
||||||
use consensus::encode::Encodable;
|
use consensus::encode::Encodable;
|
||||||
|
|
||||||
/// Any collection of objects for which a merkle root makes sense to calculate
|
/// Calculates the merkle root of a list of hashes inline
|
||||||
pub trait MerkleRoot {
|
/// into the allocated slice.
|
||||||
/// Construct a merkle tree from a collection, with elements ordered as
|
///
|
||||||
/// they were in the original collection, and return the merkle root.
|
/// In most cases, you'll want to use [bitcoin_merkle_root] instead.
|
||||||
fn merkle_root(&self) -> sha256d::Hash;
|
pub fn bitcoin_merkle_root_inline<T>(data: &mut [T]) -> T
|
||||||
}
|
where T: Hash + Encodable,
|
||||||
|
<T as Hash>::Engine: io::Write,
|
||||||
/// Calculates the merkle root of a list of txids hashes directly
|
{
|
||||||
pub fn bitcoin_merkle_root(data: Vec<sha256d::Hash>) -> sha256d::Hash {
|
|
||||||
// Base case
|
// Base case
|
||||||
if data.len() < 1 {
|
if data.len() < 1 {
|
||||||
return Default::default();
|
return Default::default();
|
||||||
}
|
}
|
||||||
if data.len() < 2 {
|
if data.len() < 2 {
|
||||||
return data[0];
|
return T::from_inner(data[0].into_inner());
|
||||||
}
|
}
|
||||||
// Recursion
|
// Recursion
|
||||||
let mut next = vec![];
|
|
||||||
for idx in 0..((data.len() + 1) / 2) {
|
for idx in 0..((data.len() + 1) / 2) {
|
||||||
let idx1 = 2 * idx;
|
let idx1 = 2 * idx;
|
||||||
let idx2 = min(idx1 + 1, data.len() - 1);
|
let idx2 = min(idx1 + 1, data.len() - 1);
|
||||||
let mut encoder = sha256d::Hash::engine();
|
let mut encoder = T::engine();
|
||||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||||
next.push(sha256d::Hash::from_engine(encoder));
|
data[idx] = T::from_engine(encoder);
|
||||||
}
|
}
|
||||||
bitcoin_merkle_root(next)
|
let half_len = data.len() / 2 + data.len() % 2;
|
||||||
|
bitcoin_merkle_root_inline(&mut data[0..half_len])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the merkle root of an iterator of hashes.
|
||||||
|
pub fn bitcoin_merkle_root<T, I>(mut iter: I) -> T
|
||||||
|
where T: Hash + Encodable,
|
||||||
|
<T as Hash>::Engine: io::Write,
|
||||||
|
I: ExactSizeIterator<Item = T>,
|
||||||
|
{
|
||||||
|
// Base case
|
||||||
|
if iter.len() == 0 {
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
if iter.len() == 1 {
|
||||||
|
return T::from_inner(iter.nth(0).unwrap().into_inner());
|
||||||
|
}
|
||||||
|
// Recursion
|
||||||
|
let half_len = iter.len() / 2 + iter.len() % 2;
|
||||||
|
let mut alloc = Vec::with_capacity(half_len);
|
||||||
|
while let Some(hash1) = iter.next() {
|
||||||
|
// If the size is odd, use the last element twice.
|
||||||
|
let hash2 = iter.next().unwrap_or(hash1);
|
||||||
|
let mut encoder = T::engine();
|
||||||
|
hash1.consensus_encode(&mut encoder).unwrap();
|
||||||
|
hash2.consensus_encode(&mut encoder).unwrap();
|
||||||
|
alloc.push(T::from_engine(encoder));
|
||||||
|
}
|
||||||
|
bitcoin_merkle_root_inline(&mut alloc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Objects which are referred to by hash
|
/// Objects which are referred to by hash
|
||||||
pub trait BitcoinHash {
|
pub trait BitcoinHash<T: Hash> {
|
||||||
/// Produces a Sha256dHash which can be used to refer to the object
|
/// Produces a Sha256dHash which can be used to refer to the object
|
||||||
fn bitcoin_hash(&self) -> sha256d::Hash;
|
fn bitcoin_hash(&self) -> T;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! extern crate bitcoin;
|
//! extern crate bitcoin;
|
||||||
//! use bitcoin::hashes::sha256d;
|
//! use bitcoin::hash_types::Txid;
|
||||||
//! use bitcoin::hashes::hex::FromHex;
|
//! use bitcoin::hashes::hex::FromHex;
|
||||||
//! use bitcoin::{Block, MerkleBlock};
|
//! use bitcoin::{Block, MerkleBlock};
|
||||||
//!
|
//!
|
||||||
|
@ -41,12 +41,12 @@
|
||||||
//! let mb: MerkleBlock = bitcoin::consensus::deserialize(&mb_bytes).unwrap();
|
//! let mb: MerkleBlock = bitcoin::consensus::deserialize(&mb_bytes).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // Authenticate and extract matched transaction ids
|
//! // Authenticate and extract matched transaction ids
|
||||||
//! let mut matches: Vec<sha256d::Hash> = vec![];
|
//! let mut matches: Vec<Txid> = vec![];
|
||||||
//! let mut index: Vec<u32> = vec![];
|
//! let mut index: Vec<u32> = vec![];
|
||||||
//! assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
|
//! assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
|
||||||
//! assert_eq!(1, matches.len());
|
//! assert_eq!(1, matches.len());
|
||||||
//! assert_eq!(
|
//! assert_eq!(
|
||||||
//! sha256d::Hash::from_hex(
|
//! Txid::from_hex(
|
||||||
//! "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap(),
|
//! "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap(),
|
||||||
//! matches[0]
|
//! matches[0]
|
||||||
//! );
|
//! );
|
||||||
|
@ -58,11 +58,12 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use hashes::{sha256d, Hash};
|
use hashes::Hash;
|
||||||
|
use hash_types::{Txid, TxMerkleNode};
|
||||||
|
|
||||||
|
use blockdata::transaction::Transaction;
|
||||||
use blockdata::constants::{MAX_BLOCK_WEIGHT, MIN_TRANSACTION_WEIGHT};
|
use blockdata::constants::{MAX_BLOCK_WEIGHT, MIN_TRANSACTION_WEIGHT};
|
||||||
use consensus::encode::{self, Decodable, Encodable};
|
use consensus::encode::{self, Decodable, Encodable};
|
||||||
use util::hash::BitcoinHash;
|
|
||||||
use util::merkleblock::MerkleBlockError::*;
|
use util::merkleblock::MerkleBlockError::*;
|
||||||
use {Block, BlockHeader};
|
use {Block, BlockHeader};
|
||||||
|
|
||||||
|
@ -119,7 +120,7 @@ pub struct PartialMerkleTree {
|
||||||
/// node-is-parent-of-matched-txid bits
|
/// node-is-parent-of-matched-txid bits
|
||||||
bits: Vec<bool>,
|
bits: Vec<bool>,
|
||||||
/// Transaction ids and internal hashes
|
/// Transaction ids and internal hashes
|
||||||
hashes: Vec<sha256d::Hash>,
|
hashes: Vec<TxMerkleNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialMerkleTree {
|
impl PartialMerkleTree {
|
||||||
|
@ -133,18 +134,18 @@ impl PartialMerkleTree {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// extern crate bitcoin;
|
/// extern crate bitcoin;
|
||||||
/// use bitcoin::hashes::sha256d;
|
/// use bitcoin::hash_types::Txid;
|
||||||
/// use bitcoin::hashes::hex::FromHex;
|
/// use bitcoin::hashes::hex::FromHex;
|
||||||
/// use bitcoin::util::merkleblock::PartialMerkleTree;
|
/// use bitcoin::util::merkleblock::PartialMerkleTree;
|
||||||
///
|
///
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// // Block 80000
|
/// // Block 80000
|
||||||
/// let txids: Vec<sha256d::Hash> = [
|
/// let txids: Vec<Txid> = [
|
||||||
/// "c06fbab289f723c6261d3030ddb6be121f7d2508d77862bb1e484f5cd7f92b25",
|
/// "c06fbab289f723c6261d3030ddb6be121f7d2508d77862bb1e484f5cd7f92b25",
|
||||||
/// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2",
|
/// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2",
|
||||||
/// ]
|
/// ]
|
||||||
/// .iter()
|
/// .iter()
|
||||||
/// .map(|hex| sha256d::Hash::from_hex(hex).unwrap())
|
/// .map(|hex| Txid::from_hex(hex).unwrap())
|
||||||
/// .collect();
|
/// .collect();
|
||||||
///
|
///
|
||||||
/// // Select the second transaction
|
/// // Select the second transaction
|
||||||
|
@ -153,7 +154,7 @@ impl PartialMerkleTree {
|
||||||
/// assert!(tree.extract_matches(&mut vec![], &mut vec![]).is_ok());
|
/// assert!(tree.extract_matches(&mut vec![], &mut vec![]).is_ok());
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_txids(txids: &[sha256d::Hash], matches: &[bool]) -> Self {
|
pub fn from_txids(txids: &[Txid], matches: &[bool]) -> Self {
|
||||||
// We can never have zero txs in a merkle block, we always need the coinbase tx
|
// We can never have zero txs in a merkle block, we always need the coinbase tx
|
||||||
assert_ne!(txids.len(), 0);
|
assert_ne!(txids.len(), 0);
|
||||||
assert_eq!(txids.len(), matches.len());
|
assert_eq!(txids.len(), matches.len());
|
||||||
|
@ -178,9 +179,9 @@ impl PartialMerkleTree {
|
||||||
/// returns the merkle root, or error in case of failure
|
/// returns the merkle root, or error in case of failure
|
||||||
pub fn extract_matches(
|
pub fn extract_matches(
|
||||||
&self,
|
&self,
|
||||||
matches: &mut Vec<sha256d::Hash>,
|
matches: &mut Vec<Txid>,
|
||||||
indexes: &mut Vec<u32>,
|
indexes: &mut Vec<u32>,
|
||||||
) -> Result<sha256d::Hash, MerkleBlockError> {
|
) -> Result<TxMerkleNode, MerkleBlockError> {
|
||||||
matches.clear();
|
matches.clear();
|
||||||
indexes.clear();
|
indexes.clear();
|
||||||
// An empty set will not work
|
// An empty set will not work
|
||||||
|
@ -220,7 +221,7 @@ impl PartialMerkleTree {
|
||||||
if hash_used != self.hashes.len() as u32 {
|
if hash_used != self.hashes.len() as u32 {
|
||||||
return Err(BadFormat("Not all hashes were consumed".to_owned()));
|
return Err(BadFormat("Not all hashes were consumed".to_owned()));
|
||||||
}
|
}
|
||||||
Ok(hash_merkle_root)
|
Ok(TxMerkleNode::from_inner(hash_merkle_root.into_inner()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to efficiently calculate the number of nodes at given height
|
/// Helper function to efficiently calculate the number of nodes at given height
|
||||||
|
@ -231,10 +232,10 @@ impl PartialMerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the hash of a node in the merkle tree (at leaf level: the txid's themselves)
|
/// Calculate the hash of a node in the merkle tree (at leaf level: the txid's themselves)
|
||||||
fn calc_hash(&self, height: u32, pos: u32, txids: &[sha256d::Hash]) -> sha256d::Hash {
|
fn calc_hash(&self, height: u32, pos: u32, txids: &[Txid]) -> TxMerkleNode {
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
// Hash at height 0 is the txid itself
|
// Hash at height 0 is the txid itself
|
||||||
txids[pos as usize]
|
TxMerkleNode::from_inner(txids[pos as usize].into_inner())
|
||||||
} else {
|
} else {
|
||||||
// Calculate left hash
|
// Calculate left hash
|
||||||
let left = self.calc_hash(height - 1, pos * 2, txids);
|
let left = self.calc_hash(height - 1, pos * 2, txids);
|
||||||
|
@ -254,7 +255,7 @@ impl PartialMerkleTree {
|
||||||
&mut self,
|
&mut self,
|
||||||
height: u32,
|
height: u32,
|
||||||
pos: u32,
|
pos: u32,
|
||||||
txids: &[sha256d::Hash],
|
txids: &[Txid],
|
||||||
matches: &[bool],
|
matches: &[bool],
|
||||||
) {
|
) {
|
||||||
// Determine whether this node is the parent of at least one matched txid
|
// Determine whether this node is the parent of at least one matched txid
|
||||||
|
@ -270,7 +271,7 @@ impl PartialMerkleTree {
|
||||||
if height == 0 || !parent_of_match {
|
if height == 0 || !parent_of_match {
|
||||||
// If at height 0, or nothing interesting below, store hash and stop
|
// If at height 0, or nothing interesting below, store hash and stop
|
||||||
let hash = self.calc_hash(height, pos, txids);
|
let hash = self.calc_hash(height, pos, txids);
|
||||||
self.hashes.push(hash);
|
self.hashes.push(hash.into());
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, don't store any hash, but descend into the subtrees
|
// Otherwise, don't store any hash, but descend into the subtrees
|
||||||
self.traverse_and_build(height - 1, pos * 2, txids, matches);
|
self.traverse_and_build(height - 1, pos * 2, txids, matches);
|
||||||
|
@ -288,9 +289,9 @@ impl PartialMerkleTree {
|
||||||
pos: u32,
|
pos: u32,
|
||||||
bits_used: &mut u32,
|
bits_used: &mut u32,
|
||||||
hash_used: &mut u32,
|
hash_used: &mut u32,
|
||||||
matches: &mut Vec<sha256d::Hash>,
|
matches: &mut Vec<Txid>,
|
||||||
indexes: &mut Vec<u32>,
|
indexes: &mut Vec<u32>,
|
||||||
) -> Result<sha256d::Hash, MerkleBlockError> {
|
) -> Result<TxMerkleNode, MerkleBlockError> {
|
||||||
if *bits_used as usize >= self.bits.len() {
|
if *bits_used as usize >= self.bits.len() {
|
||||||
return Err(BadFormat("Overflowed the bits array".to_owned()));
|
return Err(BadFormat("Overflowed the bits array".to_owned()));
|
||||||
}
|
}
|
||||||
|
@ -305,7 +306,7 @@ impl PartialMerkleTree {
|
||||||
*hash_used += 1;
|
*hash_used += 1;
|
||||||
if height == 0 && parent_of_match {
|
if height == 0 && parent_of_match {
|
||||||
// in case of height 0, we have a matched txid
|
// in case of height 0, we have a matched txid
|
||||||
matches.push(hash);
|
matches.push(Txid::from_inner(hash.into_inner()));
|
||||||
indexes.push(pos);
|
indexes.push(pos);
|
||||||
}
|
}
|
||||||
Ok(hash)
|
Ok(hash)
|
||||||
|
@ -343,11 +344,11 @@ impl PartialMerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper method to produce SHA256D(left + right)
|
/// Helper method to produce SHA256D(left + right)
|
||||||
fn parent_hash(left: sha256d::Hash, right: sha256d::Hash) -> sha256d::Hash {
|
fn parent_hash(left: TxMerkleNode, right: TxMerkleNode) -> TxMerkleNode {
|
||||||
let mut encoder = sha256d::Hash::engine();
|
let mut encoder = TxMerkleNode::engine();
|
||||||
left.consensus_encode(&mut encoder).unwrap();
|
left.consensus_encode(&mut encoder).unwrap();
|
||||||
right.consensus_encode(&mut encoder).unwrap();
|
right.consensus_encode(&mut encoder).unwrap();
|
||||||
sha256d::Hash::from_engine(encoder)
|
TxMerkleNode::from_engine(encoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +370,7 @@ impl Encodable for PartialMerkleTree {
|
||||||
impl Decodable for PartialMerkleTree {
|
impl Decodable for PartialMerkleTree {
|
||||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||||
let num_transactions: u32 = Decodable::consensus_decode(&mut d)?;
|
let num_transactions: u32 = Decodable::consensus_decode(&mut d)?;
|
||||||
let hashes: Vec<sha256d::Hash> = Decodable::consensus_decode(&mut d)?;
|
let hashes: Vec<TxMerkleNode> = Decodable::consensus_decode(&mut d)?;
|
||||||
|
|
||||||
let bytes: Vec<u8> = Decodable::consensus_decode(d)?;
|
let bytes: Vec<u8> = Decodable::consensus_decode(d)?;
|
||||||
let mut bits: Vec<bool> = vec![false; bytes.len() * 8];
|
let mut bits: Vec<bool> = vec![false; bytes.len() * 8];
|
||||||
|
@ -407,7 +408,7 @@ impl MerkleBlock {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// extern crate bitcoin;
|
/// extern crate bitcoin;
|
||||||
/// use bitcoin::hashes::sha256d;
|
/// use bitcoin::hash_types::Txid;
|
||||||
/// use bitcoin::hashes::hex::FromHex;
|
/// use bitcoin::hashes::hex::FromHex;
|
||||||
/// use bitcoin::{Block, MerkleBlock};
|
/// use bitcoin::{Block, MerkleBlock};
|
||||||
///
|
///
|
||||||
|
@ -426,25 +427,25 @@ impl MerkleBlock {
|
||||||
/// let block: Block = bitcoin::consensus::deserialize(&block_bytes).unwrap();
|
/// let block: Block = bitcoin::consensus::deserialize(&block_bytes).unwrap();
|
||||||
///
|
///
|
||||||
/// // Create a merkle block containing a single transaction
|
/// // Create a merkle block containing a single transaction
|
||||||
/// let txid = sha256d::Hash::from_hex(
|
/// let txid = Txid::from_hex(
|
||||||
/// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap();
|
/// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap();
|
||||||
/// let match_txids = vec![txid].into_iter().collect();
|
/// let match_txids = vec![txid].into_iter().collect();
|
||||||
/// let mb = MerkleBlock::from_block(&block, &match_txids);
|
/// let mb = MerkleBlock::from_block(&block, &match_txids);
|
||||||
///
|
///
|
||||||
/// // Authenticate and extract matched transaction ids
|
/// // Authenticate and extract matched transaction ids
|
||||||
/// let mut matches: Vec<sha256d::Hash> = vec![];
|
/// let mut matches: Vec<Txid> = vec![];
|
||||||
/// let mut index: Vec<u32> = vec![];
|
/// let mut index: Vec<u32> = vec![];
|
||||||
/// assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
|
/// assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
|
||||||
/// assert_eq!(txid, matches[0]);
|
/// assert_eq!(txid, matches[0]);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_block(block: &Block, match_txids: &HashSet<sha256d::Hash>) -> Self {
|
pub fn from_block(block: &Block, match_txids: &HashSet<Txid>) -> Self {
|
||||||
let header = block.header;
|
let header = block.header;
|
||||||
|
|
||||||
let mut matches: Vec<bool> = Vec::with_capacity(block.txdata.len());
|
let mut matches: Vec<bool> = Vec::with_capacity(block.txdata.len());
|
||||||
let mut hashes: Vec<sha256d::Hash> = Vec::with_capacity(block.txdata.len());
|
let mut hashes: Vec<Txid> = Vec::with_capacity(block.txdata.len());
|
||||||
|
|
||||||
for hash in block.txdata.iter().map(BitcoinHash::bitcoin_hash) {
|
for hash in block.txdata.iter().map(Transaction::txid) {
|
||||||
matches.push(match_txids.contains(&hash));
|
matches.push(match_txids.contains(&hash));
|
||||||
hashes.push(hash);
|
hashes.push(hash);
|
||||||
}
|
}
|
||||||
|
@ -458,7 +459,7 @@ impl MerkleBlock {
|
||||||
/// returns Ok(()) on success, or error in case of failure
|
/// returns Ok(()) on success, or error in case of failure
|
||||||
pub fn extract_matches(
|
pub fn extract_matches(
|
||||||
&self,
|
&self,
|
||||||
matches: &mut Vec<sha256d::Hash>,
|
matches: &mut Vec<Txid>,
|
||||||
indexes: &mut Vec<u32>,
|
indexes: &mut Vec<u32>,
|
||||||
) -> Result<(), MerkleBlockError> {
|
) -> Result<(), MerkleBlockError> {
|
||||||
let merkle_root = self.txn.extract_matches(matches, indexes)?;
|
let merkle_root = self.txn.extract_matches(matches, indexes)?;
|
||||||
|
@ -495,8 +496,9 @@ impl Decodable for MerkleBlock {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
|
use hashes::Hash;
|
||||||
use hashes::hex::{FromHex, ToHex};
|
use hashes::hex::{FromHex, ToHex};
|
||||||
use hashes::{sha256d, Hash};
|
use hash_types::{Txid, TxMerkleNode};
|
||||||
use secp256k1::rand::prelude::*;
|
use secp256k1::rand::prelude::*;
|
||||||
|
|
||||||
use consensus::encode::{deserialize, serialize};
|
use consensus::encode::{deserialize, serialize};
|
||||||
|
@ -512,11 +514,12 @@ mod tests {
|
||||||
for num_tx in tx_counts {
|
for num_tx in tx_counts {
|
||||||
// Create some fake tx ids
|
// Create some fake tx ids
|
||||||
let txids = (1..num_tx + 1) // change to `1..=num_tx` when min Rust >= 1.26.0
|
let txids = (1..num_tx + 1) // change to `1..=num_tx` when min Rust >= 1.26.0
|
||||||
.map(|i| sha256d::Hash::from_hex(&format!("{:064x}", i)).unwrap())
|
.map(|i| Txid::from_hex(&format!("{:064x}", i)).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Calculate the merkle root and height
|
// Calculate the merkle root and height
|
||||||
let merkle_root_1 = bitcoin_merkle_root(txids.clone());
|
let hashes = txids.iter().map(|t| t.as_hash());
|
||||||
|
let merkle_root_1: TxMerkleNode = bitcoin_merkle_root(hashes).into();
|
||||||
let mut height = 1;
|
let mut height = 1;
|
||||||
let mut ntx = num_tx;
|
let mut ntx = num_tx;
|
||||||
while ntx > 1 {
|
while ntx > 1 {
|
||||||
|
@ -555,7 +558,7 @@ mod tests {
|
||||||
deserialize(&serialized).expect("Could not deserialize own data");
|
deserialize(&serialized).expect("Could not deserialize own data");
|
||||||
|
|
||||||
// Extract merkle root and matched txids from copy
|
// Extract merkle root and matched txids from copy
|
||||||
let mut match_txid2 = vec![];
|
let mut match_txid2: Vec<Txid> = vec![];
|
||||||
let mut indexes = vec![];
|
let mut indexes = vec![];
|
||||||
let merkle_root_2 = pmt2
|
let merkle_root_2 = pmt2
|
||||||
.extract_matches(&mut match_txid2, &mut indexes)
|
.extract_matches(&mut match_txid2, &mut indexes)
|
||||||
|
@ -563,7 +566,7 @@ mod tests {
|
||||||
|
|
||||||
// Check that it has the same merkle root as the original, and a valid one
|
// Check that it has the same merkle root as the original, and a valid one
|
||||||
assert_eq!(merkle_root_1, merkle_root_2);
|
assert_eq!(merkle_root_1, merkle_root_2);
|
||||||
assert_ne!(merkle_root_2, sha256d::Hash::default());
|
assert_ne!(merkle_root_2, TxMerkleNode::default());
|
||||||
|
|
||||||
// check that it contains the matched transactions (in the same order!)
|
// check that it contains the matched transactions (in the same order!)
|
||||||
assert_eq!(match_txid1, match_txid2);
|
assert_eq!(match_txid1, match_txid2);
|
||||||
|
@ -585,9 +588,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn pmt_malleability() {
|
fn pmt_malleability() {
|
||||||
// Create some fake tx ids with the last 2 hashes repeating
|
// Create some fake tx ids with the last 2 hashes repeating
|
||||||
let txids: Vec<sha256d::Hash> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 10]
|
let txids: Vec<Txid> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 10]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| sha256d::Hash::from_hex(&format!("{:064x}", i)).unwrap())
|
.map(|i| Txid::from_hex(&format!("{:064x}", i)).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let matches = vec![
|
let matches = vec![
|
||||||
|
@ -628,12 +631,12 @@ mod tests {
|
||||||
fn merkleblock_construct_from_txids_found() {
|
fn merkleblock_construct_from_txids_found() {
|
||||||
let block = get_block_13b8a();
|
let block = get_block_13b8a();
|
||||||
|
|
||||||
let txids: Vec<sha256d::Hash> = [
|
let txids: Vec<Txid> = [
|
||||||
"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20",
|
"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20",
|
||||||
"f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07",
|
"f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|hex| sha256d::Hash::from_hex(hex).unwrap())
|
.map(|hex| Txid::from_hex(hex).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let txid1 = txids[0];
|
let txid1 = txids[0];
|
||||||
|
@ -644,7 +647,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(merkle_block.header.bitcoin_hash(), block.bitcoin_hash());
|
assert_eq!(merkle_block.header.bitcoin_hash(), block.bitcoin_hash());
|
||||||
|
|
||||||
let mut matches: Vec<sha256d::Hash> = vec![];
|
let mut matches: Vec<Txid> = vec![];
|
||||||
let mut index: Vec<u32> = vec![];
|
let mut index: Vec<u32> = vec![];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -670,14 +673,14 @@ mod tests {
|
||||||
let block = get_block_13b8a();
|
let block = get_block_13b8a();
|
||||||
let txids = ["c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"]
|
let txids = ["c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|hex| sha256d::Hash::from_hex(hex).unwrap())
|
.map(|hex| Txid::from_hex(hex).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let merkle_block = MerkleBlock::from_block(&block, &txids);
|
let merkle_block = MerkleBlock::from_block(&block, &txids);
|
||||||
|
|
||||||
assert_eq!(merkle_block.header.bitcoin_hash(), block.bitcoin_hash());
|
assert_eq!(merkle_block.header.bitcoin_hash(), block.bitcoin_hash());
|
||||||
|
|
||||||
let mut matches: Vec<sha256d::Hash> = vec![];
|
let mut matches: Vec<Txid> = vec![];
|
||||||
let mut index: Vec<u32> = vec![];
|
let mut index: Vec<u32> = vec![];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -699,7 +702,7 @@ mod tests {
|
||||||
let hashes = &mut self.hashes;
|
let hashes = &mut self.hashes;
|
||||||
let mut hash = hashes[n].into_inner();
|
let mut hash = hashes[n].into_inner();
|
||||||
hash[(bit >> 3) as usize] ^= 1 << (bit & 7);
|
hash[(bit >> 3) as usize] ^= 1 << (bit & 7);
|
||||||
hashes[n] = sha256d::Hash::from_slice(&hash).unwrap();
|
hashes[n] = TxMerkleNode::from_slice(&hash).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ impl Decodable for PartiallySignedTransaction {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
use hashes::sha256d;
|
use hash_types::Txid;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ mod tests {
|
||||||
lock_time: 1257139,
|
lock_time: 1257139,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: sha256d::Hash::from_hex(
|
txid: Txid::from_hex(
|
||||||
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
vout: 0,
|
vout: 0,
|
||||||
|
@ -315,7 +315,7 @@ mod tests {
|
||||||
use hex::decode as hex_decode;
|
use hex::decode as hex_decode;
|
||||||
|
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
use hashes::sha256d;
|
use hash_types::Txid;
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
|
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
|
||||||
|
@ -363,7 +363,7 @@ mod tests {
|
||||||
lock_time: 1257139,
|
lock_time: 1257139,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: sha256d::Hash::from_hex(
|
txid: Txid::from_hex(
|
||||||
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
vout: 0,
|
vout: 0,
|
||||||
|
@ -391,7 +391,7 @@ mod tests {
|
||||||
lock_time: 0,
|
lock_time: 0,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: sha256d::Hash::from_hex(
|
txid: Txid::from_hex(
|
||||||
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
|
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
vout: 1,
|
vout: 1,
|
||||||
|
@ -405,7 +405,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
TxIn {
|
TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: sha256d::Hash::from_hex(
|
txid: Txid::from_hex(
|
||||||
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
|
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
vout: 1,
|
vout: 1,
|
||||||
|
@ -548,7 +548,7 @@ mod tests {
|
||||||
let tx = &psbt.global.unsigned_tx;
|
let tx = &psbt.global.unsigned_tx;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.txid(),
|
tx.txid(),
|
||||||
sha256d::Hash::from_hex(
|
Txid::from_hex(
|
||||||
"75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115"
|
"75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115"
|
||||||
).unwrap()
|
).unwrap()
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue