diff --git a/server.go b/server.go index 4b8c468a..b20a29d4 100644 --- a/server.go +++ b/server.go @@ -788,13 +788,28 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { return } + startHeight := int32(msg.StartHeight) + maxResults := wire.MaxCFHeadersPerMsg + + // If StartHeight is positive, fetch the predecessor block hash so we can + // populate the PrevFilterHeader field. + if msg.StartHeight > 0 { + startHeight-- + maxResults++ + } + // Fetch the hashes from the block index. - hashList, err := sp.server.chain.HeightToHashRange(int32(msg.StartHeight), - &msg.StopHash, wire.MaxCFHeadersPerMsg) + hashList, err := sp.server.chain.HeightToHashRange(startHeight, + &msg.StopHash, maxResults) if err != nil { peerLog.Debugf("Invalid getcfheaders request: %v", err) } - if len(hashList) == 0 { + + // This is possible if StartHeight is one greater that the height of + // StopHash, and we pull a valid range of hashes including the previous + // filter header. + if len(hashList) == 0 || (msg.StartHeight > 0 && len(hashList) == 1) { + peerLog.Debug("No results for getcfheaders request") return } @@ -815,6 +830,37 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { // Generate cfheaders message and send it. headersMsg := wire.NewMsgCFHeaders() + + // Populate the PrevFilterHeader field. + if msg.StartHeight > 0 { + prevBlockHash := &hashList[0] + + // Fetch the raw committed filter header bytes from the + // database. + headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash( + prevBlockHash, msg.FilterType) + if err != nil { + peerLog.Errorf("Error retrieving CF header: %v", err) + return + } + if len(headerBytes) == 0 { + peerLog.Warnf("Could not obtain CF header for %v", prevBlockHash) + return + } + + // Deserialize the hash into PrevFilterHeader. + err = headersMsg.PrevFilterHeader.SetBytes(headerBytes) + if err != nil { + peerLog.Warnf("Committed filter header deserialize "+ + "failed: %v", err) + return + } + + hashList = hashList[1:] + filterHeaders = filterHeaders[1:] + } + + // Populate HeaderHashes. for i, headerBytes := range filterHeaders { if len(headerBytes) == 0 { peerLog.Warnf("Could not obtain CF header for %v", hashList[i]) diff --git a/wire/message_test.go b/wire/message_test.go index 427fdf1c..806a196e 100644 --- a/wire/message_test.go +++ b/wire/message_test.go @@ -106,7 +106,7 @@ func TestMessage(t *testing.T) { {msgGetCFilters, msgGetCFilters, pver, MainNet, 61}, {msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 61}, {msgCFilter, msgCFilter, pver, MainNet, 65}, - {msgCFHeaders, msgCFHeaders, pver, MainNet, 58}, + {msgCFHeaders, msgCFHeaders, pver, MainNet, 90}, } t.Logf("Running %d tests", len(tests)) diff --git a/wire/msgcfheaders.go b/wire/msgcfheaders.go index 0186a53f..a7ce306e 100644 --- a/wire/msgcfheaders.go +++ b/wire/msgcfheaders.go @@ -27,9 +27,10 @@ const ( // of committed filter headers per message is currently 2000. See // MsgGetCFHeaders for details on requesting the headers. type MsgCFHeaders struct { - StopHash chainhash.Hash - FilterType FilterType - HeaderHashes []*chainhash.Hash + FilterType FilterType + StopHash chainhash.Hash + PrevFilterHeader chainhash.Hash + HeaderHashes []*chainhash.Hash } // AddCFHeader adds a new committed filter header to the message. @@ -47,14 +48,20 @@ func (msg *MsgCFHeaders) AddCFHeader(headerHash *chainhash.Hash) error { // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. // This is part of the Message interface implementation. func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { - // Read stop hash - err := readElement(r, &msg.StopHash) + // Read filter type + err := readElement(r, &msg.FilterType) if err != nil { return err } - // Read filter type - err = readElement(r, &msg.FilterType) + // Read stop hash + err = readElement(r, &msg.StopHash) + if err != nil { + return err + } + + // Read prev filter header + err = readElement(r, &msg.PrevFilterHeader) if err != nil { return err } @@ -91,14 +98,20 @@ func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) // BtcEncode encodes the receiver to w using the bitcoin protocol encoding. // This is part of the Message interface implementation. func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { - // Write stop hash - err := writeElement(w, msg.StopHash) + // Write filter type + err := writeElement(w, msg.FilterType) if err != nil { return err } - // Write filter type - err = writeElement(w, msg.FilterType) + // Write stop hash + err = writeElement(w, msg.StopHash) + if err != nil { + return err + } + + // Write prev filter header + err = writeElement(w, msg.PrevFilterHeader) if err != nil { return err } @@ -154,7 +167,7 @@ func (msg *MsgCFHeaders) Command() string { func (msg *MsgCFHeaders) MaxPayloadLength(pver uint32) uint32 { // Hash size + filter type + num headers (varInt) + // (header size * max headers). - return chainhash.HashSize + 1 + MaxVarIntPayload + + return 1 + chainhash.HashSize + chainhash.HashSize + MaxVarIntPayload + (MaxCFHeaderPayload * MaxCFHeadersPerMsg) }