def match(self, key, data): """ Check whether the bytes are likely (within collision probability) to be a member of the set represented by the filter. Args: key (ByteArray): The siphash key. data (ByteArray): The data to find. Returns: bool: Whether the data is likely to be in the set. """ # An empty filter or empty data can't possibly match anything. if len(self.filterData) == 0 or len(data) == 0: return False if len(key) != 16: raise DecredError(f"key length {len(key)} != 16 not allowed") # Hash the search term with the same parameters as the filter. term = (siphash24(data.bytes(), key=key.bytes(), encoder=SiphashEncoder).littleEndian().int()) term = (term * self.modulusNM) >> 64 # Go through the search filter and look for the desired value. bitStream = BitReader(self.filterData) lastValue = 0 readInt = self.readFullUint64 while lastValue <= term: # Read the difference between previous and new value from # bitstream. try: value = readInt(bitStream) except EncodingError: # out of bytes return False # Add the previous value to it. value += lastValue if value == term: return True lastValue = value return False
def test_shortened_key(inp, key, expected): with pytest.raises(ValueError): siphash24(inp, key)
def test_siphash24(inp, key, expected): rs = siphash24(inp, key) assert rs == hexlify(expected)
def matchAny(self, key, data): """ Check whether any of the supplied data values are likely (within collision probability) to be a member of the set represented by the filter faster than calling match() for each value individually. Args: key (ByteArray): The siphash key. data list(ByteArray): The data to find. Returns: bool: Whether any member of the data is likely to be in the set. """ # An empty filter or empty data can't possibly match anything. if len(self.filterData) == 0 or len(data) == 0: return False # Create an uncompressed filter of the search values. values = [] mod = self.modulusNM keyB = key.bytes() for d in data: if len(d) == 0: continue v = (siphash24(d.bytes(), key=keyB, encoder=SiphashEncoder).littleEndian().int()) values.append((v * mod) >> 64) if len(values) == 0: return False values.sort() # Zip down the filters, comparing values until we either run out of # values to compare in one of the filters or we reach a matching value. bitStream = BitReader(self.filterData) searchSize = len(data) searchIdx = 0 filterVal = 0 readInt = self.readFullUint64 for i in range(self.n): # Read the next item to compare from the filter. try: delta = readInt(bitStream) except EncodingError: # out of bytes return False filterVal += delta again = False # Iterate through the values to search until either a match is found # or the search value exceeds the current filter value. while searchIdx < searchSize: searchVal = values[searchIdx] if searchVal == filterVal: return True # Move to the next filter item once the current search value # exceeds it. if searchVal > filterVal: again = True break searchIdx += 1 if again: continue # Exit early when there are no more values to search for. break return False
def test_shortened_key(inp, key, expected): with pytest.raises(ValueError): siphash24(inp, key) with pytest.raises(ValueError): siphashx24(inp, key)
def test_siphash24(inp, key, expected): rs = siphash24(inp, key) assert rs == hexlify(expected)