// Written in 2019 by Andrew Poelstra // SPDX-License-Identifier: CC0-1.0 // This module was largely copied from https://github.com/rust-bitcoin/murmel/blob/master/src/blockfilter.rs // 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. //! 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. //! //! ### 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 { //! // get utxo ... //! } //! //! // create a block filter for a block (server side) //! let filter = BlockFilter::new_script_filter(&block, get_script_for_coin)?; //! //! // or create a filter from known raw data //! let filter = BlockFilter::new(content); //! //! // read and evaluate a filter //! //! let query: Iterator = // .. some scripts you care about //! if filter.match_any(&block_hash, &mut query.map(|s| s.as_bytes())) { //! // get this block //! } //! ``` //! use core::cmp::{self, Ordering}; use core::convert::TryInto; use core::fmt::{self, Display, Formatter}; use bitcoin_internals::write_err; use crate::blockdata::block::Block; use crate::blockdata::script::Script; use crate::blockdata::transaction::OutPoint; use crate::consensus::encode::VarInt; use crate::consensus::{Decodable, Encodable}; use crate::hash_types::{BlockHash, FilterHash, FilterHeader}; use crate::hashes::{siphash24, Hash}; use crate::io; use crate::prelude::*; /// Golomb encoding parameter as in BIP-158, see also https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845 const P: u8 = 19; const M: u64 = 784931; /// Errors for blockfilter. #[derive(Debug)] #[non_exhaustive] pub enum Error { /// Missing UTXO, cannot calculate script filter. UtxoMissing(OutPoint), /// IO error reading or writing binary serialization of the filter. Io(io::Error), } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { match *self { Error::UtxoMissing(ref coin) => write!(f, "unresolved UTXO {}", coin), Error::Io(ref e) => write_err!(f, "IO error"; e), } } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use self::Error::*; match self { UtxoMissing(_) => None, Io(e) => Some(e), } } } impl From for Error { 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, } impl FilterHash { /// 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[..]); header_data[32..64].copy_from_slice(&previous_filter_header[..]); FilterHeader::hash(&header_data) } } impl BlockFilter { /// Creates a new filter from pre-computed data. 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, S: Borrow