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 is invoked when a peer receives a cfilter bitcoin message.
|
||||||
OnCFilter func(p *Peer, msg *wire.MsgCFilter)
|
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.
|
// 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 is invoked when a peer receives an inv bitcoin message.
|
||||||
OnInv func(p *Peer, msg *wire.MsgInv)
|
OnInv func(p *Peer, msg *wire.MsgInv)
|
||||||
|
@ -155,9 +155,9 @@ type MessageListeners struct {
|
||||||
// message.
|
// message.
|
||||||
OnGetCFilter func(p *Peer, msg *wire.MsgGetCFilter)
|
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.
|
// 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 is invoked when a peer receives a feefilter bitcoin message.
|
||||||
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
|
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
|
||||||
|
@ -1599,9 +1599,9 @@ out:
|
||||||
p.cfg.Listeners.OnGetCFilter(p, msg)
|
p.cfg.Listeners.OnGetCFilter(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *wire.MsgGetCFHeader:
|
case *wire.MsgGetCFHeaders:
|
||||||
if p.cfg.Listeners.OnGetCFHeader != nil {
|
if p.cfg.Listeners.OnGetCFHeaders != nil {
|
||||||
p.cfg.Listeners.OnGetCFHeader(p, msg)
|
p.cfg.Listeners.OnGetCFHeaders(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *wire.MsgCFilter:
|
case *wire.MsgCFilter:
|
||||||
|
@ -1609,9 +1609,9 @@ out:
|
||||||
p.cfg.Listeners.OnCFilter(p, msg)
|
p.cfg.Listeners.OnCFilter(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *wire.MsgCFHeader:
|
case *wire.MsgCFHeaders:
|
||||||
if p.cfg.Listeners.OnCFHeader != nil {
|
if p.cfg.Listeners.OnCFHeaders != nil {
|
||||||
p.cfg.Listeners.OnCFHeader(p, msg)
|
p.cfg.Listeners.OnCFHeaders(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *wire.MsgFeeFilter:
|
case *wire.MsgFeeFilter:
|
||||||
|
|
|
@ -402,13 +402,13 @@ func TestPeerListeners(t *testing.T) {
|
||||||
OnGetCFilter: func(p *peer.Peer, msg *wire.MsgGetCFilter) {
|
OnGetCFilter: func(p *peer.Peer, msg *wire.MsgGetCFilter) {
|
||||||
ok <- msg
|
ok <- msg
|
||||||
},
|
},
|
||||||
OnGetCFHeader: func(p *peer.Peer, msg *wire.MsgGetCFHeader) {
|
OnGetCFHeaders: func(p *peer.Peer, msg *wire.MsgGetCFHeaders) {
|
||||||
ok <- msg
|
ok <- msg
|
||||||
},
|
},
|
||||||
OnCFilter: func(p *peer.Peer, msg *wire.MsgCFilter) {
|
OnCFilter: func(p *peer.Peer, msg *wire.MsgCFilter) {
|
||||||
ok <- msg
|
ok <- msg
|
||||||
},
|
},
|
||||||
OnCFHeader: func(p *peer.Peer, msg *wire.MsgCFHeader) {
|
OnCFHeaders: func(p *peer.Peer, msg *wire.MsgCFHeaders) {
|
||||||
ok <- msg
|
ok <- msg
|
||||||
},
|
},
|
||||||
OnFeeFilter: func(p *peer.Peer, msg *wire.MsgFeeFilter) {
|
OnFeeFilter: func(p *peer.Peer, msg *wire.MsgFeeFilter) {
|
||||||
|
@ -539,16 +539,16 @@ func TestPeerListeners(t *testing.T) {
|
||||||
wire.NewMsgGetCFilter(&chainhash.Hash{}, false),
|
wire.NewMsgGetCFilter(&chainhash.Hash{}, false),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnGetCFHeader",
|
"OnGetCFHeaders",
|
||||||
wire.NewMsgGetCFHeader(&chainhash.Hash{}, false),
|
wire.NewMsgGetCFHeaders(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnCFilter",
|
"OnCFilter",
|
||||||
wire.NewMsgCFilter([]byte("payload")),
|
wire.NewMsgCFilter([]byte("payload")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnCFHeader",
|
"OnCFHeaders",
|
||||||
wire.NewMsgCFHeader([]byte("payload")),
|
wire.NewMsgCFHeaders(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnFeeFilter",
|
"OnFeeFilter",
|
||||||
|
|
146
server.go
146
server.go
|
@ -760,25 +760,109 @@ func (sp *serverPeer) OnGetCFilter(_ *peer.Peer, msg *wire.MsgGetCFilter) {
|
||||||
sp.QueueMessage(filterMsg, nil)
|
sp.QueueMessage(filterMsg, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnGetCFHeader is invoked when a peer receives a getcfheader bitcoin message.
|
// OnGetCFHeaders is invoked when a peer receives a getcfheader bitcoin message.
|
||||||
func (sp *serverPeer) OnGetCFHeader(_ *peer.Peer, msg *wire.MsgGetCFHeader) {
|
func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) {
|
||||||
// Ignore getcfilterheader requests if not in sync.
|
// Ignore getcfilterheader requests if not in sync.
|
||||||
if !sp.server.blockManager.IsCurrent() {
|
if !sp.server.blockManager.IsCurrent() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash(
|
// Attempt to look up the height of the provided stop hash.
|
||||||
&msg.BlockHash, msg.Extended)
|
chain := sp.server.blockManager.chain
|
||||||
|
endIdx := int32(math.MaxInt32)
|
||||||
if len(headerBytes) > 0 {
|
height, err := chain.BlockHeightByHash(&msg.HashStop)
|
||||||
peerLog.Infof("Obtained CF header for %v", msg.BlockHash)
|
if err == nil {
|
||||||
} else {
|
endIdx = height + 1
|
||||||
peerLog.Infof("Could not obtain CF header for %v: %v",
|
|
||||||
msg.BlockHash, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headerMsg := wire.NewMsgCFHeader(headerBytes)
|
// There are no block locators so a specific header is being requested
|
||||||
sp.QueueMessage(headerMsg, nil)
|
// 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
|
// 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 {
|
func newPeerConfig(sp *serverPeer) *peer.Config {
|
||||||
return &peer.Config{
|
return &peer.Config{
|
||||||
Listeners: peer.MessageListeners{
|
Listeners: peer.MessageListeners{
|
||||||
OnVersion: sp.OnVersion,
|
OnVersion: sp.OnVersion,
|
||||||
OnMemPool: sp.OnMemPool,
|
OnMemPool: sp.OnMemPool,
|
||||||
OnTx: sp.OnTx,
|
OnTx: sp.OnTx,
|
||||||
OnBlock: sp.OnBlock,
|
OnBlock: sp.OnBlock,
|
||||||
OnInv: sp.OnInv,
|
OnInv: sp.OnInv,
|
||||||
OnHeaders: sp.OnHeaders,
|
OnHeaders: sp.OnHeaders,
|
||||||
OnGetData: sp.OnGetData,
|
OnGetData: sp.OnGetData,
|
||||||
OnGetBlocks: sp.OnGetBlocks,
|
OnGetBlocks: sp.OnGetBlocks,
|
||||||
OnGetHeaders: sp.OnGetHeaders,
|
OnGetHeaders: sp.OnGetHeaders,
|
||||||
OnGetCFilter: sp.OnGetCFilter,
|
OnGetCFilter: sp.OnGetCFilter,
|
||||||
OnGetCFHeader: sp.OnGetCFHeader,
|
OnGetCFHeaders: sp.OnGetCFHeaders,
|
||||||
OnFeeFilter: sp.OnFeeFilter,
|
OnFeeFilter: sp.OnFeeFilter,
|
||||||
OnFilterAdd: sp.OnFilterAdd,
|
OnFilterAdd: sp.OnFilterAdd,
|
||||||
OnFilterClear: sp.OnFilterClear,
|
OnFilterClear: sp.OnFilterClear,
|
||||||
OnFilterLoad: sp.OnFilterLoad,
|
OnFilterLoad: sp.OnFilterLoad,
|
||||||
OnGetAddr: sp.OnGetAddr,
|
OnGetAddr: sp.OnGetAddr,
|
||||||
OnAddr: sp.OnAddr,
|
OnAddr: sp.OnAddr,
|
||||||
OnRead: sp.OnRead,
|
OnRead: sp.OnRead,
|
||||||
OnWrite: sp.OnWrite,
|
OnWrite: sp.OnWrite,
|
||||||
|
|
||||||
// Note: The reference client currently bans peers that send alerts
|
// Note: The reference client currently bans peers that send alerts
|
||||||
// not signed with its key. We could verify against their key, but
|
// not signed with its key. We could verify against their key, but
|
||||||
|
|
|
@ -28,33 +28,33 @@ const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
|
||||||
|
|
||||||
// Commands used in bitcoin message headers which describe the type of message.
|
// Commands used in bitcoin message headers which describe the type of message.
|
||||||
const (
|
const (
|
||||||
CmdVersion = "version"
|
CmdVersion = "version"
|
||||||
CmdVerAck = "verack"
|
CmdVerAck = "verack"
|
||||||
CmdGetAddr = "getaddr"
|
CmdGetAddr = "getaddr"
|
||||||
CmdAddr = "addr"
|
CmdAddr = "addr"
|
||||||
CmdGetBlocks = "getblocks"
|
CmdGetBlocks = "getblocks"
|
||||||
CmdInv = "inv"
|
CmdInv = "inv"
|
||||||
CmdGetData = "getdata"
|
CmdGetData = "getdata"
|
||||||
CmdNotFound = "notfound"
|
CmdNotFound = "notfound"
|
||||||
CmdBlock = "block"
|
CmdBlock = "block"
|
||||||
CmdTx = "tx"
|
CmdTx = "tx"
|
||||||
CmdGetHeaders = "getheaders"
|
CmdGetHeaders = "getheaders"
|
||||||
CmdHeaders = "headers"
|
CmdHeaders = "headers"
|
||||||
CmdPing = "ping"
|
CmdPing = "ping"
|
||||||
CmdPong = "pong"
|
CmdPong = "pong"
|
||||||
CmdAlert = "alert"
|
CmdAlert = "alert"
|
||||||
CmdMemPool = "mempool"
|
CmdMemPool = "mempool"
|
||||||
CmdFilterAdd = "filteradd"
|
CmdFilterAdd = "filteradd"
|
||||||
CmdFilterClear = "filterclear"
|
CmdFilterClear = "filterclear"
|
||||||
CmdFilterLoad = "filterload"
|
CmdFilterLoad = "filterload"
|
||||||
CmdMerkleBlock = "merkleblock"
|
CmdMerkleBlock = "merkleblock"
|
||||||
CmdReject = "reject"
|
CmdReject = "reject"
|
||||||
CmdSendHeaders = "sendheaders"
|
CmdSendHeaders = "sendheaders"
|
||||||
CmdFeeFilter = "feefilter"
|
CmdFeeFilter = "feefilter"
|
||||||
CmdGetCFilter = "getcfilter"
|
CmdGetCFilter = "getcfilter"
|
||||||
CmdGetCFHeader = "getcfheader"
|
CmdGetCFHeaders = "getcfheaders"
|
||||||
CmdCFilter = "cfilter"
|
CmdCFilter = "cfilter"
|
||||||
CmdCFHeader = "cfheader"
|
CmdCFHeaders = "cfheaders"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageEncoding represents the wire message encoding format to be used.
|
// MessageEncoding represents the wire message encoding format to be used.
|
||||||
|
@ -163,14 +163,14 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||||
case CmdGetCFilter:
|
case CmdGetCFilter:
|
||||||
msg = &MsgGetCFilter{}
|
msg = &MsgGetCFilter{}
|
||||||
|
|
||||||
case CmdGetCFHeader:
|
case CmdGetCFHeaders:
|
||||||
msg = &MsgGetCFHeader{}
|
msg = &MsgGetCFHeaders{}
|
||||||
|
|
||||||
case CmdCFilter:
|
case CmdCFilter:
|
||||||
msg = &MsgCFilter{}
|
msg = &MsgCFilter{}
|
||||||
|
|
||||||
case CmdCFHeader:
|
case CmdCFHeaders:
|
||||||
msg = &MsgCFHeader{}
|
msg = &MsgCFHeaders{}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unhandled command [%s]", command)
|
return nil, fmt.Errorf("unhandled command [%s]", command)
|
||||||
|
|
|
@ -70,9 +70,9 @@ func TestMessage(t *testing.T) {
|
||||||
msgMerkleBlock := NewMsgMerkleBlock(bh)
|
msgMerkleBlock := NewMsgMerkleBlock(bh)
|
||||||
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
|
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
|
||||||
msgGetCFilter := NewMsgGetCFilter(&chainhash.Hash{}, false)
|
msgGetCFilter := NewMsgGetCFilter(&chainhash.Hash{}, false)
|
||||||
msgGetCFHeader := NewMsgGetCFHeader(&chainhash.Hash{}, false)
|
msgGetCFHeaders := NewMsgGetCFHeaders()
|
||||||
msgCFilter := NewMsgCFilter([]byte("payload"))
|
msgCFilter := NewMsgCFilter([]byte("payload"))
|
||||||
msgCFHeader := NewMsgCFHeader([]byte("payload"))
|
msgCFHeaders := NewMsgCFHeaders()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in Message // Value to encode
|
in Message // Value to encode
|
||||||
|
@ -103,9 +103,9 @@ 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},
|
||||||
{msgGetCFilter, msgGetCFilter, pver, MainNet, 57},
|
{msgGetCFilter, msgGetCFilter, pver, MainNet, 57},
|
||||||
{msgGetCFHeader, msgGetCFHeader, pver, MainNet, 57},
|
{msgGetCFHeaders, msgGetCFHeaders, pver, MainNet, 62},
|
||||||
{msgCFilter, msgCFilter, pver, MainNet, 32},
|
{msgCFilter, msgCFilter, pver, MainNet, 32},
|
||||||
{msgCFHeader, msgCFHeader, pver, MainNet, 32},
|
{msgCFHeaders, msgCFHeaders, pver, MainNet, 25},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
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…
Add table
Reference in a new issue