msggetblocks.go raw

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