diff --git a/gcs/builder/builder_test.go b/gcs/builder/builder_test.go index 838e194..ca49382 100644 --- a/gcs/builder/builder_test.go +++ b/gcs/builder/builder_test.go @@ -9,7 +9,12 @@ import ( "encoding/hex" "testing" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcutil/gcs" "github.com/btcsuite/btcutil/gcs/builder" ) @@ -38,55 +43,156 @@ var ( []byte("Quentin"), } - // List of values for querying a filter using MatchAny() - contents2 = [][]byte{ - []byte("Alice"), - []byte("Betty"), - []byte("Charmaine"), - []byte("Donna"), - []byte("Edith"), - []byte("Faina"), - []byte("Georgia"), - []byte("Hannah"), - []byte("Ilsbeth"), - []byte("Jennifer"), - []byte("Kayla"), - []byte("Lena"), - []byte("Michelle"), - []byte("Natalie"), - []byte("Ophelia"), - []byte("Peggy"), - []byte("Queenie"), - } + testKey = [16]byte{0x4c, 0xb1, 0xab, 0x12, 0x57, 0x62, 0x1e, 0x41, + 0x3b, 0x8b, 0x0e, 0x26, 0x64, 0x8d, 0x4a, 0x15} + + testHash = "000000000000000000496d7ff9bd2c96154a8d64260e8b3b411e625712abb14c" + + testAddr = "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v" ) // TestUseBlockHash tests using a block hash as a filter key. func TestUseBlockHash(t *testing.T) { // Block hash #448710, pretty high difficulty. - hash, err := chainhash.NewHashFromStr("000000000000000000496d7ff9bd2c96154a8d64260e8b3b411e625712abb14c") + hash, err := chainhash.NewHashFromStr(testHash) if err != nil { t.Fatalf("Hash from string failed: %s", err.Error()) } - // Create a Builder with a key hash and check that the key is derived - // correctly. + // wire.OutPoint + outPoint := wire.OutPoint{ + Hash: *hash, + Index: 4321, + } + + // btcutil.Address + addr, err := btcutil.DecodeAddress(testAddr, &chaincfg.MainNetParams) + if err != nil { + t.Fatalf("Address decode failed: %s", err.Error()) + } + addrBytes, err := txscript.PayToAddrScript(addr) + if err != nil { + t.Fatalf("Address script build failed: %s", err.Error()) + } + + // Create a GCSBuilder with a key hash and check that the key is derived + // correctly, then test it. b := builder.WithKeyHash(hash) key, err := b.Key() if err != nil { t.Fatalf("Builder instantiation with key hash failed: %s", err.Error()) } - testKey := [16]byte{0x4c, 0xb1, 0xab, 0x12, 0x57, 0x62, 0x1e, 0x41, - 0x3b, 0x8b, 0x0e, 0x26, 0x64, 0x8d, 0x4a, 0x15} if key != testKey { t.Fatalf("Key not derived correctly from key hash:\n%s\n%s", hex.EncodeToString(key[:]), hex.EncodeToString(testKey[:])) } + BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, t) + + // Create a GCSBuilder with a key hash and non-default P and test it. + b = builder.WithKeyHashP(hash, 30) + BuilderTest(b, hash, 30, outPoint, addrBytes, t) + + // Create a GCSBuilder with a random key, set the key from a hash + // manually, check that the key is correct, and test it. + b = builder.WithRandomKey() + b.SetKeyFromHash(hash) + key, err = b.Key() + if err != nil { + t.Fatalf("Builder instantiation with known key failed: %s", + err.Error()) + } + if key != testKey { + t.Fatalf("Key not copied correctly from known key:\n%s\n%s", + hex.EncodeToString(key[:]), + hex.EncodeToString(testKey[:])) + } + BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, t) + + // Create a GCSBuilder with a random key and test it. + b = builder.WithRandomKey() + key1, err := b.Key() + if err != nil { + t.Fatalf("Builder instantiation with random key failed: %s", + err.Error()) + } + t.Logf("Random Key 1: %s", hex.EncodeToString(key1[:])) + BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, t) + + // Create a GCSBuilder with a random key and non-default P and test it. + b = builder.WithRandomKeyP(30) + key2, err := b.Key() + if err != nil { + t.Fatalf("Builder instantiation with random key failed: %s", + err.Error()) + } + t.Logf("Random Key 2: %s", hex.EncodeToString(key2[:])) + if key2 == key1 { + t.Fatalf("Random keys are the same!") + } + BuilderTest(b, hash, 30, outPoint, addrBytes, t) + + // Create a GCSBuilder with a known key and test it. + b = builder.WithKey(testKey) + key, err = b.Key() + if err != nil { + t.Fatalf("Builder instantiation with known key failed: %s", + err.Error()) + } + if key != testKey { + t.Fatalf("Key not copied correctly from known key:\n%s\n%s", + hex.EncodeToString(key[:]), + hex.EncodeToString(testKey[:])) + } + BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, t) + + // Create a GCSBuilder with a known key and non-default P and test it. + b = builder.WithKeyP(testKey, 30) + key, err = b.Key() + if err != nil { + t.Fatalf("Builder instantiation with known key failed: %s", + err.Error()) + } + if key != testKey { + t.Fatalf("Key not copied correctly from known key:\n%s\n%s", + hex.EncodeToString(key[:]), + hex.EncodeToString(testKey[:])) + } + BuilderTest(b, hash, 30, outPoint, addrBytes, t) + + // Create a GCSBuilder with a known key and too-high P and ensure error + // works throughout all functions that use it. + b = builder.WithRandomKeyP(33).SetKeyFromHash(hash).SetKey(testKey) + b.SetP(30).AddEntry(hash.CloneBytes()).AddEntries(contents) + b.AddOutPoint(outPoint).AddHash(hash).AddScript(addrBytes) + _, err = b.Key() + if err != gcs.ErrPTooBig { + t.Fatalf("No error on P too big!") + } + _, err = b.Build() + if err != gcs.ErrPTooBig { + t.Fatalf("No error on P too big!") + } +} + +func BuilderTest(b *builder.GCSBuilder, hash *chainhash.Hash, p uint8, + outPoint wire.OutPoint, addrBytes []byte, t *testing.T) { + key, err := b.Key() + if err != nil { + t.Fatalf("Builder instantiation with key hash failed: %s", + err.Error()) + } // Build a filter and test matches. b.AddEntries(contents) f, err := b.Build() + if err != nil { + t.Fatalf("Filter build failed: %s", err.Error()) + } + if f.P() != p { + t.Fatalf("Filter built with wrong probability") + } match, err := f.Match(key, []byte("Nate")) if err != nil { t.Fatalf("Filter match failed: %s", err) @@ -102,4 +208,52 @@ func TestUseBlockHash(t *testing.T) { t.Logf("False positive match, should be 1 in 2**%d!", builder.DefaultP) } + + // Add a hash, build a filter, and test matches + b.AddHash(hash) + f, err = b.Build() + if err != nil { + t.Fatalf("Filter build failed: %s", err.Error()) + } + match, err = f.Match(key, hash.CloneBytes()) + if err != nil { + t.Fatalf("Filter match failed: %s", err) + } + if !match { + t.Fatal("Filter didn't match when it should have!") + } + + // Add a wire.OutPoint, build a filter, and test matches + b.AddOutPoint(outPoint) + f, err = b.Build() + if err != nil { + t.Fatalf("Filter build failed: %s", err.Error()) + } + match, err = f.Match(key, hash.CloneBytes()) + if err != nil { + t.Fatalf("Filter match failed: %s", err) + } + if !match { + t.Fatal("Filter didn't match when it should have!") + } + + // Add a script, build a filter, and test matches + b.AddScript(addrBytes) + f, err = b.Build() + if err != nil { + t.Fatalf("Filter build failed: %s", err.Error()) + } + pushedData, err := txscript.PushedData(addrBytes) + if err != nil { + t.Fatalf("Couldn't extract pushed data from addrBytesess script: %s", + err.Error()) + } + match, err = f.MatchAny(key, pushedData) + if err != nil { + t.Fatalf("Filter match any failed: %s", err) + } + if !match { + t.Logf("Filter didn't match when it should have!") + } + }