lbcutil/bloom/merkleblock.go
Dave Collins 22c91fa80a Update for recent chainhash-related API changes. (#78)
This updates all code in the main package and subpackages to make use of
the new chainhash package since the old wire.ShaHash type and functions
have been removed in favor of the abstracted package.

Also, since this required API changes anyways and the hash algorithm is
no longer tied specifically to SHA, all other functions throughout the
code base which had "Sha" in their name have been changed to Hash so
they are not incorrectly implying the hash algorithm.

The following is an overview of the changes:

- Update all references to wire.ShaHash to the new chainhash.Hash type
- Rename the following functions and update all references:
  - Block.Sha -> Hash
  - Block.TxSha -> TxHash
  - Tx.Sha -> Hash
  - bloom.Filter.AddShaHash -> AddHash
- Rename all variables that included sha in their name to include hash
  instead
- Add license headers to coinset package files
2016-08-08 12:38:16 -05:00

125 lines
3.9 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package bloom
import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// merkleBlock is used to house intermediate information needed to generate a
// wire.MsgMerkleBlock according to a filter.
type merkleBlock struct {
numTx uint32
allHashes []*chainhash.Hash
finalHashes []*chainhash.Hash
matchedBits []byte
bits []byte
}
// calcTreeWidth calculates and returns the the number of nodes (width) or a
// merkle tree at the given depth-first height.
func (m *merkleBlock) calcTreeWidth(height uint32) uint32 {
return (m.numTx + (1 << height) - 1) >> height
}
// calcHash returns the hash for a sub-tree given a depth-first height and
// node position.
func (m *merkleBlock) calcHash(height, pos uint32) *chainhash.Hash {
if height == 0 {
return m.allHashes[pos]
}
var right *chainhash.Hash
left := m.calcHash(height-1, pos*2)
if pos*2+1 < m.calcTreeWidth(height-1) {
right = m.calcHash(height-1, pos*2+1)
} else {
right = left
}
return blockchain.HashMerkleBranches(left, right)
}
// traverseAndBuild builds a partial merkle tree using a recursive depth-first
// approach. As it calculates the hashes, it also saves whether or not each
// node is a parent node and a list of final hashes to be included in the
// merkle block.
func (m *merkleBlock) traverseAndBuild(height, pos uint32) {
// Determine whether this node is a parent of a matched node.
var isParent byte
for i := pos << height; i < (pos+1)<<height && i < m.numTx; i++ {
isParent |= m.matchedBits[i]
}
m.bits = append(m.bits, isParent)
// When the node is a leaf node or not a parent of a matched node,
// append the hash to the list that will be part of the final merkle
// block.
if height == 0 || isParent == 0x00 {
m.finalHashes = append(m.finalHashes, m.calcHash(height, pos))
return
}
// At this point, the node is an internal node and it is the parent of
// of an included leaf node.
// Descend into the left child and process its sub-tree.
m.traverseAndBuild(height-1, pos*2)
// Descend into the right child and process its sub-tree if
// there is one.
if pos*2+1 < m.calcTreeWidth(height-1) {
m.traverseAndBuild(height-1, pos*2+1)
}
}
// NewMerkleBlock returns a new *wire.MsgMerkleBlock and an array of the matched
// transaction index numbers based on the passed block and filter.
func NewMerkleBlock(block *btcutil.Block, filter *Filter) (*wire.MsgMerkleBlock, []uint32) {
numTx := uint32(len(block.Transactions()))
mBlock := merkleBlock{
numTx: numTx,
allHashes: make([]*chainhash.Hash, 0, numTx),
matchedBits: make([]byte, 0, numTx),
}
// Find and keep track of any transactions that match the filter.
var matchedIndices []uint32
for txIndex, tx := range block.Transactions() {
if filter.MatchTxAndUpdate(tx) {
mBlock.matchedBits = append(mBlock.matchedBits, 0x01)
matchedIndices = append(matchedIndices, uint32(txIndex))
} else {
mBlock.matchedBits = append(mBlock.matchedBits, 0x00)
}
mBlock.allHashes = append(mBlock.allHashes, tx.Hash())
}
// Calculate the number of merkle branches (height) in the tree.
height := uint32(0)
for mBlock.calcTreeWidth(height) > 1 {
height++
}
// Build the depth-first partial merkle tree.
mBlock.traverseAndBuild(height, 0)
// Create and return the merkle block.
msgMerkleBlock := wire.MsgMerkleBlock{
Header: block.MsgBlock().Header,
Transactions: uint32(mBlock.numTx),
Hashes: make([]*chainhash.Hash, 0, len(mBlock.finalHashes)),
Flags: make([]byte, (len(mBlock.bits)+7)/8),
}
for _, hash := range mBlock.finalHashes {
msgMerkleBlock.AddTxHash(hash)
}
for i := uint32(0); i < uint32(len(mBlock.bits)); i++ {
msgMerkleBlock.Flags[i/8] |= mBlock.bits[i] << (i % 8)
}
return &msgMerkleBlock, matchedIndices
}