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