btcd/wire/msggetblocks.go
2023-12-15 16:36:41 -08:00

151 lines
5.0 KiB
Go

// Copyright (c) 2013-2016 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"
)
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
// per message.
const MaxBlockLocatorsPerMsg = 500
// MsgGetBlocks implements the Message interface and represents a bitcoin
// getblocks message. It is used to request a list of blocks starting after the
// last known hash in the slice of block locator hashes. The list is returned
// via an inv message (MsgInv) and is limited by a specific hash to stop at or
// the maximum number of blocks per message, which is currently 500.
//
// Set the HashStop field to the hash at which to stop and use
// AddBlockLocatorHash to build up the list of block locator hashes.
//
// The algorithm for building the block locator hashes should be to add the
// hashes in reverse order until you reach the genesis block. In order to keep
// the list of locator hashes to a reasonable number of entries, first add the
// most recent 10 block hashes, then double the step each loop iteration to
// exponentially decrease the number of hashes the further away from head and
// closer to the genesis block you get.
type MsgGetBlocks struct {
ProtocolVersion uint32
BlockLocatorHashes []*chainhash.Hash
HashStop chainhash.Hash
}
// AddBlockLocatorHash adds a new block locator hash to the message.
func (msg *MsgGetBlocks) 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("MsgGetBlocks.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 *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
buf := binarySerializer.Borrow()
if _, err := io.ReadFull(r, buf[:4]); err != nil {
binarySerializer.Return(buf)
return err
}
msg.ProtocolVersion = littleEndian.Uint32(buf[:4])
// Read num block locator hashes and limit to max.
count, err := ReadVarIntBuf(r, pver, buf)
if err != nil {
binarySerializer.Return(buf)
return err
}
binarySerializer.Return(buf)
if count > MaxBlockLocatorsPerMsg {
str := fmt.Sprintf("too many block locator hashes for message "+
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
return messageError("MsgGetBlocks.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 := io.ReadFull(r, hash[:])
if err != nil {
return err
}
msg.AddBlockLocatorHash(hash)
}
_, err = io.ReadFull(r, msg.HashStop[:])
return err
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
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("MsgGetBlocks.BtcEncode", str)
}
buf := binarySerializer.Borrow()
littleEndian.PutUint32(buf[:4], msg.ProtocolVersion)
if _, err := w.Write(buf[:4]); err != nil {
binarySerializer.Return(buf)
return err
}
err := WriteVarIntBuf(w, pver, uint64(count), buf)
if err != nil {
binarySerializer.Return(buf)
return err
}
binarySerializer.Return(buf)
for _, hash := range msg.BlockLocatorHashes {
_, err := w.Write(hash[:])
if err != nil {
return err
}
}
_, err = w.Write(msg.HashStop[:])
return err
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetBlocks) Command() string {
return CmdGetBlocks
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetBlocks) MaxPayloadLength(pver uint32) uint32 {
// Protocol version 4 bytes + num hashes (varInt) + max block locator
// hashes + hash stop.
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * chainhash.HashSize) + chainhash.HashSize
}
// NewMsgGetBlocks returns a new bitcoin getblocks message that conforms to the
// Message interface using the passed parameters and defaults for the remaining
// fields.
func NewMsgGetBlocks(hashStop *chainhash.Hash) *MsgGetBlocks {
return &MsgGetBlocks{
ProtocolVersion: ProtocolVersion,
BlockLocatorHashes: make([]*chainhash.Hash, 0, MaxBlockLocatorsPerMsg),
HashStop: *hashStop,
}
}