msggetheaders.go raw

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