From 7a53a058783408b17de5f24d2c334e429cdcd964 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 19 Jan 2018 12:34:28 -0800 Subject: [PATCH] multi: Redefine GetCFHeaders to have StartHeight and StopHash. --- peer/peer_test.go | 2 +- server.go | 99 +++++++++-------------------------------- wire/message_test.go | 4 +- wire/msggetcfheaders.go | 97 +++++++++++++--------------------------- 4 files changed, 55 insertions(+), 147 deletions(-) diff --git a/peer/peer_test.go b/peer/peer_test.go index b4c0a4f4..cec2f5d2 100644 --- a/peer/peer_test.go +++ b/peer/peer_test.go @@ -540,7 +540,7 @@ func TestPeerListeners(t *testing.T) { }, { "OnGetCFHeaders", - wire.NewMsgGetCFHeaders(), + wire.NewMsgGetCFHeaders(wire.GCSFilterRegular, 0, &chainhash.Hash{}), }, { "OnCFilter", diff --git a/server.go b/server.go index 71f71ed3..4b8c468a 100644 --- a/server.go +++ b/server.go @@ -788,91 +788,36 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { return } - // Attempt to look up the height of the provided stop hash. - chain := sp.server.chain - endIdx := int32(math.MaxInt32) - height, err := chain.BlockHeightByHash(&msg.HashStop) - if err == nil { - endIdx = height + 1 - } - - // There are no block locators so a specific header is being requested - // as identified by the stop hash. - if len(msg.BlockLocatorHashes) == 0 { - // No blocks with the stop hash were found so there is nothing - // to do. Just return. This behavior mirrors the reference - // implementation. - if endIdx == math.MaxInt32 { - return - } - - // Fetch the raw committed filter header bytes from the - // database. - headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash( - &msg.HashStop, msg.FilterType) - if (err != nil) || (len(headerBytes) == 0) { - peerLog.Warnf("Could not obtain CF header for %v: %v", - msg.HashStop, err) - return - } - - // Deserialize the hash. - var header chainhash.Hash - err = header.SetBytes(headerBytes) - if err != nil { - peerLog.Warnf("Committed filter header deserialize "+ - "failed: %v", err) - return - } - - headersMsg := wire.NewMsgCFHeaders() - headersMsg.AddCFHeader(&header) - headersMsg.StopHash = msg.HashStop - headersMsg.FilterType = msg.FilterType - sp.QueueMessage(headersMsg, nil) - return - } - - // Find the most recent known block based on the block locator. - // Use the block after the genesis block if no other blocks in the - // provided locator are known. This does mean the client will start - // over with the genesis block if unknown block locators are provided. - // This mirrors the behavior in the reference implementation. - startIdx := int32(1) - for _, hash := range msg.BlockLocatorHashes { - height, err := chain.BlockHeightByHash(hash) - if err == nil { - // Start with the next hash since we know this one. - startIdx = height + 1 - break - } - } - - // Don't attempt to fetch more than we can put into a single message. - if endIdx-startIdx > wire.MaxBlockHeadersPerMsg { - endIdx = startIdx + wire.MaxBlockHeadersPerMsg - } - - // Fetch the inventory from the block database. - hashList, err := chain.HeightRange(startIdx, endIdx) + // Fetch the hashes from the block index. + hashList, err := sp.server.chain.HeightToHashRange(int32(msg.StartHeight), + &msg.StopHash, wire.MaxCFHeadersPerMsg) if err != nil { - peerLog.Warnf("Header lookup failed: %v", err) - return + peerLog.Debugf("Invalid getcfheaders request: %v", err) } if len(hashList) == 0 { return } + // Create []*chainhash.Hash from []chainhash.Hash to pass to + // FilterHeadersByBlockHashes. + hashPtrs := make([]*chainhash.Hash, len(hashList)) + for i := range hashList { + hashPtrs[i] = &hashList[i] + } + + // Fetch the raw filter header bytes from the database for all blocks. + filterHeaders, err := sp.server.cfIndex.FilterHeadersByBlockHashes(hashPtrs, + msg.FilterType) + if err != nil { + peerLog.Errorf("Error retrieving cfilters: %v", err) + return + } + // Generate cfheaders message and send it. headersMsg := wire.NewMsgCFHeaders() - for i := range hashList { - // Fetch the raw committed filter header bytes from the - // database. - headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash( - &hashList[i], msg.FilterType) - if (err != nil) || (len(headerBytes) == 0) { - peerLog.Warnf("Could not obtain CF header for %v: %v", - hashList[i], err) + for i, headerBytes := range filterHeaders { + if len(headerBytes) == 0 { + peerLog.Warnf("Could not obtain CF header for %v", hashList[i]) return } diff --git a/wire/message_test.go b/wire/message_test.go index e36fe01b..427fdf1c 100644 --- a/wire/message_test.go +++ b/wire/message_test.go @@ -70,7 +70,7 @@ func TestMessage(t *testing.T) { msgMerkleBlock := NewMsgMerkleBlock(bh) msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block") msgGetCFilters := NewMsgGetCFilters(GCSFilterExtended, 0, &chainhash.Hash{}) - msgGetCFHeaders := NewMsgGetCFHeaders() + msgGetCFHeaders := NewMsgGetCFHeaders(GCSFilterExtended, 0, &chainhash.Hash{}) msgCFilter := NewMsgCFilter(GCSFilterExtended, &chainhash.Hash{}, []byte("payload")) msgCFHeaders := NewMsgCFHeaders() @@ -104,7 +104,7 @@ func TestMessage(t *testing.T) { {msgMerkleBlock, msgMerkleBlock, pver, MainNet, 110}, {msgReject, msgReject, pver, MainNet, 79}, {msgGetCFilters, msgGetCFilters, pver, MainNet, 61}, - {msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 58}, + {msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 61}, {msgCFilter, msgCFilter, pver, MainNet, 65}, {msgCFHeaders, msgCFHeaders, pver, MainNet, 58}, } diff --git a/wire/msggetcfheaders.go b/wire/msggetcfheaders.go index d93e4e62..d7928643 100644 --- a/wire/msggetcfheaders.go +++ b/wire/msggetcfheaders.go @@ -5,7 +5,6 @@ package wire import ( - "fmt" "io" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -15,87 +14,51 @@ import ( // filter headers. It allows to set the FilterType field to get headers in the // chain of basic (0x00) or extended (0x01) headers. type MsgGetCFHeaders struct { - BlockLocatorHashes []*chainhash.Hash - HashStop chainhash.Hash - FilterType FilterType -} - -// AddBlockLocatorHash adds a new block locator hash to the message. -func (msg *MsgGetCFHeaders) AddBlockLocatorHash(hash *chainhash.Hash) error { - if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg { - str := fmt.Sprintf("too many block locator hashes for message [max %v]", - MaxBlockLocatorsPerMsg) - return messageError("MsgGetCFHeaders.AddBlockLocatorHash", str) - } - - msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash) - return nil + 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 *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { - // Read num block locator hashes and limit to max. - count, err := ReadVarInt(r, pver) - if err != nil { - return err - } - if count > MaxBlockLocatorsPerMsg { - str := fmt.Sprintf("too many block locator hashes for message "+ - "[count %v, max %v]", count, MaxBlockLocatorsPerMsg) - return messageError("MsgGetHeaders.BtcDecode", str) - } - - // Create a contiguous slice of hashes to deserialize into in order to - // reduce the number of allocations. - locatorHashes := make([]chainhash.Hash, count) - msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count) - for i := uint64(0); i < count; i++ { - hash := &locatorHashes[i] - err := readElement(r, hash) - if err != nil { - return err - } - msg.AddBlockLocatorHash(hash) - } - - err = readElement(r, &msg.HashStop) + err := readElement(r, &msg.FilterType) if err != nil { return err } - return readElement(r, &msg.FilterType) + 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 *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { - // Limit to max block locator hashes per message. - count := len(msg.BlockLocatorHashes) - if count > MaxBlockLocatorsPerMsg { - str := fmt.Sprintf("too many block locator hashes for message "+ - "[count %v, max %v]", count, MaxBlockLocatorsPerMsg) - return messageError("MsgGetHeaders.BtcEncode", str) - } - - err := WriteVarInt(w, pver, uint64(count)) + err := writeElement(w, msg.FilterType) if err != nil { return err } - for _, hash := range msg.BlockLocatorHashes { - err := writeElement(w, hash) - if err != nil { - return err - } - } - - err = writeElement(w, &msg.HashStop) + err = writeElement(w, &msg.StartHeight) if err != nil { return err } - return writeElement(w, msg.FilterType) + err = writeElement(w, &msg.StopHash) + if err != nil { + return err + } + + return nil } // Command returns the protocol command string for the message. This is part @@ -107,18 +70,18 @@ func (msg *MsgGetCFHeaders) Command() string { // MaxPayloadLength returns the maximum length the payload can be for the // receiver. This is part of the Message interface implementation. func (msg *MsgGetCFHeaders) MaxPayloadLength(pver uint32) uint32 { - // Num block locator hashes (varInt) + max allowed - // block locators + hash stop + filter type 1 byte. - return MaxVarIntPayload + (MaxBlockLocatorsPerMsg * - chainhash.HashSize) + chainhash.HashSize + 1 + // Filter type + uint32 + block hash + return 1 + 4 + chainhash.HashSize } // NewMsgGetCFHeaders returns a new bitcoin getcfheader message that conforms to // the Message interface using the passed parameters and defaults for the // remaining fields. -func NewMsgGetCFHeaders() *MsgGetCFHeaders { +func NewMsgGetCFHeaders(filterType FilterType, startHeight uint32, + stopHash *chainhash.Hash) *MsgGetCFHeaders { return &MsgGetCFHeaders{ - BlockLocatorHashes: make([]*chainhash.Hash, 0, - MaxBlockLocatorsPerMsg), + FilterType: filterType, + StartHeight: startHeight, + StopHash: *stopHash, } }