diff --git a/src/util/bip158.rs b/src/bip158.rs similarity index 83% rename from src/util/bip158.rs rename to src/bip158.rs index de3492b0..9a954814 100644 --- a/src/util/bip158.rs +++ b/src/bip158.rs @@ -39,22 +39,20 @@ //! ``` //! -use crate::prelude::*; -use crate::io; - -use core::fmt::{self, Display, Formatter}; use core::cmp::{self, Ordering}; - -use crate::hashes::{Hash, siphash24}; -use crate::hash_types::{BlockHash, FilterHash, FilterHeader}; +use core::fmt::{self, Display, Formatter}; use crate::blockdata::block::Block; use crate::blockdata::script::Script; use crate::blockdata::transaction::OutPoint; -use crate::consensus::{Decodable, Encodable}; use crate::consensus::encode::VarInt; -use crate::util::endian; +use crate::consensus::{Decodable, Encodable}; +use crate::hash_types::{BlockHash, FilterHash, FilterHeader}; +use crate::hashes::{siphash24, Hash}; use crate::internal_macros::write_err; +use crate::io; +use crate::prelude::*; +use crate::util::endian; /// Golomb encoding parameter as in BIP-158, see also https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845 const P: u8 = 19; @@ -93,16 +91,14 @@ impl std::error::Error for Error { } impl From for Error { - fn from(io: io::Error) -> Self { - Error::Io(io) - } + fn from(io: io::Error) -> Self { Error::Io(io) } } /// A block filter, as described by BIP 158. #[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockFilter { /// Golomb encoded filter - pub content: Vec + pub content: Vec, } impl FilterHash { @@ -117,14 +113,12 @@ impl FilterHash { impl BlockFilter { /// Creates a new filter from pre-computed data. - pub fn new (content: &[u8]) -> BlockFilter { - BlockFilter { content: content.to_vec() } - } + pub fn new(content: &[u8]) -> BlockFilter { BlockFilter { content: content.to_vec() } } /// Computes a SCRIPT_FILTER that contains spent and output scripts. pub fn new_script_filter(block: &Block, script_for_coin: M) -> Result where - M: Fn(&OutPoint) -> Result + M: Fn(&OutPoint) -> Result, { let mut out = Vec::new(); let mut writer = BlockFilterWriter::new(&mut out, block); @@ -192,33 +186,35 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { /// Adds consumed output scripts of a block to filter. pub fn add_input_scripts(&mut self, script_for_coin: M) -> Result<(), Error> - where M: Fn(&OutPoint) -> Result { - for script in self.block.txdata.iter() + where + M: Fn(&OutPoint) -> Result, + { + for script in self + .block + .txdata + .iter() .skip(1) // skip coinbase .flat_map(|t| t.input.iter().map(|i| &i.previous_output)) - .map(script_for_coin) { + .map(script_for_coin) + { match script { Ok(script) => self.add_element(script.as_bytes()), - Err(e) => return Err(e) + Err(e) => return Err(e), } } Ok(()) } /// Adds an arbitrary element to filter. - pub fn add_element(&mut self, data: &[u8]) { - self.writer.add_element(data); - } + pub fn add_element(&mut self, data: &[u8]) { self.writer.add_element(data); } /// Writes the block filter. - pub fn finish(&mut self) -> Result { - self.writer.finish() - } + pub fn finish(&mut self) -> Result { self.writer.finish() } } /// Reads and interprets a block filter. pub struct BlockFilterReader { - reader: GcsFilterReader + reader: GcsFilterReader, } impl BlockFilterReader { @@ -252,7 +248,7 @@ impl BlockFilterReader { /// Golomb-Rice encoded filter reader. pub struct GcsFilterReader { filter: GcsFilter, - m: u64 + m: u64, } impl GcsFilterReader { @@ -290,14 +286,13 @@ impl GcsFilterReader { loop { match data.cmp(&p) { Ordering::Equal => return Ok(true), - Ordering::Less => { + Ordering::Less => if remaining > 0 { data += self.filter.golomb_rice_decode(&mut reader)?; remaining -= 1; } else { return Ok(false); - } - } + }, Ordering::Greater => break, } } @@ -335,14 +330,13 @@ impl GcsFilterReader { loop { match data.cmp(&p) { Ordering::Equal => break, - Ordering::Less => { + Ordering::Less => if remaining > 0 { data += self.filter.golomb_rice_decode(&mut reader)?; remaining -= 1; } else { return Ok(false); - } - }, + }, Ordering::Greater => return Ok(false), } } @@ -352,27 +346,20 @@ impl GcsFilterReader { } /// Fast reduction of hash to [0, nm) range. -fn map_to_range(hash: u64, nm: u64) -> u64 { - ((hash as u128 * nm as u128) >> 64) as u64 -} +fn map_to_range(hash: u64, nm: u64) -> u64 { ((hash as u128 * nm as u128) >> 64) as u64 } /// Golomb-Rice encoded filter writer. pub struct GcsFilterWriter<'a, W> { filter: GcsFilter, writer: &'a mut W, elements: HashSet>, - m: u64 + m: u64, } impl<'a, W: io::Write> GcsFilterWriter<'a, W> { /// Creates a new [`GcsFilterWriter`] wrapping a generic writer, with specific seed to siphash. pub fn new(writer: &'a mut W, k0: u64, k1: u64, m: u64, p: u8) -> GcsFilterWriter<'a, W> { - GcsFilterWriter { - filter: GcsFilter::new(k0, k1, p), - writer, - elements: HashSet::new(), - m - } + GcsFilterWriter { filter: GcsFilter::new(k0, k1, p), writer, elements: HashSet::new(), m } } /// Adds data to the filter. @@ -387,8 +374,11 @@ impl<'a, W: io::Write> GcsFilterWriter<'a, W> { let nm = self.elements.len() as u64 * self.m; // map hashes to [0, n_elements * M) - let mut mapped: Vec<_> = self.elements.iter() - .map(|e| map_to_range(self.filter.hash(e.as_slice()), nm)).collect(); + let mut mapped: Vec<_> = self + .elements + .iter() + .map(|e| map_to_range(self.filter.hash(e.as_slice()), nm)) + .collect(); mapped.sort_unstable(); // write number of elements as varint @@ -410,17 +400,19 @@ impl<'a, W: io::Write> GcsFilterWriter<'a, W> { struct GcsFilter { k0: u64, // sip hash key k1: u64, // sip hash key - p: u8 + p: u8, } impl GcsFilter { /// Creates a new [`GcsFilter`]. - fn new(k0: u64, k1: u64, p: u8) -> GcsFilter { - GcsFilter { k0, k1, p } - } + fn new(k0: u64, k1: u64, p: u8) -> GcsFilter { GcsFilter { k0, k1, p } } /// Golomb-Rice encodes a number `n` to a bit stream (parameter 2^k). - fn golomb_rice_encode<'a, W>(&self, writer: &mut BitStreamWriter<'a, W>, n: u64) -> Result + fn golomb_rice_encode<'a, W>( + &self, + writer: &mut BitStreamWriter<'a, W>, + n: u64, + ) -> Result where W: io::Write, { @@ -439,7 +431,7 @@ impl GcsFilter { /// Golomb-Rice decodes a number from a bit stream (parameter 2^k). fn golomb_rice_decode(&self, reader: &mut BitStreamReader) -> Result where - R: io::Read + R: io::Read, { let mut q = 0u64; while reader.read(1)? == 1 { @@ -465,11 +457,7 @@ pub struct BitStreamReader<'a, R> { impl<'a, R: io::Read> BitStreamReader<'a, R> { /// Creates a new [`BitStreamReader`] that reads bitwise from a given `reader`. pub fn new(reader: &'a mut R) -> BitStreamReader<'a, R> { - BitStreamReader { - buffer: [0u8], - reader, - offset: 8, - } + BitStreamReader { buffer: [0u8], reader, offset: 8 } } /// Reads nbit bits, returning the bits in a `u64` starting with the rightmost bit. @@ -485,7 +473,10 @@ impl<'a, R: io::Read> BitStreamReader<'a, R> { /// ``` pub fn read(&mut self, mut nbits: u8) -> Result { if nbits > 64 { - return Err(io::Error::new(io::ErrorKind::Other, "can not read more than 64 bits at once")); + return Err(io::Error::new( + io::ErrorKind::Other, + "can not read more than 64 bits at once", + )); } let mut data = 0u64; while nbits > 0 { @@ -513,17 +504,16 @@ pub struct BitStreamWriter<'a, W> { impl<'a, W: io::Write> BitStreamWriter<'a, W> { /// Creates a new [`BitStreamWriter`] that writes bitwise to a given `writer`. pub fn new(writer: &'a mut W) -> BitStreamWriter<'a, W> { - BitStreamWriter { - buffer: [0u8], - writer, - offset: 0, - } + BitStreamWriter { buffer: [0u8], writer, offset: 0 } } /// Writes nbits bits from data. pub fn write(&mut self, data: u64, mut nbits: u8) -> Result { if nbits > 64 { - return Err(io::Error::new(io::ErrorKind::Other, "can not write more than 64 bits at once")); + return Err(io::Error::new( + io::ErrorKind::Other, + "can not write more than 64 bits at once", + )); } let mut wrote = 0; while nbits > 0 { @@ -553,60 +543,75 @@ impl<'a, W: io::Write> BitStreamWriter<'a, W> { #[cfg(test)] mod test { - use super::*; - use std::collections::HashMap; use serde_json::Value; + use super::*; use crate::consensus::encode::deserialize; use crate::hash_types::BlockHash; use crate::hashes::hex::FromHex; #[test] fn test_blockfilters() { - // test vectors from: https://github.com/jimpo/bitcoin/blob/c7efb652f3543b001b4dd22186a354605b14f47e/src/test/data/blockfilters.json - let data = include_str!("../../test_data/blockfilters.json"); + let data = include_str!("../test_data/blockfilters.json"); let testdata = serde_json::from_str::(data).unwrap().as_array().unwrap().clone(); for t in testdata.iter().skip(1) { let block_hash = BlockHash::from_hex(t.get(1).unwrap().as_str().unwrap()).unwrap(); - let block: Block = deserialize(&Vec::from_hex(t.get(2).unwrap().as_str().unwrap()).unwrap()).unwrap(); + let block: Block = + deserialize(&Vec::from_hex(t.get(2).unwrap().as_str().unwrap()).unwrap()).unwrap(); assert_eq!(block.block_hash(), block_hash); let scripts = t.get(3).unwrap().as_array().unwrap(); - let previous_filter_header = FilterHeader::from_hex(t.get(4).unwrap().as_str().unwrap()).unwrap(); + let previous_filter_header = + FilterHeader::from_hex(t.get(4).unwrap().as_str().unwrap()).unwrap(); let filter_content = Vec::from_hex(t.get(5).unwrap().as_str().unwrap()).unwrap(); - let filter_header = FilterHeader::from_hex(t.get(6).unwrap().as_str().unwrap()).unwrap(); + let filter_header = + FilterHeader::from_hex(t.get(6).unwrap().as_str().unwrap()).unwrap(); let mut txmap = HashMap::new(); let mut si = scripts.iter(); for tx in block.txdata.iter().skip(1) { for input in tx.input.iter() { - txmap.insert(input.previous_output, Script::from(Vec::from_hex(si.next().unwrap().as_str().unwrap()).unwrap())); + txmap.insert( + input.previous_output, + Script::from(Vec::from_hex(si.next().unwrap().as_str().unwrap()).unwrap()), + ); } } - let filter = BlockFilter::new_script_filter(&block, - |o| if let Some(s) = txmap.get(o) { - Ok(s.clone()) - } else { - Err(Error::UtxoMissing(*o)) - }).unwrap(); + let filter = BlockFilter::new_script_filter(&block, |o| { + if let Some(s) = txmap.get(o) { + Ok(s.clone()) + } else { + Err(Error::UtxoMissing(*o)) + } + }) + .unwrap(); let test_filter = BlockFilter::new(filter_content.as_slice()); assert_eq!(test_filter.content, filter.content); let block_hash = &block.block_hash(); - assert!(filter.match_all(block_hash, &mut txmap.iter() - .filter_map(|(_, s)| if !s.is_empty() { Some(s.as_bytes()) } else { None })).unwrap()); + assert!(filter + .match_all( + block_hash, + &mut txmap.iter().filter_map(|(_, s)| if !s.is_empty() { + Some(s.as_bytes()) + } else { + None + }) + ) + .unwrap()); for script in txmap.values() { let query = vec![script]; - if !script.is_empty () { - assert!(filter.match_any(block_hash, &mut query.iter() - .map(|s| s.as_bytes())).unwrap()); + if !script.is_empty() { + assert!(filter + .match_any(block_hash, &mut query.iter().map(|s| s.as_bytes())) + .unwrap()); } } @@ -649,12 +654,16 @@ mod test { { let query = vec![Vec::from_hex("abcdef").unwrap(), Vec::from_hex("eeeeee").unwrap()]; let reader = GcsFilterReader::new(0, 0, M, P); - assert!(reader.match_any(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())).unwrap()); + assert!(reader + .match_any(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())) + .unwrap()); } { let query = vec![Vec::from_hex("abcdef").unwrap(), Vec::from_hex("123456").unwrap()]; let reader = GcsFilterReader::new(0, 0, M, P); - assert!(!reader.match_any(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())).unwrap()); + assert!(!reader + .match_any(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())) + .unwrap()); } { let reader = GcsFilterReader::new(0, 0, M, P); @@ -662,7 +671,9 @@ mod test { for p in &patterns { query.push(p.clone()); } - assert!(reader.match_all(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())).unwrap()); + assert!(reader + .match_all(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())) + .unwrap()); } { let reader = GcsFilterReader::new(0, 0, M, P); @@ -671,7 +682,9 @@ mod test { query.push(p.clone()); } query.push(Vec::from_hex("abcdef").unwrap()); - assert!(!reader.match_all(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())).unwrap()); + assert!(!reader + .match_all(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice())) + .unwrap()); } } @@ -690,7 +703,10 @@ mod test { writer.flush().unwrap(); } let bytes = out; - assert_eq!("01011010110000110000000001110000", format!("{:08b}{:08b}{:08b}{:08b}", bytes[0], bytes[1], bytes[2], bytes[3])); + assert_eq!( + "01011010110000110000000001110000", + format!("{:08b}{:08b}{:08b}{:08b}", bytes[0], bytes[1], bytes[2], bytes[3]) + ); { let mut input = bytes.as_slice(); let mut reader = BitStreamReader::new(&mut input); diff --git a/src/lib.rs b/src/lib.rs index 8ea10b6c..c617f095 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ mod serde_utils; #[macro_use] pub mod network; pub mod address; +pub mod bip158; pub mod blockdata; pub mod consensus; pub mod error; diff --git a/src/util/mod.rs b/src/util/mod.rs index 36ed53d0..52f61ecd 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -20,7 +20,6 @@ pub mod misc; pub mod psbt; pub mod taproot; pub mod uint; -pub mod bip158; pub mod sighash; pub(crate) mod endian; @@ -117,3 +116,6 @@ pub(crate) fn read_to_end(mut d: D) -> Result, io::Error> { pub mod address { pub use crate::address::*; } + +#[deprecated(since = "0.30.0", note = "Please use crate::bip158")] +pub use crate::bip158;