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