Changed getcfheaders/cfheaders messages to get multiple headers.
This commit is contained in:
parent
860100019f
commit
29b5ece196
9 changed files with 440 additions and 220 deletions
20
peer/peer.go
20
peer/peer.go
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
110
server.go
110
server.go
|
@ -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
|
||||
|
@ -1629,7 +1713,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config {
|
|||
OnGetBlocks: sp.OnGetBlocks,
|
||||
OnGetHeaders: sp.OnGetHeaders,
|
||||
OnGetCFilter: sp.OnGetCFilter,
|
||||
OnGetCFHeader: sp.OnGetCFHeader,
|
||||
OnGetCFHeaders: sp.OnGetCFHeaders,
|
||||
OnFeeFilter: sp.OnFeeFilter,
|
||||
OnFilterAdd: sp.OnFilterAdd,
|
||||
OnFilterClear: sp.OnFilterClear,
|
||||
|
|
|
@ -52,9 +52,9 @@ const (
|
|||
CmdSendHeaders = "sendheaders"
|
||||
CmdFeeFilter = "feefilter"
|
||||
CmdGetCFilter = "getcfilter"
|
||||
CmdGetCFHeader = "getcfheader"
|
||||
CmdGetCFHeaders = "getcfheaders"
|
||||
CmdCFilter = "cfilter"
|
||||
CmdCFHeader = "cfheader"
|
||||
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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
139
wire/msgcfheaders.go
Normal 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),
|
||||
}
|
||||
}
|
|
@ -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
135
wire/msggetcfheaders.go
Normal 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),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue