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 }