From d79c6b83585770d81a1e0fa32e4fb9b67a8b539a Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 8 Aug 2022 15:30:26 +1000 Subject: [PATCH 01/10] Remove unnecessary use of Cursor The function `match_all` takes an iterator, it does not need to use the `Seek` trait, we can just pass in the content as a slice, no need to wrap it in a `Cursor`. --- src/util/bip158.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 3be893da..2643d8e5 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -35,8 +35,8 @@ //! use crate::prelude::*; +use crate::io; -use crate::io::{self, Cursor}; use core::fmt::{self, Display, Formatter}; use core::cmp::{self, Ordering}; @@ -139,13 +139,13 @@ impl BlockFilter { /// match any query pattern pub fn match_any(&self, block_hash: &BlockHash, query: &mut dyn Iterator) -> Result { let filter_reader = BlockFilterReader::new(block_hash); - filter_reader.match_any(&mut Cursor::new(self.content.as_slice()), query) + filter_reader.match_any(&mut self.content.as_slice(), query) } /// match all query pattern pub fn match_all(&self, block_hash: &BlockHash, query: &mut dyn Iterator) -> Result { let filter_reader = BlockFilterReader::new(block_hash); - filter_reader.match_all(&mut Cursor::new(self.content.as_slice()), query) + filter_reader.match_all(&mut self.content.as_slice(), query) } } @@ -509,8 +509,6 @@ impl<'a> BitStreamWriter<'a> { #[cfg(test)] mod test { - use crate::io::Cursor; - use crate::hash_types::BlockHash; use crate::hashes::hex::FromHex; @@ -608,14 +606,12 @@ mod test { { let query = vec![Vec::from_hex("abcdef").unwrap(), Vec::from_hex("eeeeee").unwrap()]; let reader = GCSFilterReader::new(0, 0, M, P); - let mut input = Cursor::new(bytes.clone()); - assert!(reader.match_any(&mut input, &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); - let mut input = Cursor::new(bytes.clone()); - assert!(!reader.match_any(&mut input, &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); @@ -623,8 +619,7 @@ mod test { for p in &patterns { query.push(p.clone()); } - let mut input = Cursor::new(bytes.clone()); - assert!(reader.match_all(&mut input, &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); @@ -633,8 +628,7 @@ mod test { query.push(p.clone()); } query.push(Vec::from_hex("abcdef").unwrap()); - let mut input = Cursor::new(bytes); - assert!(!reader.match_all(&mut input, &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()); } } @@ -655,7 +649,7 @@ mod test { let bytes = out; assert_eq!("01011010110000110000000001110000", format!("{:08b}{:08b}{:08b}{:08b}", bytes[0], bytes[1], bytes[2], bytes[3])); { - let mut input = Cursor::new(bytes); + let mut input = bytes.as_slice(); let mut reader = BitStreamReader::new(&mut input); assert_eq!(reader.read(1).unwrap(), 0); assert_eq!(reader.read(2).unwrap(), 2); From 28853fd3cc784458ae0ae2d24b22fcbdec918b90 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 8 Aug 2022 16:24:25 +1000 Subject: [PATCH 02/10] Use generics instead of dynamic dispatch Currently in the `bip158` module we do a bunch of dynamic dispatch using the reader/writer objects. We can improve runtime performance, at the cost of compile time, by using generics instead of dynamic dispatch. --- src/util/bip158.rs | 76 +++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 2643d8e5..2b9e49d2 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -137,27 +137,33 @@ impl BlockFilter { } /// match any query pattern - pub fn match_any(&self, block_hash: &BlockHash, query: &mut dyn Iterator) -> Result { + pub fn match_any<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result + where + I: Iterator, + { let filter_reader = BlockFilterReader::new(block_hash); filter_reader.match_any(&mut self.content.as_slice(), query) } /// match all query pattern - pub fn match_all(&self, block_hash: &BlockHash, query: &mut dyn Iterator) -> Result { + pub fn match_all<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result + where + I: Iterator, + { let filter_reader = BlockFilterReader::new(block_hash); filter_reader.match_all(&mut self.content.as_slice(), query) } } /// Compiles and writes a block filter -pub struct BlockFilterWriter<'a> { +pub struct BlockFilterWriter<'a, W> { block: &'a Block, - writer: GCSFilterWriter<'a>, + writer: GCSFilterWriter<'a, W>, } -impl<'a> BlockFilterWriter<'a> { +impl<'a, W: io::Write> BlockFilterWriter<'a, W> { /// Create a block filter writer - pub fn new(writer: &'a mut dyn io::Write, block: &'a Block) -> BlockFilterWriter<'a> { + pub fn new(writer: &'a mut W, block: &'a Block) -> BlockFilterWriter<'a, W> { let block_hash_as_int = block.block_hash().into_inner(); 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]); @@ -218,12 +224,20 @@ impl BlockFilterReader { } /// match any query pattern - pub fn match_any(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator) -> Result { + pub fn match_any<'a, I, R>(&self, reader: &mut R, query: I) -> Result + where + I: Iterator, + R: io::Read + ?Sized, + { self.reader.match_any(reader, query) } /// match all query pattern - pub fn match_all(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator) -> Result { + pub fn match_all<'a, I, R>(&self, reader: &mut R, query: I) -> Result + where + I: Iterator, + R: io::Read + ?Sized, + { self.reader.match_all(reader, query) } } @@ -242,7 +256,11 @@ impl GCSFilterReader { } /// match any query pattern - pub fn match_any(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator) -> Result { + pub fn match_any<'a, I, R>(&self, reader: &mut R, query: I) -> Result + where + I: Iterator, + R: io::Read + ?Sized, + { let mut decoder = reader; let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0)); let reader = &mut decoder; @@ -282,7 +300,11 @@ impl GCSFilterReader { } /// match all query pattern - pub fn match_all(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator) -> Result { + pub fn match_all<'a, I, R>(&self, reader: &mut R, query: I) -> Result + where + I: Iterator, + R: io::Read + ?Sized, + { let mut decoder = reader; let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0)); let reader = &mut decoder; @@ -329,16 +351,16 @@ fn map_to_range(hash: u64, nm: u64) -> u64 { } /// Colomb-Rice encoded filter writer -pub struct GCSFilterWriter<'a> { +pub struct GCSFilterWriter<'a, W> { filter: GCSFilter, - writer: &'a mut dyn io::Write, + writer: &'a mut W, elements: HashSet>, m: u64 } -impl<'a> GCSFilterWriter<'a> { +impl<'a, W: io::Write> GCSFilterWriter<'a, W> { /// Create a new GCS writer wrapping a generic writer, with specific seed to siphash - pub fn new(writer: &'a mut dyn io::Write, k0: u64, k1: u64, m: u64, p: u8) -> GCSFilterWriter<'a> { + 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, @@ -392,7 +414,10 @@ impl GCSFilter { } /// Golomb-Rice encode a number n to a bit stream (Parameter 2^k) - fn golomb_rice_encode(&self, writer: &mut BitStreamWriter, n: u64) -> Result { + fn golomb_rice_encode<'a, W>(&self, writer: &mut BitStreamWriter<'a, W>, n: u64) -> Result + where + W: io::Write, + { let mut wrote = 0; let mut q = n >> self.p; while q > 0 { @@ -406,7 +431,10 @@ impl GCSFilter { } /// Golomb-Rice decode a number from a bit stream (Parameter 2^k) - fn golomb_rice_decode(&self, reader: &mut BitStreamReader) -> Result { + fn golomb_rice_decode(&self, reader: &mut BitStreamReader) -> Result + where + R: io::Read + { let mut q = 0u64; while reader.read(1)? == 1 { q += 1; @@ -422,15 +450,15 @@ impl GCSFilter { } /// Bitwise stream reader -pub struct BitStreamReader<'a> { +pub struct BitStreamReader<'a, R> { buffer: [u8; 1], offset: u8, - reader: &'a mut dyn io::Read, + reader: &'a mut R, } -impl<'a> BitStreamReader<'a> { +impl<'a, R: io::Read> BitStreamReader<'a, R> { /// Create a new BitStreamReader that reads bitwise from a given reader - pub fn new(reader: &'a mut dyn io::Read) -> BitStreamReader { + pub fn new(reader: &'a mut R) -> BitStreamReader<'a, R> { BitStreamReader { buffer: [0u8], reader, @@ -460,15 +488,15 @@ impl<'a> BitStreamReader<'a> { } /// Bitwise stream writer -pub struct BitStreamWriter<'a> { +pub struct BitStreamWriter<'a, W> { buffer: [u8; 1], offset: u8, - writer: &'a mut dyn io::Write, + writer: &'a mut W, } -impl<'a> BitStreamWriter<'a> { +impl<'a, W: io::Write> BitStreamWriter<'a, W> { /// Create a new BitStreamWriter that writes bitwise to a given writer - pub fn new(writer: &'a mut dyn io::Write) -> BitStreamWriter { + pub fn new(writer: &'a mut W) -> BitStreamWriter<'a, W> { BitStreamWriter { buffer: [0u8], writer, From 08e55bc4f17e8f1751766dcb27266d11321fb447 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 8 Aug 2022 16:40:49 +1000 Subject: [PATCH 03/10] Remove unneeded newlines Conventionally we only put a single newline between types, impl blocks, and functions. --- src/util/bip158.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 2b9e49d2..513c5086 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -86,14 +86,12 @@ impl std::error::Error for Error { } } - impl From for Error { fn from(io: io::Error) -> Self { Error::Io(io) } } - /// a computed or read block filter #[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockFilter { @@ -208,7 +206,6 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { } } - /// Reads and interpret a block filter pub struct BlockFilterReader { reader: GCSFilterReader @@ -242,7 +239,6 @@ impl BlockFilterReader { } } - /// Golomb-Rice encoded filter reader pub struct GCSFilterReader { filter: GCSFilter, From 25d1472924babbddbddf7a3729435b767388b61c Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 14:26:38 +1000 Subject: [PATCH 04/10] Move new to top of impl block To ease reading, put the `new` method at the top of the impl block. Improve rustdocs while we do it. --- src/util/bip158.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 513c5086..5920d0d1 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -110,17 +110,17 @@ impl FilterHash { } impl BlockFilter { + /// Creates a new filter from pre-computed data. + pub fn new (content: &[u8]) -> BlockFilter { + BlockFilter { content: content.to_vec() } + } + /// compute this filter's id in a chain of filters pub fn filter_header(&self, previous_filter_header: &FilterHeader) -> FilterHeader { let filter_hash = FilterHash::hash(self.content.as_slice()); filter_hash.filter_header(previous_filter_header) } - /// create a new filter from pre-computed data - pub fn new (content: &[u8]) -> BlockFilter { - BlockFilter { content: content.to_vec() } - } - /// Compute 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 { From f6105a16a7b60cfde178b9f2d186485ce0a81322 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 14:50:41 +1000 Subject: [PATCH 05/10] Improve module docs Improve the module rustdocs for `bip158` by doing: - Add bip references - Use correct markdown headings - Use caps in title --- src/util/bip158.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 5920d0d1..373e0f76 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -5,14 +5,19 @@ // on 11. June 2019 which is licensed under Apache, that file specifically // was written entirely by Tamas Blummer, who is re-licensing its contents here as CC0. -//! BIP158 Compact Block Filters for light clients. +//! BIP 158 Compact Block Filters for Light Clients. //! //! This module implements a structure for compact filters on block data, for //! use in the BIP 157 light client protocol. The filter construction proposed //! is an alternative to Bloom filters, as used in BIP 37, that minimizes filter //! size by using Golomb-Rice coding for compression. //! -//! ## Example +//! ### Relevant BIPS +//! +//! * [BIP 157 - Client Side Block Filtering](https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki) +//! * [BIP 158 - Compact Block Filters for Light Clients](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki) +//! +//! # Examples //! //! ```ignore //! fn get_script_for_coin(coin: &OutPoint) -> Result { From e0fddce9e93b245ef485ed132c40758485fb76f9 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 15:04:27 +1000 Subject: [PATCH 06/10] Refactor new_script_filter Refactor the `new_script_filter` by doing: - Put it directly below the `new` constructor - Improve docs - Remove unneeded scope - Correctly indent `where` keyword --- src/util/bip158.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 373e0f76..b38b9680 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -120,25 +120,27 @@ impl 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 + { + let mut out = Vec::new(); + let mut writer = BlockFilterWriter::new(&mut out, block); + + writer.add_output_scripts(); + writer.add_input_scripts(script_for_coin)?; + writer.finish()?; + + Ok(BlockFilter { content: out }) + } + /// compute this filter's id in a chain of filters pub fn filter_header(&self, previous_filter_header: &FilterHeader) -> FilterHeader { let filter_hash = FilterHash::hash(self.content.as_slice()); filter_hash.filter_header(previous_filter_header) } - /// Compute 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 { - let mut out = Vec::new(); - { - let mut writer = BlockFilterWriter::new(&mut out, block); - writer.add_output_scripts(); - writer.add_input_scripts(script_for_coin)?; - writer.finish()?; - } - Ok(BlockFilter { content: out }) - } - /// match any query pattern pub fn match_any<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result where From e9846ad579c130d87867ed6f01ef73f5e7ace705 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 15:08:29 +1000 Subject: [PATCH 07/10] Improve docs on filter_header --- src/util/bip158.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index b38b9680..ef102ce6 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -135,7 +135,9 @@ impl BlockFilter { Ok(BlockFilter { content: out }) } - /// compute this filter's id in a chain of filters + /// Computes this filter's ID in a chain of filters (see [BIP 157]). + /// + /// [BIP 157]: pub fn filter_header(&self, previous_filter_header: &FilterHeader) -> FilterHeader { let filter_hash = FilterHash::hash(self.content.as_slice()); filter_hash.filter_header(previous_filter_header) From 2447d96b440ba4c760d9bb652d9d261f88a7b47f Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 15:09:04 +1000 Subject: [PATCH 08/10] Use Gcs instead of GCS Rust convention stipulates that acronyms are in snake case not all caps. We should use `Gcs` for Golomb Coded Sets --- src/util/bip158.rs | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index ef102ce6..80880fff 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -165,7 +165,7 @@ impl BlockFilter { /// Compiles and writes a block filter pub struct BlockFilterWriter<'a, W> { block: &'a Block, - writer: GCSFilterWriter<'a, W>, + writer: GcsFilterWriter<'a, W>, } impl<'a, W: io::Write> BlockFilterWriter<'a, W> { @@ -174,7 +174,7 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { let block_hash_as_int = block.block_hash().into_inner(); 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 writer = GCSFilterWriter::new(writer, k0, k1, M, P); + let writer = GcsFilterWriter::new(writer, k0, k1, M, P); BlockFilterWriter { block, writer } } @@ -217,7 +217,7 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { /// Reads and interpret a block filter pub struct BlockFilterReader { - reader: GCSFilterReader + reader: GcsFilterReader } impl BlockFilterReader { @@ -226,7 +226,7 @@ impl BlockFilterReader { let block_hash_as_int = block_hash.into_inner(); 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]); - BlockFilterReader { reader: GCSFilterReader::new(k0, k1, M, P) } + BlockFilterReader { reader: GcsFilterReader::new(k0, k1, M, P) } } /// match any query pattern @@ -249,15 +249,15 @@ impl BlockFilterReader { } /// Golomb-Rice encoded filter reader -pub struct GCSFilterReader { - filter: GCSFilter, +pub struct GcsFilterReader { + filter: GcsFilter, m: u64 } -impl GCSFilterReader { +impl GcsFilterReader { /// Create a new filter reader with specific seed to siphash - pub fn new(k0: u64, k1: u64, m: u64, p: u8) -> GCSFilterReader { - GCSFilterReader { filter: GCSFilter::new(k0, k1, p), m } + pub fn new(k0: u64, k1: u64, m: u64, p: u8) -> GcsFilterReader { + GcsFilterReader { filter: GcsFilter::new(k0, k1, p), m } } /// match any query pattern @@ -356,18 +356,18 @@ fn map_to_range(hash: u64, nm: u64) -> u64 { } /// Colomb-Rice encoded filter writer -pub struct GCSFilterWriter<'a, W> { - filter: GCSFilter, +pub struct GcsFilterWriter<'a, W> { + filter: GcsFilter, writer: &'a mut W, elements: HashSet>, m: u64 } -impl<'a, W: io::Write> GCSFilterWriter<'a, W> { - /// Create a new GCS writer 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), +impl<'a, W: io::Write> GcsFilterWriter<'a, W> { + /// Create a new Gcs writer 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 @@ -406,16 +406,16 @@ impl<'a, W: io::Write> GCSFilterWriter<'a, W> { } /// Golomb Coded Set Filter -struct GCSFilter { +struct GcsFilter { k0: u64, // sip hash key k1: u64, // sip hash key p: u8 } -impl GCSFilter { +impl GcsFilter { /// Create a new filter - 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 encode a number n to a bit stream (Parameter 2^k) @@ -627,7 +627,7 @@ mod test { let mut out = Vec::new(); { - let mut writer = GCSFilterWriter::new(&mut out, 0, 0, M, P); + let mut writer = GcsFilterWriter::new(&mut out, 0, 0, M, P); for p in &patterns { writer.add_element(p.as_slice()); } @@ -638,16 +638,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()); + 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()); } { let query = vec![Vec::from_hex("abcdef").unwrap(), Vec::from_hex("123456").unwrap()]; - let reader = GCSFilterReader::new(0, 0, M, P); + 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()); } { - let reader = GCSFilterReader::new(0, 0, M, P); + let reader = GcsFilterReader::new(0, 0, M, P); let mut query = Vec::new(); for p in &patterns { query.push(p.clone()); @@ -655,7 +655,7 @@ mod test { 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); + let reader = GcsFilterReader::new(0, 0, M, P); let mut query = Vec::new(); for p in &patterns { query.push(p.clone()); From 5cfe9169f547ec0cc35eb63372c06acd1603a2d6 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 15:50:02 +1000 Subject: [PATCH 09/10] Refactor tests module imports Refactor the import statements for the bip158 tests module. Includes removing `extern crate` which is unnecessary now we have edition 2018. --- src/util/bip158.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 80880fff..fb409a4c 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -542,16 +542,15 @@ impl<'a, W: io::Write> BitStreamWriter<'a, W> { #[cfg(test)] mod test { - use crate::hash_types::BlockHash; - use crate::hashes::hex::FromHex; - use super::*; - extern crate serde_json; - use self::serde_json::Value; + use std::collections::HashMap; + + use serde_json::Value; use crate::consensus::encode::deserialize; - use std::collections::HashMap; + use crate::hash_types::BlockHash; + use crate::hashes::hex::FromHex; #[test] fn test_blockfilters() { From 167ee8e72cec7bdf76b5bec7aec68c682b6bceee Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 9 Aug 2022 16:33:58 +1000 Subject: [PATCH 10/10] Improve docs on bip158 module Make an effort to improve the rustdocs throughout the `bip158` module. --- src/util/bip158.rs | 86 ++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/src/util/bip158.rs b/src/util/bip158.rs index fb409a4c..44e9bddd 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -60,12 +60,12 @@ use crate::internal_macros::write_err; const P: u8 = 19; const M: u64 = 784931; -/// Errors for blockfilter +/// Errors for blockfilter. #[derive(Debug)] pub enum Error { - /// missing UTXO, can not calculate script filter + /// Missing UTXO, cannot calculate script filter. UtxoMissing(OutPoint), - /// some IO error reading or writing binary serialization of the filter + /// IO error reading or writing binary serialization of the filter. Io(io::Error), } @@ -97,7 +97,7 @@ impl From for Error { } } -/// a computed or read block filter +/// A block filter, as described by BIP 158. #[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockFilter { /// Golomb encoded filter @@ -105,7 +105,7 @@ pub struct BlockFilter { } impl FilterHash { - /// compute the filter header from a filter hash and previous filter header + /// Computes the filter header from a filter hash and previous filter header. pub fn filter_header(&self, previous_filter_header: &FilterHeader) -> FilterHeader { let mut header_data = [0u8; 64]; header_data[0..32].copy_from_slice(&self[..]); @@ -143,7 +143,7 @@ impl BlockFilter { filter_hash.filter_header(previous_filter_header) } - /// match any query pattern + /// Returns true if any query matches against this [`BlockFilter`]. pub fn match_any<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result where I: Iterator, @@ -152,7 +152,7 @@ impl BlockFilter { filter_reader.match_any(&mut self.content.as_slice(), query) } - /// match all query pattern + /// Returns true if all queries match against this [`BlockFilter`]. pub fn match_all<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result where I: Iterator, @@ -162,14 +162,14 @@ impl BlockFilter { } } -/// Compiles and writes a block filter +/// Compiles and writes a block filter. pub struct BlockFilterWriter<'a, W> { block: &'a Block, writer: GcsFilterWriter<'a, W>, } impl<'a, W: io::Write> BlockFilterWriter<'a, W> { - /// Create a block filter writer + /// Creates a new [`BlockFilterWriter`] from `block`. pub fn new(writer: &'a mut W, block: &'a Block) -> BlockFilterWriter<'a, W> { let block_hash_as_int = block.block_hash().into_inner(); let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]); @@ -178,7 +178,7 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { BlockFilterWriter { block, writer } } - /// Add output scripts of the block - excluding OP_RETURN scripts + /// Adds output scripts of the block to filter (excluding OP_RETURN scripts). pub fn add_output_scripts(&mut self) { for transaction in &self.block.txdata { for output in &transaction.output { @@ -189,7 +189,7 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { } } - /// Add consumed output scripts of a block to filter + /// 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() @@ -204,24 +204,24 @@ impl<'a, W: io::Write> BlockFilterWriter<'a, W> { Ok(()) } - /// Add arbitrary element to a filter + /// Adds an arbitrary element to filter. pub fn add_element(&mut self, data: &[u8]) { self.writer.add_element(data); } - /// Write block filter + /// Writes the block filter. pub fn finish(&mut self) -> Result { self.writer.finish() } } -/// Reads and interpret a block filter +/// Reads and interprets a block filter. pub struct BlockFilterReader { reader: GcsFilterReader } impl BlockFilterReader { - /// Create a block filter reader + /// Creates a new [`BlockFilterReader`] from `block_hash`. pub fn new(block_hash: &BlockHash) -> BlockFilterReader { let block_hash_as_int = block_hash.into_inner(); let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]); @@ -229,7 +229,7 @@ impl BlockFilterReader { BlockFilterReader { reader: GcsFilterReader::new(k0, k1, M, P) } } - /// match any query pattern + /// Returns true if any query matches against this [`BlockFilterReader`]. pub fn match_any<'a, I, R>(&self, reader: &mut R, query: I) -> Result where I: Iterator, @@ -238,7 +238,7 @@ impl BlockFilterReader { self.reader.match_any(reader, query) } - /// match all query pattern + /// Returns true if all queries match against this [`BlockFilterReader`]. pub fn match_all<'a, I, R>(&self, reader: &mut R, query: I) -> Result where I: Iterator, @@ -248,19 +248,19 @@ impl BlockFilterReader { } } -/// Golomb-Rice encoded filter reader +/// Golomb-Rice encoded filter reader. pub struct GcsFilterReader { filter: GcsFilter, m: u64 } impl GcsFilterReader { - /// Create a new filter reader with specific seed to siphash + /// Creates a new [`GcsFilterReader`] with specific seed to siphash. pub fn new(k0: u64, k1: u64, m: u64, p: u8) -> GcsFilterReader { GcsFilterReader { filter: GcsFilter::new(k0, k1, p), m } } - /// match any query pattern + /// Returns true if any query matches against this [`GcsFilterReader`]. pub fn match_any<'a, I, R>(&self, reader: &mut R, query: I) -> Result where I: Iterator, @@ -304,7 +304,7 @@ impl GcsFilterReader { Ok(false) } - /// match all query pattern + /// Returns true if all queries match against this [`GcsFilterReader`]. pub fn match_all<'a, I, R>(&self, reader: &mut R, query: I) -> Result where I: Iterator, @@ -350,12 +350,12 @@ impl GcsFilterReader { } } -// fast reduction of hash to [0, nm) range +/// 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 } -/// Colomb-Rice encoded filter writer +/// Golomb-Rice encoded filter writer. pub struct GcsFilterWriter<'a, W> { filter: GcsFilter, writer: &'a mut W, @@ -364,7 +364,7 @@ pub struct GcsFilterWriter<'a, W> { } impl<'a, W: io::Write> GcsFilterWriter<'a, W> { - /// Create a new Gcs writer wrapping a generic writer, with specific seed to siphash + /// 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), @@ -374,14 +374,14 @@ impl<'a, W: io::Write> GcsFilterWriter<'a, W> { } } - /// Add some data to the filter + /// Adds data to the filter. pub fn add_element(&mut self, element: &[u8]) { if !element.is_empty() { self.elements.insert(element.to_vec()); } } - /// write the filter to the wrapped writer + /// Writes the filter to the wrapped writer. pub fn finish(&mut self) -> Result { let nm = self.elements.len() as u64 * self.m; @@ -405,7 +405,7 @@ impl<'a, W: io::Write> GcsFilterWriter<'a, W> { } } -/// Golomb Coded Set Filter +/// Golomb Coded Set Filter. struct GcsFilter { k0: u64, // sip hash key k1: u64, // sip hash key @@ -413,12 +413,12 @@ struct GcsFilter { } impl GcsFilter { - /// Create a new filter + /// Creates a new [`GcsFilter`]. fn new(k0: u64, k1: u64, p: u8) -> GcsFilter { GcsFilter { k0, k1, p } } - /// Golomb-Rice encode a number n to a bit stream (Parameter 2^k) + /// 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 where W: io::Write, @@ -435,7 +435,7 @@ impl GcsFilter { Ok(wrote) } - /// Golomb-Rice decode a number from a bit stream (Parameter 2^k) + /// 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 @@ -448,13 +448,13 @@ impl GcsFilter { Ok((q << self.p) + r) } - /// Hash an arbitrary slice with siphash using parameters of this filter + /// Hashes an arbitrary slice with siphash using parameters of this filter. fn hash(&self, element: &[u8]) -> u64 { siphash24::Hash::hash_to_u64_with_keys(self.k0, self.k1, element) } } -/// Bitwise stream reader +/// Bitwise stream reader. pub struct BitStreamReader<'a, R> { buffer: [u8; 1], offset: u8, @@ -462,7 +462,7 @@ pub struct BitStreamReader<'a, R> { } impl<'a, R: io::Read> BitStreamReader<'a, R> { - /// Create a new BitStreamReader that reads bitwise from a given reader + /// Creates a new [`BitStreamReader`] that reads bitwise from a given `reader`. pub fn new(reader: &'a mut R) -> BitStreamReader<'a, R> { BitStreamReader { buffer: [0u8], @@ -471,7 +471,17 @@ impl<'a, R: io::Read> BitStreamReader<'a, R> { } } - /// Read nbit bits + /// Reads nbit bits, returning the bits in a `u64` starting with the rightmost bit. + /// + /// # Examples + /// ``` + /// # use bitcoin::util::bip158::BitStreamReader; + /// # let data = vec![0xff]; + /// # let mut input = data.as_slice(); + /// let mut reader = BitStreamReader::new(&mut input); // input contains all 1's + /// let res = reader.read(1).expect("read failed"); + /// assert_eq!(res, 1_u64); + /// ``` 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")); @@ -492,7 +502,7 @@ impl<'a, R: io::Read> BitStreamReader<'a, R> { } } -/// Bitwise stream writer +/// Bitwise stream writer. pub struct BitStreamWriter<'a, W> { buffer: [u8; 1], offset: u8, @@ -500,7 +510,7 @@ pub struct BitStreamWriter<'a, W> { } impl<'a, W: io::Write> BitStreamWriter<'a, W> { - /// Create a new BitStreamWriter that writes bitwise to a given writer + /// Creates a new [`BitStreamWriter`] that writes bitwise to a given `writer`. pub fn new(writer: &'a mut W) -> BitStreamWriter<'a, W> { BitStreamWriter { buffer: [0u8], @@ -509,7 +519,7 @@ impl<'a, W: io::Write> BitStreamWriter<'a, W> { } } - /// Write nbits bits from data + /// 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")); @@ -527,7 +537,7 @@ impl<'a, W: io::Write> BitStreamWriter<'a, W> { Ok(wrote) } - /// flush bits not yet written + /// flush bits not yet written. pub fn flush(&mut self) -> Result { if self.offset > 0 { self.writer.write_all(&self.buffer)?;