github.com/btcsuite/btcd@v0.24.0/wire/msggetblocks.go (about)

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package wire
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  
    11  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    12  )
    13  
    14  // MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
    15  // per message.
    16  const MaxBlockLocatorsPerMsg = 500
    17  
    18  // MsgGetBlocks implements the Message interface and represents a bitcoin
    19  // getblocks message.  It is used to request a list of blocks starting after the
    20  // last known hash in the slice of block locator hashes.  The list is returned
    21  // via an inv message (MsgInv) and is limited by a specific hash to stop at or
    22  // the maximum number of blocks per message, which is currently 500.
    23  //
    24  // Set the HashStop field to the hash at which to stop and use
    25  // AddBlockLocatorHash to build up the list of block locator hashes.
    26  //
    27  // The algorithm for building the block locator hashes should be to add the
    28  // hashes in reverse order until you reach the genesis block.  In order to keep
    29  // the list of locator hashes to a reasonable number of entries, first add the
    30  // most recent 10 block hashes, then double the step each loop iteration to
    31  // exponentially decrease the number of hashes the further away from head and
    32  // closer to the genesis block you get.
    33  type MsgGetBlocks struct {
    34  	ProtocolVersion    uint32
    35  	BlockLocatorHashes []*chainhash.Hash
    36  	HashStop           chainhash.Hash
    37  }
    38  
    39  // AddBlockLocatorHash adds a new block locator hash to the message.
    40  func (msg *MsgGetBlocks) AddBlockLocatorHash(hash *chainhash.Hash) error {
    41  	if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
    42  		str := fmt.Sprintf("too many block locator hashes for message [max %v]",
    43  			MaxBlockLocatorsPerMsg)
    44  		return messageError("MsgGetBlocks.AddBlockLocatorHash", str)
    45  	}
    46  
    47  	msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
    48  	return nil
    49  }
    50  
    51  // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
    52  // This is part of the Message interface implementation.
    53  func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
    54  	buf := binarySerializer.Borrow()
    55  	defer binarySerializer.Return(buf)
    56  
    57  	if _, err := io.ReadFull(r, buf[:4]); err != nil {
    58  		return err
    59  	}
    60  	msg.ProtocolVersion = littleEndian.Uint32(buf[:4])
    61  
    62  	// Read num block locator hashes and limit to max.
    63  	count, err := ReadVarIntBuf(r, pver, buf)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	if count > MaxBlockLocatorsPerMsg {
    69  		str := fmt.Sprintf("too many block locator hashes for message "+
    70  			"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
    71  		return messageError("MsgGetBlocks.BtcDecode", str)
    72  	}
    73  
    74  	// Create a contiguous slice of hashes to deserialize into in order to
    75  	// reduce the number of allocations.
    76  	locatorHashes := make([]chainhash.Hash, count)
    77  	msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
    78  	for i := uint64(0); i < count; i++ {
    79  		hash := &locatorHashes[i]
    80  		_, err := io.ReadFull(r, hash[:])
    81  		if err != nil {
    82  			return err
    83  		}
    84  		msg.AddBlockLocatorHash(hash)
    85  	}
    86  
    87  	_, err = io.ReadFull(r, msg.HashStop[:])
    88  	return err
    89  }
    90  
    91  // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
    92  // This is part of the Message interface implementation.
    93  func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
    94  	count := len(msg.BlockLocatorHashes)
    95  	if count > MaxBlockLocatorsPerMsg {
    96  		str := fmt.Sprintf("too many block locator hashes for message "+
    97  			"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
    98  		return messageError("MsgGetBlocks.BtcEncode", str)
    99  	}
   100  
   101  	buf := binarySerializer.Borrow()
   102  	defer binarySerializer.Return(buf)
   103  
   104  	littleEndian.PutUint32(buf[:4], msg.ProtocolVersion)
   105  	if _, err := w.Write(buf[:4]); err != nil {
   106  		return err
   107  	}
   108  
   109  	err := WriteVarIntBuf(w, pver, uint64(count), buf)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	for _, hash := range msg.BlockLocatorHashes {
   115  		_, err := w.Write(hash[:])
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	_, err = w.Write(msg.HashStop[:])
   122  	return err
   123  }
   124  
   125  // Command returns the protocol command string for the message.  This is part
   126  // of the Message interface implementation.
   127  func (msg *MsgGetBlocks) Command() string {
   128  	return CmdGetBlocks
   129  }
   130  
   131  // MaxPayloadLength returns the maximum length the payload can be for the
   132  // receiver.  This is part of the Message interface implementation.
   133  func (msg *MsgGetBlocks) MaxPayloadLength(pver uint32) uint32 {
   134  	// Protocol version 4 bytes + num hashes (varInt) + max block locator
   135  	// hashes + hash stop.
   136  	return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * chainhash.HashSize) + chainhash.HashSize
   137  }
   138  
   139  // NewMsgGetBlocks returns a new bitcoin getblocks message that conforms to the
   140  // Message interface using the passed parameters and defaults for the remaining
   141  // fields.
   142  func NewMsgGetBlocks(hashStop *chainhash.Hash) *MsgGetBlocks {
   143  	return &MsgGetBlocks{
   144  		ProtocolVersion:    ProtocolVersion,
   145  		BlockLocatorHashes: make([]*chainhash.Hash, 0, MaxBlockLocatorsPerMsg),
   146  		HashStop:           *hashStop,
   147  	}
   148  }