core/filtermaps: add filtermaps tests
This commit is contained in:
parent
f85658fbe5
commit
8b5c87e30f
|
@ -0,0 +1,112 @@
|
|||
package filtermaps
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestSingleMatch(t *testing.T) {
|
||||
for count := 0; count < 100000; count++ {
|
||||
// generate a row with a single random entry
|
||||
mapIndex := rand.Uint32()
|
||||
lvIndex := uint64(mapIndex)<<logValuesPerMap + uint64(rand.Intn(valuesPerMap))
|
||||
var lvHash common.Hash
|
||||
rand.Read(lvHash[:])
|
||||
row := FilterRow{columnIndex(lvIndex, lvHash)}
|
||||
matches := row.potentialMatches(mapIndex, lvHash)
|
||||
// check if it has been reverse transformed correctly
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("Invalid length of matches (got %d, expected 1)", len(matches))
|
||||
}
|
||||
if matches[0] != lvIndex {
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("Incorrect match returned (got %d, expected %d)", matches[0], lvIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
testPmCount = 100
|
||||
testPmLen = 1000
|
||||
)
|
||||
|
||||
func TestPotentialMatches(t *testing.T) {
|
||||
var falsePositives int
|
||||
for count := 0; count < testPmCount; count++ {
|
||||
mapIndex := rand.Uint32()
|
||||
lvStart := uint64(mapIndex) << logValuesPerMap
|
||||
var row FilterRow
|
||||
lvIndices := make([]uint64, testPmLen)
|
||||
lvHashes := make([]common.Hash, testPmLen+1)
|
||||
for i := range lvIndices {
|
||||
// add testPmLen single entries with different log value hashes at different indices
|
||||
lvIndices[i] = lvStart + uint64(rand.Intn(valuesPerMap))
|
||||
rand.Read(lvHashes[i][:])
|
||||
row = append(row, columnIndex(lvIndices[i], lvHashes[i]))
|
||||
}
|
||||
// add the same log value hash at the first testPmLen log value indices of the map's range
|
||||
rand.Read(lvHashes[testPmLen][:])
|
||||
for lvIndex := lvStart; lvIndex < lvStart+testPmLen; lvIndex++ {
|
||||
row = append(row, columnIndex(lvIndex, lvHashes[testPmLen]))
|
||||
}
|
||||
// randomly duplicate some entries
|
||||
for i := 0; i < testPmLen; i++ {
|
||||
row = append(row, row[rand.Intn(len(row))])
|
||||
}
|
||||
// randomly mix up order of elements
|
||||
for i := len(row) - 1; i > 0; i-- {
|
||||
j := rand.Intn(i)
|
||||
row[i], row[j] = row[j], row[i]
|
||||
}
|
||||
// check retrieved matches while also counting false positives
|
||||
for i, lvHash := range lvHashes {
|
||||
matches := row.potentialMatches(mapIndex, lvHash)
|
||||
if i < testPmLen {
|
||||
// check single entry match
|
||||
if len(matches) < 1 {
|
||||
t.Fatalf("Invalid length of matches (got %d, expected >=1)", len(matches))
|
||||
}
|
||||
var found bool
|
||||
for _, lvi := range matches {
|
||||
if lvi == lvIndices[i] {
|
||||
found = true
|
||||
} else {
|
||||
falsePositives++
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Expected match not found (got %v, expected %d)", matches, lvIndices[i])
|
||||
}
|
||||
} else {
|
||||
// check "long series" match
|
||||
if len(matches) < testPmLen {
|
||||
t.Fatalf("Invalid length of matches (got %d, expected >=%d)", len(matches), testPmLen)
|
||||
}
|
||||
// since results are ordered, first testPmLen entries should always match exactly
|
||||
for j := 0; j < testPmLen; j++ {
|
||||
if matches[j] != lvStart+uint64(j) {
|
||||
t.Fatalf("Incorrect match at index %d (got %d, expected %d)", j, matches[j], lvStart+uint64(j))
|
||||
}
|
||||
}
|
||||
// the rest are false positives
|
||||
falsePositives += len(matches) - testPmLen
|
||||
}
|
||||
}
|
||||
}
|
||||
// Whenever looking for a certain log value hash, each entry in the row that
|
||||
// was generated by another log value hash (a "foreign entry") has an
|
||||
// 1 / valuesPerMap chance of yielding a false positive.
|
||||
// We have testPmLen unique hash entries and a testPmLen long series of entries
|
||||
// for the same hash. For each of the testPmLen unique hash entries there are
|
||||
// testPmLen*2-1 foreign entries while for the long series there are testPmLen
|
||||
// foreign entries. This means that after performing all these filtering runs,
|
||||
// we have processed 2*testPmLen^2 foreign entries, which given us an estimate
|
||||
// of how many false positives to expect.
|
||||
expFalse := testPmCount * testPmLen * testPmLen * 2 / valuesPerMap
|
||||
if falsePositives < expFalse/2 || falsePositives > expFalse*3/2 {
|
||||
t.Fatalf("False positive rate out of expected range (got %d, expected %d +-50%%)", falsePositives, expFalse)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue