From daac60675e60d6f554b5abc5d9ee02d053fcedc2 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Thu, 18 Jan 2018 19:10:42 -0800 Subject: [PATCH] multi: Redefine GetCFilters to have StartHeight and StopHash. --- peer/peer.go | 10 ++--- peer/peer_test.go | 11 +++-- server.go | 46 ++++++++++++++------- wire/message.go | 6 +-- wire/message_test.go | 6 +-- wire/msgcfilter.go | 26 ++++++------ wire/msggetcfilter.go | 62 ---------------------------- wire/msggetcfilters.go | 91 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 152 insertions(+), 106 deletions(-) delete mode 100644 wire/msggetcfilter.go create mode 100644 wire/msggetcfilters.go diff --git a/peer/peer.go b/peer/peer.go index 86619c538..b9d802a4c 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -151,9 +151,9 @@ type MessageListeners struct { // message. OnGetHeaders func(p *Peer, msg *wire.MsgGetHeaders) - // OnGetCFilter is invoked when a peer receives a getcfilter bitcoin + // OnGetCFilters is invoked when a peer receives a getcfilters bitcoin // message. - OnGetCFilter func(p *Peer, msg *wire.MsgGetCFilter) + OnGetCFilters func(p *Peer, msg *wire.MsgGetCFilters) // OnGetCFHeaders is invoked when a peer receives a getcfheaders // bitcoin message. @@ -1591,9 +1591,9 @@ out: p.cfg.Listeners.OnGetHeaders(p, msg) } - case *wire.MsgGetCFilter: - if p.cfg.Listeners.OnGetCFilter != nil { - p.cfg.Listeners.OnGetCFilter(p, msg) + case *wire.MsgGetCFilters: + if p.cfg.Listeners.OnGetCFilters != nil { + p.cfg.Listeners.OnGetCFilters(p, msg) } case *wire.MsgGetCFHeaders: diff --git a/peer/peer_test.go b/peer/peer_test.go index 5dc42a886..b4c0a4f44 100644 --- a/peer/peer_test.go +++ b/peer/peer_test.go @@ -399,7 +399,7 @@ func TestPeerListeners(t *testing.T) { OnGetHeaders: func(p *peer.Peer, msg *wire.MsgGetHeaders) { ok <- msg }, - OnGetCFilter: func(p *peer.Peer, msg *wire.MsgGetCFilter) { + OnGetCFilters: func(p *peer.Peer, msg *wire.MsgGetCFilters) { ok <- msg }, OnGetCFHeaders: func(p *peer.Peer, msg *wire.MsgGetCFHeaders) { @@ -535,9 +535,8 @@ func TestPeerListeners(t *testing.T) { wire.NewMsgGetHeaders(), }, { - "OnGetCFilter", - wire.NewMsgGetCFilter(&chainhash.Hash{}, - wire.GCSFilterRegular), + "OnGetCFilters", + wire.NewMsgGetCFilters(wire.GCSFilterRegular, 0, &chainhash.Hash{}), }, { "OnGetCFHeaders", @@ -545,8 +544,8 @@ func TestPeerListeners(t *testing.T) { }, { "OnCFilter", - wire.NewMsgCFilter(&chainhash.Hash{}, - wire.GCSFilterRegular, []byte("payload")), + wire.NewMsgCFilter(wire.GCSFilterRegular, &chainhash.Hash{}, + []byte("payload")), }, { "OnCFHeaders", diff --git a/server.go b/server.go index e49f9bfe1..71f71ed3d 100644 --- a/server.go +++ b/server.go @@ -743,26 +743,42 @@ func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) { sp.QueueMessage(&wire.MsgHeaders{Headers: blockHeaders}, nil) } -// OnGetCFilter is invoked when a peer receives a getcfilter bitcoin message. -func (sp *serverPeer) OnGetCFilter(_ *peer.Peer, msg *wire.MsgGetCFilter) { - // Ignore getcfilter requests if not in sync. +// OnGetCFilters is invoked when a peer receives a getcfilters bitcoin message. +func (sp *serverPeer) OnGetCFilters(_ *peer.Peer, msg *wire.MsgGetCFilters) { + // Ignore getcfilters requests if not in sync. if !sp.server.syncManager.IsCurrent() { return } - filterBytes, err := sp.server.cfIndex.FilterByBlockHash(&msg.BlockHash, - msg.FilterType) - - if len(filterBytes) > 0 { - peerLog.Tracef("Obtained CF for %v", msg.BlockHash) - } else { - peerLog.Warnf("Could not obtain CF for %v: %v", msg.BlockHash, - err) + hashes, err := sp.server.chain.HeightToHashRange(int32(msg.StartHeight), + &msg.StopHash, wire.MaxGetCFiltersReqRange) + if err != nil { + peerLog.Debugf("Invalid getcfilters request: %v", err) + return } - filterMsg := wire.NewMsgCFilter(&msg.BlockHash, msg.FilterType, - filterBytes) - sp.QueueMessage(filterMsg, nil) + // Create []*chainhash.Hash from []chainhash.Hash to pass to + // FiltersByBlockHashes. + hashPtrs := make([]*chainhash.Hash, len(hashes)) + for i := range hashes { + hashPtrs[i] = &hashes[i] + } + + filters, err := sp.server.cfIndex.FiltersByBlockHashes(hashPtrs, + msg.FilterType) + if err != nil { + peerLog.Errorf("Error retrieving cfilters: %v", err) + return + } + + for i, filterBytes := range filters { + if len(filterBytes) == 0 { + peerLog.Warnf("Could not obtain cfilter for %v", hashes[i]) + return + } + filterMsg := wire.NewMsgCFilter(msg.FilterType, &hashes[i], filterBytes) + sp.QueueMessage(filterMsg, nil) + } } // OnGetCFHeaders is invoked when a peer receives a getcfheader bitcoin message. @@ -1724,7 +1740,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config { OnGetData: sp.OnGetData, OnGetBlocks: sp.OnGetBlocks, OnGetHeaders: sp.OnGetHeaders, - OnGetCFilter: sp.OnGetCFilter, + OnGetCFilters: sp.OnGetCFilters, OnGetCFHeaders: sp.OnGetCFHeaders, OnFeeFilter: sp.OnFeeFilter, OnFilterAdd: sp.OnFilterAdd, diff --git a/wire/message.go b/wire/message.go index 9a34a2fa5..f00267d72 100644 --- a/wire/message.go +++ b/wire/message.go @@ -51,7 +51,7 @@ const ( CmdReject = "reject" CmdSendHeaders = "sendheaders" CmdFeeFilter = "feefilter" - CmdGetCFilter = "getcfilter" + CmdGetCFilters = "getcfilters" CmdGetCFHeaders = "getcfheaders" CmdCFilter = "cfilter" CmdCFHeaders = "cfheaders" @@ -160,8 +160,8 @@ func makeEmptyMessage(command string) (Message, error) { case CmdFeeFilter: msg = &MsgFeeFilter{} - case CmdGetCFilter: - msg = &MsgGetCFilter{} + case CmdGetCFilters: + msg = &MsgGetCFilters{} case CmdGetCFHeaders: msg = &MsgGetCFHeaders{} diff --git a/wire/message_test.go b/wire/message_test.go index 69853f05d..e36fe01bb 100644 --- a/wire/message_test.go +++ b/wire/message_test.go @@ -69,9 +69,9 @@ func TestMessage(t *testing.T) { bh := NewBlockHeader(1, &chainhash.Hash{}, &chainhash.Hash{}, 0, 0) msgMerkleBlock := NewMsgMerkleBlock(bh) msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block") - msgGetCFilter := NewMsgGetCFilter(&chainhash.Hash{}, GCSFilterExtended) + msgGetCFilters := NewMsgGetCFilters(GCSFilterExtended, 0, &chainhash.Hash{}) msgGetCFHeaders := NewMsgGetCFHeaders() - msgCFilter := NewMsgCFilter(&chainhash.Hash{}, GCSFilterExtended, + msgCFilter := NewMsgCFilter(GCSFilterExtended, &chainhash.Hash{}, []byte("payload")) msgCFHeaders := NewMsgCFHeaders() @@ -103,7 +103,7 @@ func TestMessage(t *testing.T) { {msgFilterLoad, msgFilterLoad, pver, MainNet, 35}, {msgMerkleBlock, msgMerkleBlock, pver, MainNet, 110}, {msgReject, msgReject, pver, MainNet, 79}, - {msgGetCFilter, msgGetCFilter, pver, MainNet, 57}, + {msgGetCFilters, msgGetCFilters, pver, MainNet, 61}, {msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 58}, {msgCFilter, msgCFilter, pver, MainNet, 65}, {msgCFHeaders, msgCFHeaders, pver, MainNet, 58}, diff --git a/wire/msgcfilter.go b/wire/msgcfilter.go index 77b4602c4..7387587fd 100644 --- a/wire/msgcfilter.go +++ b/wire/msgcfilter.go @@ -30,26 +30,28 @@ const ( // MsgCFilter implements the Message interface and represents a bitcoin cfilter // message. It is used to deliver a committed filter in response to a -// getcfilter (MsgGetCFilter) message. +// getcfilters (MsgGetCFilters) message. type MsgCFilter struct { - BlockHash chainhash.Hash FilterType FilterType + BlockHash chainhash.Hash Data []byte } // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. // This is part of the Message interface implementation. func (msg *MsgCFilter) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { - // Read the hash of the filter's block - err := readElement(r, &msg.BlockHash) - if err != nil { - return err - } // Read filter type - err = readElement(r, &msg.FilterType) + err := readElement(r, &msg.FilterType) if err != nil { return err } + + // Read the hash of the filter's block + err = readElement(r, &msg.BlockHash) + if err != nil { + return err + } + // Read filter data msg.Data, err = ReadVarBytes(r, pver, MaxCFilterDataSize, "cfilter data") @@ -66,12 +68,12 @@ func (msg *MsgCFilter) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) er return messageError("MsgCFilter.BtcEncode", str) } - err := writeElement(w, msg.BlockHash) + err := writeElement(w, msg.FilterType) if err != nil { return err } - err = writeElement(w, msg.FilterType) + err = writeElement(w, msg.BlockHash) if err != nil { return err } @@ -110,11 +112,11 @@ func (msg *MsgCFilter) MaxPayloadLength(pver uint32) uint32 { // NewMsgCFilter returns a new bitcoin cfilter message that conforms to the // Message interface. See MsgCFilter for details. -func NewMsgCFilter(blockHash *chainhash.Hash, filterType FilterType, +func NewMsgCFilter(filterType FilterType, blockHash *chainhash.Hash, data []byte) *MsgCFilter { return &MsgCFilter{ - BlockHash: *blockHash, FilterType: filterType, + BlockHash: *blockHash, Data: data, } } diff --git a/wire/msggetcfilter.go b/wire/msggetcfilter.go deleted file mode 100644 index 527a391e5..000000000 --- a/wire/msggetcfilter.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package wire - -import ( - "io" - - "github.com/btcsuite/btcd/chaincfg/chainhash" -) - -// MsgGetCFilter implements the Message interface and represents a bitcoin -// getcfilter message. It is used to request a committed filter for a block. -type MsgGetCFilter struct { - BlockHash chainhash.Hash - FilterType FilterType -} - -// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. -// This is part of the Message interface implementation. -func (msg *MsgGetCFilter) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { - err := readElement(r, &msg.BlockHash) - if err != nil { - return err - } - return readElement(r, &msg.FilterType) -} - -// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. -// This is part of the Message interface implementation. -func (msg *MsgGetCFilter) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { - err := writeElement(w, &msg.BlockHash) - if err != nil { - return err - } - return writeElement(w, msg.FilterType) -} - -// Command returns the protocol command string for the message. This is part -// of the Message interface implementation. -func (msg *MsgGetCFilter) Command() string { - return CmdGetCFilter -} - -// MaxPayloadLength returns the maximum length the payload can be for the -// receiver. This is part of the Message interface implementation. -func (msg *MsgGetCFilter) MaxPayloadLength(pver uint32) uint32 { - // Block hash + filter type. - return chainhash.HashSize + 1 -} - -// NewMsgGetCFilter returns a new bitcoin getcfilter message that conforms to -// the Message interface using the passed parameters and defaults for the -// remaining fields. -func NewMsgGetCFilter(blockHash *chainhash.Hash, - filterType FilterType) *MsgGetCFilter { - return &MsgGetCFilter{ - BlockHash: *blockHash, - FilterType: filterType, - } -} diff --git a/wire/msggetcfilters.go b/wire/msggetcfilters.go new file mode 100644 index 000000000..1156eea84 --- /dev/null +++ b/wire/msggetcfilters.go @@ -0,0 +1,91 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "io" + + "github.com/roasbeef/btcd/chaincfg/chainhash" +) + +// MaxGetCFiltersReqRange the maximum number of filters that may be requested in +// a getcfheaders message. +const MaxGetCFiltersReqRange = 100 + +// MsgGetCFilters implements the Message interface and represents a bitcoin +// getcfilters message. It is used to request committed filters for a range of +// blocks. +type MsgGetCFilters struct { + FilterType FilterType + StartHeight uint32 + StopHash chainhash.Hash +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgGetCFilters) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { + err := readElement(r, &msg.FilterType) + if err != nil { + return err + } + + err = readElement(r, &msg.StartHeight) + if err != nil { + return err + } + + err = readElement(r, &msg.StopHash) + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgGetCFilters) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { + err := writeElement(w, msg.FilterType) + if err != nil { + return err + } + + err = writeElement(w, &msg.StartHeight) + if err != nil { + return err + } + + err = writeElement(w, &msg.StopHash) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgGetCFilters) Command() string { + return CmdGetCFilters +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgGetCFilters) MaxPayloadLength(pver uint32) uint32 { + // Filter type + uint32 + block hash + return 1 + 4 + chainhash.HashSize +} + +// NewMsgGetCFilters returns a new bitcoin getcfilters message that conforms to +// the Message interface using the passed parameters and defaults for the +// remaining fields. +func NewMsgGetCFilters(filterType FilterType, startHeight uint32, + stopHash *chainhash.Hash) *MsgGetCFilters { + return &MsgGetCFilters{ + FilterType: filterType, + StartHeight: startHeight, + StopHash: *stopHash, + } +}