github.com/btcsuite/btcd@v0.24.0/wire/msgmerkleblock.go (about)

     1  // Copyright (c) 2014-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  // maxFlagsPerMerkleBlock is the maximum number of flag bytes that could
    15  // possibly fit into a merkle block.  Since each transaction is represented by
    16  // a single bit, this is the max number of transactions per block divided by
    17  // 8 bits per byte.  Then an extra one to cover partials.
    18  const maxFlagsPerMerkleBlock = maxTxPerBlock / 8
    19  
    20  // MsgMerkleBlock implements the Message interface and represents a bitcoin
    21  // merkleblock message which is used to reset a Bloom filter.
    22  //
    23  // This message was not added until protocol version BIP0037Version.
    24  type MsgMerkleBlock struct {
    25  	Header       BlockHeader
    26  	Transactions uint32
    27  	Hashes       []*chainhash.Hash
    28  	Flags        []byte
    29  }
    30  
    31  // AddTxHash adds a new transaction hash to the message.
    32  func (msg *MsgMerkleBlock) AddTxHash(hash *chainhash.Hash) error {
    33  	if len(msg.Hashes)+1 > maxTxPerBlock {
    34  		str := fmt.Sprintf("too many tx hashes for message [max %v]",
    35  			maxTxPerBlock)
    36  		return messageError("MsgMerkleBlock.AddTxHash", str)
    37  	}
    38  
    39  	msg.Hashes = append(msg.Hashes, hash)
    40  	return nil
    41  }
    42  
    43  // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
    44  // This is part of the Message interface implementation.
    45  func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
    46  	if pver < BIP0037Version {
    47  		str := fmt.Sprintf("merkleblock message invalid for protocol "+
    48  			"version %d", pver)
    49  		return messageError("MsgMerkleBlock.BtcDecode", str)
    50  	}
    51  
    52  	buf := binarySerializer.Borrow()
    53  	defer binarySerializer.Return(buf)
    54  
    55  	err := readBlockHeaderBuf(r, pver, &msg.Header, buf)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	if _, err := io.ReadFull(r, buf[:4]); err != nil {
    61  		return err
    62  	}
    63  	msg.Transactions = littleEndian.Uint32(buf[:4])
    64  
    65  	// Read num block locator hashes and limit to max.
    66  	count, err := ReadVarIntBuf(r, pver, buf)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	if count > maxTxPerBlock {
    71  		str := fmt.Sprintf("too many transaction hashes for message "+
    72  			"[count %v, max %v]", count, maxTxPerBlock)
    73  		return messageError("MsgMerkleBlock.BtcDecode", str)
    74  	}
    75  
    76  	// Create a contiguous slice of hashes to deserialize into in order to
    77  	// reduce the number of allocations.
    78  	hashes := make([]chainhash.Hash, count)
    79  	msg.Hashes = make([]*chainhash.Hash, 0, count)
    80  	for i := uint64(0); i < count; i++ {
    81  		hash := &hashes[i]
    82  		_, err := io.ReadFull(r, hash[:])
    83  		if err != nil {
    84  			return err
    85  		}
    86  		msg.AddTxHash(hash)
    87  	}
    88  
    89  	msg.Flags, err = ReadVarBytesBuf(r, pver, buf, maxFlagsPerMerkleBlock,
    90  		"merkle block flags size")
    91  	return err
    92  }
    93  
    94  // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
    95  // This is part of the Message interface implementation.
    96  func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
    97  	if pver < BIP0037Version {
    98  		str := fmt.Sprintf("merkleblock message invalid for protocol "+
    99  			"version %d", pver)
   100  		return messageError("MsgMerkleBlock.BtcEncode", str)
   101  	}
   102  
   103  	// Read num transaction hashes and limit to max.
   104  	numHashes := len(msg.Hashes)
   105  	if numHashes > maxTxPerBlock {
   106  		str := fmt.Sprintf("too many transaction hashes for message "+
   107  			"[count %v, max %v]", numHashes, maxTxPerBlock)
   108  		return messageError("MsgMerkleBlock.BtcDecode", str)
   109  	}
   110  	numFlagBytes := len(msg.Flags)
   111  	if numFlagBytes > maxFlagsPerMerkleBlock {
   112  		str := fmt.Sprintf("too many flag bytes for message [count %v, "+
   113  			"max %v]", numFlagBytes, maxFlagsPerMerkleBlock)
   114  		return messageError("MsgMerkleBlock.BtcDecode", str)
   115  	}
   116  
   117  	buf := binarySerializer.Borrow()
   118  	defer binarySerializer.Return(buf)
   119  
   120  	err := writeBlockHeaderBuf(w, pver, &msg.Header, buf)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	littleEndian.PutUint32(buf[:4], msg.Transactions)
   126  	if _, err := w.Write(buf[:4]); err != nil {
   127  		return err
   128  	}
   129  
   130  	err = WriteVarIntBuf(w, pver, uint64(numHashes), buf)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	for _, hash := range msg.Hashes {
   135  		_, err := w.Write(hash[:])
   136  		if err != nil {
   137  			return err
   138  		}
   139  	}
   140  
   141  	err = WriteVarBytesBuf(w, pver, msg.Flags, buf)
   142  	return err
   143  }
   144  
   145  // Command returns the protocol command string for the message.  This is part
   146  // of the Message interface implementation.
   147  func (msg *MsgMerkleBlock) Command() string {
   148  	return CmdMerkleBlock
   149  }
   150  
   151  // MaxPayloadLength returns the maximum length the payload can be for the
   152  // receiver.  This is part of the Message interface implementation.
   153  func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
   154  	return MaxBlockPayload
   155  }
   156  
   157  // NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to
   158  // the Message interface.  See MsgMerkleBlock for details.
   159  func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock {
   160  	return &MsgMerkleBlock{
   161  		Header:       *bh,
   162  		Transactions: 0,
   163  		Hashes:       make([]*chainhash.Hash, 0),
   164  		Flags:        make([]byte, 0),
   165  	}
   166  }