mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
multi: Remove everything to do about bloom filters.
This commit is contained in:
parent
b3ce2ad2ad
commit
5a4f367b66
@ -1,25 +0,0 @@
|
||||
bloom
|
||||
=====
|
||||
|
||||
[](http://godoc.org/github.com/decred/dcrd/bloom)
|
||||
|
||||
Package bloom provides an API for dealing with decred-specific bloom filters.
|
||||
|
||||
A comprehensive suite of tests is provided to ensure proper functionality.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/decred/dcrd/bloom
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [NewFilter Example](https://godoc.org/github.com/decred/dcrd/bloom#example-package--NewFilter)
|
||||
Demonstrates how to create a new bloom filter, add a transaction hash to it,
|
||||
and check if the filter matches the transaction.
|
||||
|
||||
## License
|
||||
|
||||
Package bloom is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
||||
@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2018 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package bloom provides an API for dealing with decred-specific bloom filters.
|
||||
*/
|
||||
package bloom
|
||||
@ -1,46 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2017 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/bloom"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// This example demonstrates how to create a new bloom filter, add a transaction
|
||||
// hash to it, and check if the filter matches the transaction.
|
||||
func Example_newFilter() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
tweak := rand.Uint32()
|
||||
|
||||
// Create a new bloom filter intended to hold 10 elements with a 0.01%
|
||||
// false positive rate and does not include any automatic update
|
||||
// functionality when transactions are matched.
|
||||
filter := bloom.NewFilter(10, tweak, 0.0001, wire.BloomUpdateNone)
|
||||
|
||||
// Create a transaction hash and add it to the filter. This particular
|
||||
// trasaction is the first transaction in block 310,000 of the main
|
||||
// bitcoin block chain.
|
||||
txHashStr := "fd611c56ca0d378cdcd16244b45c2ba9588da3adac367c4ef43e808b280b8a45"
|
||||
txHash, err := chainhash.NewHashFromStr(txHashStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
filter.AddHash(txHash)
|
||||
|
||||
// Show that the filter matches.
|
||||
matches := filter.Matches(txHash[:])
|
||||
fmt.Println("Filter Matches?:", matches)
|
||||
|
||||
// Output:
|
||||
// Filter Matches?: true
|
||||
}
|
||||
356
bloom/filter.go
356
bloom/filter.go
@ -1,356 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// ln2Squared is simply the square of the natural log of 2.
|
||||
const ln2Squared = math.Ln2 * math.Ln2
|
||||
|
||||
// minUint32 is a convenience function to return the minimum value of the two
|
||||
// passed uint32 values.
|
||||
func minUint32(a, b uint32) uint32 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Filter defines a bloom filter that provides easy manipulation of raw
|
||||
// filter data.
|
||||
type Filter struct {
|
||||
mtx sync.Mutex
|
||||
msgFilterLoad *wire.MsgFilterLoad
|
||||
}
|
||||
|
||||
// NewFilter creates a new bloom filter instance, mainly to be used by SPV
|
||||
// clients. The tweak parameter is a random value added to the seed value.
|
||||
// The false positive rate is the probability of a false positive where 1.0 is
|
||||
// "match everything" and zero is unachievable. Thus, providing any false
|
||||
// positive rates less than 0 or greater than 1 will be adjusted to the valid
|
||||
// range.
|
||||
//
|
||||
// For more information on what values to use for both elements and fprate,
|
||||
// see https://en.wikipedia.org/wiki/Bloom_filter.
|
||||
func NewFilter(elements, tweak uint32, fprate float64, flags wire.BloomUpdateType) *Filter {
|
||||
// Massage the false positive rate to sane values.
|
||||
if fprate > 1.0 {
|
||||
fprate = 1.0
|
||||
}
|
||||
if fprate < 1e-9 {
|
||||
fprate = 1e-9
|
||||
}
|
||||
|
||||
// Calculate the size of the filter in bytes for the given number of
|
||||
// elements and false positive rate.
|
||||
//
|
||||
// Equivalent to m = -(n*ln(p) / ln(2)^2), where m is in bits.
|
||||
// Then clamp it to the maximum filter size and convert to bytes.
|
||||
dataLen := uint32(-1 * float64(elements) * math.Log(fprate) / ln2Squared)
|
||||
dataLen = minUint32(dataLen, wire.MaxFilterLoadFilterSize*8) / 8
|
||||
|
||||
// Calculate the number of hash functions based on the size of the
|
||||
// filter calculated above and the number of elements.
|
||||
//
|
||||
// Equivalent to k = (m/n) * ln(2)
|
||||
// Then clamp it to the maximum allowed hash funcs.
|
||||
hashFuncs := uint32(float64(dataLen*8) / float64(elements) * math.Ln2)
|
||||
hashFuncs = minUint32(hashFuncs, wire.MaxFilterLoadHashFuncs)
|
||||
|
||||
data := make([]byte, dataLen)
|
||||
msg := wire.NewMsgFilterLoad(data, hashFuncs, tweak, flags)
|
||||
|
||||
return &Filter{
|
||||
msgFilterLoad: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadFilter creates a new Filter instance with the given underlying
|
||||
// wire.MsgFilterLoad.
|
||||
func LoadFilter(filter *wire.MsgFilterLoad) *Filter {
|
||||
return &Filter{
|
||||
msgFilterLoad: filter,
|
||||
}
|
||||
}
|
||||
|
||||
// IsLoaded returns true if a filter is loaded, otherwise false.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) IsLoaded() bool {
|
||||
bf.mtx.Lock()
|
||||
loaded := bf.msgFilterLoad != nil
|
||||
bf.mtx.Unlock()
|
||||
return loaded
|
||||
}
|
||||
|
||||
// Reload loads a new filter replacing any existing filter.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) Reload(filter *wire.MsgFilterLoad) {
|
||||
bf.mtx.Lock()
|
||||
bf.msgFilterLoad = filter
|
||||
bf.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Unload unloads the bloom filter.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) Unload() {
|
||||
bf.mtx.Lock()
|
||||
bf.msgFilterLoad = nil
|
||||
bf.mtx.Unlock()
|
||||
}
|
||||
|
||||
// hash returns the bit offset in the bloom filter which corresponds to the
|
||||
// passed data for the given indepedent hash function number.
|
||||
func (bf *Filter) hash(hashNum uint32, data []byte) uint32 {
|
||||
// bitcoind: 0xfba4c795 chosen as it guarantees a reasonable bit
|
||||
// difference between hashNum values.
|
||||
//
|
||||
// Note that << 3 is equivalent to multiplying by 8, but is faster.
|
||||
// Thus the returned hash is brought into range of the number of bits
|
||||
// the filter has and returned.
|
||||
mm := MurmurHash3(hashNum*0xfba4c795+bf.msgFilterLoad.Tweak, data)
|
||||
return mm % (uint32(len(bf.msgFilterLoad.Filter)) << 3)
|
||||
}
|
||||
|
||||
// matches returns true if the bloom filter might contain the passed data and
|
||||
// false if it definitely does not.
|
||||
//
|
||||
// This function MUST be called with the filter lock held.
|
||||
func (bf *Filter) matches(data []byte) bool {
|
||||
if bf.msgFilterLoad == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// The bloom filter does not contain the data if any of the bit offsets
|
||||
// which result from hashing the data using each independent hash
|
||||
// function are not set. The shifts and masks below are a faster
|
||||
// equivalent of:
|
||||
// arrayIndex := idx / 8 (idx >> 3)
|
||||
// bitOffset := idx % 8 (idx & 7)
|
||||
/// if filter[arrayIndex] & 1<<bitOffset == 0 { ... }
|
||||
for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
|
||||
idx := bf.hash(i, data)
|
||||
if bf.msgFilterLoad.Filter[idx>>3]&(1<<(idx&7)) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Matches returns true if the bloom filter might contain the passed data and
|
||||
// false if it definitely does not.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) Matches(data []byte) bool {
|
||||
bf.mtx.Lock()
|
||||
match := bf.matches(data)
|
||||
bf.mtx.Unlock()
|
||||
return match
|
||||
}
|
||||
|
||||
// matchesOutPoint returns true if the bloom filter might contain the passed
|
||||
// outpoint and false if it definitely does not.
|
||||
//
|
||||
// This function MUST be called with the filter lock held.
|
||||
func (bf *Filter) matchesOutPoint(outpoint *wire.OutPoint) bool {
|
||||
// Serialize
|
||||
var buf [chainhash.HashSize + 4]byte
|
||||
copy(buf[:], outpoint.Hash[:])
|
||||
binary.LittleEndian.PutUint32(buf[chainhash.HashSize:], outpoint.Index)
|
||||
|
||||
return bf.matches(buf[:])
|
||||
}
|
||||
|
||||
// MatchesOutPoint returns true if the bloom filter might contain the passed
|
||||
// outpoint and false if it definitely does not.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) MatchesOutPoint(outpoint *wire.OutPoint) bool {
|
||||
bf.mtx.Lock()
|
||||
match := bf.matchesOutPoint(outpoint)
|
||||
bf.mtx.Unlock()
|
||||
return match
|
||||
}
|
||||
|
||||
// add adds the passed byte slice to the bloom filter.
|
||||
//
|
||||
// This function MUST be called with the filter lock held.
|
||||
func (bf *Filter) add(data []byte) {
|
||||
if bf.msgFilterLoad == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Adding data to a bloom filter consists of setting all of the bit
|
||||
// offsets which result from hashing the data using each independent
|
||||
// hash function. The shifts and masks below are a faster equivalent
|
||||
// of:
|
||||
// arrayIndex := idx / 8 (idx >> 3)
|
||||
// bitOffset := idx % 8 (idx & 7)
|
||||
/// filter[arrayIndex] |= 1<<bitOffset
|
||||
for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
|
||||
idx := bf.hash(i, data)
|
||||
bf.msgFilterLoad.Filter[idx>>3] |= (1 << (7 & idx))
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds the passed byte slice to the bloom filter.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) Add(data []byte) {
|
||||
bf.mtx.Lock()
|
||||
bf.add(data)
|
||||
bf.mtx.Unlock()
|
||||
}
|
||||
|
||||
// AddHash adds the passed chainhash.Hash to the Filter.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) AddHash(hash *chainhash.Hash) {
|
||||
bf.mtx.Lock()
|
||||
bf.add(hash[:])
|
||||
bf.mtx.Unlock()
|
||||
}
|
||||
|
||||
// addOutPoint adds the passed transaction outpoint to the bloom filter.
|
||||
//
|
||||
// This function MUST be called with the filter lock held.
|
||||
func (bf *Filter) addOutPoint(outpoint *wire.OutPoint) {
|
||||
// Serialize
|
||||
var buf [chainhash.HashSize + 4]byte
|
||||
copy(buf[:], outpoint.Hash[:])
|
||||
binary.LittleEndian.PutUint32(buf[chainhash.HashSize:], outpoint.Index)
|
||||
|
||||
bf.add(buf[:])
|
||||
}
|
||||
|
||||
// AddOutPoint adds the passed transaction outpoint to the bloom filter.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) AddOutPoint(outpoint *wire.OutPoint) {
|
||||
bf.mtx.Lock()
|
||||
bf.addOutPoint(outpoint)
|
||||
bf.mtx.Unlock()
|
||||
}
|
||||
|
||||
// maybeAddOutpoint potentially adds the passed outpoint to the bloom filter
|
||||
// depending on the bloom update flags and the type of the passed public key
|
||||
// script.
|
||||
//
|
||||
// This function MUST be called with the filter lock held.
|
||||
func (bf *Filter) maybeAddOutpoint(pkScrVer uint16, pkScript []byte, outHash *chainhash.Hash, outIdx uint32, outTree int8) {
|
||||
switch bf.msgFilterLoad.Flags {
|
||||
case wire.BloomUpdateAll:
|
||||
outpoint := wire.NewOutPoint(outHash, outIdx, outTree)
|
||||
bf.addOutPoint(outpoint)
|
||||
case wire.BloomUpdateP2PubkeyOnly:
|
||||
class := txscript.GetScriptClass(pkScrVer, pkScript)
|
||||
if class == txscript.PubKeyTy || class == txscript.MultiSigTy {
|
||||
outpoint := wire.NewOutPoint(outHash, outIdx, outTree)
|
||||
bf.addOutPoint(outpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchTxAndUpdate returns true if the bloom filter matches data within the
|
||||
// passed transaction, otherwise false is returned. If the filter does match
|
||||
// the passed transaction, it will also update the filter depending on the bloom
|
||||
// update flags set via the loaded filter if needed.
|
||||
//
|
||||
// This function MUST be called with the filter lock held.
|
||||
func (bf *Filter) matchTxAndUpdate(tx *dcrutil.Tx) bool {
|
||||
// Check if the filter matches the hash of the transaction.
|
||||
// This is useful for finding transactions when they appear in a block.
|
||||
matched := bf.matches(tx.Hash()[:])
|
||||
|
||||
// Check if the filter matches any data elements in the public key
|
||||
// scripts of any of the outputs. When it does, add the outpoint that
|
||||
// matched so transactions which spend from the matched transaction are
|
||||
// also included in the filter. This removes the burden of updating the
|
||||
// filter for this scenario from the client. It is also more efficient
|
||||
// on the network since it avoids the need for another filteradd message
|
||||
// from the client and avoids some potential races that could otherwise
|
||||
// occur.
|
||||
for i, txOut := range tx.MsgTx().TxOut {
|
||||
pushedData, err := txscript.PushedData(txOut.PkScript)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, data := range pushedData {
|
||||
if !bf.matches(data) {
|
||||
continue
|
||||
}
|
||||
|
||||
matched = true
|
||||
bf.maybeAddOutpoint(txOut.Version, txOut.PkScript,
|
||||
tx.Hash(), uint32(i), tx.Tree())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing more to do if a match has already been made.
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
// At this point, the transaction and none of the data elements in the
|
||||
// public key scripts of its outputs matched.
|
||||
|
||||
// Check if the filter matches any outpoints this transaction spends or
|
||||
// any any data elements in the signature scripts of any of the inputs.
|
||||
for _, txin := range tx.MsgTx().TxIn {
|
||||
if bf.matchesOutPoint(&txin.PreviousOutPoint) {
|
||||
return true
|
||||
}
|
||||
|
||||
pushedData, err := txscript.PushedData(txin.SignatureScript)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, data := range pushedData {
|
||||
if bf.matches(data) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MatchTxAndUpdate returns true if the bloom filter matches data within the
|
||||
// passed transaction, otherwise false is returned. If the filter does match
|
||||
// the passed transaction, it will also update the filter depending on the bloom
|
||||
// update flags set via the loaded filter if needed.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) MatchTxAndUpdate(tx *dcrutil.Tx) bool {
|
||||
bf.mtx.Lock()
|
||||
match := bf.matchTxAndUpdate(tx)
|
||||
bf.mtx.Unlock()
|
||||
return match
|
||||
}
|
||||
|
||||
// MsgFilterLoad returns the underlying wire.MsgFilterLoad for the bloom
|
||||
// filter.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bf *Filter) MsgFilterLoad() *wire.MsgFilterLoad {
|
||||
bf.mtx.Lock()
|
||||
msg := bf.msgFilterLoad
|
||||
bf.mtx.Unlock()
|
||||
return msg
|
||||
}
|
||||
@ -1,641 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2017 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/bloom"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// TestFilterLarge ensures a maximum sized filter can be created.
|
||||
func TestFilterLarge(t *testing.T) {
|
||||
f := bloom.NewFilter(100000000, 0, 0.01, wire.BloomUpdateNone)
|
||||
if len(f.MsgFilterLoad().Filter) > wire.MaxFilterLoadFilterSize {
|
||||
t.Errorf("TestFilterLarge test failed: %d > %d",
|
||||
len(f.MsgFilterLoad().Filter), wire.MaxFilterLoadFilterSize)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterLoad ensures loading and unloading of a filter pass.
|
||||
func TestFilterLoad(t *testing.T) {
|
||||
merkle := wire.MsgFilterLoad{}
|
||||
|
||||
f := bloom.LoadFilter(&merkle)
|
||||
if !f.IsLoaded() {
|
||||
t.Errorf("TestFilterLoad IsLoaded test failed: want %v got %v",
|
||||
true, !f.IsLoaded())
|
||||
return
|
||||
}
|
||||
f.Unload()
|
||||
if f.IsLoaded() {
|
||||
t.Errorf("TestFilterLoad IsLoaded test failed: want %v got %v",
|
||||
f.IsLoaded(), false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterInsert ensures inserting data into the filter causes that data
|
||||
// to be matched and the resulting serialized MsgFilterLoad is the expected
|
||||
// value.
|
||||
func TestFilterInsert(t *testing.T) {
|
||||
var tests = []struct {
|
||||
hex string
|
||||
insert bool
|
||||
}{
|
||||
{"99108ad8ed9bb6274d3980bab5a85c048f0950c8", true},
|
||||
{"19108ad8ed9bb6274d3980bab5a85c048f0950c8", false},
|
||||
{"b5a2c786d9ef4658287ced5914b37a1b4aa32eee", true},
|
||||
{"b9300670b4c5366e95b2699e8b18bc75e5f729c5", true},
|
||||
}
|
||||
|
||||
f := bloom.NewFilter(3, 0, 0.01, wire.BloomUpdateAll)
|
||||
|
||||
for i, test := range tests {
|
||||
data, err := hex.DecodeString(test.hex)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsert DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
if test.insert {
|
||||
f.Add(data)
|
||||
}
|
||||
|
||||
result := f.Matches(data)
|
||||
if test.insert != result {
|
||||
t.Errorf("TestFilterInsert Matches test #%d failure: got %v want %v\n",
|
||||
i, result, test.insert)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
want, err := hex.DecodeString("03614e9b050000000000000001")
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsert DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
got := bytes.NewBuffer(nil)
|
||||
err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsert BtcDecode failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(got.Bytes(), want) {
|
||||
t.Errorf("TestFilterInsert failure: got %v want %v\n",
|
||||
got.Bytes(), want)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterFPRange checks that new filters made with out of range
|
||||
// false positive targets result in either max or min false positive rates.
|
||||
func TestFilterFPRange(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
hash string
|
||||
want string
|
||||
filter *bloom.Filter
|
||||
}{
|
||||
{
|
||||
name: "fprates > 1 should be clipped at 1",
|
||||
hash: "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041",
|
||||
want: "00000000000000000001",
|
||||
filter: bloom.NewFilter(1, 0, 20.9999999769, wire.BloomUpdateAll),
|
||||
},
|
||||
{
|
||||
name: "fprates less than 1e-9 should be clipped at min",
|
||||
hash: "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041",
|
||||
want: "0566d97a91a91b0000000000000001",
|
||||
filter: bloom.NewFilter(1, 0, 0, wire.BloomUpdateAll),
|
||||
},
|
||||
{
|
||||
name: "negative fprates should be clipped at min",
|
||||
hash: "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041",
|
||||
want: "0566d97a91a91b0000000000000001",
|
||||
filter: bloom.NewFilter(1, 0, -1, wire.BloomUpdateAll),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
// Convert test input to appropriate types.
|
||||
hash, err := chainhash.NewHashFromStr(test.hash)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
want, err := hex.DecodeString(test.want)
|
||||
if err != nil {
|
||||
t.Errorf("DecodeString unexpected error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the test hash to the bloom filter and ensure the
|
||||
// filter serializes to the expected bytes.
|
||||
f := test.filter
|
||||
f.AddHash(hash)
|
||||
got := bytes.NewBuffer(nil)
|
||||
err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion)
|
||||
if err != nil {
|
||||
t.Errorf("BtcDecode unexpected error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(got.Bytes(), want) {
|
||||
t.Errorf("serialized filter mismatch: got %x want %x\n",
|
||||
got.Bytes(), want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterInsert ensures inserting data into the filter with a tweak causes
|
||||
// that data to be matched and the resulting serialized MsgFilterLoad is the
|
||||
// expected value.
|
||||
func TestFilterInsertWithTweak(t *testing.T) {
|
||||
var tests = []struct {
|
||||
hex string
|
||||
insert bool
|
||||
}{
|
||||
{"99108ad8ed9bb6274d3980bab5a85c048f0950c8", true},
|
||||
{"19108ad8ed9bb6274d3980bab5a85c048f0950c8", false},
|
||||
{"b5a2c786d9ef4658287ced5914b37a1b4aa32eee", true},
|
||||
{"b9300670b4c5366e95b2699e8b18bc75e5f729c5", true},
|
||||
}
|
||||
|
||||
f := bloom.NewFilter(3, 2147483649, 0.01, wire.BloomUpdateAll)
|
||||
|
||||
for i, test := range tests {
|
||||
data, err := hex.DecodeString(test.hex)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
if test.insert {
|
||||
f.Add(data)
|
||||
}
|
||||
|
||||
result := f.Matches(data)
|
||||
if test.insert != result {
|
||||
t.Errorf("TestFilterInsertWithTweak Matches test #%d failure: got %v want %v\n",
|
||||
i, result, test.insert)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
want, err := hex.DecodeString("03ce4299050000000100008001")
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
got := bytes.NewBuffer(nil)
|
||||
err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertWithTweak BtcDecode failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(got.Bytes(), want) {
|
||||
t.Errorf("TestFilterInsertWithTweak failure: got %v want %v\n",
|
||||
got.Bytes(), want)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterInsertKey ensures inserting public keys and addresses works as
|
||||
// expected.
|
||||
func TestFilterInsertKey(t *testing.T) {
|
||||
secret := "PtWU93QdrNBasyWA7GDJ3ycEN5aQRF69EynXJfmnyWDS4G7pzpEvN"
|
||||
|
||||
wif, err := dcrutil.DecodeWIF(secret)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertKey DecodeWIF failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
f := bloom.NewFilter(2, 0, 0.001, wire.BloomUpdateAll)
|
||||
f.Add(wif.SerializePubKey())
|
||||
f.Add(dcrutil.Hash160(wif.SerializePubKey()))
|
||||
|
||||
want, err := hex.DecodeString("03323f6e080000000000000001")
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
got := bytes.NewBuffer(nil)
|
||||
err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertWithTweak BtcDecode failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(got.Bytes(), want) {
|
||||
t.Errorf("TestFilterInsertWithTweak failure: got %v want %v\n",
|
||||
got.Bytes(), want)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterBloomMatch(t *testing.T) {
|
||||
// tx 2 from blk 10000
|
||||
str := "0100000001a4fbbbca2416ba4c10c94be9f4a650d37fc4f9a1a4ecded9cc2" +
|
||||
"714aa0a529a750000000000ffffffff02c2d0b32f0000000000001976a91" +
|
||||
"499678d10a90c8df40e4c9af742aa6ebc7764a60e88acbe01611c0000000" +
|
||||
"000001976a9147701528df10cf0c14f9e53925031bd398796c1f988ac000" +
|
||||
"000000000000001e0b52b4c0000000003270000020000006b48304502210" +
|
||||
"08003ce072e4b67f9a98129ac2f58e3de6e06f47a15e248d4375d19dfb52" +
|
||||
"7a02d02204ab0a0dfe7c69024ae8e524e01d1c45183efda945a0d411e4e9" +
|
||||
"4b69be21efbe601210270c906c3ba64ba5eb3943cc012a3b142ef169f066" +
|
||||
"002515bf9ec1bd9b7e27f0d"
|
||||
strBytes, err := hex.DecodeString(str)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failure: %v", err)
|
||||
return
|
||||
}
|
||||
tx, err := dcrutil.NewTxFromBytes(strBytes)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewTxFromBytes failure: %v", err)
|
||||
return
|
||||
}
|
||||
spendingStr := "01000000018c5e1f62f83d750a0ee228c228731eae241e6b483e5b63be199" +
|
||||
"12846eb2d11500000000000ffffffff02ed44871d0000000000001976a91" +
|
||||
"461788151a27fad1a9c609fa29a2bd43886e2dd4088ac75a815120000000" +
|
||||
"000001976a91483419547ee3db5c0ee29f347740ff7f448e8ab2c88ac000" +
|
||||
"000000000000001c2d0b32f0000000010270000010000006b48304502210" +
|
||||
"0aca38b780893b6be3287efa908ace8bb8b91af0477ab433f101889b86bb" +
|
||||
"d9c2d0220789a177956f91c75141ea527573294a20f6fc0ea8bd5cc33550" +
|
||||
"4a0654ae197e30121025516815b900e10e51824ea1f451fd197fb11209af" +
|
||||
"60c5c52f9a8cf3edad5dc09"
|
||||
spendingTxBytes, err := hex.DecodeString(spendingStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failure: %v", err)
|
||||
return
|
||||
}
|
||||
spendingTx, err := dcrutil.NewTxFromBytes(spendingTxBytes)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewTxFromBytes failure: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr := "50112deb46289119be635b3e486b1e24ae1e7328c228e20e0a753df8621f5e8c"
|
||||
hash, err := chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.AddHash(hash)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match hash %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "8c5e1f62f83d750a0ee228c228731eae241e6b483e5b63be19912846eb2d1150"
|
||||
hashBytes, err := hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.Add(hashBytes)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match hash %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "30450221008003ce072e4b67f9a98129ac2f58e3de6e06f47a15e248d43" +
|
||||
"75d19dfb527a02d02204ab0a0dfe7c69024ae8e524e01d1c45183efda945a0" +
|
||||
"d411e4e94b69be21efbe601"
|
||||
hashBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.Add(hashBytes)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match input signature %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "0270c906c3ba64ba5eb3943cc012a3b142ef169f066002515bf9ec1bd9b7e27f0d"
|
||||
hashBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.Add(hashBytes)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match input pubkey %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "99678d10a90c8df40e4c9af742aa6ebc7764a60e"
|
||||
hashBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
f.Add(hashBytes)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match output address %s", inputStr)
|
||||
}
|
||||
if !f.MatchTxAndUpdate(spendingTx) {
|
||||
t.Errorf("TestFilterBloomMatch spendingTx didn't match output address %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "7701528df10cf0c14f9e53925031bd398796c1f9"
|
||||
hashBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.Add(hashBytes)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match output address %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "759a520aaa1427ccd9deeca4a1f9c47fd350a6f4e94bc9104cba1624cabbfba4"
|
||||
hash, err = chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
outpoint := wire.NewOutPoint(hash, 0, wire.TxTreeRegular)
|
||||
f.AddOutPoint(outpoint)
|
||||
if !f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch didn't match outpoint %s", inputStr)
|
||||
}
|
||||
// XXX unchanged from btcd
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"
|
||||
hash, err = chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.AddHash(hash)
|
||||
if f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch matched hash %s", inputStr)
|
||||
}
|
||||
|
||||
// XXX unchanged from btcd
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "0000006d2965547608b9e15d9032a7b9d64fa431"
|
||||
hashBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
f.Add(hashBytes)
|
||||
if f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch matched address %s", inputStr)
|
||||
}
|
||||
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "759a520aaa1427ccd9deeca4a1f9c47fd350a6f4e94bc9104cba1624cabbfba4"
|
||||
hash, err = chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
outpoint = wire.NewOutPoint(hash, 1, wire.TxTreeRegular)
|
||||
f.AddOutPoint(outpoint)
|
||||
if f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch matched outpoint %s", inputStr)
|
||||
}
|
||||
|
||||
// XXX unchanged from btcd
|
||||
f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
inputStr = "000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"
|
||||
hash, err = chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
outpoint = wire.NewOutPoint(hash, 0, wire.TxTreeRegular)
|
||||
f.AddOutPoint(outpoint)
|
||||
if f.MatchTxAndUpdate(tx) {
|
||||
t.Errorf("TestFilterBloomMatch matched outpoint %s", inputStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterInsertUpdateNone(t *testing.T) {
|
||||
f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateNone)
|
||||
|
||||
// Add the generation pubkey
|
||||
inputStr := "0270c906c3ba64ba5eb3943cc012a3b142ef169f066002515bf9ec1bd9b7e27f0d"
|
||||
inputBytes, err := hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertUpdateNone DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
f.Add(inputBytes)
|
||||
|
||||
// Add the output address for the 4th transaction
|
||||
inputStr = "b6efd80d99179f4f4ff6f4dd0a007d018c385d21"
|
||||
inputBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertUpdateNone DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
f.Add(inputBytes)
|
||||
|
||||
inputStr = "147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"
|
||||
hash, err := chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertUpdateNone NewHashFromStr failed: %v", err)
|
||||
return
|
||||
}
|
||||
outpoint := wire.NewOutPoint(hash, 0, wire.TxTreeRegular)
|
||||
|
||||
if f.MatchesOutPoint(outpoint) {
|
||||
t.Errorf("TestFilterInsertUpdateNone matched outpoint %s", inputStr)
|
||||
return
|
||||
}
|
||||
|
||||
inputStr = "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"
|
||||
hash, err = chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertUpdateNone NewHashFromStr failed: %v", err)
|
||||
return
|
||||
}
|
||||
outpoint = wire.NewOutPoint(hash, 0, wire.TxTreeRegular)
|
||||
|
||||
if f.MatchesOutPoint(outpoint) {
|
||||
t.Errorf("TestFilterInsertUpdateNone matched outpoint %s", inputStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterInsertP2PubKeyOnly(t *testing.T) {
|
||||
blockStr := "000000003ffad804971ce6b8a13c8162287222d91395fa77b6bea6b4" +
|
||||
"626b4827000000004259bd8a4d5d5f8469f390903d27b9cab2ea03822fbe" +
|
||||
"616478756ada751a283be8e4aa58eeb75810e22031cc2756442b7f68c77b" +
|
||||
"3735522fc5490be1676253c301005f031703bb61050000005c1300006fb2" +
|
||||
"381c30dd35680000000091940000fc0900000cadf15600d9781d3a5f237b" +
|
||||
"083799410f00000000000000000000000000000000000000000000000000" +
|
||||
"000002010000000100000000000000000000000000000000000000000000" +
|
||||
"00000000000000000000ffffffff00ffffffff032727750c000000000000" +
|
||||
"17a9144fa6cbd0dbe5ec407fe4c8ad374e667771fa0d4487000000000000" +
|
||||
"00000000266a249194000000000000000000000000000000000000000000" +
|
||||
"0000000000b90c53023ee736e6d7eebe4a0000000000001976a914bfe0f6" +
|
||||
"3eda5d7db3ee05661051c026f5296ffc7888ac0000000000000000011512" +
|
||||
"34570000000000000000ffffffff0800002f646372642f01000000020c57" +
|
||||
"441e66eaa72bc76ab54faaa0ec87941f4423a463c5e34d7f23f247d65e9d" +
|
||||
"0200000001ffffffff31b7ffaec935e787dc03670d4d13ee0066717cb750" +
|
||||
"2cb4d925b09b7f692a73580400000001ffffffff0200ca9a3b0000000000" +
|
||||
"0023210351b64c01163b9184637671e27d6d29b3a205203710f6fbc5a6e1" +
|
||||
"b646a11984d6ac81001f060000000000001976a9144feba3e04d91d9dc90" +
|
||||
"ebb13ce91bf9bcfe32ff9288ac000000000000000002c29f493300000000" +
|
||||
"8f9400000a0000006a4730440220186741de60d6fe75d2206b62780edabe" +
|
||||
"8a0a13f823aaeca58f0d5a8dedfa99f202201dc84a1bd26d14723ddf2fe4" +
|
||||
"216d4a24d788597fa22255d144ce119d7544ce39012103879a3b3666bf03" +
|
||||
"88ab33e6e24e77c3c219044f7c5d778b71ac1837817095830da72e700e00" +
|
||||
"000000909400000e0000006b4830450221008cc441f58be0eedc713b867d" +
|
||||
"c8dfff21e2f6c2e38a906a0af5aa0625b2683f6b0220308bcaf6628b3c68" +
|
||||
"19da76ebb2ca5e5d1ddde6e5035193d75e3e667731deadc4012102a871fe" +
|
||||
"4e4f77121f1cbaf2db62190539687092b465e252a3bd732791ca442d5405" +
|
||||
"010000000200000000000000000000000000000000000000000000000000" +
|
||||
"00000000000000ffffffff00ffffffff91ed802c4a23cca0517dbc2d89f7" +
|
||||
"b6f8a2d1491a27ed25e3d3f621bac03316da0000000001ffffffff030000" +
|
||||
"0000000000000000266a243ffad804971ce6b8a13c8162287222d91395fa" +
|
||||
"77b6bea6b4626b4827000000009094000000000000000000000000046a02" +
|
||||
"010003d0db190100000000001abb76a914339f1f6a41d7ef65ffd80bbb8c" +
|
||||
"daaa215a16472d88ac000000000000000002e47d79070000000000000000" +
|
||||
"ffffffff04deadbeef1f52621201000000938b0000090000006a47304402" +
|
||||
"207184b4d7a95a559b1142f50cfde3e40bf460572aea4d5eeabad062e45c" +
|
||||
"9a8f5102202304acbcc9cc55e58770e51d4fc9ac81af9c790a65d251ea4d" +
|
||||
"556bc7ee96fb47012103c46bdbf6b24be97eac230ca9b23e928e2e76750d" +
|
||||
"3d3c8b29e15cebb64ba01f86010000000200000000000000000000000000" +
|
||||
"00000000000000000000000000000000000000ffffffff00ffffffff17be" +
|
||||
"9d003549030c20c018a71745af6f6a02d96637f49f5a548ee7fe0ac84420" +
|
||||
"0000000001ffffffff0300000000000000000000266a243ffad804971ce6" +
|
||||
"b8a13c8162287222d91395fa77b6bea6b4626b4827000000009094000000" +
|
||||
"000000000000000000046a02010071c339cf0000000000001abb76a9147f" +
|
||||
"1aaac9f04febbf9dadd262b1c710729970445988ac000000000000000002" +
|
||||
"e47d79070000000000000000ffffffff04deadbeef8d45c0c70000000099" +
|
||||
"8f0000070000006a473044022054a09af013f1a74960686bf5c36f3eb822" +
|
||||
"5ffc96e76a54dc1aac9fdea65cff2b022010359dfcd16ac77f3a6ab47309" +
|
||||
"e106a885fa2d031d5bac0824f4432a77ccdae001210389e445603d66ce44" +
|
||||
"92ec74d9b1974268a2e83413c84df87895184bc6f46ae1d7010000000200" +
|
||||
"000000000000000000000000000000000000000000000000000000000000" +
|
||||
"00ffffffff00ffffffff49a883465f82c0483b3ff7da057cab44a42ecbc8" +
|
||||
"27c344a960a2a33c95263f760000000001ffffffff030000000000000000" +
|
||||
"0000266a243ffad804971ce6b8a13c8162287222d91395fa77b6bea6b462" +
|
||||
"6b4827000000009094000000000000000000000000046a0201002c9d63bf" +
|
||||
"0000000000001abb76a91449d402e31fc08414f565febaa1b69eccc2fb8d" +
|
||||
"2688ac000000000000000002e47d79070000000000000000ffffffff04de" +
|
||||
"adbeef481feab70000000064870000120000006a473044022008daa49081" +
|
||||
"d7dc6c466a4d3fd4184927e8b77fd1f5721f206bf0d13a2c00b07802202d" +
|
||||
"89841ad5346de14db95c3e5b15a0d82e0df1f50a235f67aeec08b98f4b36" +
|
||||
"7a012102907261a4670a9d0ff918724af50f2ecb7947831dad30ac285afa" +
|
||||
"1b90549aa282010000000200000000000000000000000000000000000000" +
|
||||
"00000000000000000000000000ffffffff00ffffffffb6df4ea74c958f39" +
|
||||
"65ecc90b9226cfdc2d7986b1df30998cf3e827d7e9fbf4b80000000001ff" +
|
||||
"ffffff0300000000000000000000266a243ffad804971ce6b8a13c816228" +
|
||||
"7222d91395fa77b6bea6b4626b4827000000009094000000000000000000" +
|
||||
"000000046a020100f3a613b90000000000001abb76a9147f9321c65ee805" +
|
||||
"d87f67b0b0e9dac4f0779c1d3a88ac000000000000000002e47d79070000" +
|
||||
"000000000000ffffffff04deadbeef0f299ab100000000408d0000130000" +
|
||||
"006b4830450221008c5759dd9f0c40c61f61f5b67c0b3f860d4d59859fb2" +
|
||||
"451dbeab9956b3ceb05602203e81d5b88b83e279dc7feca13c8e5eb58676" +
|
||||
"158243058ec43d6366933397be4a0121031e6757ee86e94a02e567db7d4e" +
|
||||
"c4ba5088f6b5fdbfe9b2794b70b42b52b879200100000002000000000000" +
|
||||
"0000000000000000000000000000000000000000000000000000ffffffff" +
|
||||
"00ffffffff9e74ead771c313f0c0bb739860eba39d04a8cce201cda626dc" +
|
||||
"6a7dd4e83b3f770000000001ffffffff0400000000000000000000266a24" +
|
||||
"3ffad804971ce6b8a13c8162287222d91395fa77b6bea6b4626b48270000" +
|
||||
"00009094000000000000000000000000046a0201000793a0e00000000000" +
|
||||
"001abb76a914c8c5a2cbd183871718dcb14b5f198561450d157e88ac596e" +
|
||||
"8f250000000000001abb76a9147112f7d015baeb129e732b9c4805e492ef" +
|
||||
"d0a2fb88ac000000000000000002e47d79070000000000000000ffffffff" +
|
||||
"04deadbeef7d83b6fe0000000022920000070000006a4730440220160ff7" +
|
||||
"bd84190d8d837ac6bd782be475d23d50832872626453bd70ea63d9a6a002" +
|
||||
"20603afdd51fc8e3fead1fd9dbae410b8d0617969ad5119e6dc2b0855d7b" +
|
||||
"48e73501210297ab850cae270e9438693353e40444c7e714e179505e8b12" +
|
||||
"1c042f4869e42072"
|
||||
blockBytes, err := hex.DecodeString(blockStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertP2PubKeyOnly DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
block, err := dcrutil.NewBlockFromBytes(blockBytes)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertP2PubKeyOnly NewBlockFromBytes failed: %v", err)
|
||||
return
|
||||
}
|
||||
f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateP2PubkeyOnly)
|
||||
|
||||
// Generation pubkey
|
||||
inputStr := "0351b64c01163b9184637671e27d6d29b3a205203710f6fbc5a6e1b646a11984d6"
|
||||
inputBytes, err := hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertP2PubKeyOnly DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
f.Add(inputBytes)
|
||||
|
||||
// Output address of 2nd transaction
|
||||
inputStr = "4feba3e04d91d9dc90ebb13ce91bf9bcfe32ff92"
|
||||
inputBytes, err = hex.DecodeString(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestFilterInsertP2PubKeyOnly DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
f.Add(inputBytes)
|
||||
|
||||
// Ignore return value -- this is just used to update the filter.
|
||||
_, _ = bloom.NewMerkleBlock(block, f)
|
||||
|
||||
// We should match the generation pubkey
|
||||
inputStr = "8199f30ccc006056ed79cf0a3cd0b67a195ffd46903d42adc7babe2ed2f2e371"
|
||||
hash, err := chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlockP2PubKeyOnly NewHashFromStr failed: %v", err)
|
||||
return
|
||||
}
|
||||
outpoint := wire.NewOutPoint(hash, 0, wire.TxTreeRegular)
|
||||
if !f.MatchesOutPoint(outpoint) {
|
||||
t.Errorf("TestMerkleBlockP2PubKeyOnly didn't match the generation "+
|
||||
"outpoint %s", inputStr)
|
||||
return
|
||||
}
|
||||
|
||||
// We should not match the 4th transaction, which is not p2pk
|
||||
inputStr = "d7314aaf54253c651e8258c7d22c574af8804611f0dcea79fd9a47f4565d85ad"
|
||||
hash, err = chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlockP2PubKeyOnly NewHashFromStr failed: %v", err)
|
||||
return
|
||||
}
|
||||
outpoint = wire.NewOutPoint(hash, 0, wire.TxTreeRegular)
|
||||
if f.MatchesOutPoint(outpoint) {
|
||||
t.Errorf("TestMerkleBlockP2PubKeyOnly matched outpoint %s", inputStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterReload(t *testing.T) {
|
||||
f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
|
||||
bFilter := bloom.LoadFilter(f.MsgFilterLoad())
|
||||
if bFilter.MsgFilterLoad() == nil {
|
||||
t.Errorf("TestFilterReload LoadFilter test failed")
|
||||
return
|
||||
}
|
||||
bFilter.Reload(nil)
|
||||
|
||||
if bFilter.MsgFilterLoad() != nil {
|
||||
t.Errorf("TestFilterReload Reload test failed")
|
||||
}
|
||||
}
|
||||
@ -1,126 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// merkleBlock is used to house intermediate information needed to generate a
|
||||
// wire.MsgMerkleBlock according to a filter.
|
||||
type merkleBlock struct {
|
||||
numTx uint32
|
||||
allHashes []*chainhash.Hash
|
||||
finalHashes []*chainhash.Hash
|
||||
matchedBits []byte
|
||||
bits []byte
|
||||
}
|
||||
|
||||
// calcTreeWidth calculates and returns the the number of nodes (width) or a
|
||||
// merkle tree at the given depth-first height.
|
||||
func (m *merkleBlock) calcTreeWidth(height uint32) uint32 {
|
||||
return (m.numTx + (1 << height) - 1) >> height
|
||||
}
|
||||
|
||||
// calcHash returns the hash for a sub-tree given a depth-first height and
|
||||
// node position.
|
||||
func (m *merkleBlock) calcHash(height, pos uint32) *chainhash.Hash {
|
||||
if height == 0 {
|
||||
return m.allHashes[pos]
|
||||
}
|
||||
|
||||
var right *chainhash.Hash
|
||||
left := m.calcHash(height-1, pos*2)
|
||||
if pos*2+1 < m.calcTreeWidth(height-1) {
|
||||
right = m.calcHash(height-1, pos*2+1)
|
||||
} else {
|
||||
right = left
|
||||
}
|
||||
return blockchain.HashMerkleBranches(left, right)
|
||||
}
|
||||
|
||||
// traverseAndBuild builds a partial merkle tree using a recursive depth-first
|
||||
// approach. As it calculates the hashes, it also saves whether or not each
|
||||
// node is a parent node and a list of final hashes to be included in the
|
||||
// merkle block.
|
||||
func (m *merkleBlock) traverseAndBuild(height, pos uint32) {
|
||||
// Determine whether this node is a parent of a matched node.
|
||||
var isParent byte
|
||||
for i := pos << height; i < (pos+1)<<height && i < m.numTx; i++ {
|
||||
isParent |= m.matchedBits[i]
|
||||
}
|
||||
m.bits = append(m.bits, isParent)
|
||||
|
||||
// When the node is a leaf node or not a parent of a matched node,
|
||||
// append the hash to the list that will be part of the final merkle
|
||||
// block.
|
||||
if height == 0 || isParent == 0x00 {
|
||||
m.finalHashes = append(m.finalHashes, m.calcHash(height, pos))
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, the node is an internal node and it is the parent of
|
||||
// of an included leaf node.
|
||||
|
||||
// Descend into the left child and process its sub-tree.
|
||||
m.traverseAndBuild(height-1, pos*2)
|
||||
|
||||
// Descend into the right child and process its sub-tree if
|
||||
// there is one.
|
||||
if pos*2+1 < m.calcTreeWidth(height-1) {
|
||||
m.traverseAndBuild(height-1, pos*2+1)
|
||||
}
|
||||
}
|
||||
|
||||
// NewMerkleBlock returns a new *wire.MsgMerkleBlock and an array of the matched
|
||||
// transaction index numbers based on the passed block and filter.
|
||||
func NewMerkleBlock(block *dcrutil.Block, filter *Filter) (*wire.MsgMerkleBlock, []uint32) {
|
||||
numTx := uint32(len(block.Transactions()))
|
||||
mBlock := merkleBlock{
|
||||
numTx: numTx,
|
||||
allHashes: make([]*chainhash.Hash, 0, numTx),
|
||||
matchedBits: make([]byte, 0, numTx),
|
||||
}
|
||||
|
||||
// Find and keep track of any transactions that match the filter.
|
||||
var matchedIndices []uint32
|
||||
for txIndex, tx := range block.Transactions() {
|
||||
if filter.MatchTxAndUpdate(tx) {
|
||||
mBlock.matchedBits = append(mBlock.matchedBits, 0x01)
|
||||
matchedIndices = append(matchedIndices, uint32(txIndex))
|
||||
} else {
|
||||
mBlock.matchedBits = append(mBlock.matchedBits, 0x00)
|
||||
}
|
||||
mBlock.allHashes = append(mBlock.allHashes, tx.Hash())
|
||||
}
|
||||
|
||||
// Calculate the number of merkle branches (height) in the tree.
|
||||
height := uint32(0)
|
||||
for mBlock.calcTreeWidth(height) > 1 {
|
||||
height++
|
||||
}
|
||||
|
||||
// Build the depth-first partial merkle tree.
|
||||
mBlock.traverseAndBuild(height, 0)
|
||||
|
||||
// Create and return the merkle block.
|
||||
msgMerkleBlock := wire.MsgMerkleBlock{
|
||||
Header: block.MsgBlock().Header,
|
||||
Transactions: mBlock.numTx,
|
||||
Hashes: make([]*chainhash.Hash, 0, len(mBlock.finalHashes)),
|
||||
Flags: make([]byte, (len(mBlock.bits)+7)/8),
|
||||
}
|
||||
for _, hash := range mBlock.finalHashes {
|
||||
msgMerkleBlock.AddTxHash(hash)
|
||||
}
|
||||
for i := uint32(0); i < uint32(len(mBlock.bits)); i++ {
|
||||
msgMerkleBlock.Flags[i/8] |= mBlock.bits[i] << (i % 8)
|
||||
}
|
||||
return &msgMerkleBlock, matchedIndices
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2017 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/bloom"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
func TestMerkleBlock3(t *testing.T) {
|
||||
blockStr := "0100000073cf056852529ffadc50b49589218795adc4d3f24170950d49f201000000000033fd46dda0acfa5c0651c58bee00362b04186c5b4d1045d37751b25779148649000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000ffff011b00c2eb0b00000000000100007e0100006614b956bee4fc44442bf144050552b3010000000000000000000000000000000000000000000000000000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff03fa1a981200000000000017a914f5916158e3e2c4551c1796708db8367207ed13bb8700000000000000000000266a24000100000000000000000000000000000000000000000000000000004dfb774daa8f3a76dea1906f0000000000001976a914b74b7476bdbcf03d18f12cca1766b0ddfd030cdd88ac000000000000000001d8bc28820000000000000000ffffffff0800002f646372642f00"
|
||||
|
||||
blockBytes, err := hex.DecodeString(blockStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlock3 DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
blk, err := dcrutil.NewBlockFromBytes(blockBytes)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlock3 NewBlockFromBytes failed: %v", err)
|
||||
return
|
||||
}
|
||||
f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll)
|
||||
|
||||
inputStr := "4986147957b25177d345104d5b6c18042b3600ee8bc551065cfaaca0dd46fd33"
|
||||
hash, err := chainhash.NewHashFromStr(inputStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlock3 NewHashFromStr failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
f.AddHash(hash)
|
||||
|
||||
mBlock, _ := bloom.NewMerkleBlock(blk, f)
|
||||
wantStr := "0100000073cf056852529ffadc50b49589218795adc4d3f24170950d49f201000000000033fd46dda0acfa5c0651c58bee00362b04186c5b4d1045d37751b25779148649000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000ffff011b00c2eb0b00000000000100007e0100006614b956bee4fc44442bf144050552b3010000000000000000000000000000000000000000000000000000000100000001d0f51e5a4978d736eb3d4a8d615bee74943756b4745d29b27ab2943bc1307cc800000000000100"
|
||||
want, err := hex.DecodeString(wantStr)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlock3 DecodeString failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
got := bytes.NewBuffer(nil)
|
||||
err = mBlock.BtcEncode(got, wire.ProtocolVersion)
|
||||
if err != nil {
|
||||
t.Errorf("TestMerkleBlock3 BtcEncode failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(want, got.Bytes()) {
|
||||
t.Errorf("TestMerkleBlock3 failed merkle block comparison: "+
|
||||
"got %v want %v", got.Bytes(), want)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// The following constants are used by the MurmurHash3 algorithm.
|
||||
const (
|
||||
murmurC1 = 0xcc9e2d51
|
||||
murmurC2 = 0x1b873593
|
||||
murmurR1 = 15
|
||||
murmurR2 = 13
|
||||
murmurM = 5
|
||||
murmurN = 0xe6546b64
|
||||
)
|
||||
|
||||
// MurmurHash3 implements a non-cryptographic hash function using the
|
||||
// MurmurHash3 algorithm. This implementation yields a 32-bit hash value which
|
||||
// is suitable for general hash-based lookups. The seed can be used to
|
||||
// effectively randomize the hash function. This makes it ideal for use in
|
||||
// bloom filters which need multiple independent hash functions.
|
||||
func MurmurHash3(seed uint32, data []byte) uint32 {
|
||||
var k uint32
|
||||
dataLen := uint32(len(data))
|
||||
hash := seed
|
||||
numBlocks := dataLen / 4
|
||||
|
||||
// Calculate the hash in 4-byte chunks.
|
||||
for i := uint32(0); i < numBlocks; i++ {
|
||||
k = binary.LittleEndian.Uint32(data[i*4:])
|
||||
k *= murmurC1
|
||||
k = (k << murmurR1) | (k >> (32 - murmurR1))
|
||||
k *= murmurC2
|
||||
|
||||
hash ^= k
|
||||
hash = (hash << murmurR2) | (hash >> (32 - murmurR2))
|
||||
hash = hash*murmurM + murmurN
|
||||
}
|
||||
|
||||
// Handle remaining bytes.
|
||||
tailIdx := numBlocks * 4
|
||||
k = 0
|
||||
|
||||
switch dataLen & 3 {
|
||||
case 3:
|
||||
k ^= uint32(data[tailIdx+2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
k ^= uint32(data[tailIdx+1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
k ^= uint32(data[tailIdx])
|
||||
k *= murmurC1
|
||||
k = (k << murmurR1) | (k >> (32 - murmurR1))
|
||||
k *= murmurC2
|
||||
hash ^= k
|
||||
}
|
||||
|
||||
// Finalization.
|
||||
hash ^= dataLen
|
||||
hash ^= hash >> 16
|
||||
hash *= 0x85ebca6b
|
||||
hash ^= hash >> 13
|
||||
hash *= 0xc2b2ae35
|
||||
hash ^= hash >> 16
|
||||
|
||||
return hash
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2017 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bloom_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/bloom"
|
||||
)
|
||||
|
||||
// TestMurmurHash3 ensure the MurmurHash3 function produces the correct hash
|
||||
// when given various seeds and data.
|
||||
func TestMurmurHash3(t *testing.T) {
|
||||
var tests = []struct {
|
||||
seed uint32
|
||||
data []byte
|
||||
out uint32
|
||||
}{
|
||||
{0x00000000, []byte{}, 0x00000000},
|
||||
{0xfba4c795, []byte{}, 0x6a396f08},
|
||||
{0xffffffff, []byte{}, 0x81f16f39},
|
||||
{0x00000000, []byte{0x00}, 0x514e28b7},
|
||||
{0xfba4c795, []byte{0x00}, 0xea3f0b17},
|
||||
{0x00000000, []byte{0xff}, 0xfd6cf10d},
|
||||
{0x00000000, []byte{0x00, 0x11}, 0x16c6b7ab},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22}, 0x8eb51c3d},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22, 0x33}, 0xb4471bf8},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44}, 0xe2301fa8},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, 0xfc2e4a15},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}, 0xb074502c},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}, 0x8034d2a0},
|
||||
{0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, 0xb4698def},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
result := bloom.MurmurHash3(test.seed, test.data)
|
||||
if result != test.out {
|
||||
t.Errorf("MurmurHash3 test #%d failed: got %v want %v\n",
|
||||
i, result, test.out)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,7 +146,6 @@ type config struct {
|
||||
BlockMaxSize uint32 `long:"blockmaxsize" description:"Maximum block size in bytes to be used when creating a block"`
|
||||
BlockPrioritySize uint32 `long:"blockprioritysize" description:"Size in bytes for high-priority/low-fee transactions when creating a block"`
|
||||
GetWorkKeys []string `long:"getworkkey" description:"DEPRECATED -- Use the --miningaddr option instead"`
|
||||
NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"`
|
||||
SigCacheMaxSize uint `long:"sigcachemaxsize" description:"The maximum number of entries in the signature verification cache"`
|
||||
NonAggressive bool `long:"nonaggressive" description:"Disable mining off of the parent block of the blockchain if there aren't enough voters"`
|
||||
NoMiningStateSync bool `long:"nominingstatesync" description:"Disable synchronizing the mining state with other nodes"`
|
||||
|
||||
1
doc.go
1
doc.go
@ -117,7 +117,6 @@ Application Options:
|
||||
--nominingstatesync Disable synchronizing the mining state with other nodes
|
||||
--allowoldvotes Enable the addition of very old votes to the mempool
|
||||
|
||||
--nopeerbloomfilters Disable bloom filtering support.
|
||||
--sigcachemaxsize= The maximum number of entries in the signature
|
||||
verification cache.
|
||||
--blocksonly Do not accept transactions from remote peers.
|
||||
|
||||
@ -34,17 +34,13 @@ A quick overview of the major features peer provides are as follows:
|
||||
they see fit (proxies, etc)
|
||||
- User agent name and version
|
||||
- Bitcoin network
|
||||
- Service support signalling (full nodes, bloom filters, etc)
|
||||
- Service support signalling (full nodes, etc)
|
||||
- Maximum supported protocol version
|
||||
- Ability to register callbacks for handling bitcoin protocol messages
|
||||
- Inventory message batching and send trickling with known inventory detection
|
||||
and avoidance
|
||||
- Automatic periodic keep-alive pinging and pong responses
|
||||
- Random nonce generation and self connection detection
|
||||
- Proper handling of bloom filter related commands when the caller does not
|
||||
specify the related flag to signal support
|
||||
- Disconnects the peer when the protocol version is high enough
|
||||
- Does not invoke the related callbacks for older protocol versions
|
||||
- Snapshottable peer statistics such as the total number of bytes read and
|
||||
written, the remote address, user agent, and negotiated protocol version
|
||||
- Helper functions pushing addresses, getblocks, getheaders, and reject
|
||||
|
||||
@ -30,17 +30,13 @@ A quick overview of the major features peer provides are as follows:
|
||||
they see fit (proxies, etc)
|
||||
- User agent name and version
|
||||
- Decred network
|
||||
- Service support signalling (full nodes, bloom filters, etc)
|
||||
- Service support signalling (full nodes, etc)
|
||||
- Maximum supported protocol version
|
||||
- Ability to register callbacks for handling Decred protocol messages
|
||||
- Inventory message batching and send trickling with known inventory detection
|
||||
and avoidance
|
||||
- Automatic periodic keep-alive pinging and pong responses
|
||||
- Random nonce generation and self connection detection
|
||||
- Proper handling of bloom filter related commands when the caller does not
|
||||
specify the related flag to signal support
|
||||
- Disconnects the peer when the protocol version is high enough
|
||||
- Does not invoke the related callbacks for older protocol versions
|
||||
- Snapshottable peer statistics such as the total number of bytes read and
|
||||
written, the remote address, user agent, and negotiated protocol version
|
||||
- Helper functions pushing addresses, getblocks, getheaders, and reject
|
||||
|
||||
35
peer/peer.go
35
peer/peer.go
@ -169,21 +169,6 @@ type MessageListeners struct {
|
||||
// OnFeeFilter is invoked when a peer receives a feefilter wire message.
|
||||
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
|
||||
|
||||
// OnFilterAdd is invoked when a peer receives a filteradd wire message.
|
||||
OnFilterAdd func(p *Peer, msg *wire.MsgFilterAdd)
|
||||
|
||||
// OnFilterClear is invoked when a peer receives a filterclear wire
|
||||
// message.
|
||||
OnFilterClear func(p *Peer, msg *wire.MsgFilterClear)
|
||||
|
||||
// OnFilterLoad is invoked when a peer receives a filterload wire
|
||||
// message.
|
||||
OnFilterLoad func(p *Peer, msg *wire.MsgFilterLoad)
|
||||
|
||||
// OnMerkleBlock is invoked when a peer receives a merkleblock wire
|
||||
// message.
|
||||
OnMerkleBlock func(p *Peer, msg *wire.MsgMerkleBlock)
|
||||
|
||||
// OnVersion is invoked when a peer receives a version wire message.
|
||||
OnVersion func(p *Peer, msg *wire.MsgVersion)
|
||||
|
||||
@ -1569,26 +1554,6 @@ out:
|
||||
p.cfg.Listeners.OnFeeFilter(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgFilterAdd:
|
||||
if p.cfg.Listeners.OnFilterAdd != nil {
|
||||
p.cfg.Listeners.OnFilterAdd(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgFilterClear:
|
||||
if p.cfg.Listeners.OnFilterClear != nil {
|
||||
p.cfg.Listeners.OnFilterClear(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgFilterLoad:
|
||||
if p.cfg.Listeners.OnFilterLoad != nil {
|
||||
p.cfg.Listeners.OnFilterLoad(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgMerkleBlock:
|
||||
if p.cfg.Listeners.OnMerkleBlock != nil {
|
||||
p.cfg.Listeners.OnMerkleBlock(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgReject:
|
||||
if p.cfg.Listeners.OnReject != nil {
|
||||
p.cfg.Listeners.OnReject(p, msg)
|
||||
|
||||
@ -386,18 +386,6 @@ func TestPeerListeners(t *testing.T) {
|
||||
OnFeeFilter: func(p *peer.Peer, msg *wire.MsgFeeFilter) {
|
||||
ok <- msg
|
||||
},
|
||||
OnFilterAdd: func(p *peer.Peer, msg *wire.MsgFilterAdd) {
|
||||
ok <- msg
|
||||
},
|
||||
OnFilterClear: func(p *peer.Peer, msg *wire.MsgFilterClear) {
|
||||
ok <- msg
|
||||
},
|
||||
OnFilterLoad: func(p *peer.Peer, msg *wire.MsgFilterLoad) {
|
||||
ok <- msg
|
||||
},
|
||||
OnMerkleBlock: func(p *peer.Peer, msg *wire.MsgMerkleBlock) {
|
||||
ok <- msg
|
||||
},
|
||||
OnVersion: func(p *peer.Peer, msg *wire.MsgVersion) {
|
||||
ok <- msg
|
||||
},
|
||||
@ -534,26 +522,6 @@ func TestPeerListeners(t *testing.T) {
|
||||
"OnFeeFilter",
|
||||
wire.NewMsgFeeFilter(15000),
|
||||
},
|
||||
{
|
||||
"OnFilterAdd",
|
||||
wire.NewMsgFilterAdd([]byte{0x01}),
|
||||
},
|
||||
{
|
||||
"OnFilterClear",
|
||||
wire.NewMsgFilterClear(),
|
||||
},
|
||||
{
|
||||
"OnFilterLoad",
|
||||
wire.NewMsgFilterLoad([]byte{0x01}, 10, 0, wire.BloomUpdateNone),
|
||||
},
|
||||
{
|
||||
"OnMerkleBlock",
|
||||
wire.NewMsgMerkleBlock(wire.NewBlockHeader(0,
|
||||
&chainhash.Hash{}, &chainhash.Hash{},
|
||||
&chainhash.Hash{}, 1, [6]byte{},
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, [32]byte{},
|
||||
binary.LittleEndian.Uint32([]byte{0xb0, 0x1d, 0xfa, 0xce}))),
|
||||
},
|
||||
// only one version message is allowed
|
||||
// only one verack message is allowed
|
||||
{
|
||||
|
||||
@ -169,9 +169,6 @@ const FileContents = `[Application Options]
|
||||
; Disable listening for incoming connections. This will override all listeners.
|
||||
; nolisten=1
|
||||
|
||||
; Disable peer bloom filtering.
|
||||
; nopeerbloomfilters=1
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
; RPC server options - The following options control the built-in RPC server
|
||||
|
||||
196
server.go
196
server.go
@ -22,7 +22,6 @@ import (
|
||||
"github.com/decred/dcrd/addrmgr"
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/blockchain/indexers"
|
||||
"github.com/decred/dcrd/bloom"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/connmgr"
|
||||
@ -40,7 +39,7 @@ import (
|
||||
const (
|
||||
// defaultServices describes the default services that are supported by
|
||||
// the server.
|
||||
defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom | wire.SFNodeCF
|
||||
defaultServices = wire.SFNodeNetwork | wire.SFNodeCF
|
||||
|
||||
// defaultRequiredServices describes the default services that are
|
||||
// required to be supported by outbound peers.
|
||||
@ -198,7 +197,6 @@ type serverPeer struct {
|
||||
requestQueue []*wire.InvVect
|
||||
requestedTxns map[chainhash.Hash]struct{}
|
||||
requestedBlocks map[chainhash.Hash]struct{}
|
||||
filter *bloom.Filter
|
||||
knownAddresses map[string]struct{}
|
||||
banScore connmgr.DynamicBanScore
|
||||
quit chan struct{}
|
||||
@ -216,7 +214,6 @@ func newServerPeer(s *server, isPersistent bool) *serverPeer {
|
||||
persistent: isPersistent,
|
||||
requestedTxns: make(map[chainhash.Hash]struct{}),
|
||||
requestedBlocks: make(map[chainhash.Hash]struct{}),
|
||||
filter: bloom.LoadFilter(nil),
|
||||
knownAddresses: make(map[string]struct{}),
|
||||
quit: make(chan struct{}),
|
||||
txProcessed: make(chan struct{}, 1),
|
||||
@ -333,8 +330,7 @@ func (sp *serverPeer) OnVersion(p *peer.Peer, msg *wire.MsgVersion) {
|
||||
// Signal the block manager this peer is a new sync candidate.
|
||||
sp.server.blockManager.NewPeer(sp)
|
||||
|
||||
// Choose whether or not to relay transactions before a filter command
|
||||
// is received.
|
||||
// Choose whether or not to relay transactions.
|
||||
sp.setDisableRelayTx(msg.DisableRelayTx)
|
||||
|
||||
// Update the address manager and request known addresses from the
|
||||
@ -375,8 +371,7 @@ func (sp *serverPeer) OnVersion(p *peer.Peer, msg *wire.MsgVersion) {
|
||||
|
||||
// OnMemPool is invoked when a peer receives a mempool wire message. It creates
|
||||
// and sends an inventory message with the contents of the memory pool up to the
|
||||
// maximum inventory allowed per message. When the peer has a bloom filter
|
||||
// loaded, the contents are filtered accordingly.
|
||||
// maximum inventory allowed per message.
|
||||
func (sp *serverPeer) OnMemPool(p *peer.Peer, msg *wire.MsgMemPool) {
|
||||
// A decaying ban score increase is applied to prevent flooding.
|
||||
// The ban score accumulates and passes the ban threshold if a burst of
|
||||
@ -394,15 +389,10 @@ func (sp *serverPeer) OnMemPool(p *peer.Peer, msg *wire.MsgMemPool) {
|
||||
invMsg := wire.NewMsgInvSizeHint(uint(len(txDescs)))
|
||||
|
||||
for i, txDesc := range txDescs {
|
||||
// Either add all transactions when there is no bloom filter,
|
||||
// or only the transactions that match the filter when there is
|
||||
// one.
|
||||
if !sp.filter.IsLoaded() || sp.filter.MatchTxAndUpdate(txDesc.Tx) {
|
||||
iv := wire.NewInvVect(wire.InvTypeTx, txDesc.Tx.Hash())
|
||||
invMsg.AddInvVect(iv)
|
||||
if i+1 >= wire.MaxInvPerMsg {
|
||||
break
|
||||
}
|
||||
iv := wire.NewInvVect(wire.InvTypeTx, txDesc.Tx.Hash())
|
||||
invMsg.AddInvVect(iv)
|
||||
if i+1 >= wire.MaxInvPerMsg {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -652,8 +642,6 @@ func (sp *serverPeer) OnGetData(p *peer.Peer, msg *wire.MsgGetData) {
|
||||
err = sp.server.pushTxMsg(sp, &iv.Hash, c, waitChan)
|
||||
case wire.InvTypeBlock:
|
||||
err = sp.server.pushBlockMsg(sp, &iv.Hash, c, waitChan)
|
||||
case wire.InvTypeFilteredBlock:
|
||||
err = sp.server.pushMerkleBlockMsg(sp, &iv.Hash, c, waitChan)
|
||||
default:
|
||||
peerLog.Warnf("Unknown type in inventory request %d",
|
||||
iv.Type)
|
||||
@ -1075,41 +1063,6 @@ func (sp *serverPeer) OnGetCFTypes(p *peer.Peer, msg *wire.MsgGetCFTypes) {
|
||||
sp.QueueMessage(cfTypesMsg, nil)
|
||||
}
|
||||
|
||||
// enforceNodeBloomFlag disconnects the peer if the server is not configured to
|
||||
// allow bloom filters. Additionally, if the peer has negotiated to a protocol
|
||||
// version that is high enough to observe the bloom filter service support bit,
|
||||
// it will be banned since it is intentionally violating the protocol.
|
||||
func (sp *serverPeer) enforceNodeBloomFlag(cmd string) bool {
|
||||
if sp.server.services&wire.SFNodeBloom != wire.SFNodeBloom {
|
||||
// Ban the peer if the protocol version is high enough that the
|
||||
// peer is knowingly violating the protocol and banning is
|
||||
// enabled.
|
||||
//
|
||||
// NOTE: Even though the addBanScore function already examines
|
||||
// whether or not banning is enabled, it is checked here as well
|
||||
// to ensure the violation is logged and the peer is
|
||||
// disconnected regardless.
|
||||
if sp.ProtocolVersion() >= wire.NodeBloomVersion &&
|
||||
!cfg.DisableBanning {
|
||||
|
||||
// Disonnect the peer regardless of whether it was
|
||||
// banned.
|
||||
sp.addBanScore(100, 0, cmd)
|
||||
sp.Disconnect()
|
||||
return false
|
||||
}
|
||||
|
||||
// Disconnect the peer regardless of protocol version or banning
|
||||
// state.
|
||||
peerLog.Debugf("%s sent an unsupported %s request -- "+
|
||||
"disconnecting", sp, cmd)
|
||||
sp.Disconnect()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// enforceNodeCFFlag disconnects the peer if the server is not configured to
|
||||
// allow committed filters. Additionally, if the peer has negotiated to a
|
||||
// protocol version that is high enough to observe the committed filter service
|
||||
@ -1146,63 +1099,6 @@ func (sp *serverPeer) enforceNodeCFFlag(cmd string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// OnFilterAdd is invoked when a peer receives a filteradd wire message and is
|
||||
// used by remote peers to add data to an already loaded bloom filter. The peer
|
||||
// will be disconnected if a filter is not loaded when this message is received.
|
||||
func (sp *serverPeer) OnFilterAdd(p *peer.Peer, msg *wire.MsgFilterAdd) {
|
||||
// Disconnect and/or ban depending on the node bloom services flag and
|
||||
// negotiated protocol version.
|
||||
if !sp.enforceNodeBloomFlag(msg.Command()) {
|
||||
return
|
||||
}
|
||||
|
||||
if sp.filter.IsLoaded() {
|
||||
peerLog.Debugf("%s sent a filteradd request with no filter "+
|
||||
"loaded -- disconnecting", p)
|
||||
p.Disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
sp.filter.Add(msg.Data)
|
||||
}
|
||||
|
||||
// OnFilterClear is invoked when a peer receives a filterclear wire message and
|
||||
// is used by remote peers to clear an already loaded bloom filter. The peer
|
||||
// will be disconnected if a filter is not loaded when this message is received.
|
||||
func (sp *serverPeer) OnFilterClear(p *peer.Peer, msg *wire.MsgFilterClear) {
|
||||
// Disconnect and/or ban depending on the node bloom services flag and
|
||||
// negotiated protocol version.
|
||||
if !sp.enforceNodeBloomFlag(msg.Command()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !sp.filter.IsLoaded() {
|
||||
peerLog.Debugf("%s sent a filterclear request with no "+
|
||||
"filter loaded -- disconnecting", p)
|
||||
p.Disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
sp.filter.Unload()
|
||||
}
|
||||
|
||||
// OnFilterLoad is invoked when a peer receives a filterload wire message and it
|
||||
// is used to load a bloom filter that should be used for delivering merkle
|
||||
// blocks and associated transactions that match the filter.
|
||||
func (sp *serverPeer) OnFilterLoad(p *peer.Peer, msg *wire.MsgFilterLoad) {
|
||||
// Disconnect and/or ban depending on the node bloom services flag and
|
||||
// negotiated protocol version.
|
||||
if !sp.enforceNodeBloomFlag(msg.Command()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Transaction relay is no longer disabled once a filterload message is
|
||||
// received regardless of its original state.
|
||||
sp.setDisableRelayTx(false)
|
||||
|
||||
sp.filter.Reload(msg)
|
||||
}
|
||||
|
||||
// OnGetAddr is invoked when a peer receives a getaddr wire message and is used
|
||||
// to provide the peer with known addresses from the address manager.
|
||||
func (sp *serverPeer) OnGetAddr(p *peer.Peer, msg *wire.MsgGetAddr) {
|
||||
@ -1424,64 +1320,6 @@ func (s *server) pushBlockMsg(sp *serverPeer, hash *chainhash.Hash, doneChan cha
|
||||
return nil
|
||||
}
|
||||
|
||||
// pushMerkleBlockMsg sends a merkleblock message for the provided block hash to
|
||||
// the connected peer. Since a merkle block requires the peer to have a filter
|
||||
// loaded, this call will simply be ignored if there is no filter loaded. An
|
||||
// error is returned if the block hash is not known.
|
||||
func (s *server) pushMerkleBlockMsg(sp *serverPeer, hash *chainhash.Hash, doneChan chan<- struct{}, waitChan <-chan struct{}) error {
|
||||
// Do not send a response if the peer doesn't have a filter loaded.
|
||||
if !sp.filter.IsLoaded() {
|
||||
if doneChan != nil {
|
||||
doneChan <- struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fetch the raw block bytes from the database.
|
||||
blk, err := sp.server.blockManager.chain.BlockByHash(hash)
|
||||
if err != nil {
|
||||
peerLog.Tracef("Unable to fetch requested block hash %v: %v",
|
||||
hash, err)
|
||||
|
||||
if doneChan != nil {
|
||||
doneChan <- struct{}{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate a merkle block by filtering the requested block according
|
||||
// to the filter for the peer.
|
||||
merkle, matchedTxIndices := bloom.NewMerkleBlock(blk, sp.filter)
|
||||
|
||||
// Once we have fetched data wait for any previous operation to finish.
|
||||
if waitChan != nil {
|
||||
<-waitChan
|
||||
}
|
||||
|
||||
// Send the merkleblock. Only send the done channel with this message
|
||||
// if no transactions will be sent afterwards.
|
||||
var dc chan<- struct{}
|
||||
if len(matchedTxIndices) == 0 {
|
||||
dc = doneChan
|
||||
}
|
||||
sp.QueueMessage(merkle, dc)
|
||||
|
||||
// Finally, send any matched transactions.
|
||||
blkTransactions := blk.MsgBlock().Transactions
|
||||
for i, txIndex := range matchedTxIndices {
|
||||
// Only send the done channel on the final transaction.
|
||||
var dc chan<- struct{}
|
||||
if i == len(matchedTxIndices)-1 {
|
||||
dc = doneChan
|
||||
}
|
||||
if txIndex < uint32(len(blkTransactions)) {
|
||||
sp.QueueMessage(blkTransactions[txIndex], dc)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleUpdatePeerHeight updates the heights of all peers who were known to
|
||||
// announce a block we recently accepted.
|
||||
func (s *server) handleUpdatePeerHeights(state *peerState, umsg updatePeerHeightsMsg) {
|
||||
@ -1656,20 +1494,6 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) {
|
||||
if sp.relayTxDisabled() {
|
||||
return
|
||||
}
|
||||
// Don't relay the transaction if there is a bloom
|
||||
// filter loaded and the transaction doesn't match it.
|
||||
if sp.filter.IsLoaded() {
|
||||
tx, ok := msg.data.(*dcrutil.Tx)
|
||||
if !ok {
|
||||
peerLog.Warnf("Underlying data for tx" +
|
||||
" inv relay is not a transaction")
|
||||
return
|
||||
}
|
||||
|
||||
if !sp.filter.MatchTxAndUpdate(tx) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Queue the inventory to be relayed with the next batch.
|
||||
@ -1884,9 +1708,6 @@ func newPeerConfig(sp *serverPeer) *peer.Config {
|
||||
OnGetCFilter: sp.OnGetCFilter,
|
||||
OnGetCFHeaders: sp.OnGetCFHeaders,
|
||||
OnGetCFTypes: sp.OnGetCFTypes,
|
||||
OnFilterAdd: sp.OnFilterAdd,
|
||||
OnFilterClear: sp.OnFilterClear,
|
||||
OnFilterLoad: sp.OnFilterLoad,
|
||||
OnGetAddr: sp.OnGetAddr,
|
||||
OnAddr: sp.OnAddr,
|
||||
OnRead: sp.OnRead,
|
||||
@ -2498,9 +2319,6 @@ func standardScriptVerifyFlags(chain *blockchain.BlockChain) (txscript.ScriptFla
|
||||
// connections from peers.
|
||||
func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Params, interrupt <-chan struct{}) (*server, error) {
|
||||
services := defaultServices
|
||||
if cfg.NoPeerBloomFilters {
|
||||
services &^= wire.SFNodeBloom
|
||||
}
|
||||
if cfg.NoCFilters {
|
||||
services &^= wire.SFNodeCF
|
||||
}
|
||||
|
||||
@ -627,47 +627,6 @@ func BenchmarkDecodeNotFound(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDecodeMerkleBlock performs a benchmark on how long it takes to
|
||||
// decode a reasonably sized merkleblock message.
|
||||
func BenchmarkDecodeMerkleBlock(b *testing.B) {
|
||||
// Create a message with random data.
|
||||
pver := ProtocolVersion
|
||||
var m MsgMerkleBlock
|
||||
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", 10000))
|
||||
if err != nil {
|
||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
||||
}
|
||||
m.Header = *NewBlockHeader(1, hash, hash, hash, 0,
|
||||
[6]byte{}, 0, 0, 0, 0, 0, 0, 0, 0, uint32(10000),
|
||||
[32]byte{}, 0xbadc0ffe)
|
||||
for i := 0; i < 105; i++ {
|
||||
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
|
||||
if err != nil {
|
||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
||||
}
|
||||
m.AddTxHash(hash)
|
||||
m.AddSTxHash(hash)
|
||||
if i%8 == 0 {
|
||||
m.Flags = append(m.Flags, uint8(i))
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize it so the bytes are available to test the decode below.
|
||||
var bb bytes.Buffer
|
||||
if err := m.BtcEncode(&bb, pver); err != nil {
|
||||
b.Fatalf("MsgMerkleBlock.BtcEncode: unexpected error: %v", err)
|
||||
}
|
||||
buf := bb.Bytes()
|
||||
|
||||
r := bytes.NewReader(buf)
|
||||
var msg MsgMerkleBlock
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.Seek(0, 0)
|
||||
msg.BtcDecode(r, pver)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkTxHash performs a benchmark on how long it takes to hash a
|
||||
// transaction.
|
||||
func BenchmarkTxHash(b *testing.B) {
|
||||
|
||||
@ -340,14 +340,6 @@ func readElement(r io.Reader, element interface{}) error {
|
||||
*e = CurrencyNet(rv)
|
||||
return nil
|
||||
|
||||
case *BloomUpdateType:
|
||||
rv, err := binarySerializer.Uint8(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = BloomUpdateType(rv)
|
||||
return nil
|
||||
|
||||
case *RejectCode:
|
||||
rv, err := binarySerializer.Uint8(r)
|
||||
if err != nil {
|
||||
@ -471,13 +463,6 @@ func writeElement(w io.Writer, element interface{}) error {
|
||||
}
|
||||
return nil
|
||||
|
||||
case BloomUpdateType:
|
||||
err := binarySerializer.PutUint8(w, uint8(e))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case RejectCode:
|
||||
err := binarySerializer.PutUint8(w, uint8(e))
|
||||
if err != nil {
|
||||
|
||||
@ -46,10 +46,6 @@ const (
|
||||
CmdMemPool = "mempool"
|
||||
CmdMiningState = "miningstate"
|
||||
CmdGetMiningState = "getminings"
|
||||
CmdFilterAdd = "filteradd"
|
||||
CmdFilterClear = "filterclear"
|
||||
CmdFilterLoad = "filterload"
|
||||
CmdMerkleBlock = "merkleblock"
|
||||
CmdReject = "reject"
|
||||
CmdSendHeaders = "sendheaders"
|
||||
CmdFeeFilter = "feefilter"
|
||||
@ -128,18 +124,6 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||
case CmdGetMiningState:
|
||||
msg = &MsgGetMiningState{}
|
||||
|
||||
case CmdFilterAdd:
|
||||
msg = &MsgFilterAdd{}
|
||||
|
||||
case CmdFilterClear:
|
||||
msg = &MsgFilterClear{}
|
||||
|
||||
case CmdFilterLoad:
|
||||
msg = &MsgFilterLoad{}
|
||||
|
||||
case CmdMerkleBlock:
|
||||
msg = &MsgMerkleBlock{}
|
||||
|
||||
case CmdReject:
|
||||
msg = &MsgReject{}
|
||||
|
||||
|
||||
@ -69,9 +69,6 @@ func TestMessage(t *testing.T) {
|
||||
msgGetHeaders := NewMsgGetHeaders()
|
||||
msgHeaders := NewMsgHeaders()
|
||||
msgMemPool := NewMsgMemPool()
|
||||
msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
|
||||
msgFilterClear := NewMsgFilterClear()
|
||||
msgFilterLoad := NewMsgFilterLoad([]byte{0x01}, 10, 0, BloomUpdateNone)
|
||||
msgGetCFilter := NewMsgGetCFilter(&chainhash.Hash{}, GCSFilterExtended)
|
||||
msgGetCFHeaders := NewMsgGetCFHeaders()
|
||||
msgGetCFTypes := NewMsgGetCFTypes()
|
||||
@ -79,26 +76,6 @@ func TestMessage(t *testing.T) {
|
||||
[]byte("payload"))
|
||||
msgCFHeaders := NewMsgCFHeaders()
|
||||
msgCFTypes := NewMsgCFTypes([]FilterType{GCSFilterExtended})
|
||||
bh := NewBlockHeader(
|
||||
int32(0), // Version
|
||||
&chainhash.Hash{}, // PrevHash
|
||||
&chainhash.Hash{}, // MerkleRoot
|
||||
&chainhash.Hash{}, // StakeRoot
|
||||
uint16(0x0000), // VoteBits
|
||||
[6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // FinalState
|
||||
uint16(0x0000), // Voters
|
||||
uint8(0x00), // FreshStake
|
||||
uint8(0x00), // Revocations
|
||||
uint32(0), // Poolsize
|
||||
uint32(0x00000000), // Bits
|
||||
int64(0x0000000000000000), // Sbits
|
||||
uint32(0), // Height
|
||||
uint32(0), // Size
|
||||
uint32(0x00000000), // Nonce
|
||||
[32]byte{}, // ExtraData
|
||||
uint32(0xcab005e0), // StakeVersion
|
||||
)
|
||||
msgMerkleBlock := NewMsgMerkleBlock(bh)
|
||||
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
|
||||
|
||||
tests := []struct {
|
||||
@ -123,10 +100,6 @@ func TestMessage(t *testing.T) {
|
||||
{msgGetHeaders, msgGetHeaders, pver, MainNet, 61}, // [12]
|
||||
{msgHeaders, msgHeaders, pver, MainNet, 25}, // [13]
|
||||
{msgMemPool, msgMemPool, pver, MainNet, 24}, // [15]
|
||||
{msgFilterAdd, msgFilterAdd, pver, MainNet, 26}, // [16]
|
||||
{msgFilterClear, msgFilterClear, pver, MainNet, 24}, // [17]
|
||||
{msgFilterLoad, msgFilterLoad, pver, MainNet, 35}, // [18]
|
||||
{msgMerkleBlock, msgMerkleBlock, pver, MainNet, 215}, // [19]
|
||||
{msgReject, msgReject, pver, MainNet, 79}, // [20]
|
||||
{msgGetCFilter, msgGetCFilter, pver, MainNet, 57}, // [21]
|
||||
{msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 58}, // [22]
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
// Copyright (c) 2014-2015 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxFilterAddDataSize is the maximum byte size of a data
|
||||
// element to add to the Bloom filter. It is equal to the
|
||||
// maximum element size of a script.
|
||||
MaxFilterAddDataSize = 520
|
||||
)
|
||||
|
||||
// MsgFilterAdd implements the Message interface and represents a decred
|
||||
// filteradd message. It is used to add a data element to an existing Bloom
|
||||
// filter.
|
||||
//
|
||||
// This message was not added until protocol version BIP0037Version.
|
||||
type MsgFilterAdd struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// BtcDecode decodes r using the Decred protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterAdd) BtcDecode(r io.Reader, pver uint32) error {
|
||||
var err error
|
||||
msg.Data, err = ReadVarBytes(r, pver, MaxFilterAddDataSize,
|
||||
"filteradd data")
|
||||
return err
|
||||
}
|
||||
|
||||
// BtcEncode encodes the receiver to w using the Decred protocol encoding.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32) error {
|
||||
size := len(msg.Data)
|
||||
if size > MaxFilterAddDataSize {
|
||||
str := fmt.Sprintf("filteradd size too large for message "+
|
||||
"[size %v, max %v]", size, MaxFilterAddDataSize)
|
||||
return messageError("MsgFilterAdd.BtcEncode", str)
|
||||
}
|
||||
|
||||
return WriteVarBytes(w, pver, msg.Data)
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgFilterAdd) Command() string {
|
||||
return CmdFilterAdd
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterAdd) MaxPayloadLength(pver uint32) uint32 {
|
||||
return uint32(VarIntSerializeSize(MaxFilterAddDataSize)) +
|
||||
MaxFilterAddDataSize
|
||||
}
|
||||
|
||||
// NewMsgFilterAdd returns a new Decred filteradd message that conforms to the
|
||||
// Message interface. See MsgFilterAdd for details.
|
||||
func NewMsgFilterAdd(data []byte) *MsgFilterAdd {
|
||||
return &MsgFilterAdd{
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFilterAddLatest tests the MsgFilterAdd API against the latest protocol
|
||||
// version.
|
||||
func TestFilterAddLatest(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
data := []byte{0x01, 0x02}
|
||||
msg := NewMsgFilterAdd(data)
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "filteradd"
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgFilterAdd: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
wantPayload := uint32(523)
|
||||
maxPayload := msg.MaxPayloadLength(pver)
|
||||
if maxPayload != wantPayload {
|
||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||
"protocol version %d - got %v, want %v", pver,
|
||||
maxPayload, wantPayload)
|
||||
}
|
||||
|
||||
// Test encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err := msg.BtcEncode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err)
|
||||
}
|
||||
|
||||
// Test decode with latest protocol version.
|
||||
var readmsg MsgFilterAdd
|
||||
err = readmsg.BtcDecode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterAddMaxDataSize tests the MsgFilterAdd API maximum data size.
|
||||
func TestFilterAddMaxDataSize(t *testing.T) {
|
||||
data := bytes.Repeat([]byte{0xff}, 521)
|
||||
msg := NewMsgFilterAdd(data)
|
||||
|
||||
// Encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err := msg.BtcEncode(&buf, ProtocolVersion)
|
||||
if err == nil {
|
||||
t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't "+
|
||||
"have %v", msg)
|
||||
}
|
||||
|
||||
// Decode with latest protocol version.
|
||||
readbuf := bytes.NewReader(data)
|
||||
err = msg.BtcDecode(readbuf, ProtocolVersion)
|
||||
if err == nil {
|
||||
t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+
|
||||
"have %v", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterAddWireErrors performs negative tests against wire encode and decode
|
||||
// of MsgFilterAdd to confirm error paths work correctly.
|
||||
func TestFilterAddWireErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
baseData := []byte{0x01, 0x02, 0x03, 0x04}
|
||||
baseFilterAdd := NewMsgFilterAdd(baseData)
|
||||
baseFilterAddEncoded := append([]byte{0x04}, baseData...)
|
||||
|
||||
tests := []struct {
|
||||
in *MsgFilterAdd // Value to encode
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Latest protocol version with intentional read/write errors.
|
||||
// Force error in data size.
|
||||
{
|
||||
baseFilterAdd, baseFilterAddEncoded, pver, 0,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in data.
|
||||
{
|
||||
baseFilterAdd, baseFilterAddEncoded, pver, 1,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to wire format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := test.in.BtcEncode(w, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||
t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if _, ok := err.(*MessageError); !ok {
|
||||
if err != test.writeErr {
|
||||
t.Errorf("BtcEncode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Decode from wire format.
|
||||
var msg MsgFilterAdd
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
err = msg.BtcDecode(r, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if _, ok := err.(*MessageError); !ok {
|
||||
if err != test.readErr {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
// Copyright (c) 2014-2015 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// MsgFilterClear implements the Message interface and represents a decred
|
||||
// filterclear message which is used to reset a Bloom filter.
|
||||
//
|
||||
// This message was not added until protocol version BIP0037Version and has
|
||||
// no payload.
|
||||
type MsgFilterClear struct{}
|
||||
|
||||
// BtcDecode decodes r using the Decred protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterClear) BtcDecode(r io.Reader, pver uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BtcEncode encodes the receiver to w using the Decred protocol encoding.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterClear) BtcEncode(w io.Writer, pver uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgFilterClear) Command() string {
|
||||
return CmdFilterClear
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterClear) MaxPayloadLength(pver uint32) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// NewMsgFilterClear returns a new Decred filterclear message that conforms to the Message
|
||||
// interface. See MsgFilterClear for details.
|
||||
func NewMsgFilterClear() *MsgFilterClear {
|
||||
return &MsgFilterClear{}
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// TestFilterCLearLatest tests the MsgFilterClear API against the latest
|
||||
// protocol version.
|
||||
func TestFilterClearLatest(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
msg := NewMsgFilterClear()
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "filterclear"
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgFilterClear: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
wantPayload := uint32(0)
|
||||
maxPayload := msg.MaxPayloadLength(pver)
|
||||
if maxPayload != wantPayload {
|
||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||
"protocol version %d - got %v, want %v", pver,
|
||||
maxPayload, wantPayload)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterClearWire tests the MsgFilterClear wire encode and decode for
|
||||
// various protocol versions.
|
||||
func TestFilterClearWire(t *testing.T) {
|
||||
msgFilterClear := NewMsgFilterClear()
|
||||
msgFilterClearEncoded := []byte{}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgFilterClear // Message to encode
|
||||
out *MsgFilterClear // Expected decoded message
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
}{
|
||||
// Latest protocol version.
|
||||
{
|
||||
msgFilterClear,
|
||||
msgFilterClear,
|
||||
msgFilterClearEncoded,
|
||||
ProtocolVersion,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode the message to wire format.
|
||||
var buf bytes.Buffer
|
||||
err := test.in.BtcEncode(&buf, test.pver)
|
||||
if err != nil {
|
||||
t.Errorf("BtcEncode #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode the message from wire format.
|
||||
var msg MsgFilterClear
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
err = msg.BtcDecode(rbuf, test.pver)
|
||||
if err != nil {
|
||||
t.Errorf("BtcDecode #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(&msg, test.out) {
|
||||
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(msg), spew.Sdump(test.out))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
// Copyright (c) 2014-2015 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// BloomUpdateType specifies how the filter is updated when a match is found
|
||||
type BloomUpdateType uint8
|
||||
|
||||
const (
|
||||
// BloomUpdateNone indicates the filter is not adjusted when a match is
|
||||
// found.
|
||||
BloomUpdateNone BloomUpdateType = 0
|
||||
|
||||
// BloomUpdateAll indicates if the filter matches any data element in a
|
||||
// public key script, the outpoint is serialized and inserted into the
|
||||
// filter.
|
||||
BloomUpdateAll BloomUpdateType = 1
|
||||
|
||||
// BloomUpdateP2PubkeyOnly indicates if the filter matches a data
|
||||
// element in a public key script and the script is of the standard
|
||||
// pay-to-pubkey or multisig, the outpoint is serialized and inserted
|
||||
// into the filter.
|
||||
BloomUpdateP2PubkeyOnly BloomUpdateType = 2
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxFilterLoadHashFuncs is the maximum number of hash functions to
|
||||
// load into the Bloom filter.
|
||||
MaxFilterLoadHashFuncs = 50
|
||||
|
||||
// MaxFilterLoadFilterSize is the maximum size in bytes a filter may be.
|
||||
MaxFilterLoadFilterSize = 36000
|
||||
)
|
||||
|
||||
// MsgFilterLoad implements the Message interface and represents a decred
|
||||
// filterload message which is used to reset a Bloom filter.
|
||||
//
|
||||
// This message was not added until protocol version BIP0037Version.
|
||||
type MsgFilterLoad struct {
|
||||
Filter []byte
|
||||
HashFuncs uint32
|
||||
Tweak uint32
|
||||
Flags BloomUpdateType
|
||||
}
|
||||
|
||||
// BtcDecode decodes r using the Decred protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterLoad) BtcDecode(r io.Reader, pver uint32) error {
|
||||
var err error
|
||||
msg.Filter, err = ReadVarBytes(r, pver, MaxFilterLoadFilterSize,
|
||||
"filterload filter size")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = readElements(r, &msg.HashFuncs, &msg.Tweak, &msg.Flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.HashFuncs > MaxFilterLoadHashFuncs {
|
||||
str := fmt.Sprintf("too many filter hash functions for message "+
|
||||
"[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs)
|
||||
return messageError("MsgFilterLoad.BtcDecode", str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BtcEncode encodes the receiver to w using the Decred protocol encoding.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32) error {
|
||||
size := len(msg.Filter)
|
||||
if size > MaxFilterLoadFilterSize {
|
||||
str := fmt.Sprintf("filterload filter size too large for message "+
|
||||
"[size %v, max %v]", size, MaxFilterLoadFilterSize)
|
||||
return messageError("MsgFilterLoad.BtcEncode", str)
|
||||
}
|
||||
|
||||
if msg.HashFuncs > MaxFilterLoadHashFuncs {
|
||||
str := fmt.Sprintf("too many filter hash functions for message "+
|
||||
"[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs)
|
||||
return messageError("MsgFilterLoad.BtcEncode", str)
|
||||
}
|
||||
|
||||
err := WriteVarBytes(w, pver, msg.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeElements(w, msg.HashFuncs, msg.Tweak, msg.Flags)
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgFilterLoad) Command() string {
|
||||
return CmdFilterLoad
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgFilterLoad) MaxPayloadLength(pver uint32) uint32 {
|
||||
// Num filter bytes (varInt) + filter + 4 bytes hash funcs +
|
||||
// 4 bytes tweak + 1 byte flags.
|
||||
return uint32(VarIntSerializeSize(MaxFilterLoadFilterSize)) +
|
||||
MaxFilterLoadFilterSize + 9
|
||||
}
|
||||
|
||||
// NewMsgFilterLoad returns a new Decred filterload message that conforms to
|
||||
// the Message interface. See MsgFilterLoad for details.
|
||||
func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad {
|
||||
return &MsgFilterLoad{
|
||||
Filter: filter,
|
||||
HashFuncs: hashFuncs,
|
||||
Tweak: tweak,
|
||||
Flags: flags,
|
||||
}
|
||||
}
|
||||
@ -1,196 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFilterCLearLatest tests the MsgFilterLoad API against the latest protocol
|
||||
// version.
|
||||
func TestFilterLoadLatest(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
data := []byte{0x01, 0x02}
|
||||
msg := NewMsgFilterLoad(data, 10, 0, 0)
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "filterload"
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgFilterLoad: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
wantPayload := uint32(36012)
|
||||
maxPayload := msg.MaxPayloadLength(pver)
|
||||
if maxPayload != wantPayload {
|
||||
t.Errorf("MaxPayLoadLength: wrong max payload length for "+
|
||||
"protocol version %d - got %v, want %v", pver,
|
||||
maxPayload, wantPayload)
|
||||
}
|
||||
|
||||
// Test encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err := msg.BtcEncode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("encode of MsgFilterLoad failed %v err <%v>", msg, err)
|
||||
}
|
||||
|
||||
// Test decode with latest protocol version.
|
||||
readmsg := MsgFilterLoad{}
|
||||
err = readmsg.BtcDecode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("decode of MsgFilterLoad failed [%v] err <%v>", buf, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterLoadMaxFilterSize tests the MsgFilterLoad API maximum filter size.
|
||||
func TestFilterLoadMaxFilterSize(t *testing.T) {
|
||||
data := bytes.Repeat([]byte{0xff}, 36001)
|
||||
msg := NewMsgFilterLoad(data, 10, 0, 0)
|
||||
|
||||
// Encode with latest protocol version.;
|
||||
var buf bytes.Buffer
|
||||
err := msg.BtcEncode(&buf, ProtocolVersion)
|
||||
if err == nil {
|
||||
t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't "+
|
||||
"have %v", msg)
|
||||
}
|
||||
|
||||
// Decode with latest protocol version.
|
||||
readbuf := bytes.NewReader(data)
|
||||
err = msg.BtcDecode(readbuf, ProtocolVersion)
|
||||
if err == nil {
|
||||
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't "+
|
||||
"have %v", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterLoadMaxHashFuncsSize tests the MsgFilterLoad API maximum hash functions.
|
||||
func TestFilterLoadMaxHashFuncsSize(t *testing.T) {
|
||||
data := bytes.Repeat([]byte{0xff}, 10)
|
||||
msg := NewMsgFilterLoad(data, 61, 0, 0)
|
||||
|
||||
// Encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err := msg.BtcEncode(&buf, ProtocolVersion)
|
||||
if err == nil {
|
||||
t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v",
|
||||
msg)
|
||||
}
|
||||
|
||||
newBuf := []byte{
|
||||
0x0a, // filter size
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // filter
|
||||
0x3d, 0x00, 0x00, 0x00, // max hash funcs
|
||||
0x00, 0x00, 0x00, 0x00, // tweak
|
||||
0x00, // update Type
|
||||
}
|
||||
// Decode with latest protocol version.
|
||||
readbuf := bytes.NewReader(newBuf)
|
||||
err = msg.BtcDecode(readbuf, ProtocolVersion)
|
||||
if err == nil {
|
||||
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v",
|
||||
msg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFilterLoadWireErrors performs negative tests against wire encode and decode
|
||||
// of MsgFilterLoad to confirm error paths work correctly.
|
||||
func TestFilterLoadWireErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
baseFilter := []byte{0x01, 0x02, 0x03, 0x04}
|
||||
baseFilterLoad := NewMsgFilterLoad(baseFilter, 10, 0, BloomUpdateNone)
|
||||
baseFilterLoadEncoded := append([]byte{0x04}, baseFilter...)
|
||||
baseFilterLoadEncoded = append(baseFilterLoadEncoded,
|
||||
0x00, 0x00, 0x00, 0x0a, // HashFuncs
|
||||
0x00, 0x00, 0x00, 0x00, // Tweak
|
||||
0x00) // Flags
|
||||
|
||||
tests := []struct {
|
||||
in *MsgFilterLoad // Value to encode
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Latest protocol version with intentional read/write errors.
|
||||
// Force error in filter size.
|
||||
{
|
||||
baseFilterLoad, baseFilterLoadEncoded, pver, 0,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in filter.
|
||||
{
|
||||
baseFilterLoad, baseFilterLoadEncoded, pver, 1,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in hash funcs.
|
||||
{
|
||||
baseFilterLoad, baseFilterLoadEncoded, pver, 5,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in tweak.
|
||||
{
|
||||
baseFilterLoad, baseFilterLoadEncoded, pver, 9,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in flags.
|
||||
{
|
||||
baseFilterLoad, baseFilterLoadEncoded, pver, 13,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to wire format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := test.in.BtcEncode(w, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||
t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if _, ok := err.(*MessageError); !ok {
|
||||
if err != test.writeErr {
|
||||
t.Errorf("BtcEncode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Decode from wire format.
|
||||
var msg MsgFilterLoad
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
err = msg.BtcDecode(r, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if _, ok := err.(*MessageError); !ok {
|
||||
if err != test.readErr {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,227 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// maxFlagsPerMerkleBlock is the maximum number of flag bytes that could
|
||||
// possibly fit into a merkle block of the given protocol version.
|
||||
func maxFlagsPerMerkleBlock(pver uint32) uint32 {
|
||||
// Each transaction is represented by a single bit, so the result is the
|
||||
// max number of transactions per block divided by 8 bits per byte.
|
||||
// Then an extra one to cover partials.
|
||||
return uint32(MaxTxPerTxTree(ProtocolVersion)/8) + 1
|
||||
}
|
||||
|
||||
// MsgMerkleBlock implements the Message interface and represents a decred
|
||||
// merkleblock message which is used to reset a Bloom filter.
|
||||
//
|
||||
// This message was not added until protocol version BIP0037Version.
|
||||
type MsgMerkleBlock struct {
|
||||
Header BlockHeader
|
||||
Transactions uint32
|
||||
Hashes []*chainhash.Hash
|
||||
STransactions uint32
|
||||
SHashes []*chainhash.Hash
|
||||
Flags []byte
|
||||
}
|
||||
|
||||
// AddTxHash adds a new transaction hash to the message.
|
||||
func (msg *MsgMerkleBlock) AddTxHash(hash *chainhash.Hash) error {
|
||||
maxTxPerTree := MaxTxPerTxTree(ProtocolVersion)
|
||||
if uint64(len(msg.Hashes)+1) > maxTxPerTree {
|
||||
str := fmt.Sprintf("too many tx hashes for message [max %v]",
|
||||
maxTxPerTree)
|
||||
return messageError("MsgMerkleBlock.AddTxHash", str)
|
||||
}
|
||||
|
||||
msg.Hashes = append(msg.Hashes, hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSTxHash adds a new stake transaction hash to the message.
|
||||
func (msg *MsgMerkleBlock) AddSTxHash(hash *chainhash.Hash) error {
|
||||
maxTxPerTree := MaxTxPerTxTree(ProtocolVersion)
|
||||
if uint64(len(msg.SHashes)+1) > maxTxPerTree {
|
||||
str := fmt.Sprintf("too many tx hashes for message [max %v]",
|
||||
maxTxPerTree)
|
||||
return messageError("MsgMerkleBlock.AddSTxHash", str)
|
||||
}
|
||||
|
||||
msg.SHashes = append(msg.SHashes, hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// BtcDecode decodes r using the Decred protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error {
|
||||
err := readBlockHeader(r, pver, &msg.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = readElement(r, &msg.Transactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read num block locator hashes and limit to max.
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxTxPerTree := MaxTxPerTxTree(pver)
|
||||
if count > maxTxPerTree {
|
||||
str := fmt.Sprintf("too many transaction hashes for message "+
|
||||
"[count %v, max %v]", count, maxTxPerTree)
|
||||
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||
}
|
||||
|
||||
// Create a contiguous slice of hashes to deserialize into in order to
|
||||
// reduce the number of allocations.
|
||||
hashes := make([]chainhash.Hash, count)
|
||||
msg.Hashes = make([]*chainhash.Hash, 0, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
hash := &hashes[i]
|
||||
err := readElement(r, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg.AddTxHash(hash)
|
||||
}
|
||||
|
||||
err = readElement(r, &msg.STransactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read num block locator hashes for stake and limit to max.
|
||||
scount, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if scount > maxTxPerTree {
|
||||
str := fmt.Sprintf("too many stransaction hashes for message "+
|
||||
"[count %v, max %v]", scount, maxTxPerTree)
|
||||
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||
}
|
||||
|
||||
hashes = make([]chainhash.Hash, count)
|
||||
msg.SHashes = make([]*chainhash.Hash, 0, scount)
|
||||
for i := uint64(0); i < scount; i++ {
|
||||
hash := &hashes[i]
|
||||
err := readElement(r, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg.AddSTxHash(hash)
|
||||
}
|
||||
|
||||
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock(pver),
|
||||
"merkle block flags size")
|
||||
return err
|
||||
}
|
||||
|
||||
// BtcEncode encodes the receiver to w using the Decred protocol encoding.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error {
|
||||
// Read num transaction hashes and limit to max.
|
||||
numHashes := uint64(len(msg.Hashes))
|
||||
maxTxPerTree := MaxTxPerTxTree(pver)
|
||||
if numHashes > maxTxPerTree {
|
||||
str := fmt.Sprintf("too many transaction hashes for message "+
|
||||
"[count %v, max %v]", numHashes, maxTxPerTree)
|
||||
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||
}
|
||||
// Read num stake transaction hashes and limit to max.
|
||||
numSHashes := uint64(len(msg.SHashes))
|
||||
if numSHashes > maxTxPerTree {
|
||||
str := fmt.Sprintf("too many stake transaction hashes for message "+
|
||||
"[count %v, max %v]", numHashes, maxTxPerTree)
|
||||
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||
}
|
||||
|
||||
numFlagBytes := uint32(len(msg.Flags))
|
||||
maxFlags := maxFlagsPerMerkleBlock(pver)
|
||||
if numFlagBytes > maxFlags {
|
||||
str := fmt.Sprintf("too many flag bytes for message [count %v, "+
|
||||
"max %v]", numFlagBytes, maxFlags)
|
||||
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||
}
|
||||
|
||||
err := writeBlockHeader(w, pver, &msg.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeElement(w, msg.Transactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = WriteVarInt(w, pver, numHashes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, hash := range msg.Hashes {
|
||||
err = writeElement(w, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = writeElement(w, msg.STransactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = WriteVarInt(w, pver, numSHashes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, hash := range msg.SHashes {
|
||||
err = writeElement(w, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return WriteVarBytes(w, pver, msg.Flags)
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgMerkleBlock) Command() string {
|
||||
return CmdMerkleBlock
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||
// Protocol version 3 and lower have a different max block payload.
|
||||
if pver <= 3 {
|
||||
return MaxBlockPayloadV3
|
||||
}
|
||||
|
||||
return MaxBlockPayload
|
||||
}
|
||||
|
||||
// NewMsgMerkleBlock returns a new Decred merkleblock message that conforms to
|
||||
// the Message interface. See MsgMerkleBlock for details.
|
||||
func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock {
|
||||
return &MsgMerkleBlock{
|
||||
Header: *bh,
|
||||
Transactions: 0,
|
||||
Hashes: make([]*chainhash.Hash, 0),
|
||||
SHashes: make([]*chainhash.Hash, 0),
|
||||
Flags: make([]byte, 0),
|
||||
}
|
||||
}
|
||||
@ -1,521 +0,0 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// TestMerkleBlock tests the MsgMerkleBlock API.
|
||||
func TestMerkleBlock(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// Test block header.
|
||||
bh := NewBlockHeader(
|
||||
int32(pver),
|
||||
&testBlock.Header.PrevBlock, // PrevHash
|
||||
&testBlock.Header.MerkleRoot, // MerkleRootHash
|
||||
&testBlock.Header.StakeRoot, // StakeRoot
|
||||
uint16(0x0000), // VoteBits
|
||||
[6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // FinalState
|
||||
uint16(0x0000), // Voters
|
||||
uint8(0x00), // FreshStake
|
||||
uint8(0x00), // Revocations
|
||||
uint32(0), // Poolsize
|
||||
testBlock.Header.Bits, // Bits
|
||||
int64(0x0000000000000000), // Sbits
|
||||
uint32(1), // Height
|
||||
uint32(0), // Size
|
||||
testBlock.Header.Nonce, // Nonce
|
||||
[32]byte{}, // ExtraData
|
||||
uint32(0x7e1eca57), // StakeVersion
|
||||
)
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "merkleblock"
|
||||
msg := NewMsgMerkleBlock(bh)
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgBlock: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
// Num addresses (varInt) + max allowed addresses.
|
||||
wantPayload := uint32(1310720)
|
||||
maxPayload := msg.MaxPayloadLength(pver)
|
||||
if maxPayload != wantPayload {
|
||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||
"protocol version %d - got %v, want %v", pver,
|
||||
maxPayload, wantPayload)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for protocol version 3.
|
||||
wantPayload = uint32(1000000)
|
||||
maxPayload = msg.MaxPayloadLength(3)
|
||||
if maxPayload != wantPayload {
|
||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||
"protocol version %d - got %v, want %v", 3,
|
||||
maxPayload, wantPayload)
|
||||
}
|
||||
|
||||
// Load maxTxPerBlock hashes
|
||||
data := make([]byte, 32)
|
||||
for i := uint64(0); i < MaxTxPerTxTree(pver); i++ {
|
||||
rand.Read(data)
|
||||
hash, err := chainhash.NewHash(data)
|
||||
if err != nil {
|
||||
t.Errorf("NewHash failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = msg.AddTxHash(hash); err != nil {
|
||||
t.Errorf("AddTxHash failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
if err = msg.AddSTxHash(hash); err != nil {
|
||||
t.Errorf("AddSTxHash failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Add one more Tx to test failure.
|
||||
rand.Read(data)
|
||||
hash, err := chainhash.NewHash(data)
|
||||
if err != nil {
|
||||
t.Errorf("NewHash failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = msg.AddTxHash(hash); err == nil {
|
||||
t.Errorf("AddTxHash succeeded when it should have failed")
|
||||
return
|
||||
}
|
||||
|
||||
// Add one more STx to test failure.
|
||||
rand.Read(data)
|
||||
hash, err = chainhash.NewHash(data)
|
||||
if err != nil {
|
||||
t.Errorf("NewHash failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = msg.AddSTxHash(hash); err == nil {
|
||||
t.Errorf("AddTxHash succeeded when it should have failed")
|
||||
return
|
||||
}
|
||||
|
||||
// Test encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err = msg.BtcEncode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("encode of MsgMerkleBlock failed %v err <%v>", msg, err)
|
||||
}
|
||||
|
||||
// Test decode with latest protocol version.
|
||||
readmsg := MsgMerkleBlock{}
|
||||
err = readmsg.BtcDecode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("decode of MsgMerkleBlock failed [%v] err <%v>", buf, err)
|
||||
}
|
||||
|
||||
// Force extra hash to test maxTxPerBlock.
|
||||
msg.Hashes = append(msg.Hashes, hash)
|
||||
err = msg.BtcEncode(&buf, pver)
|
||||
if err == nil {
|
||||
t.Errorf("encode of MsgMerkleBlock succeeded with too many " +
|
||||
"tx hashes when it should have failed")
|
||||
return
|
||||
}
|
||||
|
||||
// Force too many flag bytes to test maxFlagsPerMerkleBlock.
|
||||
// Reset the number of hashes back to a valid value.
|
||||
msg.Hashes = msg.Hashes[len(msg.Hashes)-1:]
|
||||
msg.Flags = make([]byte, maxFlagsPerMerkleBlock(pver)+1)
|
||||
err = msg.BtcEncode(&buf, pver)
|
||||
if err == nil {
|
||||
t.Errorf("encode of MsgMerkleBlock succeeded with too many " +
|
||||
"flag bytes when it should have failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestMerkleBlockWire tests the MsgMerkleBlock wire encode and decode for
|
||||
// various numbers of transaction hashes and protocol versions.
|
||||
func TestMerkleBlockWire(t *testing.T) {
|
||||
tests := []struct {
|
||||
in *MsgMerkleBlock // Message to encode
|
||||
out *MsgMerkleBlock // Expected decoded message
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
}{
|
||||
// Latest protocol version.
|
||||
{
|
||||
&testMerkleBlock, &testMerkleBlock,
|
||||
testMerkleBlockBytes, ProtocolVersion,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode the message to wire format.
|
||||
var buf bytes.Buffer
|
||||
err := test.in.BtcEncode(&buf, test.pver)
|
||||
if err != nil {
|
||||
t.Errorf("BtcEncode #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode the message from wire format.
|
||||
var msg MsgMerkleBlock
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
err = msg.BtcDecode(rbuf, test.pver)
|
||||
if err != nil {
|
||||
t.Errorf("BtcDecode #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(&msg, test.out) {
|
||||
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(&msg), spew.Sdump(test.out))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMerkleBlockWireErrors performs negative tests against wire encode and
|
||||
// decode of MsgBlock to confirm error paths work correctly.
|
||||
func TestMerkleBlockWireErrors(t *testing.T) {
|
||||
// Use protocol version 70001 specifically here instead of the latest
|
||||
// because the test data is using bytes encoded with that protocol
|
||||
// version.
|
||||
pver := uint32(1)
|
||||
|
||||
tests := []struct {
|
||||
in *MsgMerkleBlock // Value to encode
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Force error in version. [0]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 0,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in prev block hash. [1]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 4,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in merkle root. [2]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 36,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in stake merkle root. [3]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 68,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in VoteBits. [4]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 100,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in FinalState. [5]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 102,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in Voters. [6]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 108,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in FreshStake. [7]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 110,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in Revocations. [8]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 111,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in poolsize. [9]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 112,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in difficulty bits. [10]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 116,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in stake difficulty bits. [11]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 120,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in height. [12]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 128,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in size. [13]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 132,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in timestamp. [14]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 136,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in header nonce. [15]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 140,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in transaction count. [16]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 180,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in num hashes. [17]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 184,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in hashes. [18]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 185,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in num flag bytes. [19]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 254,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
// Force error in flag bytes. [20]
|
||||
{
|
||||
&testMerkleBlock, testMerkleBlockBytes, pver, 255,
|
||||
io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to wire format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := test.in.BtcEncode(w, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||
t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if _, ok := err.(*MessageError); !ok {
|
||||
if err != test.writeErr {
|
||||
t.Errorf("BtcEncode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Decode from wire format.
|
||||
var msg MsgMerkleBlock
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
err = msg.BtcDecode(r, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if _, ok := err.(*MessageError); !ok {
|
||||
if err != test.readErr {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMerkleBlockOverflowErrors performs tests to ensure encoding and decoding
|
||||
// merkle blocks that are intentionally crafted to use large values for the
|
||||
// number of hashes and flags are handled properly. This could otherwise
|
||||
// potentially be used as an attack vector.
|
||||
func TestMerkleBlockOverflowErrors(t *testing.T) {
|
||||
// Use protocol version 1 specifically here instead of the latest
|
||||
// protocol version because the test data is using bytes encoded with
|
||||
// that version.
|
||||
pver := uint32(1)
|
||||
|
||||
// Create bytes for a merkle block that claims to have more than the max
|
||||
// allowed tx hashes.
|
||||
var buf bytes.Buffer
|
||||
WriteVarInt(&buf, pver, MaxTxPerTxTree(pver)+1)
|
||||
numHashesOffset := 140
|
||||
exceedMaxHashes := make([]byte, numHashesOffset)
|
||||
copy(exceedMaxHashes, testMerkleBlockBytes[:numHashesOffset])
|
||||
exceedMaxHashes = append(exceedMaxHashes, buf.Bytes()...)
|
||||
|
||||
// Create bytes for a merkle block that claims to have more than the max
|
||||
// allowed flag bytes.
|
||||
buf.Reset()
|
||||
WriteVarInt(&buf, pver, uint64(maxFlagsPerMerkleBlock(pver)+1))
|
||||
numFlagBytesOffset := 210
|
||||
exceedMaxFlagBytes := make([]byte, numFlagBytesOffset)
|
||||
copy(exceedMaxFlagBytes, testMerkleBlockBytes[:numFlagBytesOffset])
|
||||
exceedMaxFlagBytes = append(exceedMaxFlagBytes, buf.Bytes()...)
|
||||
|
||||
tests := []struct {
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
err error // Expected error
|
||||
}{
|
||||
// Block that claims to have more than max allowed hashes.
|
||||
{exceedMaxHashes, pver, io.ErrUnexpectedEOF},
|
||||
// Block that claims to have more than max allowed flag bytes.
|
||||
{exceedMaxFlagBytes, pver, io.ErrUnexpectedEOF},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Decode from wire format.
|
||||
var msg MsgMerkleBlock
|
||||
r := bytes.NewReader(test.buf)
|
||||
err := msg.BtcDecode(r, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
|
||||
i, err, reflect.TypeOf(test.err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testMerkleBlock is a basic normative merkle block that is used throughout the
|
||||
// tests.
|
||||
var testMerkleBlock = MsgMerkleBlock{
|
||||
Header: BlockHeader{
|
||||
Version: 1,
|
||||
PrevBlock: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
|
||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}),
|
||||
MerkleRoot: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
|
||||
}),
|
||||
StakeRoot: chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
|
||||
}),
|
||||
VoteBits: uint16(0x0000),
|
||||
FinalState: [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
Voters: uint16(0x0000),
|
||||
FreshStake: uint8(0x00),
|
||||
Revocations: uint8(0x00),
|
||||
Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST
|
||||
Bits: 0x1d00ffff, // 486604799
|
||||
SBits: int64(0x0000000000000000),
|
||||
Nonce: 0x9962e301, // 2573394689
|
||||
ExtraData: [32]byte{},
|
||||
StakeVersion: uint32(0x7e1eca57),
|
||||
},
|
||||
Transactions: 1,
|
||||
Hashes: []*chainhash.Hash{
|
||||
(*chainhash.Hash)(&[chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
|
||||
}),
|
||||
},
|
||||
STransactions: 1,
|
||||
SHashes: []*chainhash.Hash{
|
||||
(*chainhash.Hash)(&[chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e,
|
||||
}),
|
||||
},
|
||||
Flags: []byte{0x80},
|
||||
}
|
||||
|
||||
// testMerkleBlockBytes is the serialized bytes for the above test merkle block.
|
||||
var testMerkleBlockBytes = []byte{
|
||||
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
|
||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // StakeRoot
|
||||
0x00, 0x00, // VoteBits
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FinalState
|
||||
0x00, 0x00, // Voters
|
||||
0x00, // FreshStake
|
||||
0x00, // Revocations
|
||||
0x00, 0x00, 0x00, 0x00, // Poolsize
|
||||
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SBits
|
||||
0x00, 0x00, 0x00, 0x00, // Height
|
||||
0x00, 0x00, 0x00, 0x00, // Size
|
||||
0x61, 0xbc, 0x66, 0x49, // Timestamp
|
||||
0x01, 0xe3, 0x62, 0x99, // Nonce
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ExtraData
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x57, 0xca, 0x1e, 0x7e, // StakeVersion
|
||||
0x01, 0x00, 0x00, 0x00, // TxnCount (regular) [180]
|
||||
0x01, // Num hashes (regular) [184]
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // Hash [185]
|
||||
0x01, 0x00, 0x00, 0x00, // TxnCount (stake) [217]
|
||||
0x01, // Num hashes (stake) [221]
|
||||
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
|
||||
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
|
||||
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
|
||||
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // Hash [222]
|
||||
0x01, // Num flag bytes [254]
|
||||
0x80, // Flags [255]
|
||||
}
|
||||
@ -20,7 +20,7 @@ const (
|
||||
ProtocolVersion uint32 = 6
|
||||
|
||||
// NodeBloomVersion is the protocol version which added the SFNodeBloom
|
||||
// service flag.
|
||||
// service flag (unused).
|
||||
NodeBloomVersion uint32 = 2
|
||||
|
||||
// SendHeadersVersion is the protocol version which added a new
|
||||
|
||||
Loading…
Reference in New Issue
Block a user