github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/bloom/filter.go (about)

     1  // Copyright (c) 2014-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package bloom
     6  
     7  import (
     8  	"encoding/binary"
     9  	"math"
    10  	"sync"
    11  
    12  	"github.com/mit-dci/lit/btcutil"
    13  	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
    14  	"github.com/mit-dci/lit/btcutil/txscript"
    15  	"github.com/mit-dci/lit/wire"
    16  )
    17  
    18  // ln2Squared is simply the square of the natural log of 2.
    19  const ln2Squared = math.Ln2 * math.Ln2
    20  
    21  // minUint32 is a convenience function to return the minimum value of the two
    22  // passed uint32 values.
    23  func minUint32(a, b uint32) uint32 {
    24  	if a < b {
    25  		return a
    26  	}
    27  	return b
    28  }
    29  
    30  // Filter defines a bitcoin bloom filter that provides easy manipulation of raw
    31  // filter data.
    32  type Filter struct {
    33  	mtx           sync.Mutex
    34  	msgFilterLoad *wire.MsgFilterLoad
    35  }
    36  
    37  // NewFilter creates a new bloom filter instance, mainly to be used by SPV
    38  // clients.  The tweak parameter is a random value added to the seed value.
    39  // The false positive rate is the probability of a false positive where 1.0 is
    40  // "match everything" and zero is unachievable.  Thus, providing any false
    41  // positive rates less than 0 or greater than 1 will be adjusted to the valid
    42  // range.
    43  //
    44  // For more information on what values to use for both elements and fprate,
    45  // see https://en.wikipedia.org/wiki/Bloom_filter.
    46  func NewFilter(elements, tweak uint32, fprate float64, flags wire.BloomUpdateType) *Filter {
    47  	// Massage the false positive rate to sane values.
    48  	if fprate > 1.0 {
    49  		fprate = 1.0
    50  	}
    51  	if fprate < 1e-9 {
    52  		fprate = 1e-9
    53  	}
    54  
    55  	// Calculate the size of the filter in bytes for the given number of
    56  	// elements and false positive rate.
    57  	//
    58  	// Equivalent to m = -(n*ln(p) / ln(2)^2), where m is in bits.
    59  	// Then clamp it to the maximum filter size and convert to bytes.
    60  	dataLen := uint32(-1 * float64(elements) * math.Log(fprate) / ln2Squared)
    61  	dataLen = minUint32(dataLen, wire.MaxFilterLoadFilterSize*8) / 8
    62  
    63  	// Calculate the number of hash functions based on the size of the
    64  	// filter calculated above and the number of elements.
    65  	//
    66  	// Equivalent to k = (m/n) * ln(2)
    67  	// Then clamp it to the maximum allowed hash funcs.
    68  	hashFuncs := uint32(float64(dataLen*8) / float64(elements) * math.Ln2)
    69  	hashFuncs = minUint32(hashFuncs, wire.MaxFilterLoadHashFuncs)
    70  
    71  	data := make([]byte, dataLen)
    72  	msg := wire.NewMsgFilterLoad(data, hashFuncs, tweak, flags)
    73  
    74  	return &Filter{
    75  		msgFilterLoad: msg,
    76  	}
    77  }
    78  
    79  // LoadFilter creates a new Filter instance with the given underlying
    80  // wire.MsgFilterLoad.
    81  func LoadFilter(filter *wire.MsgFilterLoad) *Filter {
    82  	return &Filter{
    83  		msgFilterLoad: filter,
    84  	}
    85  }
    86  
    87  // IsLoaded returns true if a filter is loaded, otherwise false.
    88  //
    89  // This function is safe for concurrent access.
    90  func (bf *Filter) IsLoaded() bool {
    91  	bf.mtx.Lock()
    92  	loaded := bf.msgFilterLoad != nil
    93  	bf.mtx.Unlock()
    94  	return loaded
    95  }
    96  
    97  // Reload loads a new filter replacing any existing filter.
    98  //
    99  // This function is safe for concurrent access.
   100  func (bf *Filter) Reload(filter *wire.MsgFilterLoad) {
   101  	bf.mtx.Lock()
   102  	bf.msgFilterLoad = filter
   103  	bf.mtx.Unlock()
   104  }
   105  
   106  // Unload unloads the bloom filter.
   107  //
   108  // This function is safe for concurrent access.
   109  func (bf *Filter) Unload() {
   110  	bf.mtx.Lock()
   111  	bf.msgFilterLoad = nil
   112  	bf.mtx.Unlock()
   113  }
   114  
   115  // hash returns the bit offset in the bloom filter which corresponds to the
   116  // passed data for the given indepedent hash function number.
   117  func (bf *Filter) hash(hashNum uint32, data []byte) uint32 {
   118  	// bitcoind: 0xfba4c795 chosen as it guarantees a reasonable bit
   119  	// difference between hashNum values.
   120  	//
   121  	// Note that << 3 is equivalent to multiplying by 8, but is faster.
   122  	// Thus the returned hash is brought into range of the number of bits
   123  	// the filter has and returned.
   124  	mm := MurmurHash3(hashNum*0xfba4c795+bf.msgFilterLoad.Tweak, data)
   125  	return mm % (uint32(len(bf.msgFilterLoad.Filter)) << 3)
   126  }
   127  
   128  // matches returns true if the bloom filter might contain the passed data and
   129  // false if it definitely does not.
   130  //
   131  // This function MUST be called with the filter lock held.
   132  func (bf *Filter) matches(data []byte) bool {
   133  	if bf.msgFilterLoad == nil {
   134  		return false
   135  	}
   136  
   137  	// The bloom filter does not contain the data if any of the bit offsets
   138  	// which result from hashing the data using each independent hash
   139  	// function are not set.  The shifts and masks below are a faster
   140  	// equivalent of:
   141  	//   arrayIndex := idx / 8     (idx >> 3)
   142  	//   bitOffset := idx % 8      (idx & 7)
   143  	///  if filter[arrayIndex] & 1<<bitOffset == 0 { ... }
   144  	for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
   145  		idx := bf.hash(i, data)
   146  		if bf.msgFilterLoad.Filter[idx>>3]&(1<<(idx&7)) == 0 {
   147  			return false
   148  		}
   149  	}
   150  	return true
   151  }
   152  
   153  // Matches returns true if the bloom filter might contain the passed data and
   154  // false if it definitely does not.
   155  //
   156  // This function is safe for concurrent access.
   157  func (bf *Filter) Matches(data []byte) bool {
   158  	bf.mtx.Lock()
   159  	match := bf.matches(data)
   160  	bf.mtx.Unlock()
   161  	return match
   162  }
   163  
   164  // matchesOutPoint returns true if the bloom filter might contain the passed
   165  // outpoint and false if it definitely does not.
   166  //
   167  // This function MUST be called with the filter lock held.
   168  func (bf *Filter) matchesOutPoint(outpoint *wire.OutPoint) bool {
   169  	// Serialize
   170  	var buf [chainhash.HashSize + 4]byte
   171  	copy(buf[:], outpoint.Hash[:])
   172  	binary.LittleEndian.PutUint32(buf[chainhash.HashSize:], outpoint.Index)
   173  
   174  	return bf.matches(buf[:])
   175  }
   176  
   177  // MatchesOutPoint returns true if the bloom filter might contain the passed
   178  // outpoint and false if it definitely does not.
   179  //
   180  // This function is safe for concurrent access.
   181  func (bf *Filter) MatchesOutPoint(outpoint *wire.OutPoint) bool {
   182  	bf.mtx.Lock()
   183  	match := bf.matchesOutPoint(outpoint)
   184  	bf.mtx.Unlock()
   185  	return match
   186  }
   187  
   188  // add adds the passed byte slice to the bloom filter.
   189  //
   190  // This function MUST be called with the filter lock held.
   191  func (bf *Filter) add(data []byte) {
   192  	if bf.msgFilterLoad == nil {
   193  		return
   194  	}
   195  
   196  	// Adding data to a bloom filter consists of setting all of the bit
   197  	// offsets which result from hashing the data using each independent
   198  	// hash function.  The shifts and masks below are a faster equivalent
   199  	// of:
   200  	//   arrayIndex := idx / 8    (idx >> 3)
   201  	//   bitOffset := idx % 8     (idx & 7)
   202  	///  filter[arrayIndex] |= 1<<bitOffset
   203  	for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
   204  		idx := bf.hash(i, data)
   205  		bf.msgFilterLoad.Filter[idx>>3] |= (1 << (7 & idx))
   206  	}
   207  }
   208  
   209  // Add adds the passed byte slice to the bloom filter.
   210  //
   211  // This function is safe for concurrent access.
   212  func (bf *Filter) Add(data []byte) {
   213  	bf.mtx.Lock()
   214  	bf.add(data)
   215  	bf.mtx.Unlock()
   216  }
   217  
   218  // AddHash adds the passed chainhash.Hash to the Filter.
   219  //
   220  // This function is safe for concurrent access.
   221  func (bf *Filter) AddHash(hash *chainhash.Hash) {
   222  	bf.mtx.Lock()
   223  	bf.add(hash[:])
   224  	bf.mtx.Unlock()
   225  }
   226  
   227  // addOutPoint adds the passed transaction outpoint to the bloom filter.
   228  //
   229  // This function MUST be called with the filter lock held.
   230  func (bf *Filter) addOutPoint(outpoint *wire.OutPoint) {
   231  	// Serialize
   232  	var buf [chainhash.HashSize + 4]byte
   233  	copy(buf[:], outpoint.Hash[:])
   234  	binary.LittleEndian.PutUint32(buf[chainhash.HashSize:], outpoint.Index)
   235  
   236  	bf.add(buf[:])
   237  }
   238  
   239  // AddOutPoint adds the passed transaction outpoint to the bloom filter.
   240  //
   241  // This function is safe for concurrent access.
   242  func (bf *Filter) AddOutPoint(outpoint *wire.OutPoint) {
   243  	bf.mtx.Lock()
   244  	bf.addOutPoint(outpoint)
   245  	bf.mtx.Unlock()
   246  }
   247  
   248  // maybeAddOutpoint potentially adds the passed outpoint to the bloom filter
   249  // depending on the bloom update flags and the type of the passed public key
   250  // script.
   251  //
   252  // This function MUST be called with the filter lock held.
   253  func (bf *Filter) maybeAddOutpoint(pkScript []byte, outHash *chainhash.Hash, outIdx uint32) {
   254  	switch bf.msgFilterLoad.Flags {
   255  	case wire.BloomUpdateAll:
   256  		outpoint := wire.NewOutPoint(outHash, outIdx)
   257  		bf.addOutPoint(outpoint)
   258  	case wire.BloomUpdateP2PubkeyOnly:
   259  		class := txscript.GetScriptClass(pkScript)
   260  		if class == txscript.PubKeyTy || class == txscript.MultiSigTy {
   261  			outpoint := wire.NewOutPoint(outHash, outIdx)
   262  			bf.addOutPoint(outpoint)
   263  		}
   264  	}
   265  }
   266  
   267  // matchTxAndUpdate returns true if the bloom filter matches data within the
   268  // passed transaction, otherwise false is returned.  If the filter does match
   269  // the passed transaction, it will also update the filter depending on the bloom
   270  // update flags set via the loaded filter if needed.
   271  //
   272  // This function MUST be called with the filter lock held.
   273  func (bf *Filter) matchTxAndUpdate(tx *btcutil.Tx) bool {
   274  	// Check if the filter matches the hash of the transaction.
   275  	// This is useful for finding transactions when they appear in a block.
   276  	matched := bf.matches(tx.Hash()[:])
   277  
   278  	// Check if the filter matches any data elements in the public key
   279  	// scripts of any of the outputs.  When it does, add the outpoint that
   280  	// matched so transactions which spend from the matched transaction are
   281  	// also included in the filter.  This removes the burden of updating the
   282  	// filter for this scenario from the client.  It is also more efficient
   283  	// on the network since it avoids the need for another filteradd message
   284  	// from the client and avoids some potential races that could otherwise
   285  	// occur.
   286  	for i, txOut := range tx.MsgTx().TxOut {
   287  		pushedData, err := txscript.PushedData(txOut.PkScript)
   288  		if err != nil {
   289  			continue
   290  		}
   291  
   292  		for _, data := range pushedData {
   293  			if !bf.matches(data) {
   294  				continue
   295  			}
   296  
   297  			matched = true
   298  			bf.maybeAddOutpoint(txOut.PkScript, tx.Hash(), uint32(i))
   299  			break
   300  		}
   301  	}
   302  
   303  	// Nothing more to do if a match has already been made.
   304  	if matched {
   305  		return true
   306  	}
   307  
   308  	// At this point, the transaction and none of the data elements in the
   309  	// public key scripts of its outputs matched.
   310  
   311  	// Check if the filter matches any outpoints this transaction spends or
   312  	// any any data elements in the signature scripts of any of the inputs.
   313  	for _, txin := range tx.MsgTx().TxIn {
   314  		if bf.matchesOutPoint(&txin.PreviousOutPoint) {
   315  			return true
   316  		}
   317  
   318  		pushedData, err := txscript.PushedData(txin.SignatureScript)
   319  		if err != nil {
   320  			continue
   321  		}
   322  		for _, data := range pushedData {
   323  			if bf.matches(data) {
   324  				return true
   325  			}
   326  		}
   327  	}
   328  
   329  	return false
   330  }
   331  
   332  // MatchTxAndUpdate returns true if the bloom filter matches data within the
   333  // passed transaction, otherwise false is returned.  If the filter does match
   334  // the passed transaction, it will also update the filter depending on the bloom
   335  // update flags set via the loaded filter if needed.
   336  //
   337  // This function is safe for concurrent access.
   338  func (bf *Filter) MatchTxAndUpdate(tx *btcutil.Tx) bool {
   339  	bf.mtx.Lock()
   340  	match := bf.matchTxAndUpdate(tx)
   341  	bf.mtx.Unlock()
   342  	return match
   343  }
   344  
   345  // MsgFilterLoad returns the underlying wire.MsgFilterLoad for the bloom
   346  // filter.
   347  //
   348  // This function is safe for concurrent access.
   349  func (bf *Filter) MsgFilterLoad() *wire.MsgFilterLoad {
   350  	bf.mtx.Lock()
   351  	msg := bf.msgFilterLoad
   352  	bf.mtx.Unlock()
   353  	return msg
   354  }