multi: Redefine GetCFHeaders to have StartHeight and StopHash.

This commit is contained in:
Jim Posen 2018-01-19 12:34:28 -08:00 committed by Olaoluwa Osuntokun
parent daac60675e
commit 7a53a05878
4 changed files with 55 additions and 147 deletions

View file

@ -540,7 +540,7 @@ func TestPeerListeners(t *testing.T) {
}, },
{ {
"OnGetCFHeaders", "OnGetCFHeaders",
wire.NewMsgGetCFHeaders(), wire.NewMsgGetCFHeaders(wire.GCSFilterRegular, 0, &chainhash.Hash{}),
}, },
{ {
"OnCFilter", "OnCFilter",

View file

@ -788,91 +788,36 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) {
return return
} }
// Attempt to look up the height of the provided stop hash. // Fetch the hashes from the block index.
chain := sp.server.chain hashList, err := sp.server.chain.HeightToHashRange(int32(msg.StartHeight),
endIdx := int32(math.MaxInt32) &msg.StopHash, wire.MaxCFHeadersPerMsg)
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)
if err != nil { if err != nil {
peerLog.Warnf("Header lookup failed: %v", err) peerLog.Debugf("Invalid getcfheaders request: %v", err)
return
} }
if len(hashList) == 0 { if len(hashList) == 0 {
return 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. // Generate cfheaders message and send it.
headersMsg := wire.NewMsgCFHeaders() headersMsg := wire.NewMsgCFHeaders()
for i := range hashList { for i, headerBytes := range filterHeaders {
// Fetch the raw committed filter header bytes from the if len(headerBytes) == 0 {
// database. peerLog.Warnf("Could not obtain CF header for %v", hashList[i])
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)
return return
} }

View file

@ -70,7 +70,7 @@ func TestMessage(t *testing.T) {
msgMerkleBlock := NewMsgMerkleBlock(bh) msgMerkleBlock := NewMsgMerkleBlock(bh)
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block") msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
msgGetCFilters := NewMsgGetCFilters(GCSFilterExtended, 0, &chainhash.Hash{}) msgGetCFilters := NewMsgGetCFilters(GCSFilterExtended, 0, &chainhash.Hash{})
msgGetCFHeaders := NewMsgGetCFHeaders() msgGetCFHeaders := NewMsgGetCFHeaders(GCSFilterExtended, 0, &chainhash.Hash{})
msgCFilter := NewMsgCFilter(GCSFilterExtended, &chainhash.Hash{}, msgCFilter := NewMsgCFilter(GCSFilterExtended, &chainhash.Hash{},
[]byte("payload")) []byte("payload"))
msgCFHeaders := NewMsgCFHeaders() msgCFHeaders := NewMsgCFHeaders()
@ -104,7 +104,7 @@ func TestMessage(t *testing.T) {
{msgMerkleBlock, msgMerkleBlock, pver, MainNet, 110}, {msgMerkleBlock, msgMerkleBlock, pver, MainNet, 110},
{msgReject, msgReject, pver, MainNet, 79}, {msgReject, msgReject, pver, MainNet, 79},
{msgGetCFilters, msgGetCFilters, pver, MainNet, 61}, {msgGetCFilters, msgGetCFilters, pver, MainNet, 61},
{msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 58}, {msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 61},
{msgCFilter, msgCFilter, pver, MainNet, 65}, {msgCFilter, msgCFilter, pver, MainNet, 65},
{msgCFHeaders, msgCFHeaders, pver, MainNet, 58}, {msgCFHeaders, msgCFHeaders, pver, MainNet, 58},
} }

View file

@ -5,7 +5,6 @@
package wire package wire
import ( import (
"fmt"
"io" "io"
"github.com/btcsuite/btcd/chaincfg/chainhash" "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 // filter headers. It allows to set the FilterType field to get headers in the
// chain of basic (0x00) or extended (0x01) headers. // chain of basic (0x00) or extended (0x01) headers.
type MsgGetCFHeaders struct { type MsgGetCFHeaders struct {
BlockLocatorHashes []*chainhash.Hash FilterType FilterType
HashStop chainhash.Hash StartHeight uint32
FilterType FilterType StopHash chainhash.Hash
}
// 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
} }
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
// Read num block locator hashes and limit to max. err := readElement(r, &msg.FilterType)
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)
if err != nil { if err != nil {
return err 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. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { func (msg *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
// Limit to max block locator hashes per message. err := writeElement(w, msg.FilterType)
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))
if err != nil { if err != nil {
return err return err
} }
for _, hash := range msg.BlockLocatorHashes { err = writeElement(w, &msg.StartHeight)
err := writeElement(w, hash)
if err != nil {
return err
}
}
err = writeElement(w, &msg.HashStop)
if err != nil { if err != nil {
return err 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 // 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 // MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation. // receiver. This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) MaxPayloadLength(pver uint32) uint32 { func (msg *MsgGetCFHeaders) MaxPayloadLength(pver uint32) uint32 {
// Num block locator hashes (varInt) + max allowed // Filter type + uint32 + block hash
// block locators + hash stop + filter type 1 byte. return 1 + 4 + chainhash.HashSize
return MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
chainhash.HashSize) + chainhash.HashSize + 1
} }
// NewMsgGetCFHeaders returns a new bitcoin getcfheader message that conforms to // NewMsgGetCFHeaders returns a new bitcoin getcfheader message that conforms to
// the Message interface using the passed parameters and defaults for the // the Message interface using the passed parameters and defaults for the
// remaining fields. // remaining fields.
func NewMsgGetCFHeaders() *MsgGetCFHeaders { func NewMsgGetCFHeaders(filterType FilterType, startHeight uint32,
stopHash *chainhash.Hash) *MsgGetCFHeaders {
return &MsgGetCFHeaders{ return &MsgGetCFHeaders{
BlockLocatorHashes: make([]*chainhash.Hash, 0, FilterType: filterType,
MaxBlockLocatorsPerMsg), StartHeight: startHeight,
StopHash: *stopHash,
} }
} }