Changed getcfheaders/cfheaders messages to get multiple headers.

This commit is contained in:
Alex 2017-03-08 16:14:39 -08:00 committed by Olaoluwa Osuntokun
parent 860100019f
commit 29b5ece196
9 changed files with 440 additions and 220 deletions

View file

@ -126,9 +126,9 @@ type MessageListeners struct {
// OnCFilter is invoked when a peer receives a cfilter bitcoin message.
OnCFilter func(p *Peer, msg *wire.MsgCFilter)
// OnCFHeader is invoked when a peer receives a cfheader bitcoin
// OnCFHeaders is invoked when a peer receives a cfheader bitcoin
// message.
OnCFHeader func(p *Peer, msg *wire.MsgCFHeader)
OnCFHeaders func(p *Peer, msg *wire.MsgCFHeaders)
// OnInv is invoked when a peer receives an inv bitcoin message.
OnInv func(p *Peer, msg *wire.MsgInv)
@ -155,9 +155,9 @@ type MessageListeners struct {
// message.
OnGetCFilter func(p *Peer, msg *wire.MsgGetCFilter)
// OnGetCFHeader is invoked when a peer receives a getcfheader
// OnGetCFHeaders is invoked when a peer receives a getcfheader
// bitcoin message.
OnGetCFHeader func(p *Peer, msg *wire.MsgGetCFHeader)
OnGetCFHeaders func(p *Peer, msg *wire.MsgGetCFHeaders)
// OnFeeFilter is invoked when a peer receives a feefilter bitcoin message.
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
@ -1599,9 +1599,9 @@ out:
p.cfg.Listeners.OnGetCFilter(p, msg)
}
case *wire.MsgGetCFHeader:
if p.cfg.Listeners.OnGetCFHeader != nil {
p.cfg.Listeners.OnGetCFHeader(p, msg)
case *wire.MsgGetCFHeaders:
if p.cfg.Listeners.OnGetCFHeaders != nil {
p.cfg.Listeners.OnGetCFHeaders(p, msg)
}
case *wire.MsgCFilter:
@ -1609,9 +1609,9 @@ out:
p.cfg.Listeners.OnCFilter(p, msg)
}
case *wire.MsgCFHeader:
if p.cfg.Listeners.OnCFHeader != nil {
p.cfg.Listeners.OnCFHeader(p, msg)
case *wire.MsgCFHeaders:
if p.cfg.Listeners.OnCFHeaders != nil {
p.cfg.Listeners.OnCFHeaders(p, msg)
}
case *wire.MsgFeeFilter:

View file

@ -402,13 +402,13 @@ func TestPeerListeners(t *testing.T) {
OnGetCFilter: func(p *peer.Peer, msg *wire.MsgGetCFilter) {
ok <- msg
},
OnGetCFHeader: func(p *peer.Peer, msg *wire.MsgGetCFHeader) {
OnGetCFHeaders: func(p *peer.Peer, msg *wire.MsgGetCFHeaders) {
ok <- msg
},
OnCFilter: func(p *peer.Peer, msg *wire.MsgCFilter) {
ok <- msg
},
OnCFHeader: func(p *peer.Peer, msg *wire.MsgCFHeader) {
OnCFHeaders: func(p *peer.Peer, msg *wire.MsgCFHeaders) {
ok <- msg
},
OnFeeFilter: func(p *peer.Peer, msg *wire.MsgFeeFilter) {
@ -539,16 +539,16 @@ func TestPeerListeners(t *testing.T) {
wire.NewMsgGetCFilter(&chainhash.Hash{}, false),
},
{
"OnGetCFHeader",
wire.NewMsgGetCFHeader(&chainhash.Hash{}, false),
"OnGetCFHeaders",
wire.NewMsgGetCFHeaders(),
},
{
"OnCFilter",
wire.NewMsgCFilter([]byte("payload")),
},
{
"OnCFHeader",
wire.NewMsgCFHeader([]byte("payload")),
"OnCFHeaders",
wire.NewMsgCFHeaders(),
},
{
"OnFeeFilter",

146
server.go
View file

@ -760,25 +760,109 @@ func (sp *serverPeer) OnGetCFilter(_ *peer.Peer, msg *wire.MsgGetCFilter) {
sp.QueueMessage(filterMsg, nil)
}
// OnGetCFHeader is invoked when a peer receives a getcfheader bitcoin message.
func (sp *serverPeer) OnGetCFHeader(_ *peer.Peer, msg *wire.MsgGetCFHeader) {
// OnGetCFHeaders is invoked when a peer receives a getcfheader bitcoin message.
func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) {
// Ignore getcfilterheader requests if not in sync.
if !sp.server.blockManager.IsCurrent() {
return
}
headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash(
&msg.BlockHash, msg.Extended)
if len(headerBytes) > 0 {
peerLog.Infof("Obtained CF header for %v", msg.BlockHash)
} else {
peerLog.Infof("Could not obtain CF header for %v: %v",
msg.BlockHash, err)
// Attempt to look up the height of the provided stop hash.
chain := sp.server.blockManager.chain
endIdx := int32(math.MaxInt32)
height, err := chain.BlockHeightByHash(&msg.HashStop)
if err == nil {
endIdx = height + 1
}
headerMsg := wire.NewMsgCFHeader(headerBytes)
sp.QueueMessage(headerMsg, nil)
// 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.Extended)
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)
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 {
peerLog.Warnf("Header lookup failed: %v", err)
return
}
// Generate cfheaders message and send it.
headersMsg := wire.NewMsgCFHeaders()
var header chainhash.Hash
for i := range hashList {
// Fetch the raw committed filter header bytes from the
// database.
headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash(
&hashList[i], msg.Extended)
if (err != nil) || (len(headerBytes) == 0) {
peerLog.Warnf("Could not obtain CF header for %v: %v",
hashList[i], err)
return
}
// Deserialize the hash.
err = header.SetBytes(headerBytes)
if err != nil {
peerLog.Warnf("Committed filter header deserialize "+
"failed: %v", err)
return
}
headersMsg.AddCFHeader(&header)
}
sp.QueueMessage(headersMsg, nil)
}
// enforceNodeBloomFlag disconnects the peer if the server is not configured to
@ -1619,25 +1703,25 @@ func disconnectPeer(peerList map[int32]*serverPeer, compareFunc func(*serverPeer
func newPeerConfig(sp *serverPeer) *peer.Config {
return &peer.Config{
Listeners: peer.MessageListeners{
OnVersion: sp.OnVersion,
OnMemPool: sp.OnMemPool,
OnTx: sp.OnTx,
OnBlock: sp.OnBlock,
OnInv: sp.OnInv,
OnHeaders: sp.OnHeaders,
OnGetData: sp.OnGetData,
OnGetBlocks: sp.OnGetBlocks,
OnGetHeaders: sp.OnGetHeaders,
OnGetCFilter: sp.OnGetCFilter,
OnGetCFHeader: sp.OnGetCFHeader,
OnFeeFilter: sp.OnFeeFilter,
OnFilterAdd: sp.OnFilterAdd,
OnFilterClear: sp.OnFilterClear,
OnFilterLoad: sp.OnFilterLoad,
OnGetAddr: sp.OnGetAddr,
OnAddr: sp.OnAddr,
OnRead: sp.OnRead,
OnWrite: sp.OnWrite,
OnVersion: sp.OnVersion,
OnMemPool: sp.OnMemPool,
OnTx: sp.OnTx,
OnBlock: sp.OnBlock,
OnInv: sp.OnInv,
OnHeaders: sp.OnHeaders,
OnGetData: sp.OnGetData,
OnGetBlocks: sp.OnGetBlocks,
OnGetHeaders: sp.OnGetHeaders,
OnGetCFilter: sp.OnGetCFilter,
OnGetCFHeaders: sp.OnGetCFHeaders,
OnFeeFilter: sp.OnFeeFilter,
OnFilterAdd: sp.OnFilterAdd,
OnFilterClear: sp.OnFilterClear,
OnFilterLoad: sp.OnFilterLoad,
OnGetAddr: sp.OnGetAddr,
OnAddr: sp.OnAddr,
OnRead: sp.OnRead,
OnWrite: sp.OnWrite,
// Note: The reference client currently bans peers that send alerts
// not signed with its key. We could verify against their key, but

View file

@ -28,33 +28,33 @@ const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
// Commands used in bitcoin message headers which describe the type of message.
const (
CmdVersion = "version"
CmdVerAck = "verack"
CmdGetAddr = "getaddr"
CmdAddr = "addr"
CmdGetBlocks = "getblocks"
CmdInv = "inv"
CmdGetData = "getdata"
CmdNotFound = "notfound"
CmdBlock = "block"
CmdTx = "tx"
CmdGetHeaders = "getheaders"
CmdHeaders = "headers"
CmdPing = "ping"
CmdPong = "pong"
CmdAlert = "alert"
CmdMemPool = "mempool"
CmdFilterAdd = "filteradd"
CmdFilterClear = "filterclear"
CmdFilterLoad = "filterload"
CmdMerkleBlock = "merkleblock"
CmdReject = "reject"
CmdSendHeaders = "sendheaders"
CmdFeeFilter = "feefilter"
CmdGetCFilter = "getcfilter"
CmdGetCFHeader = "getcfheader"
CmdCFilter = "cfilter"
CmdCFHeader = "cfheader"
CmdVersion = "version"
CmdVerAck = "verack"
CmdGetAddr = "getaddr"
CmdAddr = "addr"
CmdGetBlocks = "getblocks"
CmdInv = "inv"
CmdGetData = "getdata"
CmdNotFound = "notfound"
CmdBlock = "block"
CmdTx = "tx"
CmdGetHeaders = "getheaders"
CmdHeaders = "headers"
CmdPing = "ping"
CmdPong = "pong"
CmdAlert = "alert"
CmdMemPool = "mempool"
CmdFilterAdd = "filteradd"
CmdFilterClear = "filterclear"
CmdFilterLoad = "filterload"
CmdMerkleBlock = "merkleblock"
CmdReject = "reject"
CmdSendHeaders = "sendheaders"
CmdFeeFilter = "feefilter"
CmdGetCFilter = "getcfilter"
CmdGetCFHeaders = "getcfheaders"
CmdCFilter = "cfilter"
CmdCFHeaders = "cfheaders"
)
// MessageEncoding represents the wire message encoding format to be used.
@ -163,14 +163,14 @@ func makeEmptyMessage(command string) (Message, error) {
case CmdGetCFilter:
msg = &MsgGetCFilter{}
case CmdGetCFHeader:
msg = &MsgGetCFHeader{}
case CmdGetCFHeaders:
msg = &MsgGetCFHeaders{}
case CmdCFilter:
msg = &MsgCFilter{}
case CmdCFHeader:
msg = &MsgCFHeader{}
case CmdCFHeaders:
msg = &MsgCFHeaders{}
default:
return nil, fmt.Errorf("unhandled command [%s]", command)

View file

@ -70,9 +70,9 @@ func TestMessage(t *testing.T) {
msgMerkleBlock := NewMsgMerkleBlock(bh)
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
msgGetCFilter := NewMsgGetCFilter(&chainhash.Hash{}, false)
msgGetCFHeader := NewMsgGetCFHeader(&chainhash.Hash{}, false)
msgGetCFHeaders := NewMsgGetCFHeaders()
msgCFilter := NewMsgCFilter([]byte("payload"))
msgCFHeader := NewMsgCFHeader([]byte("payload"))
msgCFHeaders := NewMsgCFHeaders()
tests := []struct {
in Message // Value to encode
@ -103,9 +103,9 @@ func TestMessage(t *testing.T) {
{msgMerkleBlock, msgMerkleBlock, pver, MainNet, 110},
{msgReject, msgReject, pver, MainNet, 79},
{msgGetCFilter, msgGetCFilter, pver, MainNet, 57},
{msgGetCFHeader, msgGetCFHeader, pver, MainNet, 57},
{msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 62},
{msgCFilter, msgCFilter, pver, MainNet, 32},
{msgCFHeader, msgCFHeader, pver, MainNet, 32},
{msgCFHeaders, msgCFHeaders, pver, MainNet, 25},
}
t.Logf("Running %d tests", len(tests))

View file

@ -1,81 +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 (
"fmt"
"io"
"github.com/btcsuite/fastsha256"
)
const (
// MaxCFHeaderDataSize is the maximum byte size of a committed
// filter header.
MaxCFHeaderDataSize = fastsha256.Size
)
type MsgCFHeader struct {
Data []byte
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCFHeader) BtcDecode(r io.Reader, pver uint32) error {
var err error
msg.Data, err = ReadVarBytes(r, pver, MaxCFHeaderDataSize,
"cf header data")
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCFHeader) BtcEncode(w io.Writer, pver uint32) error {
size := len(msg.Data)
if size > MaxCFHeaderDataSize {
str := fmt.Sprintf("cf header size too large for message "+
"[size %v, max %v]", size, MaxCFHeaderDataSize)
return messageError("MsgCFHeader.BtcEncode", str)
}
return WriteVarBytes(w, pver, msg.Data)
}
// Deserialize decodes a filter header from r into the receiver using a format
// that is suitable for long-term storage such as a database. This function
// differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
// protocol as it was sent across the network. The wire encoding can
// technically differ depending on the protocol version and doesn't even really
// need to match the format of a stored filter header at all. As of the time
// this comment was written, the encoded filter header is the same in both
// instances, but there is a distinct difference and separating the two allows
// the API to be flexible enough to deal with changes.
func (msg *MsgCFHeader) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// and the stable long-term storage format. As a result, make use of
// BtcDecode.
return msg.BtcDecode(r, 0)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgCFHeader) Command() string {
return CmdCFHeader
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgCFHeader) MaxPayloadLength(pver uint32) uint32 {
return uint32(VarIntSerializeSize(MaxCFHeaderDataSize)) +
MaxCFHeaderDataSize
}
// NewMsgCFHeader returns a new bitcoin cfheader message that conforms to
// the Message interface. See MsgCFHeader for details.
func NewMsgCFHeader(data []byte) *MsgCFHeader {
return &MsgCFHeader{
Data: data,
}
}

139
wire/msgcfheaders.go Normal file
View file

@ -0,0 +1,139 @@
// 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 (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
const (
// MaxCFHeaderPayload is the maximum byte size of a committed
// filter header.
MaxCFHeaderPayload = chainhash.HashSize
// MaxCFHeadersPerMsg is the maximum number of committed filter headers
// that can be in a single bitcoin cfheaders message.
MaxCFHeadersPerMsg = 2000
)
// MsgCFHeaders implements the Message interface and represents a bitcoin
// cfheaders message. It is used to deliver committed filter header information
// in response to a getcfheaders message (MsgGetCFHeaders). The maximum number
// of committed filter headers per message is currently 2000. See
// MsgGetCFHeaders for details on requesting the headers.
type MsgCFHeaders struct {
HeaderHashes []*chainhash.Hash
}
// AddCFHeader adds a new committed filter header to the message.
func (msg *MsgCFHeaders) AddCFHeader(headerHash *chainhash.Hash) error {
if len(msg.HeaderHashes)+1 > MaxCFHeadersPerMsg {
str := fmt.Sprintf("too many block headers in message [max %v]",
MaxBlockHeadersPerMsg)
return messageError("MsgCFHeaders.AddCFHeader", str)
}
msg.HeaderHashes = append(msg.HeaderHashes, headerHash)
return nil
}
// 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) error {
count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
// Limit to max committed filter headers per message.
if count > MaxCFHeadersPerMsg {
str := fmt.Sprintf("too many committed filter headers for "+
"message [count %v, max %v]", count,
MaxBlockHeadersPerMsg)
return messageError("MsgCFHeaders.BtcDecode", str)
}
// Create a contiguous slice of headers to deserialize into in order to
// reduce the number of allocations.
headers := make([]chainhash.Hash, count)
msg.HeaderHashes = make([]*chainhash.Hash, 0, count)
for i := uint64(0); i < count; i++ {
cfh := &headers[i]
err := readElement(r, &cfh)
if err != nil {
return err
}
msg.AddCFHeader(cfh)
}
return nil
}
// 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) error {
// Limit to max committed headers per message.
count := len(msg.HeaderHashes)
if count > MaxCFHeadersPerMsg {
str := fmt.Sprintf("too many committed filter headers for "+
"message [count %v, max %v]", count,
MaxBlockHeadersPerMsg)
return messageError("MsgCFHeaders.BtcEncode", str)
}
err := WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, cfh := range msg.HeaderHashes {
err := writeElement(w, cfh)
if err != nil {
return err
}
}
return nil
}
// Deserialize decodes a filter header from r into the receiver using a format
// that is suitable for long-term storage such as a database. This function
// differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
// protocol as it was sent across the network. The wire encoding can
// technically differ depending on the protocol version and doesn't even really
// need to match the format of a stored filter header at all. As of the time
// this comment was written, the encoded filter header is the same in both
// instances, but there is a distinct difference and separating the two allows
// the API to be flexible enough to deal with changes.
func (msg *MsgCFHeaders) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// and the stable long-term storage format. As a result, make use of
// BtcDecode.
return msg.BtcDecode(r, 0)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgCFHeaders) Command() string {
return CmdCFHeaders
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgCFHeaders) MaxPayloadLength(pver uint32) uint32 {
// Num headers (varInt) + (header size * max allowed headers).
return MaxVarIntPayload + (MaxCFHeaderPayload * MaxBlockHeadersPerMsg)
}
// NewMsgCFHeaders returns a new bitcoin cfheaders message that conforms to
// the Message interface. See MsgCFHeaders for details.
func NewMsgCFHeaders() *MsgCFHeaders {
return &MsgCFHeaders{
HeaderHashes: make([]*chainhash.Hash, 0, MaxCFHeadersPerMsg),
}
}

View file

@ -1,57 +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"
)
type MsgGetCFHeader struct {
BlockHash chainhash.Hash
Extended bool
}
func (msg *MsgGetCFHeader) BtcDecode(r io.Reader, pver uint32) error {
err := readElement(r, &msg.BlockHash)
if err != nil {
return err
}
return readElement(r, &msg.Extended)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetCFHeader) BtcEncode(w io.Writer, pver uint32) error {
err := writeElement(w, &msg.BlockHash)
if err != nil {
return err
}
return writeElement(w, msg.Extended)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetCFHeader) Command() string {
return CmdGetCFHeader
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetCFHeader) MaxPayloadLength(pver uint32) uint32 {
// Block hash + Extended flag.
return chainhash.HashSize + 1
}
// NewMsgGetCFHeader returns a new bitcoin getcfheader message that conforms to
// the Message interface using the passed parameters and defaults for the
// remaining fields.
func NewMsgGetCFHeader(blockHash *chainhash.Hash, extended bool) *MsgGetCFHeader {
return &MsgGetCFHeader{
BlockHash: *blockHash,
Extended: extended,
}
}

135
wire/msggetcfheaders.go Normal file
View file

@ -0,0 +1,135 @@
// 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 (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// MsgGetCFHeaders is a message similar to MsgGetHeaders, but for committed
// filter headers. It allows to set the Extended field to get headers in the
// chain of basic (false) or extended (true) headers.
type MsgGetCFHeaders struct {
ProtocolVersion uint32
BlockLocatorHashes []*chainhash.Hash
HashStop chainhash.Hash
Extended bool
}
// 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.
// This is part of the Message interface implementation.
func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32) error {
err := readElement(r, &msg.ProtocolVersion)
if err != nil {
return err
}
// 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)
if err != nil {
return err
}
return readElement(r, &msg.Extended)
}
// 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) 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 := writeElement(w, msg.ProtocolVersion)
if err != nil {
return err
}
err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for _, hash := range msg.BlockLocatorHashes {
err := writeElement(w, hash)
if err != nil {
return err
}
}
err = writeElement(w, &msg.HashStop)
if err != nil {
return err
}
return writeElement(w, msg.Extended)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetCFHeaders) Command() string {
return CmdGetCFHeaders
}
// 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 {
// Version 4 bytes + num block locator hashes (varInt) + max allowed
// block locators + hash stop + Extended flag 1 byte.
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
chainhash.HashSize) + chainhash.HashSize + 1
}
// 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 {
return &MsgGetCFHeaders{
BlockLocatorHashes: make([]*chainhash.Hash, 0,
MaxBlockLocatorsPerMsg),
}
}