multi: Remove everything to do about bloom filters.

This commit is contained in:
David Hill 2018-03-30 15:42:37 -04:00 committed by Dave Collins
parent b3ce2ad2ad
commit 5a4f367b66
30 changed files with 10 additions and 3178 deletions

View File

@ -1,25 +0,0 @@
bloom
=====
[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](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.

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
{

View File

@ -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
View File

@ -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
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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{}

View File

@ -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]

View File

@ -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,
}
}

View File

@ -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
}
}
}
}

View File

@ -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{}
}

View File

@ -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
}
}
}

View File

@ -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,
}
}

View File

@ -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
}
}
}
}

View File

@ -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),
}
}

View File

@ -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]
}

View File

@ -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