mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 22:26:47 +00:00
[NOD-241] Implement lower resolution peer rendezvous point discovery (#353)
* [NOD-241] Implement lower resolution peer rendezvous point discovery * [NOD-241] Implement lower resolution peer rendezvous point discovery * [NOD-241] Find exact rendezvous point * [NOD-241] Find exact rendezvous point * [NOD-241] Fix tests * [NOD-241] Remove hash stop from MsgBlockLocator and add tests to MsgBlockLocator and MsgGetBlockLocator * [NOD-241] Change everywhere startHash to hashStart and change comments * [NOD-241] Fix locateBlockNodes to stop at hashStop * [NOD-241] Formatted locatorSummary. * [NOD-241] Fix node reversal * [NOD-241] Fix hash start and hash stop order, and don't include startNode in dag.blockLocator * [NOD-241] rename locateBlockNodes -> getBlueBlocksBetween and add a comment to it * [NOD-241] change hash start to start hash and hash stop to stop hash * [NOD-241] Move block locator stuff to a different file * [NOD-241] Rename msggetblocks.go to msggetblockinvs.go * [NOD-241] Format project * [NOD-241] Rename rpcserverSyncManager.LocateHeaders to GetBlueBlocksHeadersBetween * [NOD-241] Move the logic of finding the highest shared block to OnBlockLocator * [NOD-241] Rename chainHeight -> nextChainHeight * [NOD-241] Fix typo in comment
This commit is contained in:
parent
70737e4e94
commit
d2daf334a5
143
blockdag/blocklocator.go
Normal file
143
blockdag/blocklocator.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/daglabs/btcd/util"
|
||||||
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockLocator is used to help locate a specific block. The algorithm for
|
||||||
|
// building the block locator is to add block hashes in reverse order on the
|
||||||
|
// block's selected parent chain until the desired stop block is reached.
|
||||||
|
// In order to keep the list of locator hashes to a reasonable number of entries,
|
||||||
|
// the step between each entry is doubled each loop iteration to exponentially
|
||||||
|
// decrease the number of hashes as a function of the distance from the block
|
||||||
|
// being located.
|
||||||
|
//
|
||||||
|
// For example, assume a selected parent chain with IDs as depicted below, and the
|
||||||
|
// stop block is genesis:
|
||||||
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
|
//
|
||||||
|
// The block locator for block 17 would be the hashes of blocks:
|
||||||
|
// [17 16 14 11 7 2 genesis]
|
||||||
|
type BlockLocator []*daghash.Hash
|
||||||
|
|
||||||
|
// BlockLocatorFromHashes returns a block locator from start and stop hash.
|
||||||
|
// See BlockLocator for details on the algorithm used to create a block locator.
|
||||||
|
//
|
||||||
|
// In addition to the general algorithm referenced above, this function will
|
||||||
|
// return the block locator for the selected tip if the passed hash is not currently
|
||||||
|
// known.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (dag *BlockDAG) BlockLocatorFromHashes(startHash, stopHash *daghash.Hash) BlockLocator {
|
||||||
|
dag.dagLock.RLock()
|
||||||
|
defer dag.dagLock.RUnlock()
|
||||||
|
startNode := dag.index.LookupNode(startHash)
|
||||||
|
var stopNode *blockNode
|
||||||
|
if !stopHash.IsEqual(&daghash.ZeroHash) {
|
||||||
|
stopNode = dag.index.LookupNode(stopHash)
|
||||||
|
}
|
||||||
|
return dag.blockLocator(startNode, stopNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LatestBlockLocator returns a block locator for the current tips of the DAG.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (dag *BlockDAG) LatestBlockLocator() BlockLocator {
|
||||||
|
dag.dagLock.RLock()
|
||||||
|
defer dag.dagLock.RUnlock()
|
||||||
|
return dag.blockLocator(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockLocator returns a block locator for the passed start and stop nodes.
|
||||||
|
// The default value for the start node is the selected tip, and the default
|
||||||
|
// values of the stop node is the genesis block.
|
||||||
|
//
|
||||||
|
// See the BlockLocator type comments for more details.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the DAG state lock held (for reads).
|
||||||
|
func (dag *BlockDAG) blockLocator(startNode, stopNode *blockNode) BlockLocator {
|
||||||
|
// Use the selected tip if requested.
|
||||||
|
if startNode == nil {
|
||||||
|
startNode = dag.virtual.selectedParent
|
||||||
|
}
|
||||||
|
|
||||||
|
if stopNode == nil {
|
||||||
|
stopNode = dag.genesis
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use the selected parent of the start node, so the
|
||||||
|
// block locator won't contain the start node.
|
||||||
|
startNode = startNode.selectedParent
|
||||||
|
|
||||||
|
// If the start node or the stop node are not in the
|
||||||
|
// virtual's selected parent chain, we replace them with their
|
||||||
|
// closest selected parent that is part of the virtual's
|
||||||
|
// selected parent chain.
|
||||||
|
for !dag.IsInSelectedParentChain(stopNode.hash) {
|
||||||
|
stopNode = stopNode.selectedParent
|
||||||
|
}
|
||||||
|
|
||||||
|
for !dag.IsInSelectedParentChain(startNode.hash) {
|
||||||
|
startNode = startNode.selectedParent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the max number of entries that will ultimately be in the
|
||||||
|
// block locator. See the description of the algorithm for how these
|
||||||
|
// numbers are derived.
|
||||||
|
|
||||||
|
// startNode.hash + stopNode.hash.
|
||||||
|
// Then floor(log2(startNode.chainHeight-stopNode.chainHeight)) entries for the skip portion.
|
||||||
|
maxEntries := 2 + util.FastLog2Floor(startNode.chainHeight-stopNode.chainHeight)
|
||||||
|
locator := make(BlockLocator, 0, maxEntries)
|
||||||
|
|
||||||
|
step := uint64(1)
|
||||||
|
for node := startNode; node != nil; {
|
||||||
|
locator = append(locator, node.hash)
|
||||||
|
|
||||||
|
// Nothing more to add once the stop node has been added.
|
||||||
|
if node.chainHeight == stopNode.chainHeight {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate chainHeight of previous node to include ensuring the
|
||||||
|
// final node is stopNode.
|
||||||
|
nextChainHeight := node.chainHeight - step
|
||||||
|
if nextChainHeight < stopNode.chainHeight {
|
||||||
|
nextChainHeight = stopNode.chainHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk backwards through the nodes to the correct ancestor.
|
||||||
|
node = node.SelectedAncestor(nextChainHeight)
|
||||||
|
|
||||||
|
// Double the distance between included hashes.
|
||||||
|
step *= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return locator
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindNextLocatorBoundaries returns the lowest unknown block locator, hash
|
||||||
|
// and the highest known block locator hash. This is used to create the
|
||||||
|
// next block locator to find the highest shared known chain block with the
|
||||||
|
// sync peer.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the DAG state lock held (for reads).
|
||||||
|
func (dag *BlockDAG) FindNextLocatorBoundaries(locator BlockLocator) (startHash, stopHash *daghash.Hash) {
|
||||||
|
// Find the most recent locator block hash in the DAG. In the case none of
|
||||||
|
// the hashes in the locator are in the DAG, fall back to the genesis block.
|
||||||
|
stopNode := dag.genesis
|
||||||
|
nextBlockLocatorIndex := int64(len(locator) - 1)
|
||||||
|
for i, hash := range locator {
|
||||||
|
node := dag.index.LookupNode(hash)
|
||||||
|
if node != nil {
|
||||||
|
stopNode = node
|
||||||
|
nextBlockLocatorIndex = int64(i) - 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nextBlockLocatorIndex < 0 {
|
||||||
|
return nil, stopNode.hash
|
||||||
|
}
|
||||||
|
return locator[nextBlockLocatorIndex], stopNode.hash
|
||||||
|
}
|
263
blockdag/dag.go
263
blockdag/dag.go
@ -31,21 +31,6 @@ const (
|
|||||||
FinalityInterval = 100
|
FinalityInterval = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockLocator is used to help locate a specific block. The algorithm for
|
|
||||||
// building the block locator is to add block hashes in reverse order on the
|
|
||||||
// block's selected parent chain until the genesis block is reached.
|
|
||||||
// In order to keep the list of locator hashes to a reasonable number of entries,
|
|
||||||
// the step between each entry is doubled each loop iteration to exponentially
|
|
||||||
// decrease the number of hashes as a function of the distance from the block
|
|
||||||
// being located.
|
|
||||||
//
|
|
||||||
// For example, assume a selected parent chain with IDs as depicted below:
|
|
||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
|
||||||
//
|
|
||||||
// The block locator for block 17 would be the hashes of blocks:
|
|
||||||
// [17 16 14 11 7 2 genesis]
|
|
||||||
type BlockLocator []*daghash.Hash
|
|
||||||
|
|
||||||
// orphanBlock represents a block that we don't yet have the parent for. It
|
// orphanBlock represents a block that we don't yet have the parent for. It
|
||||||
// is a normal block plus an expiration time to prevent caching the orphan
|
// is a normal block plus an expiration time to prevent caching the orphan
|
||||||
// forever.
|
// forever.
|
||||||
@ -1520,90 +1505,6 @@ func (dag *BlockDAG) HeaderByHash(hash *daghash.Hash) (*wire.BlockHeader, error)
|
|||||||
return node.Header(), nil
|
return node.Header(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockLocatorFromHash traverses the selected parent chain of the given block hash
|
|
||||||
// until it finds a block that exists in the virtual's selected parent chain, and
|
|
||||||
// then it returns its block locator.
|
|
||||||
// See BlockLocator for details on the algorithm used to create a block locator.
|
|
||||||
//
|
|
||||||
// In addition to the general algorithm referenced above, this function will
|
|
||||||
// return the block locator for the selected tip if the passed hash is not currently
|
|
||||||
// known.
|
|
||||||
//
|
|
||||||
// This function is safe for concurrent access.
|
|
||||||
func (dag *BlockDAG) BlockLocatorFromHash(hash *daghash.Hash) BlockLocator {
|
|
||||||
dag.dagLock.RLock()
|
|
||||||
defer dag.dagLock.RUnlock()
|
|
||||||
node := dag.index.LookupNode(hash)
|
|
||||||
if node != nil {
|
|
||||||
for !dag.IsInSelectedParentChain(node.hash) {
|
|
||||||
node = node.selectedParent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
locator := dag.blockLocator(node)
|
|
||||||
return locator
|
|
||||||
}
|
|
||||||
|
|
||||||
// LatestBlockLocator returns a block locator for the current tips of the DAG.
|
|
||||||
//
|
|
||||||
// This function is safe for concurrent access.
|
|
||||||
func (dag *BlockDAG) LatestBlockLocator() BlockLocator {
|
|
||||||
dag.dagLock.RLock()
|
|
||||||
defer dag.dagLock.RUnlock()
|
|
||||||
locator := dag.blockLocator(nil)
|
|
||||||
return locator
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockLocator returns a block locator for the passed block node. The passed
|
|
||||||
// node can be nil in which case the block locator for the selected tip will be
|
|
||||||
// returned.
|
|
||||||
//
|
|
||||||
// See the BlockLocator type comments for more details.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the DAG state lock held (for reads).
|
|
||||||
func (dag *BlockDAG) blockLocator(node *blockNode) BlockLocator {
|
|
||||||
// Use the selected tip if requested.
|
|
||||||
if node == nil {
|
|
||||||
node = dag.virtual.selectedParent
|
|
||||||
}
|
|
||||||
if node == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the max number of entries that will ultimately be in the
|
|
||||||
// block locator. See the description of the algorithm for how these
|
|
||||||
// numbers are derived.
|
|
||||||
|
|
||||||
// Requested hash itself + genesis block.
|
|
||||||
// Then floor(log2(height-10)) entries for the skip portion.
|
|
||||||
maxEntries := 2 + util.FastLog2Floor(node.height)
|
|
||||||
locator := make(BlockLocator, 0, maxEntries)
|
|
||||||
|
|
||||||
step := uint64(1)
|
|
||||||
for node != nil {
|
|
||||||
locator = append(locator, node.hash)
|
|
||||||
|
|
||||||
// Nothing more to add once the genesis block has been added.
|
|
||||||
if node.height == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate height of previous node to include ensuring the
|
|
||||||
// final node is the genesis block.
|
|
||||||
height := node.height - step
|
|
||||||
if height < 0 {
|
|
||||||
height = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk backwards through the nodes to the correct ancestor.
|
|
||||||
node = node.SelectedAncestor(height)
|
|
||||||
|
|
||||||
// Double the distance between included hashes.
|
|
||||||
step *= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
return locator
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockChainHeightByHash returns the chain height of the block with the given
|
// BlockChainHeightByHash returns the chain height of the block with the given
|
||||||
// hash in the DAG.
|
// hash in the DAG.
|
||||||
//
|
//
|
||||||
@ -1708,67 +1609,13 @@ func (dag *BlockDAG) IntervalBlockHashes(endHash *daghash.Hash, interval uint64,
|
|||||||
return hashes, nil
|
return hashes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// locateInventory returns the node of the block after the first known block in
|
// getBlueBlocksHashesBetween returns the hashes of the blocks after the provided
|
||||||
// the locator along with the number of subsequent nodes needed to either reach
|
// start hash until the provided stop hash is reached, or up to the
|
||||||
// the provided stop hash or the provided max number of entries.
|
// provided max number of block hashes.
|
||||||
//
|
|
||||||
// In addition, there are two special cases:
|
|
||||||
//
|
|
||||||
// - When no locators are provided, the stop hash is treated as a request for
|
|
||||||
// that block, so it will either return the node associated with the stop hash
|
|
||||||
// if it is known, or nil if it is unknown
|
|
||||||
// - When locators are provided, but none of them are known, nodes starting
|
|
||||||
// after the genesis block will be returned
|
|
||||||
//
|
|
||||||
// This is primarily a helper function for the locateBlocks and locateHeaders
|
|
||||||
// functions.
|
|
||||||
//
|
//
|
||||||
// This function MUST be called with the DAG state lock held (for reads).
|
// This function MUST be called with the DAG state lock held (for reads).
|
||||||
func (dag *BlockDAG) locateInventory(locator BlockLocator, stopHash *daghash.Hash, maxEntries uint32) (*blockNode, uint32) {
|
func (dag *BlockDAG) getBlueBlocksHashesBetween(startHash, stopHash *daghash.Hash, maxHashes uint64) []*daghash.Hash {
|
||||||
// There are no block locators so a specific block is being requested
|
nodes := dag.getBlueBlocksBetween(startHash, stopHash, maxHashes)
|
||||||
// as identified by the stop hash.
|
|
||||||
stopNode := dag.index.LookupNode(stopHash)
|
|
||||||
if len(locator) == 0 {
|
|
||||||
if stopNode == nil {
|
|
||||||
// No blocks with the stop hash were found so there is
|
|
||||||
// nothing to do.
|
|
||||||
return nil, 0
|
|
||||||
}
|
|
||||||
return stopNode, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the most recent locator block hash in the DAG. In the case none of
|
|
||||||
// the hashes in the locator are in the DAG, fall back to the genesis block.
|
|
||||||
startNode := dag.genesis
|
|
||||||
for _, hash := range locator {
|
|
||||||
node := dag.index.LookupNode(hash)
|
|
||||||
if node != nil {
|
|
||||||
startNode = node
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Estimate how many entries are needed.
|
|
||||||
estimatedEntries := uint32((dag.selectedTip().blueScore - startNode.blueScore) + 1)
|
|
||||||
if stopNode != nil && stopNode.height >= startNode.height {
|
|
||||||
estimatedEntries = uint32((stopNode.blueScore - startNode.blueScore) + 1)
|
|
||||||
}
|
|
||||||
if estimatedEntries > maxEntries {
|
|
||||||
estimatedEntries = maxEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
return startNode, estimatedEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
// locateBlocks returns the hashes of the blocks after the first known block in
|
|
||||||
// the locator until the provided stop hash is reached, or up to the provided
|
|
||||||
// max number of block hashes.
|
|
||||||
//
|
|
||||||
// See the comment on the exported function for more details on special cases.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the DAG state lock held (for reads).
|
|
||||||
func (dag *BlockDAG) locateBlocks(locator BlockLocator, stopHash *daghash.Hash, maxHashes uint32) []*daghash.Hash {
|
|
||||||
nodes := dag.locateBlockNodes(locator, stopHash, maxHashes)
|
|
||||||
hashes := make([]*daghash.Hash, len(nodes))
|
hashes := make([]*daghash.Hash, len(nodes))
|
||||||
for i, node := range nodes {
|
for i, node := range nodes {
|
||||||
hashes[i] = node.hash
|
hashes[i] = node.hash
|
||||||
@ -1776,67 +1623,65 @@ func (dag *BlockDAG) locateBlocks(locator BlockLocator, stopHash *daghash.Hash,
|
|||||||
return hashes
|
return hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dag *BlockDAG) locateBlockNodes(locator BlockLocator, stopHash *daghash.Hash, maxEntries uint32) []*blockNode {
|
func (dag *BlockDAG) getBlueBlocksBetween(startHash, stopHash *daghash.Hash, maxEntries uint64) []*blockNode {
|
||||||
// Find the first known block in the locator and the estimated number of
|
startNode := dag.index.LookupNode(startHash)
|
||||||
// nodes after it needed while respecting the stop hash and max entries.
|
if startNode == nil {
|
||||||
node, estimatedEntries := dag.locateInventory(locator, stopHash, maxEntries)
|
|
||||||
if estimatedEntries == 0 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stopNode := dag.index.LookupNode(stopHash)
|
stopNode := dag.index.LookupNode(stopHash)
|
||||||
|
if stopNode == nil {
|
||||||
|
stopNode = dag.selectedTip()
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order to get no more then maxEntries of blue blocks from
|
||||||
|
// the future of the start node (including itself), we iterate
|
||||||
|
// the selected parent chain of the stopNode and add the blues
|
||||||
|
// each node (including the stopNode itself). This is why the
|
||||||
|
// number of returned blocks will be
|
||||||
|
// stopNode.blueScore-startNode.blueScore+1.
|
||||||
|
// If stopNode.blueScore-startNode.blueScore+1 > maxEntries, we
|
||||||
|
// first iterate on the selected parent chain of the stop node
|
||||||
|
// until we find a new stop node
|
||||||
|
// where stopNode.blueScore-startNode.blueScore+1 <= maxEntries
|
||||||
|
|
||||||
|
for stopNode.blueScore-startNode.blueScore+1 > maxEntries {
|
||||||
|
stopNode = stopNode.selectedParent
|
||||||
|
}
|
||||||
|
|
||||||
// Populate and return the found nodes.
|
// Populate and return the found nodes.
|
||||||
nodes := make([]*blockNode, 0, estimatedEntries)
|
nodes := make([]*blockNode, 0, stopNode.blueScore-startNode.blueScore+1)
|
||||||
queue := newUpHeap()
|
nodes = append(nodes, stopNode)
|
||||||
queue.pushSet(node.children)
|
for current := stopNode; current != startNode; current = current.selectedParent {
|
||||||
|
for _, blue := range current.blues {
|
||||||
visited := newSet()
|
nodes = append(nodes, blue)
|
||||||
for queue.Len() > 0 && uint32(len(nodes)) < maxEntries {
|
|
||||||
var current *blockNode
|
|
||||||
current = queue.pop()
|
|
||||||
if !visited.contains(current) {
|
|
||||||
visited.add(current)
|
|
||||||
isBeforeStop := (stopNode == nil) || (current.height < stopNode.height)
|
|
||||||
if isBeforeStop || current.hash.IsEqual(stopHash) {
|
|
||||||
nodes = append(nodes, current)
|
|
||||||
}
|
|
||||||
if isBeforeStop {
|
|
||||||
queue.pushSet(current.children)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reversedNodes := make([]*blockNode, len(nodes))
|
||||||
|
for i, node := range nodes {
|
||||||
|
reversedNodes[len(reversedNodes)-i-1] = node
|
||||||
}
|
}
|
||||||
return nodes
|
return reversedNodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocateBlocks returns the hashes of the blocks after the first known block in
|
// GetBlueBlocksHashesBetween returns the hashes of the blue blocks after the
|
||||||
// the locator until the provided stop hash is reached, or up to the provided
|
// provided start hash until the provided stop hash is reached, or up to the
|
||||||
// max number of block hashes.
|
// provided max number of block hashes.
|
||||||
//
|
|
||||||
// In addition, there are two special cases:
|
|
||||||
//
|
|
||||||
// - When no locators are provided, the stop hash is treated as a request for
|
|
||||||
// that block, so it will either return the stop hash itself if it is known,
|
|
||||||
// or nil if it is unknown
|
|
||||||
// - When locators are provided, but none of them are known, hashes starting
|
|
||||||
// after the genesis block will be returned
|
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (dag *BlockDAG) LocateBlocks(locator BlockLocator, stopHash *daghash.Hash, maxHashes uint32) []*daghash.Hash {
|
func (dag *BlockDAG) GetBlueBlocksHashesBetween(startHash, stopHash *daghash.Hash, maxHashes uint64) []*daghash.Hash {
|
||||||
dag.dagLock.RLock()
|
dag.dagLock.RLock()
|
||||||
hashes := dag.locateBlocks(locator, stopHash, maxHashes)
|
hashes := dag.getBlueBlocksHashesBetween(startHash, stopHash, maxHashes)
|
||||||
dag.dagLock.RUnlock()
|
dag.dagLock.RUnlock()
|
||||||
return hashes
|
return hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
// locateHeaders returns the headers of the blocks after the first known block
|
// getBlueBlocksHeadersBetween returns the headers of the blue blocks after the
|
||||||
// in the locator until the provided stop hash is reached, or up to the provided
|
// provided start hash until the provided stop hash is reached, or up to the
|
||||||
// max number of block headers.
|
// provided max number of block headers.
|
||||||
//
|
|
||||||
// See the comment on the exported function for more details on special cases.
|
|
||||||
//
|
//
|
||||||
// This function MUST be called with the DAG state lock held (for reads).
|
// This function MUST be called with the DAG state lock held (for reads).
|
||||||
func (dag *BlockDAG) locateHeaders(locator BlockLocator, stopHash *daghash.Hash, maxHeaders uint32) []*wire.BlockHeader {
|
func (dag *BlockDAG) getBlueBlocksHeadersBetween(startHash, stopHash *daghash.Hash, maxHeaders uint64) []*wire.BlockHeader {
|
||||||
nodes := dag.locateBlockNodes(locator, stopHash, maxHeaders)
|
nodes := dag.getBlueBlocksBetween(startHash, stopHash, maxHeaders)
|
||||||
headers := make([]*wire.BlockHeader, len(nodes))
|
headers := make([]*wire.BlockHeader, len(nodes))
|
||||||
for i, node := range nodes {
|
for i, node := range nodes {
|
||||||
headers[i] = node.Header()
|
headers[i] = node.Header()
|
||||||
@ -1880,22 +1725,14 @@ func (dag *BlockDAG) RUnlock() {
|
|||||||
dag.dagLock.RUnlock()
|
dag.dagLock.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocateHeaders returns the headers of the blocks after the first known block
|
// GetBlueBlocksHeadersBetween returns the headers of the blocks after the provided
|
||||||
// in the locator until the provided stop hash is reached, or up to a max of
|
// start hash until the provided stop hash is reached, or up to the
|
||||||
// wire.MaxBlockHeadersPerMsg headers.
|
// provided max number of block headers.
|
||||||
//
|
|
||||||
// In addition, there are two special cases:
|
|
||||||
//
|
|
||||||
// - When no locators are provided, the stop hash is treated as a request for
|
|
||||||
// that header, so it will either return the header for the stop hash itself
|
|
||||||
// if it is known, or nil if it is unknown
|
|
||||||
// - When locators are provided, but none of them are known, headers starting
|
|
||||||
// after the genesis block will be returned
|
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (dag *BlockDAG) LocateHeaders(locator BlockLocator, stopHash *daghash.Hash) []*wire.BlockHeader {
|
func (dag *BlockDAG) GetBlueBlocksHeadersBetween(startHash, stopHash *daghash.Hash) []*wire.BlockHeader {
|
||||||
dag.dagLock.RLock()
|
dag.dagLock.RLock()
|
||||||
headers := dag.locateHeaders(locator, stopHash, wire.MaxBlockHeadersPerMsg)
|
headers := dag.getBlueBlocksHeadersBetween(startHash, stopHash, wire.MaxBlockHeadersPerMsg)
|
||||||
dag.dagLock.RUnlock()
|
dag.dagLock.RUnlock()
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,16 @@ func TestFinality(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFinalityInterval tests that the finality interval is
|
||||||
|
// smaller then wire.MaxInvPerMsg, so when a peer receives
|
||||||
|
// a getblocks message it should always be able to send
|
||||||
|
// all the necessary invs.
|
||||||
|
func TestFinalityInterval(t *testing.T) {
|
||||||
|
if blockdag.FinalityInterval > wire.MaxInvPerMsg {
|
||||||
|
t.Errorf("blockdag.FinalityInterval should be lower or equal to wire.MaxInvPerMsg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestSubnetworkRegistry tests the full subnetwork registry flow
|
// TestSubnetworkRegistry tests the full subnetwork registry flow
|
||||||
func TestSubnetworkRegistry(t *testing.T) {
|
func TestSubnetworkRegistry(t *testing.T) {
|
||||||
params := dagconfig.SimNetParams
|
params := dagconfig.SimNetParams
|
||||||
|
@ -108,7 +108,7 @@ func NewGetTopHeadersCmd(startHash *string) *GetTopHeadersCmd {
|
|||||||
// NOTE: This is a btcsuite extension ported from
|
// NOTE: This is a btcsuite extension ported from
|
||||||
// github.com/decred/dcrd/dcrjson.
|
// github.com/decred/dcrd/dcrjson.
|
||||||
type GetHeadersCmd struct {
|
type GetHeadersCmd struct {
|
||||||
BlockLocators []string `json:"blockLocators"`
|
StartHash string `json:"startHash"`
|
||||||
StopHash string `json:"stopHash"`
|
StopHash string `json:"stopHash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,9 +117,9 @@ type GetHeadersCmd struct {
|
|||||||
//
|
//
|
||||||
// NOTE: This is a btcsuite extension ported from
|
// NOTE: This is a btcsuite extension ported from
|
||||||
// github.com/decred/dcrd/dcrjson.
|
// github.com/decred/dcrd/dcrjson.
|
||||||
func NewGetHeadersCmd(blockLocators []string, stopHash string) *GetHeadersCmd {
|
func NewGetHeadersCmd(startHash, stopHash string) *GetHeadersCmd {
|
||||||
return &GetHeadersCmd{
|
return &GetHeadersCmd{
|
||||||
BlockLocators: blockLocators,
|
StartHash: startHash,
|
||||||
StopHash: stopHash,
|
StopHash: stopHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,40 +139,34 @@ func TestBtcdExtCmds(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "getHeaders",
|
name: "getHeaders",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
return btcjson.NewCmd("getHeaders", []string{}, "")
|
return btcjson.NewCmd("getHeaders", "", "")
|
||||||
},
|
},
|
||||||
staticCmd: func() interface{} {
|
staticCmd: func() interface{} {
|
||||||
return btcjson.NewGetHeadersCmd(
|
return btcjson.NewGetHeadersCmd(
|
||||||
[]string{},
|
"",
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getHeaders","params":[[],""],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getHeaders","params":["",""],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetHeadersCmd{
|
unmarshalled: &btcjson.GetHeadersCmd{
|
||||||
BlockLocators: []string{},
|
StartHash: "",
|
||||||
StopHash: "",
|
StopHash: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "getHeaders - with arguments",
|
name: "getHeaders - with arguments",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
return btcjson.NewCmd("getHeaders", []string{"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", "0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10"}, "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7")
|
return btcjson.NewCmd("getHeaders", "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7")
|
||||||
},
|
},
|
||||||
staticCmd: func() interface{} {
|
staticCmd: func() interface{} {
|
||||||
return btcjson.NewGetHeadersCmd(
|
return btcjson.NewGetHeadersCmd(
|
||||||
[]string{
|
|
||||||
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||||
"0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10",
|
|
||||||
},
|
|
||||||
"000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
"000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getHeaders","params":[["000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16","0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10"],"000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getHeaders","params":["000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16","000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetHeadersCmd{
|
unmarshalled: &btcjson.GetHeadersCmd{
|
||||||
BlockLocators: []string{
|
StartHash: "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||||
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
|
||||||
"0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10",
|
|
||||||
},
|
|
||||||
StopHash: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
StopHash: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,11 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Printf("\nPrivate key (base-58): %s\n", base58.Encode(privateKey.Serialize()))
|
fmt.Printf("\nPrivate key (base-58): %s\n", base58.Encode(privateKey.Serialize()))
|
||||||
|
wif, err := util.NewWIF(privateKey, activeNetParams.PrivateKeyID, true)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error generating wif: %s", err))
|
||||||
|
}
|
||||||
|
fmt.Printf("\nPrivate key wif: %s\n", wif)
|
||||||
addr, err := util.NewAddressPubKeyHashFromPublicKey(privateKey.PubKey().SerializeCompressed(), activeNetParams.Prefix)
|
addr, err := util.NewAddressPubKeyHashFromPublicKey(privateKey.PubKey().SerializeCompressed(), activeNetParams.Prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to generate p2pkh address: %s", err)
|
fmt.Fprintf(os.Stderr, "Failed to generate p2pkh address: %s", err)
|
||||||
|
@ -157,7 +157,7 @@ type SyncManager struct {
|
|||||||
shutdown int32
|
shutdown int32
|
||||||
dag *blockdag.BlockDAG
|
dag *blockdag.BlockDAG
|
||||||
txMemPool *mempool.TxPool
|
txMemPool *mempool.TxPool
|
||||||
chainParams *dagconfig.Params
|
dagParams *dagconfig.Params
|
||||||
progressLogger *blockProgressLogger
|
progressLogger *blockProgressLogger
|
||||||
msgChan chan interface{}
|
msgChan chan interface{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
@ -177,6 +177,40 @@ type SyncManager struct {
|
|||||||
nextCheckpoint *dagconfig.Checkpoint
|
nextCheckpoint *dagconfig.Checkpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sm *SyncManager) PushGetBlockInvsOrHeaders(peer *peerpkg.Peer, startHash *daghash.Hash) error {
|
||||||
|
// When the current height is less than a known checkpoint we
|
||||||
|
// can use block headers to learn about which blocks comprise
|
||||||
|
// the DAG up to the checkpoint and perform less validation
|
||||||
|
// for them. This is possible since each header contains the
|
||||||
|
// hash of the previous header and a merkle root. Therefore if
|
||||||
|
// we validate all of the received headers link together
|
||||||
|
// properly and the checkpoint hashes match, we can be sure the
|
||||||
|
// hashes for the blocks in between are accurate. Further, once
|
||||||
|
// the full blocks are downloaded, the merkle root is computed
|
||||||
|
// and compared against the value in the header which proves the
|
||||||
|
// full block hasn't been tampered with.
|
||||||
|
//
|
||||||
|
// Once we have passed the final checkpoint, or checkpoints are
|
||||||
|
// disabled, use standard inv messages learn about the blocks
|
||||||
|
// and fully validate them. Finally, regression test mode does
|
||||||
|
// not support the headers-first approach so do normal block
|
||||||
|
// downloads when in regression test mode.
|
||||||
|
if sm.nextCheckpoint != nil &&
|
||||||
|
sm.dag.ChainHeight() < sm.nextCheckpoint.ChainHeight &&
|
||||||
|
sm.dagParams != &dagconfig.RegressionNetParams {
|
||||||
|
//TODO: (Ori) This is probably wrong. Done only for compilation
|
||||||
|
err := peer.PushGetHeadersMsg(startHash, sm.nextCheckpoint.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sm.headersFirstMode = true
|
||||||
|
log.Infof("Downloading headers for blocks %d to "+
|
||||||
|
"%d from peer %s", sm.dag.ChainHeight()+1,
|
||||||
|
sm.nextCheckpoint.ChainHeight, peer.Addr()) //TODO: (Ori) This is probably wrong. Done only for compilation
|
||||||
|
}
|
||||||
|
return peer.PushGetBlockInvsMsg(startHash, &daghash.ZeroHash)
|
||||||
|
}
|
||||||
|
|
||||||
// resetHeaderState sets the headers-first mode state to values appropriate for
|
// resetHeaderState sets the headers-first mode state to values appropriate for
|
||||||
// syncing from a new peer.
|
// syncing from a new peer.
|
||||||
func (sm *SyncManager) resetHeaderState(newestHash *daghash.Hash, newestHeight uint64) {
|
func (sm *SyncManager) resetHeaderState(newestHash *daghash.Hash, newestHeight uint64) {
|
||||||
@ -261,39 +295,16 @@ func (sm *SyncManager) startSync() {
|
|||||||
// to send.
|
// to send.
|
||||||
sm.requestedBlocks = make(map[daghash.Hash]struct{})
|
sm.requestedBlocks = make(map[daghash.Hash]struct{})
|
||||||
|
|
||||||
locator := sm.dag.LatestBlockLocator()
|
|
||||||
|
|
||||||
log.Infof("Syncing to block %s from peer %s",
|
log.Infof("Syncing to block %s from peer %s",
|
||||||
bestPeer.SelectedTip(), bestPeer.Addr())
|
bestPeer.SelectedTip(), bestPeer.Addr())
|
||||||
|
|
||||||
// When the current height is less than a known checkpoint we
|
|
||||||
// can use block headers to learn about which blocks comprise
|
|
||||||
// the chain up to the checkpoint and perform less validation
|
|
||||||
// for them. This is possible since each header contains the
|
|
||||||
// hash of the previous header and a merkle root. Therefore if
|
|
||||||
// we validate all of the received headers link together
|
|
||||||
// properly and the checkpoint hashes match, we can be sure the
|
|
||||||
// hashes for the blocks in between are accurate. Further, once
|
|
||||||
// the full blocks are downloaded, the merkle root is computed
|
|
||||||
// and compared against the value in the header which proves the
|
|
||||||
// full block hasn't been tampered with.
|
|
||||||
//
|
|
||||||
// Once we have passed the final checkpoint, or checkpoints are
|
|
||||||
// disabled, use standard inv messages learn about the blocks
|
|
||||||
// and fully validate them. Finally, regression test mode does
|
|
||||||
// not support the headers-first approach so do normal block
|
|
||||||
// downloads when in regression test mode.
|
|
||||||
if sm.nextCheckpoint != nil &&
|
if sm.nextCheckpoint != nil &&
|
||||||
sm.dag.ChainHeight() < sm.nextCheckpoint.ChainHeight &&
|
sm.dag.ChainHeight() < sm.nextCheckpoint.ChainHeight &&
|
||||||
sm.chainParams != &dagconfig.RegressionNetParams { //TODO: (Ori) This is probably wrong. Done only for compilation
|
sm.dagParams != &dagconfig.RegressionNetParams {
|
||||||
|
//TODO: (Ori) This is probably wrong. Done only for compilation
|
||||||
bestPeer.PushGetHeadersMsg(locator, sm.nextCheckpoint.Hash)
|
bestPeer.PushGetBlockLocatorMsg(sm.nextCheckpoint.Hash, sm.dagParams.GenesisHash)
|
||||||
sm.headersFirstMode = true
|
|
||||||
log.Infof("Downloading headers for blocks %d to "+
|
|
||||||
"%d from peer %s", sm.dag.ChainHeight()+1,
|
|
||||||
sm.nextCheckpoint.ChainHeight, bestPeer.Addr()) //TODO: (Ori) This is probably wrong. Done only for compilation
|
|
||||||
} else {
|
} else {
|
||||||
bestPeer.PushGetBlockInvsMsg(locator, &daghash.ZeroHash)
|
bestPeer.PushGetBlockLocatorMsg(&daghash.ZeroHash, sm.dagParams.GenesisHash)
|
||||||
}
|
}
|
||||||
sm.syncPeer = bestPeer
|
sm.syncPeer = bestPeer
|
||||||
} else {
|
} else {
|
||||||
@ -307,7 +318,7 @@ func (sm *SyncManager) isSyncCandidate(peer *peerpkg.Peer) bool {
|
|||||||
// Typically a peer is not a candidate for sync if it's not a full node,
|
// Typically a peer is not a candidate for sync if it's not a full node,
|
||||||
// however regression test is special in that the regression tool is
|
// however regression test is special in that the regression tool is
|
||||||
// not a full node and still needs to be considered a sync candidate.
|
// not a full node and still needs to be considered a sync candidate.
|
||||||
if sm.chainParams == &dagconfig.RegressionNetParams {
|
if sm.dagParams == &dagconfig.RegressionNetParams {
|
||||||
// The peer is not a candidate if it's not coming from localhost
|
// The peer is not a candidate if it's not coming from localhost
|
||||||
// or the hostname can't be determined for some reason.
|
// or the hostname can't be determined for some reason.
|
||||||
host, _, err := net.SplitHostPort(peer.Addr())
|
host, _, err := net.SplitHostPort(peer.Addr())
|
||||||
@ -517,7 +528,7 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
|
|||||||
// the peer or ignore the block when we're in regression test
|
// the peer or ignore the block when we're in regression test
|
||||||
// mode in this case so the chain code is actually fed the
|
// mode in this case so the chain code is actually fed the
|
||||||
// duplicate blocks.
|
// duplicate blocks.
|
||||||
if sm.chainParams != &dagconfig.RegressionNetParams {
|
if sm.dagParams != &dagconfig.RegressionNetParams {
|
||||||
log.Warnf("Got unrequested block %s from %s -- "+
|
log.Warnf("Got unrequested block %s from %s -- "+
|
||||||
"disconnecting", blockHash, peer.Addr())
|
"disconnecting", blockHash, peer.Addr())
|
||||||
peer.Disconnect()
|
peer.Disconnect()
|
||||||
@ -654,8 +665,7 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
|
|||||||
parentHash := sm.nextCheckpoint.Hash
|
parentHash := sm.nextCheckpoint.Hash
|
||||||
sm.nextCheckpoint = sm.findNextHeaderCheckpoint(prevHeight)
|
sm.nextCheckpoint = sm.findNextHeaderCheckpoint(prevHeight)
|
||||||
if sm.nextCheckpoint != nil {
|
if sm.nextCheckpoint != nil {
|
||||||
locator := blockdag.BlockLocator([]*daghash.Hash{parentHash})
|
err := peer.PushGetHeadersMsg(parentHash, sm.nextCheckpoint.Hash)
|
||||||
err := peer.PushGetHeadersMsg(locator, sm.nextCheckpoint.Hash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Failed to send getheaders message to "+
|
log.Warnf("Failed to send getheaders message to "+
|
||||||
"peer %s: %s", peer.Addr(), err)
|
"peer %s: %s", peer.Addr(), err)
|
||||||
@ -673,8 +683,7 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
|
|||||||
sm.headersFirstMode = false
|
sm.headersFirstMode = false
|
||||||
sm.headerList.Init()
|
sm.headerList.Init()
|
||||||
log.Infof("Reached the final checkpoint -- switching to normal mode")
|
log.Infof("Reached the final checkpoint -- switching to normal mode")
|
||||||
locator := blockdag.BlockLocator([]*daghash.Hash{blockHash})
|
err = peer.PushGetBlockInvsMsg(blockHash, &daghash.ZeroHash)
|
||||||
err = peer.PushGetBlockInvsMsg(locator, &daghash.ZeroHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Failed to send getblockinvs message to peer %s: %s",
|
log.Warnf("Failed to send getblockinvs message to peer %s: %s",
|
||||||
peer.Addr(), err)
|
peer.Addr(), err)
|
||||||
@ -859,8 +868,7 @@ func (sm *SyncManager) handleHeadersMsg(hmsg *headersMsg) {
|
|||||||
// This header is not a checkpoint, so request the next batch of
|
// This header is not a checkpoint, so request the next batch of
|
||||||
// headers starting from the latest known header and ending with the
|
// headers starting from the latest known header and ending with the
|
||||||
// next checkpoint.
|
// next checkpoint.
|
||||||
locator := blockdag.BlockLocator([]*daghash.Hash{finalHash})
|
err := peer.PushGetHeadersMsg(finalHash, sm.nextCheckpoint.Hash)
|
||||||
err := peer.PushGetHeadersMsg(locator, sm.nextCheckpoint.Hash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Failed to send getheaders message to "+
|
log.Warnf("Failed to send getheaders message to "+
|
||||||
"peer %s: %s", peer.Addr(), err)
|
"peer %s: %s", peer.Addr(), err)
|
||||||
@ -1015,10 +1023,8 @@ func (sm *SyncManager) handleInvMsg(imsg *invMsg) {
|
|||||||
if i == lastBlock && peer == sm.syncPeer {
|
if i == lastBlock && peer == sm.syncPeer {
|
||||||
// Request blocks after the first block's ancestor that exists
|
// Request blocks after the first block's ancestor that exists
|
||||||
// in the selected path chain, one up to the
|
// in the selected path chain, one up to the
|
||||||
// final one the remote peer knows about (zero
|
// final one the remote peer knows about.
|
||||||
// stop hash).
|
peer.PushGetBlockLocatorMsg(iv.Hash, &daghash.ZeroHash)
|
||||||
locator := sm.dag.BlockLocatorFromHash(iv.Hash)
|
|
||||||
peer.PushGetBlockInvsMsg(locator, &daghash.ZeroHash)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1408,7 +1414,7 @@ func New(config *Config) (*SyncManager, error) {
|
|||||||
peerNotifier: config.PeerNotifier,
|
peerNotifier: config.PeerNotifier,
|
||||||
dag: config.DAG,
|
dag: config.DAG,
|
||||||
txMemPool: config.TxMemPool,
|
txMemPool: config.TxMemPool,
|
||||||
chainParams: config.ChainParams,
|
dagParams: config.ChainParams,
|
||||||
rejectedTxns: make(map[daghash.TxID]struct{}),
|
rejectedTxns: make(map[daghash.TxID]struct{}),
|
||||||
requestedTxns: make(map[daghash.TxID]struct{}),
|
requestedTxns: make(map[daghash.TxID]struct{}),
|
||||||
requestedBlocks: make(map[daghash.Hash]struct{}),
|
requestedBlocks: make(map[daghash.Hash]struct{}),
|
||||||
|
27
peer/log.go
27
peer/log.go
@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/btcsuite/btclog"
|
"github.com/btcsuite/btclog"
|
||||||
"github.com/daglabs/btcd/logger"
|
"github.com/daglabs/btcd/logger"
|
||||||
"github.com/daglabs/btcd/txscript"
|
"github.com/daglabs/btcd/txscript"
|
||||||
"github.com/daglabs/btcd/util/daghash"
|
|
||||||
"github.com/daglabs/btcd/util/panics"
|
"github.com/daglabs/btcd/util/panics"
|
||||||
"github.com/daglabs/btcd/wire"
|
"github.com/daglabs/btcd/wire"
|
||||||
)
|
)
|
||||||
@ -90,16 +89,6 @@ func invSummary(invList []*wire.InvVect) string {
|
|||||||
return fmt.Sprintf("size %d", invLen)
|
return fmt.Sprintf("size %d", invLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// locatorSummary returns a block locator as a human-readable string.
|
|
||||||
func locatorSummary(locator []*daghash.Hash, stopHash *daghash.Hash) string {
|
|
||||||
if len(locator) > 0 {
|
|
||||||
return fmt.Sprintf("locator %s, stop %s", locator[0], stopHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("no locator, stop %s", stopHash)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// sanitizeString strips any characters which are even remotely dangerous, such
|
// sanitizeString strips any characters which are even remotely dangerous, such
|
||||||
// as html control characters, from the passed string. It also limits it to
|
// as html control characters, from the passed string. It also limits it to
|
||||||
// the passed maximum size, which can be 0 for unlimited. When the string is
|
// the passed maximum size, which can be 0 for unlimited. When the string is
|
||||||
@ -179,10 +168,22 @@ func messageSummary(msg wire.Message) string {
|
|||||||
return invSummary(msg.InvList)
|
return invSummary(msg.InvList)
|
||||||
|
|
||||||
case *wire.MsgGetBlockInvs:
|
case *wire.MsgGetBlockInvs:
|
||||||
return locatorSummary(msg.BlockLocatorHashes, msg.StopHash)
|
return fmt.Sprintf("start hash %s, stop hash %s", msg.StartHash,
|
||||||
|
msg.StopHash)
|
||||||
|
|
||||||
case *wire.MsgGetHeaders:
|
case *wire.MsgGetHeaders:
|
||||||
return locatorSummary(msg.BlockLocatorHashes, msg.StopHash)
|
return fmt.Sprintf("start hash %s, stop hash %s", msg.StartHash,
|
||||||
|
msg.StopHash)
|
||||||
|
|
||||||
|
case *wire.MsgGetBlockLocator:
|
||||||
|
return fmt.Sprintf("start hash %s, stop hash %s", msg.StartHash,
|
||||||
|
msg.StopHash)
|
||||||
|
|
||||||
|
case *wire.MsgBlockLocator:
|
||||||
|
if len(msg.BlockLocatorHashes) > 0 {
|
||||||
|
return fmt.Sprintf("locator first hash: %s, last hash: %s", msg.BlockLocatorHashes[0], msg.BlockLocatorHashes[len(msg.BlockLocatorHashes)-1])
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("no locator")
|
||||||
|
|
||||||
case *wire.MsgHeaders:
|
case *wire.MsgHeaders:
|
||||||
return fmt.Sprintf("num %d", len(msg.Headers))
|
return fmt.Sprintf("num %d", len(msg.Headers))
|
||||||
|
122
peer/peer.go
122
peer/peer.go
@ -137,6 +137,12 @@ type MessageListeners struct {
|
|||||||
// OnInv is invoked when a peer receives an inv bitcoin message.
|
// OnInv is invoked when a peer receives an inv bitcoin message.
|
||||||
OnInv func(p *Peer, msg *wire.MsgInv)
|
OnInv func(p *Peer, msg *wire.MsgInv)
|
||||||
|
|
||||||
|
// OnGetBlockLocator is invoked when a peer receives a getlocator bitcoin message.
|
||||||
|
OnGetBlockLocator func(p *Peer, msg *wire.MsgGetBlockLocator)
|
||||||
|
|
||||||
|
// OnBlockLocator is invoked when a peer receives a locator bitcoin message.
|
||||||
|
OnBlockLocator func(p *Peer, msg *wire.MsgBlockLocator)
|
||||||
|
|
||||||
// OnHeaders is invoked when a peer receives a headers bitcoin message.
|
// OnHeaders is invoked when a peer receives a headers bitcoin message.
|
||||||
OnHeaders func(p *Peer, msg *wire.MsgHeaders)
|
OnHeaders func(p *Peer, msg *wire.MsgHeaders)
|
||||||
|
|
||||||
@ -445,10 +451,10 @@ type Peer struct {
|
|||||||
|
|
||||||
knownInventory *mruInventoryMap
|
knownInventory *mruInventoryMap
|
||||||
prevGetBlockInvsMtx sync.Mutex
|
prevGetBlockInvsMtx sync.Mutex
|
||||||
prevGetBlockInvsBegin *daghash.Hash
|
prevGetBlockInvsStart *daghash.Hash
|
||||||
prevGetBlockInvsStop *daghash.Hash
|
prevGetBlockInvsStop *daghash.Hash
|
||||||
prevGetHdrsMtx sync.Mutex
|
prevGetHdrsMtx sync.Mutex
|
||||||
prevGetHdrsBegin *daghash.Hash
|
prevGetHdrsStart *daghash.Hash
|
||||||
prevGetHdrsStop *daghash.Hash
|
prevGetHdrsStop *daghash.Hash
|
||||||
|
|
||||||
// These fields keep track of statistics for the peer and are protected
|
// These fields keep track of statistics for the peer and are protected
|
||||||
@ -858,33 +864,48 @@ func (p *Peer) PushAddrMsg(addresses []*wire.NetAddress, subnetworkID *subnetwor
|
|||||||
return msg.AddrList, nil
|
return msg.AddrList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Peer) PushGetBlockLocatorMsg(startHash, stopHash *daghash.Hash) {
|
||||||
|
msg := wire.NewMsgGetBlockLocator(startHash, stopHash)
|
||||||
|
p.QueueMessage(msg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// PushGetBlockInvsMsg sends a getblockinvs message for the provided block locator
|
// PushGetBlockInvsMsg sends a getblockinvs message for the provided block locator
|
||||||
// and stop hash. It will ignore back-to-back duplicate requests.
|
// and stop hash. It will ignore back-to-back duplicate requests.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (p *Peer) PushGetBlockInvsMsg(locator blockdag.BlockLocator, stopHash *daghash.Hash) error {
|
func (p *Peer) PushGetBlockInvsMsg(startHash, stopHash *daghash.Hash) error {
|
||||||
// Extract the begin hash from the block locator, if one was specified,
|
|
||||||
// to use for filtering duplicate getblockinvs requests.
|
|
||||||
var beginHash *daghash.Hash
|
|
||||||
if len(locator) > 0 {
|
|
||||||
beginHash = locator[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter duplicate getblockinvs requests.
|
// Filter duplicate getblockinvs requests.
|
||||||
p.prevGetBlockInvsMtx.Lock()
|
p.prevGetBlockInvsMtx.Lock()
|
||||||
isDuplicate := p.prevGetBlockInvsStop != nil && p.prevGetBlockInvsBegin != nil &&
|
isDuplicate := p.prevGetBlockInvsStop != nil && p.prevGetBlockInvsStart != nil &&
|
||||||
beginHash != nil && stopHash.IsEqual(p.prevGetBlockInvsStop) &&
|
startHash != nil && stopHash.IsEqual(p.prevGetBlockInvsStop) &&
|
||||||
beginHash.IsEqual(p.prevGetBlockInvsBegin)
|
startHash.IsEqual(p.prevGetBlockInvsStart)
|
||||||
p.prevGetBlockInvsMtx.Unlock()
|
p.prevGetBlockInvsMtx.Unlock()
|
||||||
|
|
||||||
if isDuplicate {
|
if isDuplicate {
|
||||||
log.Tracef("Filtering duplicate [getblockinvs] with begin "+
|
log.Tracef("Filtering duplicate [getblockinvs] with start "+
|
||||||
"hash %s, stop hash %s", beginHash, stopHash)
|
"hash %s, stop hash %s", startHash, stopHash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the getblockinvs request and queue it to be sent.
|
// Construct the getblockinvs request and queue it to be sent.
|
||||||
msg := wire.NewMsgGetBlockInvs(stopHash)
|
msg := wire.NewMsgGetBlockInvs(startHash, stopHash)
|
||||||
|
p.QueueMessage(msg, nil)
|
||||||
|
|
||||||
|
// Update the previous getblockinvs request information for filtering
|
||||||
|
// duplicates.
|
||||||
|
p.prevGetBlockInvsMtx.Lock()
|
||||||
|
p.prevGetBlockInvsStart = startHash
|
||||||
|
p.prevGetBlockInvsStop = stopHash
|
||||||
|
p.prevGetBlockInvsMtx.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBlockLocatorMsg sends a locator message for the provided block locator.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (p *Peer) PushBlockLocatorMsg(locator blockdag.BlockLocator) error {
|
||||||
|
// Construct the locator request and queue it to be sent.
|
||||||
|
msg := wire.NewMsgBlockLocator()
|
||||||
for _, hash := range locator {
|
for _, hash := range locator {
|
||||||
err := msg.AddBlockLocatorHash(hash)
|
err := msg.AddBlockLocatorHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -892,13 +913,6 @@ func (p *Peer) PushGetBlockInvsMsg(locator blockdag.BlockLocator, stopHash *dagh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.QueueMessage(msg, nil)
|
p.QueueMessage(msg, nil)
|
||||||
|
|
||||||
// Update the previous getblockinvs request information for filtering
|
|
||||||
// duplicates.
|
|
||||||
p.prevGetBlockInvsMtx.Lock()
|
|
||||||
p.prevGetBlockInvsBegin = beginHash
|
|
||||||
p.prevGetBlockInvsStop = stopHash
|
|
||||||
p.prevGetBlockInvsMtx.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -906,42 +920,28 @@ func (p *Peer) PushGetBlockInvsMsg(locator blockdag.BlockLocator, stopHash *dagh
|
|||||||
// and stop hash. It will ignore back-to-back duplicate requests.
|
// and stop hash. It will ignore back-to-back duplicate requests.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (p *Peer) PushGetHeadersMsg(locator blockdag.BlockLocator, stopHash *daghash.Hash) error {
|
func (p *Peer) PushGetHeadersMsg(startHash, stopHash *daghash.Hash) error {
|
||||||
// Extract the begin hash from the block locator, if one was specified,
|
|
||||||
// to use for filtering duplicate getheaders requests.
|
|
||||||
var beginHash *daghash.Hash
|
|
||||||
if len(locator) > 0 {
|
|
||||||
beginHash = locator[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter duplicate getheaders requests.
|
// Filter duplicate getheaders requests.
|
||||||
p.prevGetHdrsMtx.Lock()
|
p.prevGetHdrsMtx.Lock()
|
||||||
isDuplicate := p.prevGetHdrsStop != nil && p.prevGetHdrsBegin != nil &&
|
isDuplicate := p.prevGetHdrsStop != nil && p.prevGetHdrsStart != nil &&
|
||||||
beginHash != nil && stopHash.IsEqual(p.prevGetHdrsStop) &&
|
startHash != nil && stopHash.IsEqual(p.prevGetHdrsStop) &&
|
||||||
beginHash.IsEqual(p.prevGetHdrsBegin)
|
startHash.IsEqual(p.prevGetHdrsStart)
|
||||||
p.prevGetHdrsMtx.Unlock()
|
p.prevGetHdrsMtx.Unlock()
|
||||||
|
|
||||||
if isDuplicate {
|
if isDuplicate {
|
||||||
log.Tracef("Filtering duplicate [getheaders] with begin hash %s",
|
log.Tracef("Filtering duplicate [getheaders] with start hash %s",
|
||||||
beginHash)
|
startHash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the getheaders request and queue it to be sent.
|
// Construct the getheaders request and queue it to be sent.
|
||||||
msg := wire.NewMsgGetHeaders()
|
msg := wire.NewMsgGetHeaders(startHash, stopHash)
|
||||||
msg.StopHash = stopHash
|
|
||||||
for _, hash := range locator {
|
|
||||||
err := msg.AddBlockLocatorHash(hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.QueueMessage(msg, nil)
|
p.QueueMessage(msg, nil)
|
||||||
|
|
||||||
// Update the previous getheaders request information for filtering
|
// Update the previous getheaders request information for filtering
|
||||||
// duplicates.
|
// duplicates.
|
||||||
p.prevGetHdrsMtx.Lock()
|
p.prevGetHdrsMtx.Lock()
|
||||||
p.prevGetHdrsBegin = beginHash
|
p.prevGetHdrsStart = startHash
|
||||||
p.prevGetHdrsStop = stopHash
|
p.prevGetHdrsStop = stopHash
|
||||||
p.prevGetHdrsMtx.Unlock()
|
p.prevGetHdrsMtx.Unlock()
|
||||||
return nil
|
return nil
|
||||||
@ -1517,6 +1517,16 @@ out:
|
|||||||
p.cfg.Listeners.OnGetData(p, msg)
|
p.cfg.Listeners.OnGetData(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *wire.MsgGetBlockLocator:
|
||||||
|
if p.cfg.Listeners.OnGetBlockLocator != nil {
|
||||||
|
p.cfg.Listeners.OnGetBlockLocator(p, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *wire.MsgBlockLocator:
|
||||||
|
if p.cfg.Listeners.OnBlockLocator != nil {
|
||||||
|
p.cfg.Listeners.OnBlockLocator(p, msg)
|
||||||
|
}
|
||||||
|
|
||||||
case *wire.MsgGetBlockInvs:
|
case *wire.MsgGetBlockInvs:
|
||||||
if p.cfg.Listeners.OnGetBlockInvs != nil {
|
if p.cfg.Listeners.OnGetBlockInvs != nil {
|
||||||
p.cfg.Listeners.OnGetBlockInvs(p, msg)
|
p.cfg.Listeners.OnGetBlockInvs(p, msg)
|
||||||
@ -1748,26 +1758,6 @@ cleanup:
|
|||||||
log.Tracef("Peer queue handler done for %s", p)
|
log.Tracef("Peer queue handler done for %s", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldLogWriteError returns whether or not the passed error, which is
|
|
||||||
// expected to have come from writing to the remote peer in the outHandler,
|
|
||||||
// should be logged.
|
|
||||||
func (p *Peer) shouldLogWriteError(err error) bool {
|
|
||||||
// No logging when the peer is being forcibly disconnected.
|
|
||||||
if atomic.LoadInt32(&p.disconnect) != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// No logging when the remote peer has been disconnected.
|
|
||||||
if err == io.EOF {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if opErr, ok := err.(*net.OpError); ok && !opErr.Temporary() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// outHandler handles all outgoing messages for the peer. It must be run as a
|
// outHandler handles all outgoing messages for the peer. It must be run as a
|
||||||
// goroutine. It uses a buffered channel to serialize output messages while
|
// goroutine. It uses a buffered channel to serialize output messages while
|
||||||
// allowing the sender to continue running asynchronously.
|
// allowing the sender to continue running asynchronously.
|
||||||
@ -1789,10 +1779,8 @@ out:
|
|||||||
err := p.writeMessage(msg.msg)
|
err := p.writeMessage(msg.msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Disconnect()
|
p.Disconnect()
|
||||||
if p.shouldLogWriteError(err) {
|
|
||||||
log.Errorf("Failed to send message to "+
|
log.Errorf("Failed to send message to "+
|
||||||
"%s: %s", p, err)
|
"%s: %s", p, err)
|
||||||
}
|
|
||||||
if msg.doneChan != nil {
|
if msg.doneChan != nil {
|
||||||
msg.doneChan <- struct{}{}
|
msg.doneChan <- struct{}{}
|
||||||
}
|
}
|
||||||
|
@ -514,11 +514,11 @@ func TestPeerListeners(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnGetBlockInvs",
|
"OnGetBlockInvs",
|
||||||
wire.NewMsgGetBlockInvs(&daghash.Hash{}),
|
wire.NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnGetHeaders",
|
"OnGetHeaders",
|
||||||
wire.NewMsgGetHeaders(),
|
wire.NewMsgGetHeaders(&daghash.Hash{}, &daghash.Hash{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OnGetCFilters",
|
"OnGetCFilters",
|
||||||
@ -694,7 +694,7 @@ func TestOutboundPeer(t *testing.T) {
|
|||||||
p2.QueueMessage(wire.NewMsgPing(1), nil)
|
p2.QueueMessage(wire.NewMsgPing(1), nil)
|
||||||
p2.QueueMessage(wire.NewMsgMemPool(), nil)
|
p2.QueueMessage(wire.NewMsgMemPool(), nil)
|
||||||
p2.QueueMessage(wire.NewMsgGetData(), nil)
|
p2.QueueMessage(wire.NewMsgGetData(), nil)
|
||||||
p2.QueueMessage(wire.NewMsgGetHeaders(), nil)
|
p2.QueueMessage(wire.NewMsgGetHeaders(&daghash.ZeroHash, &daghash.ZeroHash), nil)
|
||||||
p2.QueueMessage(wire.NewMsgFeeFilter(20000), nil)
|
p2.QueueMessage(wire.NewMsgFeeFilter(20000), nil)
|
||||||
|
|
||||||
p2.Disconnect()
|
p2.Disconnect()
|
||||||
|
@ -216,16 +216,16 @@ func (c *Client) GetTopHeaders(startHash *daghash.Hash) ([]wire.BlockHeader, err
|
|||||||
//
|
//
|
||||||
// NOTE: This is a btcsuite extension ported from
|
// NOTE: This is a btcsuite extension ported from
|
||||||
// github.com/decred/dcrrpcclient.
|
// github.com/decred/dcrrpcclient.
|
||||||
func (c *Client) GetHeadersAsync(blockLocators []*daghash.Hash, stopHash *daghash.Hash) FutureGetHeadersResult {
|
func (c *Client) GetHeadersAsync(startHash, stopHash *daghash.Hash) FutureGetHeadersResult {
|
||||||
locators := make([]string, len(blockLocators))
|
startHashStr := ""
|
||||||
for i := range blockLocators {
|
if startHash != nil {
|
||||||
locators[i] = blockLocators[i].String()
|
startHashStr = startHash.String()
|
||||||
}
|
}
|
||||||
hash := ""
|
stopHashStr := ""
|
||||||
if stopHash != nil {
|
if stopHash != nil {
|
||||||
hash = stopHash.String()
|
stopHashStr = stopHash.String()
|
||||||
}
|
}
|
||||||
cmd := btcjson.NewGetHeadersCmd(locators, hash)
|
cmd := btcjson.NewGetHeadersCmd(startHashStr, stopHashStr)
|
||||||
return c.sendCmd(cmd)
|
return c.sendCmd(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +235,8 @@ func (c *Client) GetHeadersAsync(blockLocators []*daghash.Hash, stopHash *daghas
|
|||||||
//
|
//
|
||||||
// NOTE: This is a btcsuite extension ported from
|
// NOTE: This is a btcsuite extension ported from
|
||||||
// github.com/decred/dcrrpcclient.
|
// github.com/decred/dcrrpcclient.
|
||||||
func (c *Client) GetHeaders(blockLocators []*daghash.Hash, stopHash *daghash.Hash) ([]wire.BlockHeader, error) {
|
func (c *Client) GetHeaders(startHash, stopHash *daghash.Hash) ([]wire.BlockHeader, error) {
|
||||||
return c.GetHeadersAsync(blockLocators, stopHash).Receive()
|
return c.GetHeadersAsync(startHash, stopHash).Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FutureSessionResult is a future promise to deliver the result of a
|
// FutureSessionResult is a future promise to deliver the result of a
|
||||||
|
@ -682,21 +682,80 @@ func (sp *Peer) OnGetData(_ *peer.Peer, msg *wire.MsgGetData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnGetBlockLocator is invoked when a peer receives a getlocator bitcoin
|
||||||
|
// message.
|
||||||
|
func (sp *Peer) OnGetBlockLocator(_ *peer.Peer, msg *wire.MsgGetBlockLocator) {
|
||||||
|
locator := sp.server.DAG.BlockLocatorFromHashes(msg.StartHash, msg.StopHash)
|
||||||
|
|
||||||
|
if len(locator) == 0 {
|
||||||
|
peerLog.Infof("Couldn't build a block locator between blocks %s and %s"+
|
||||||
|
" that was requested from peer %s",
|
||||||
|
sp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := sp.PushBlockLocatorMsg(locator)
|
||||||
|
if err != nil {
|
||||||
|
peerLog.Errorf("Failed to send block locator message to peer %s: %s",
|
||||||
|
sp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnBlockLocator is invoked when a peer receives a locator bitcoin
|
||||||
|
// message.
|
||||||
|
func (sp *Peer) OnBlockLocator(_ *peer.Peer, msg *wire.MsgBlockLocator) {
|
||||||
|
// Find the highest known shared block between the peers, and asks
|
||||||
|
// the block and its future from the peer. If the block is not
|
||||||
|
// found, create a lower resolution block locator and send it to
|
||||||
|
// the peer in order to find it in the next iteration.
|
||||||
|
dag := sp.server.DAG
|
||||||
|
if len(msg.BlockLocatorHashes) == 0 {
|
||||||
|
peerLog.Warnf("Got empty block locator from peer %s",
|
||||||
|
sp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If the first hash of the block locator is known, it means we found
|
||||||
|
// the highest shared block.
|
||||||
|
firstHash := msg.BlockLocatorHashes[0]
|
||||||
|
exists, err := dag.BlockExists(firstHash)
|
||||||
|
if err != nil {
|
||||||
|
peerLog.Errorf("Error checking if first hash in the block"+
|
||||||
|
" locator (%s) exists in the dag: %s",
|
||||||
|
msg.BlockLocatorHashes[0], sp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
err := sp.server.SyncManager.PushGetBlockInvsOrHeaders(sp.Peer, firstHash)
|
||||||
|
if err != nil {
|
||||||
|
peerLog.Errorf("Failed pushing get blocks message for peer %s: %s",
|
||||||
|
sp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
startHash, stopHash := dag.FindNextLocatorBoundaries(msg.BlockLocatorHashes)
|
||||||
|
if startHash == nil {
|
||||||
|
panic("Couldn't find any unknown hashes in the block locator.")
|
||||||
|
}
|
||||||
|
sp.PushGetBlockLocatorMsg(startHash, stopHash)
|
||||||
|
}
|
||||||
|
|
||||||
// OnGetBlockInvs is invoked when a peer receives a getblockinvs bitcoin
|
// OnGetBlockInvs is invoked when a peer receives a getblockinvs bitcoin
|
||||||
// message.
|
// message.
|
||||||
|
// It finds the blue future between msg.StartHash and msg.StopHash
|
||||||
|
// and send the invs to the requesting peer.
|
||||||
func (sp *Peer) OnGetBlockInvs(_ *peer.Peer, msg *wire.MsgGetBlockInvs) {
|
func (sp *Peer) OnGetBlockInvs(_ *peer.Peer, msg *wire.MsgGetBlockInvs) {
|
||||||
// Find the most recent known block in the dag based on the block
|
|
||||||
// locator and fetch all of the block hashes after it until either
|
|
||||||
// wire.MaxBlocksPerMsg have been fetched or the provided stop hash is
|
|
||||||
// encountered.
|
|
||||||
//
|
|
||||||
// Use the block after the genesis block if no other blocks in the
|
|
||||||
// provided locator are known. This does mean the client will start
|
|
||||||
// over with the genesis block if unknown block locators are provided.
|
|
||||||
//
|
|
||||||
// This mirrors the behavior in the reference implementation.
|
|
||||||
dag := sp.server.DAG
|
dag := sp.server.DAG
|
||||||
hashList := dag.LocateBlocks(msg.BlockLocatorHashes, msg.StopHash,
|
// We want to prevent a situation where the syncing peer needs
|
||||||
|
// to call getblocks once again, but the block we sent him
|
||||||
|
// won't affect his selected chain, so next time it'll try
|
||||||
|
// to find the highest shared chain block, it'll find the
|
||||||
|
// same one as before.
|
||||||
|
// To prevent that we use blockdag.FinalityInterval as maxHashes.
|
||||||
|
// This way, if one getblocks is not enough to get the peer
|
||||||
|
// synced, we can know for sure that its selected chain will
|
||||||
|
// change, so we'll have higher shared chain block.
|
||||||
|
hashList := dag.GetBlueBlocksHashesBetween(msg.StartHash, msg.StopHash,
|
||||||
wire.MaxInvPerMsg)
|
wire.MaxInvPerMsg)
|
||||||
|
|
||||||
// Generate inventory message.
|
// Generate inventory message.
|
||||||
@ -728,10 +787,8 @@ func (sp *Peer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) {
|
|||||||
// Use the block after the genesis block if no other blocks in the
|
// Use the block after the genesis block if no other blocks in the
|
||||||
// provided locator are known. This does mean the client will start
|
// provided locator are known. This does mean the client will start
|
||||||
// over with the genesis block if unknown block locators are provided.
|
// over with the genesis block if unknown block locators are provided.
|
||||||
//
|
|
||||||
// This mirrors the behavior in the reference implementation.
|
|
||||||
dag := sp.server.DAG
|
dag := sp.server.DAG
|
||||||
headers := dag.LocateHeaders(msg.BlockLocatorHashes, msg.StopHash)
|
headers := dag.GetBlueBlocksHeadersBetween(msg.StartHash, msg.StopHash)
|
||||||
|
|
||||||
// Send found headers to the requesting peer.
|
// Send found headers to the requesting peer.
|
||||||
blockHeaders := make([]*wire.BlockHeader, len(headers))
|
blockHeaders := make([]*wire.BlockHeader, len(headers))
|
||||||
@ -1778,6 +1835,8 @@ func newPeerConfig(sp *Peer) *peer.Config {
|
|||||||
OnInv: sp.OnInv,
|
OnInv: sp.OnInv,
|
||||||
OnHeaders: sp.OnHeaders,
|
OnHeaders: sp.OnHeaders,
|
||||||
OnGetData: sp.OnGetData,
|
OnGetData: sp.OnGetData,
|
||||||
|
OnGetBlockLocator: sp.OnGetBlockLocator,
|
||||||
|
OnBlockLocator: sp.OnBlockLocator,
|
||||||
OnGetBlockInvs: sp.OnGetBlockInvs,
|
OnGetBlockInvs: sp.OnGetBlockInvs,
|
||||||
OnGetHeaders: sp.OnGetHeaders,
|
OnGetHeaders: sp.OnGetHeaders,
|
||||||
OnGetCFilters: sp.OnGetCFilters,
|
OnGetCFilters: sp.OnGetCFilters,
|
||||||
|
@ -269,12 +269,12 @@ func (b *rpcSyncMgr) SyncPeerID() int32 {
|
|||||||
return b.syncMgr.SyncPeerID()
|
return b.syncMgr.SyncPeerID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocateBlocks returns the hashes of the blocks after the first known block in
|
// GetBlueBlocksHeadersBetween returns the headers of the blocks after the provided
|
||||||
// the provided locators until the provided stop hash or the current tip is
|
// start hash until the provided stop hash is reached, or up to the
|
||||||
// reached, up to a max of wire.MaxBlockHeadersPerMsg hashes.
|
// provided max number of block headers.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access and is part of the
|
// This function is safe for concurrent access and is part of the
|
||||||
// rpcserverSyncManager interface implementation.
|
// rpcserverSyncManager interface implementation.
|
||||||
func (b *rpcSyncMgr) LocateHeaders(locators []*daghash.Hash, stopHash *daghash.Hash) []*wire.BlockHeader {
|
func (b *rpcSyncMgr) GetBlueBlocksHeadersBetween(startHash, stopHash *daghash.Hash) []*wire.BlockHeader {
|
||||||
return b.server.DAG.LocateHeaders(locators, stopHash)
|
return b.server.DAG.GetBlueBlocksHeadersBetween(startHash, stopHash)
|
||||||
}
|
}
|
||||||
|
@ -2383,24 +2383,21 @@ func handleGetTopHeaders(s *Server, cmd interface{}, closeChan <-chan struct{})
|
|||||||
func handleGetHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleGetHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
c := cmd.(*btcjson.GetHeadersCmd)
|
c := cmd.(*btcjson.GetHeadersCmd)
|
||||||
|
|
||||||
// Fetch the requested headers from chain while respecting the provided
|
startHash := &daghash.ZeroHash
|
||||||
// block locators and stop hash.
|
if c.StartHash != "" {
|
||||||
blockLocators := make([]*daghash.Hash, len(c.BlockLocators))
|
err := daghash.Decode(startHash, c.StartHash)
|
||||||
for i := range c.BlockLocators {
|
|
||||||
blockLocator, err := daghash.NewHashFromStr(c.BlockLocators[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, rpcDecodeHexError(c.BlockLocators[i])
|
|
||||||
}
|
|
||||||
blockLocators[i] = blockLocator
|
|
||||||
}
|
|
||||||
var stopHash daghash.Hash
|
|
||||||
if c.StopHash != "" {
|
|
||||||
err := daghash.Decode(&stopHash, c.StopHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, rpcDecodeHexError(c.StopHash)
|
return nil, rpcDecodeHexError(c.StopHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers := s.cfg.SyncMgr.LocateHeaders(blockLocators, &stopHash)
|
stopHash := &daghash.ZeroHash
|
||||||
|
if c.StopHash != "" {
|
||||||
|
err := daghash.Decode(stopHash, c.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, rpcDecodeHexError(c.StopHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers := s.cfg.SyncMgr.GetBlueBlocksHeadersBetween(startHash, stopHash)
|
||||||
|
|
||||||
// Return the serialized block headers as hex-encoded strings.
|
// Return the serialized block headers as hex-encoded strings.
|
||||||
hexBlockHeaders := make([]string, len(headers))
|
hexBlockHeaders := make([]string, len(headers))
|
||||||
@ -4238,11 +4235,11 @@ type rpcserverSyncManager interface {
|
|||||||
// used to sync from or 0 if there is none.
|
// used to sync from or 0 if there is none.
|
||||||
SyncPeerID() int32
|
SyncPeerID() int32
|
||||||
|
|
||||||
// LocateHeaders returns the headers of the blocks after the first known
|
// GetBlueBlocksHeadersBetween returns the headers of the blocks after the first known
|
||||||
// block in the provided locators until the provided stop hash or the
|
// block in the provided locators until the provided stop hash or the
|
||||||
// current tip is reached, up to a max of wire.MaxBlockHeadersPerMsg
|
// current tip is reached, up to a max of wire.MaxBlockHeadersPerMsg
|
||||||
// hashes.
|
// hashes.
|
||||||
LocateHeaders(locators []*daghash.Hash, stopHash *daghash.Hash) []*wire.BlockHeader
|
GetBlueBlocksHeadersBetween(startHash, stopHash *daghash.Hash) []*wire.BlockHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
// rpcserverConfig is a descriptor containing the RPC server configuration.
|
// rpcserverConfig is a descriptor containing the RPC server configuration.
|
||||||
|
@ -401,12 +401,12 @@ var helpDescsEnUS = map[string]string{
|
|||||||
|
|
||||||
// GetTopHeadersCmd help.
|
// GetTopHeadersCmd help.
|
||||||
"getTopHeaders--synopsis": "Returns the top block headers starting with the provided start hash (not inclusive)",
|
"getTopHeaders--synopsis": "Returns the top block headers starting with the provided start hash (not inclusive)",
|
||||||
"getTopHeaders-startHash": "Block hash to stop including block headers for; if not found, all headers to the latest known block are returned.",
|
"getTopHeaders-startHash": "Block hash to start including block headers from; if not found, it'll start from the virtual.",
|
||||||
"getTopHeaders--result0": "Serialized block headers of all located blocks, limited to some arbitrary maximum number of hashes (currently 2000, which matches the wire protocol headers message, but this is not guaranteed)",
|
"getTopHeaders--result0": "Serialized block headers of all located blocks, limited to some arbitrary maximum number of hashes (currently 2000, which matches the wire protocol headers message, but this is not guaranteed)",
|
||||||
|
|
||||||
// GetHeadersCmd help.
|
// GetHeadersCmd help.
|
||||||
"getHeaders--synopsis": "Returns block headers starting with the first known block hash from the request",
|
"getHeaders--synopsis": "Returns block headers starting with the first known block hash from the request",
|
||||||
"getHeaders-blockLocators": "JSON array of hex-encoded hashes of blocks. Headers are returned starting from the first known hash in this list",
|
"getHeaders-startHash": "Block hash to start including headers from; if not found, it'll start from the genesis block.",
|
||||||
"getHeaders-stopHash": "Block hash to stop including block headers for; if not found, all headers to the latest known block are returned.",
|
"getHeaders-stopHash": "Block hash to stop including block headers for; if not found, all headers to the latest known block are returned.",
|
||||||
"getHeaders--result0": "Serialized block headers of all located blocks, limited to some arbitrary maximum number of hashes (currently 2000, which matches the wire protocol headers message, but this is not guaranteed)",
|
"getHeaders--result0": "Serialized block headers of all located blocks, limited to some arbitrary maximum number of hashes (currently 2000, which matches the wire protocol headers message, but this is not guaranteed)",
|
||||||
|
|
||||||
|
@ -377,18 +377,12 @@ func BenchmarkWriteBlockHeader(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BenchmarkDecodeGetHeaders performs a benchmark on how long it takes to
|
// BenchmarkDecodeGetHeaders performs a benchmark on how long it takes to
|
||||||
// decode a getheaders message with the maximum number of block locator hashes.
|
// decode a getheaders message.
|
||||||
func BenchmarkDecodeGetHeaders(b *testing.B) {
|
func BenchmarkDecodeGetHeaders(b *testing.B) {
|
||||||
// Create a message with the maximum number of block locators.
|
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
var m MsgGetHeaders
|
var m MsgGetHeaders
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
m.StartHash = &daghash.Hash{1}
|
||||||
hash, err := daghash.NewHashFromStr(fmt.Sprintf("%x", i))
|
m.StopHash = &daghash.Hash{1}
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
m.AddBlockLocatorHash(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize it so the bytes are available to test the decode below.
|
// Serialize it so the bytes are available to test the decode below.
|
||||||
var bb bytes.Buffer
|
var bb bytes.Buffer
|
||||||
@ -446,18 +440,12 @@ func BenchmarkDecodeHeaders(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BenchmarkDecodeGetBlockInvs performs a benchmark on how long it takes to
|
// BenchmarkDecodeGetBlockInvs performs a benchmark on how long it takes to
|
||||||
// decode a getblockinvs message with the maximum number of block locator hashes.
|
// decode a getblockinvs message.
|
||||||
func BenchmarkDecodeGetBlockInvs(b *testing.B) {
|
func BenchmarkDecodeGetBlockInvs(b *testing.B) {
|
||||||
// Create a message with the maximum number of block locators.
|
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
var m MsgGetBlockInvs
|
var m MsgGetBlockInvs
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
m.StartHash = &daghash.Hash{1}
|
||||||
hash, err := daghash.NewHashFromStr(fmt.Sprintf("%x", i))
|
m.StopHash = &daghash.Hash{1}
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
m.AddBlockLocatorHash(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize it so the bytes are available to test the decode below.
|
// Serialize it so the bytes are available to test the decode below.
|
||||||
var bb bytes.Buffer
|
var bb bytes.Buffer
|
||||||
|
@ -57,6 +57,8 @@ const (
|
|||||||
CmdCFilter = "cfilter"
|
CmdCFilter = "cfilter"
|
||||||
CmdCFHeaders = "cfheaders"
|
CmdCFHeaders = "cfheaders"
|
||||||
CmdCFCheckpt = "cfcheckpt"
|
CmdCFCheckpt = "cfcheckpt"
|
||||||
|
CmdGetBlockLocator = "getlocator"
|
||||||
|
CmdBlockLocator = "locator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message is an interface that describes a bitcoin message. A type that
|
// Message is an interface that describes a bitcoin message. A type that
|
||||||
@ -99,6 +101,12 @@ func makeEmptyMessage(command string) (Message, error) {
|
|||||||
case CmdGetData:
|
case CmdGetData:
|
||||||
msg = &MsgGetData{}
|
msg = &MsgGetData{}
|
||||||
|
|
||||||
|
case CmdGetBlockLocator:
|
||||||
|
msg = &MsgGetBlockLocator{}
|
||||||
|
|
||||||
|
case CmdBlockLocator:
|
||||||
|
msg = &MsgBlockLocator{}
|
||||||
|
|
||||||
case CmdNotFound:
|
case CmdNotFound:
|
||||||
msg = &MsgNotFound{}
|
msg = &MsgNotFound{}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ func TestMessage(t *testing.T) {
|
|||||||
msgVerack := NewMsgVerAck()
|
msgVerack := NewMsgVerAck()
|
||||||
msgGetAddr := NewMsgGetAddr(false, nil)
|
msgGetAddr := NewMsgGetAddr(false, nil)
|
||||||
msgAddr := NewMsgAddr(false, nil)
|
msgAddr := NewMsgAddr(false, nil)
|
||||||
msgGetBlockInvs := NewMsgGetBlockInvs(&daghash.Hash{})
|
msgGetBlockInvs := NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{})
|
||||||
msgBlock := &blockOne
|
msgBlock := &blockOne
|
||||||
msgInv := NewMsgInv()
|
msgInv := NewMsgInv()
|
||||||
msgGetData := NewMsgGetData()
|
msgGetData := NewMsgGetData()
|
||||||
@ -59,7 +59,9 @@ func TestMessage(t *testing.T) {
|
|||||||
msgTx := NewNativeMsgTx(1, nil, nil)
|
msgTx := NewNativeMsgTx(1, nil, nil)
|
||||||
msgPing := NewMsgPing(123123)
|
msgPing := NewMsgPing(123123)
|
||||||
msgPong := NewMsgPong(123123)
|
msgPong := NewMsgPong(123123)
|
||||||
msgGetHeaders := NewMsgGetHeaders()
|
msgGetHeaders := NewMsgGetHeaders(&daghash.Hash{}, &daghash.Hash{})
|
||||||
|
msgGetBlockLocator := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||||
|
msgBlockLocator := NewMsgBlockLocator()
|
||||||
msgSendHeaders := NewMsgSendHeaders()
|
msgSendHeaders := NewMsgSendHeaders()
|
||||||
msgFeeFilter := NewMsgFeeFilter(123456)
|
msgFeeFilter := NewMsgFeeFilter(123456)
|
||||||
msgHeaders := NewMsgHeaders()
|
msgHeaders := NewMsgHeaders()
|
||||||
@ -90,7 +92,7 @@ func TestMessage(t *testing.T) {
|
|||||||
{msgVerack, msgVerack, pver, MainNet, 24},
|
{msgVerack, msgVerack, pver, MainNet, 24},
|
||||||
{msgGetAddr, msgGetAddr, pver, MainNet, 26},
|
{msgGetAddr, msgGetAddr, pver, MainNet, 26},
|
||||||
{msgAddr, msgAddr, pver, MainNet, 27},
|
{msgAddr, msgAddr, pver, MainNet, 27},
|
||||||
{msgGetBlockInvs, msgGetBlockInvs, pver, MainNet, 61},
|
{msgGetBlockInvs, msgGetBlockInvs, pver, MainNet, 88},
|
||||||
{msgBlock, msgBlock, pver, MainNet, 372},
|
{msgBlock, msgBlock, pver, MainNet, 372},
|
||||||
{msgInv, msgInv, pver, MainNet, 25},
|
{msgInv, msgInv, pver, MainNet, 25},
|
||||||
{msgGetData, msgGetData, pver, MainNet, 25},
|
{msgGetData, msgGetData, pver, MainNet, 25},
|
||||||
@ -98,7 +100,9 @@ func TestMessage(t *testing.T) {
|
|||||||
{msgTx, msgTx, pver, MainNet, 58},
|
{msgTx, msgTx, pver, MainNet, 58},
|
||||||
{msgPing, msgPing, pver, MainNet, 32},
|
{msgPing, msgPing, pver, MainNet, 32},
|
||||||
{msgPong, msgPong, pver, MainNet, 32},
|
{msgPong, msgPong, pver, MainNet, 32},
|
||||||
{msgGetHeaders, msgGetHeaders, pver, MainNet, 61},
|
{msgGetHeaders, msgGetHeaders, pver, MainNet, 88},
|
||||||
|
{msgGetBlockLocator, msgGetBlockLocator, pver, MainNet, 88},
|
||||||
|
{msgBlockLocator, msgBlockLocator, pver, MainNet, 25},
|
||||||
{msgSendHeaders, msgSendHeaders, pver, MainNet, 24},
|
{msgSendHeaders, msgSendHeaders, pver, MainNet, 24},
|
||||||
{msgFeeFilter, msgFeeFilter, pver, MainNet, 32},
|
{msgFeeFilter, msgFeeFilter, pver, MainNet, 32},
|
||||||
{msgHeaders, msgHeaders, pver, MainNet, 25},
|
{msgHeaders, msgHeaders, pver, MainNet, 25},
|
||||||
|
112
wire/msgblocklocator.go
Normal file
112
wire/msgblocklocator.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
|
||||||
|
// per message.
|
||||||
|
const MaxBlockLocatorsPerMsg = 500
|
||||||
|
|
||||||
|
// MsgBlockLocator implements the Message interface and represents a bitcoin
|
||||||
|
// locator message. It is used to find the highest known chain block with
|
||||||
|
// a peer that is syncing with you.
|
||||||
|
type MsgBlockLocator struct {
|
||||||
|
BlockLocatorHashes []*daghash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBlockLocatorHash adds a new block locator hash to the message.
|
||||||
|
func (msg *MsgBlockLocator) AddBlockLocatorHash(hash *daghash.Hash) error {
|
||||||
|
if len(msg.BlockLocatorHashes) >= MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message [max %d]",
|
||||||
|
MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgBlockLocator.AddBlockLocatorHash", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgBlockLocator) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
|
// Read num block locator hashes and limit to max.
|
||||||
|
count, err := ReadVarInt(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message "+
|
||||||
|
"[count %d, max %d]", count, MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgBlockLocator.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
locatorHashes := make([]daghash.Hash, count)
|
||||||
|
msg.BlockLocatorHashes = make([]*daghash.Hash, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
hash := &locatorHashes[i]
|
||||||
|
err := ReadElement(r, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = msg.AddBlockLocatorHash(hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgBlockLocator) BtcEncode(w io.Writer, pver uint32) error {
|
||||||
|
// Limit to max block locator hashes per message.
|
||||||
|
count := len(msg.BlockLocatorHashes)
|
||||||
|
if count > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message "+
|
||||||
|
"[count %d, max %d]", count, MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgBlockLocator.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hash := range msg.BlockLocatorHashes {
|
||||||
|
err := WriteElement(w, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgBlockLocator) Command() string {
|
||||||
|
return CmdBlockLocator
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgBlockLocator) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Num block locator hashes (varInt) + max allowed block
|
||||||
|
// locators.
|
||||||
|
return MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
|
||||||
|
daghash.HashSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgBlockLocator returns a new bitcoin locator message that conforms to
|
||||||
|
// the Message interface. See MsgBlockLocator for details.
|
||||||
|
func NewMsgBlockLocator() *MsgBlockLocator {
|
||||||
|
return &MsgBlockLocator{
|
||||||
|
BlockLocatorHashes: make([]*daghash.Hash, 0,
|
||||||
|
MaxBlockLocatorsPerMsg),
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,3 @@
|
|||||||
// 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 wire
|
package wire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -14,42 +10,29 @@ import (
|
|||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestGetBlockInvs tests the MsgGetBlockInvs API.
|
// TestBlockLocator tests the MsgBlockLocator API.
|
||||||
func TestGetBlockInvs(t *testing.T) {
|
func TestBlockLocator(t *testing.T) {
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
|
|
||||||
// Block 99500 hash.
|
|
||||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
locatorHash, err := daghash.NewHashFromStr(hashStr)
|
locatorHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 100000 hash.
|
msg := NewMsgBlockLocator()
|
||||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
|
||||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we get the same data back out.
|
|
||||||
msg := NewMsgGetBlockInvs(stopHash)
|
|
||||||
if !msg.StopHash.IsEqual(stopHash) {
|
|
||||||
t.Errorf("NewMsgGetBlockInvs: wrong stop hash - got %v, want %v",
|
|
||||||
msg.StopHash, stopHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
// Ensure the command is expected value.
|
||||||
wantCmd := "getblockinvs"
|
wantCmd := "locator"
|
||||||
if cmd := msg.Command(); cmd != wantCmd {
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
t.Errorf("NewMsgGetBlockInvs: wrong command - got %v want %v",
|
t.Errorf("NewMsgBlockLocator: wrong command - got %v want %v",
|
||||||
cmd, wantCmd)
|
cmd, wantCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
// Protocol version 4 bytes + num hashes (varInt) + max block locator
|
// Num hashes (varInt) + max block locator
|
||||||
// hashes + hash stop.
|
// hashes.
|
||||||
wantPayload := uint32(16045)
|
wantPayload := uint32(16009)
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
if maxPayload != wantPayload {
|
if maxPayload != wantPayload {
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
@ -80,70 +63,46 @@ func TestGetBlockInvs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetBlockInvsWire tests the MsgGetBlockInvs wire encode and decode for various
|
// TestBlockLocatorWire tests the MsgBlockLocator wire encode and decode for various
|
||||||
// numbers of block locator hashes and protocol versions.
|
// numbers of block locator hashes.
|
||||||
func TestGetBlockInvsWire(t *testing.T) {
|
func TestBlockLocatorWire(t *testing.T) {
|
||||||
// Set protocol inside getblockinvs message.
|
|
||||||
pver := uint32(1)
|
|
||||||
|
|
||||||
// Block 99499 hash.
|
|
||||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 99500 hash.
|
|
||||||
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
hashLocator2, err := daghash.NewHashFromStr(hashStr)
|
hashLocator2, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 100000 hash.
|
// MsgBlockLocator message with no block locators.
|
||||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
noLocators := NewMsgBlockLocator()
|
||||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgGetBlockInvs message with no block locators or stop hash.
|
|
||||||
noLocators := NewMsgGetBlockInvs(&daghash.Hash{})
|
|
||||||
noLocators.ProtocolVersion = pver
|
|
||||||
noLocatorsEncoded := []byte{
|
noLocatorsEncoded := []byte{
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0x00, // Varint for number of block locator hashes
|
0x00, // Varint for number of block locator hashes
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetBlockInvs message with multiple block locators and a stop hash.
|
// MsgBlockLocator message with multiple block locators.
|
||||||
multiLocators := NewMsgGetBlockInvs(stopHash)
|
multiLocators := NewMsgBlockLocator()
|
||||||
multiLocators.AddBlockLocatorHash(hashLocator2)
|
multiLocators.AddBlockLocatorHash(hashLocator2)
|
||||||
multiLocators.AddBlockLocatorHash(hashLocator)
|
multiLocators.AddBlockLocatorHash(hashLocator)
|
||||||
multiLocators.ProtocolVersion = pver
|
|
||||||
multiLocatorsEncoded := []byte{
|
multiLocatorsEncoded := []byte{
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0x02, // Varint for number of block locator hashes
|
0x02, // Varint for number of block locator hashes
|
||||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
||||||
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
|
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
|
||||||
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
|
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
|
||||||
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
|
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // first hash
|
||||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // second hash
|
||||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
|
||||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
|
||||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
|
||||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in *MsgGetBlockInvs // Message to encode
|
in *MsgBlockLocator // Message to encode
|
||||||
out *MsgGetBlockInvs // Expected decoded message
|
out *MsgBlockLocator // Expected decoded message
|
||||||
buf []byte // Wire encoding
|
buf []byte // Wire encoding
|
||||||
pver uint32 // Protocol version for wire encoding
|
pver uint32 // Protocol version for wire encoding
|
||||||
}{
|
}{
|
||||||
@ -180,7 +139,7 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decode the message from wire format.
|
// Decode the message from wire format.
|
||||||
var msg MsgGetBlockInvs
|
var msg MsgBlockLocator
|
||||||
rbuf := bytes.NewReader(test.buf)
|
rbuf := bytes.NewReader(test.buf)
|
||||||
err = msg.BtcDecode(rbuf, test.pver)
|
err = msg.BtcDecode(rbuf, test.pver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -195,43 +154,32 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetBlockInvsWireErrors performs negative tests against wire encode and
|
// TestBlockLocatorWireErrors performs negative tests against wire encode and
|
||||||
// decode of MsgGetBlockInvs to confirm error paths work correctly.
|
// decode of MsgBlockLocator to confirm error paths work correctly.
|
||||||
func TestGetBlockInvsWireErrors(t *testing.T) {
|
func TestBlockLocatorWireErrors(t *testing.T) {
|
||||||
// Set protocol inside getheaders message. Use protocol version 1
|
// Set protocol inside locator message. Use protocol version 1
|
||||||
// specifically here instead of the latest because the test data is
|
// specifically here instead of the latest because the test data is
|
||||||
// using bytes encoded with that protocol version.
|
// using bytes encoded with that protocol version.
|
||||||
pver := uint32(1)
|
pver := uint32(1)
|
||||||
wireErr := &MessageError{}
|
wireErr := &MessageError{}
|
||||||
|
|
||||||
// Block 99499 hash.
|
|
||||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 99500 hash.
|
|
||||||
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
hashLocator2, err := daghash.NewHashFromStr(hashStr)
|
hashLocator2, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 100000 hash.
|
// MsgBlockLocator message with multiple block locators and a stop hash.
|
||||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
baseGetBlocks := NewMsgBlockLocator()
|
||||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
baseGetBlocks.AddBlockLocatorHash(hashLocator2)
|
||||||
if err != nil {
|
baseGetBlocks.AddBlockLocatorHash(hashLocator)
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
baseGetBlocksEncoded := []byte{
|
||||||
}
|
|
||||||
|
|
||||||
// MsgGetBlockInvs message with multiple block locators and a stop hash.
|
|
||||||
baseGetBlockInvs := NewMsgGetBlockInvs(stopHash)
|
|
||||||
baseGetBlockInvs.ProtocolVersion = pver
|
|
||||||
baseGetBlockInvs.AddBlockLocatorHash(hashLocator2)
|
|
||||||
baseGetBlockInvs.AddBlockLocatorHash(hashLocator)
|
|
||||||
baseGetBlockInvsEncoded := []byte{
|
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0x02, // Varint for number of block locator hashes
|
0x02, // Varint for number of block locator hashes
|
||||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
||||||
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
|
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
|
||||||
@ -241,43 +189,34 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
|
|||||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
|
||||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
|
||||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
|
||||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
|
||||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message that forces an error by having more than the max allowed
|
// Message that forces an error by having more than the max allowed
|
||||||
// block locator hashes.
|
// block locator hashes.
|
||||||
maxGetBlockInvs := NewMsgGetBlockInvs(stopHash)
|
maxGetBlocks := NewMsgBlockLocator()
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
||||||
maxGetBlockInvs.AddBlockLocatorHash(mainNetGenesisHash)
|
maxGetBlocks.AddBlockLocatorHash(mainNetGenesisHash)
|
||||||
}
|
}
|
||||||
maxGetBlockInvs.BlockLocatorHashes = append(maxGetBlockInvs.BlockLocatorHashes,
|
maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes,
|
||||||
mainNetGenesisHash)
|
mainNetGenesisHash)
|
||||||
maxGetBlockInvsEncoded := []byte{
|
maxGetBlocksEncoded := []byte{
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
|
0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in *MsgGetBlockInvs // Value to encode
|
in *MsgBlockLocator // Value to encode
|
||||||
buf []byte // Wire encoding
|
buf []byte // Wire encoding
|
||||||
pver uint32 // Protocol version for wire encoding
|
pver uint32 // Protocol version for wire encoding
|
||||||
max int // Max size of fixed buffer to induce errors
|
max int // Max size of fixed buffer to induce errors
|
||||||
writeErr error // Expected write error
|
writeErr error // Expected write error
|
||||||
readErr error // Expected read error
|
readErr error // Expected read error
|
||||||
}{
|
}{
|
||||||
// Force error in protocol version.
|
|
||||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
|
||||||
// Force error in block locator hash count.
|
// Force error in block locator hash count.
|
||||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 4, io.ErrShortWrite, io.EOF},
|
{baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
// Force error in block locator hashes.
|
// Force error in block locator hashes.
|
||||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 5, io.ErrShortWrite, io.EOF},
|
{baseGetBlocks, baseGetBlocksEncoded, pver, 1, io.ErrShortWrite, io.EOF},
|
||||||
// Force error in stop hash.
|
|
||||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 69, io.ErrShortWrite, io.EOF},
|
|
||||||
// Force error with greater than max block locator hashes.
|
// Force error with greater than max block locator hashes.
|
||||||
{maxGetBlockInvs, maxGetBlockInvsEncoded, pver, 7, wireErr, wireErr},
|
{maxGetBlocks, maxGetBlocksEncoded, pver, 3, wireErr, wireErr},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
t.Logf("Running %d tests", len(tests))
|
||||||
@ -302,7 +241,7 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decode from wire format.
|
// Decode from wire format.
|
||||||
var msg MsgGetBlockInvs
|
var msg MsgBlockLocator
|
||||||
r := newFixedReader(test.max, test.buf)
|
r := newFixedReader(test.max, test.buf)
|
||||||
err = msg.BtcDecode(r, test.pver)
|
err = msg.BtcDecode(r, test.pver)
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
66
wire/msggetblockinvs.go
Normal file
66
wire/msggetblockinvs.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgGetBlockInvs implements the Message interface and represents a bitcoin
|
||||||
|
// getblockinvs message. It is used to request a list of blocks starting after the
|
||||||
|
// start hash and until the stop hash.
|
||||||
|
type MsgGetBlockInvs struct {
|
||||||
|
StartHash *daghash.Hash
|
||||||
|
StopHash *daghash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockInvs) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
|
msg.StartHash = &daghash.Hash{}
|
||||||
|
err := ReadElement(r, msg.StartHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.StopHash = &daghash.Hash{}
|
||||||
|
return ReadElement(r, msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockInvs) BtcEncode(w io.Writer, pver uint32) error {
|
||||||
|
err := WriteElement(w, msg.StartHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteElement(w, msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockInvs) Command() string {
|
||||||
|
return CmdGetBlockInvs
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockInvs) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// start hash + stop hash.
|
||||||
|
return 2 * daghash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetBlockInvs returns a new bitcoin getblockinvs message that conforms to the
|
||||||
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
|
// fields.
|
||||||
|
func NewMsgGetBlockInvs(startHash, stopHash *daghash.Hash) *MsgGetBlockInvs {
|
||||||
|
return &MsgGetBlockInvs{
|
||||||
|
StartHash: startHash,
|
||||||
|
StopHash: stopHash,
|
||||||
|
}
|
||||||
|
}
|
238
wire/msggetblockinvs_test.go
Normal file
238
wire/msggetblockinvs_test.go
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestGetBlockInvs tests the MsgGetBlockInvs API.
|
||||||
|
func TestGetBlockInvs(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we get the same data back out.
|
||||||
|
msg := NewMsgGetBlockInvs(startHash, stopHash)
|
||||||
|
if !msg.StopHash.IsEqual(stopHash) {
|
||||||
|
t.Errorf("NewMsgGetBlockInvs: wrong stop hash - got %v, want %v",
|
||||||
|
msg.StopHash, stopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := "getblockinvs"
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgGetBlockInvs: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure max payload is start hash (32 bytes) + stop hash (32 bytes).
|
||||||
|
wantPayload := uint32(64)
|
||||||
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
|
if maxPayload != wantPayload {
|
||||||
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
"protocol version %d - got %v, want %v", pver,
|
||||||
|
maxPayload, wantPayload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetBlockInvsWire tests the MsgGetBlockInvs wire encode and decode for various
|
||||||
|
// numbers of block locator hashes and protocol versions.
|
||||||
|
func TestGetBlockInvsWire(t *testing.T) {
|
||||||
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgGetBlocks message with no start or stop hash.
|
||||||
|
noStartOrStop := NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{})
|
||||||
|
noStartOrStopEncoded := []byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgGetBlockInvs message with a start hash and a stop hash.
|
||||||
|
withStartAndStopHash := NewMsgGetBlockInvs(startHash, stopHash)
|
||||||
|
withStartAndStopHashEncoded := []byte{
|
||||||
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||||
|
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||||
|
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||||
|
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgGetBlockInvs // Message to encode
|
||||||
|
out *MsgGetBlockInvs // Expected decoded message
|
||||||
|
buf []byte // Wire encoding
|
||||||
|
pver uint32 // Protocol version for wire encoding
|
||||||
|
}{
|
||||||
|
// Latest protocol version with no block locators.
|
||||||
|
{
|
||||||
|
noStartOrStop,
|
||||||
|
noStartOrStop,
|
||||||
|
noStartOrStopEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Latest protocol version with multiple block locators.
|
||||||
|
{
|
||||||
|
withStartAndStopHash,
|
||||||
|
withStartAndStopHash,
|
||||||
|
withStartAndStopHashEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode the message to wire format.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.BtcEncode(&buf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BtcEncode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the message from wire format.
|
||||||
|
var msg MsgGetBlockInvs
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = msg.BtcDecode(rbuf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BtcDecode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&msg, test.out) {
|
||||||
|
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&msg), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetBlockInvsWireErrors performs negative tests against wire encode and
|
||||||
|
// decode of MsgGetBlockInvs to confirm error paths work correctly.
|
||||||
|
func TestGetBlockInvsWireErrors(t *testing.T) {
|
||||||
|
// Set protocol inside getheaders message.
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgGetBlockInvs message with multiple block locators and a stop hash.
|
||||||
|
baseGetBlocks := NewMsgGetBlockInvs(startHash, stopHash)
|
||||||
|
baseGetBlocksEncoded := []byte{
|
||||||
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||||
|
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||||
|
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||||
|
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgGetBlockInvs // Value to encode
|
||||||
|
buf []byte // Wire encoding
|
||||||
|
pver uint32 // Protocol version for wire encoding
|
||||||
|
max int // Max size of fixed buffer to induce errors
|
||||||
|
writeErr error // Expected write error
|
||||||
|
readErr error // Expected read error
|
||||||
|
}{
|
||||||
|
// Force error in start hash.
|
||||||
|
{baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in stop hash.
|
||||||
|
{baseGetBlocks, baseGetBlocksEncoded, pver, 32, io.ErrShortWrite, io.EOF},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode to wire format.
|
||||||
|
w := newFixedWriter(test.max)
|
||||||
|
err := test.in.BtcEncode(w, test.pver)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||||
|
t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For errors which are not of type MessageError, check them for
|
||||||
|
// equality.
|
||||||
|
if _, ok := err.(*MessageError); !ok {
|
||||||
|
if err != test.writeErr {
|
||||||
|
t.Errorf("BtcEncode #%d wrong error got: %v, "+
|
||||||
|
"want: %v", i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode from wire format.
|
||||||
|
var msg MsgGetBlockInvs
|
||||||
|
r := newFixedReader(test.max, test.buf)
|
||||||
|
err = msg.BtcDecode(r, test.pver)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||||
|
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For errors which are not of type MessageError, check them for
|
||||||
|
// equality.
|
||||||
|
if _, ok := err.(*MessageError); !ok {
|
||||||
|
if err != test.readErr {
|
||||||
|
t.Errorf("BtcDecode #%d wrong error got: %v, "+
|
||||||
|
"want: %v", i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
wire/msggetblocklocator.go
Normal file
65
wire/msggetblocklocator.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgGetBlockLocator struct {
|
||||||
|
StartHash *daghash.Hash
|
||||||
|
StopHash *daghash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockLocator) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
|
msg.StartHash = &daghash.Hash{}
|
||||||
|
err := ReadElement(r, msg.StartHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.StopHash = &daghash.Hash{}
|
||||||
|
err = ReadElement(r, msg.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockLocator) BtcEncode(w io.Writer, pver uint32) error {
|
||||||
|
err := WriteElement(w, msg.StartHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteElement(w, msg.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockLocator) Command() string {
|
||||||
|
return CmdGetBlockLocator
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlockLocator) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return daghash.HashSize * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetBlockLocator returns a new getlocator message that conforms to the
|
||||||
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
|
// fields.
|
||||||
|
func NewMsgGetBlockLocator(startHash, stopHash *daghash.Hash) *MsgGetBlockLocator {
|
||||||
|
return &MsgGetBlockLocator{
|
||||||
|
StartHash: startHash,
|
||||||
|
StopHash: stopHash,
|
||||||
|
}
|
||||||
|
}
|
221
wire/msggetblocklocator_test.go
Normal file
221
wire/msggetblocklocator_test.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestGetBlockLocator tests the MsgGetBlockLocator API.
|
||||||
|
func TestGetBlockLocator(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := "getlocator"
|
||||||
|
msg := NewMsgGetBlockLocator(startHash, &daghash.ZeroHash)
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgGetBlockLocator: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure max payload is start hash (32 bytes) + stop hash (32 bytes)..
|
||||||
|
wantPayload := uint32(64)
|
||||||
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
|
if maxPayload != wantPayload {
|
||||||
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
"protocol version %d - got %v, want %v", pver,
|
||||||
|
maxPayload, wantPayload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetBlockLocatorWire tests the MsgGetBlockLocator wire encode and decode.
|
||||||
|
func TestGetBlockLocatorWire(t *testing.T) {
|
||||||
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgGetBlockLocator message with no block locators or stop hash.
|
||||||
|
noStartAndStopHash := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||||
|
noStartAndStopHashEncoded := []byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgGetBlockLocator message with multiple block locators and a stop hash.
|
||||||
|
withStartAndStopHash := NewMsgGetBlockLocator(startHash, stopHash)
|
||||||
|
withStartAndStopHashEncoded := []byte{
|
||||||
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||||
|
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||||
|
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||||
|
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgGetBlockLocator // Message to encode
|
||||||
|
out *MsgGetBlockLocator // Expected decoded message
|
||||||
|
buf []byte // Wire encoding
|
||||||
|
pver uint32 // Protocol version for wire encoding
|
||||||
|
}{
|
||||||
|
// Message with no start hash and stop hash.
|
||||||
|
{
|
||||||
|
noStartAndStopHash,
|
||||||
|
noStartAndStopHash,
|
||||||
|
noStartAndStopHashEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Message with start hash and stop hash.
|
||||||
|
{
|
||||||
|
withStartAndStopHash,
|
||||||
|
withStartAndStopHash,
|
||||||
|
withStartAndStopHashEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode the message to wire format.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.BtcEncode(&buf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BtcEncode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the message from wire format.
|
||||||
|
var msg MsgGetBlockLocator
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = msg.BtcDecode(rbuf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BtcDecode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&msg, test.out) {
|
||||||
|
t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&msg), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetBlockLocatorWireErrors performs negative tests against wire encode and
|
||||||
|
// decode of MsgGetBlockLocator to confirm error paths work correctly.
|
||||||
|
func TestGetBlockLocatorWireErrors(t *testing.T) {
|
||||||
|
// Set protocol inside getlocator message.
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgGetBlockLocator message with multiple block locators and a stop hash.
|
||||||
|
baseGetBlockLocator := NewMsgGetBlockLocator(startHash, stopHash)
|
||||||
|
baseGetBlockLocatorEncoded := []byte{
|
||||||
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||||
|
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||||
|
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||||
|
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgGetBlockLocator // Value to encode
|
||||||
|
buf []byte // Wire encoding
|
||||||
|
pver uint32 // Protocol version for wire encoding
|
||||||
|
max int // Max size of fixed buffer to induce errors
|
||||||
|
writeErr error // Expected write error
|
||||||
|
readErr error // Expected read error
|
||||||
|
}{
|
||||||
|
// Force error in start hash.
|
||||||
|
{baseGetBlockLocator, baseGetBlockLocatorEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in stop hash.
|
||||||
|
{baseGetBlockLocator, baseGetBlockLocatorEncoded, pver, 32, io.ErrShortWrite, io.EOF},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode to wire format.
|
||||||
|
w := newFixedWriter(test.max)
|
||||||
|
err := test.in.BtcEncode(w, test.pver)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||||
|
t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For errors which are not of type MessageError, check them for
|
||||||
|
// equality.
|
||||||
|
if _, ok := err.(*MessageError); !ok {
|
||||||
|
if err != test.writeErr {
|
||||||
|
t.Errorf("BtcEncode #%d wrong error got: %v, "+
|
||||||
|
"want: %v", i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode from wire format.
|
||||||
|
var msg MsgGetBlockLocator
|
||||||
|
r := newFixedReader(test.max, test.buf)
|
||||||
|
err = msg.BtcDecode(r, test.pver)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||||
|
t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For errors which are not of type MessageError, check them for
|
||||||
|
// equality.
|
||||||
|
if _, ok := err.(*MessageError); !ok {
|
||||||
|
if err != test.readErr {
|
||||||
|
t.Errorf("BtcDecode #%d wrong error got: %v, "+
|
||||||
|
"want: %v", i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,140 +0,0 @@
|
|||||||
// 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 wire
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/daglabs/btcd/util/daghash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
|
|
||||||
// per message.
|
|
||||||
const MaxBlockLocatorsPerMsg = 500
|
|
||||||
|
|
||||||
// MsgGetBlockInvs implements the Message interface and represents a bitcoin
|
|
||||||
// getblockinvss message. It is used to request a list of blocks starting after
|
|
||||||
// the last known hash in the slice of block locator hashes. The list is
|
|
||||||
// returned via an inv message (MsgInv) and is limited by a specific hash to
|
|
||||||
// stop at or the maximum number of blocks per message, which is currently 500.
|
|
||||||
//
|
|
||||||
// Set the StopHash field to the hash at which to stop and use
|
|
||||||
// AddBlockLocatorHash to build up the list of block locator hashes.
|
|
||||||
//
|
|
||||||
// The algorithm for building the block locator hashes should be to add the
|
|
||||||
// hashes in reverse order until you reach the genesis block. In order to keep
|
|
||||||
// the list of locator hashes to a reasonable number of entries, first add the
|
|
||||||
// most recent 10 block hashes, then double the step each loop iteration to
|
|
||||||
// exponentially decrease the number of hashes the further away from head and
|
|
||||||
// closer to the genesis block you get.
|
|
||||||
type MsgGetBlockInvs struct {
|
|
||||||
ProtocolVersion uint32
|
|
||||||
BlockLocatorHashes []*daghash.Hash
|
|
||||||
StopHash *daghash.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBlockLocatorHash adds a new block locator hash to the message.
|
|
||||||
func (msg *MsgGetBlockInvs) AddBlockLocatorHash(hash *daghash.Hash) error {
|
|
||||||
if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message [max %d]",
|
|
||||||
MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgGetBlockInvs.AddBlockLocatorHash", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
|
||||||
// This is part of the Message interface implementation.
|
|
||||||
func (msg *MsgGetBlockInvs) BtcDecode(r io.Reader, pver uint32) error {
|
|
||||||
err := ReadElement(r, &msg.ProtocolVersion)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read num block locator hashes and limit to max.
|
|
||||||
count, err := ReadVarInt(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if count > MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message "+
|
|
||||||
"[count %d, max %d]", count, MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgGetBlockInvs.BtcDecode", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a contiguous slice of hashes to deserialize into in order to
|
|
||||||
// reduce the number of allocations.
|
|
||||||
locatorHashes := make([]daghash.Hash, count)
|
|
||||||
msg.BlockLocatorHashes = make([]*daghash.Hash, 0, count)
|
|
||||||
for i := uint64(0); i < count; i++ {
|
|
||||||
hash := &locatorHashes[i]
|
|
||||||
err := ReadElement(r, hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg.AddBlockLocatorHash(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.StopHash = &daghash.Hash{}
|
|
||||||
return ReadElement(r, msg.StopHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
|
||||||
// This is part of the Message interface implementation.
|
|
||||||
func (msg *MsgGetBlockInvs) BtcEncode(w io.Writer, pver uint32) error {
|
|
||||||
count := len(msg.BlockLocatorHashes)
|
|
||||||
if count > MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message "+
|
|
||||||
"[count %d, max %d]", count, MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgGetBlockInvs.BtcEncode", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := WriteElement(w, msg.ProtocolVersion)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = WriteVarInt(w, uint64(count))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hash := range msg.BlockLocatorHashes {
|
|
||||||
err = WriteElement(w, hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WriteElement(w, msg.StopHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgGetBlockInvs) Command() string {
|
|
||||||
return CmdGetBlockInvs
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
|
||||||
// receiver. This is part of the Message interface implementation.
|
|
||||||
func (msg *MsgGetBlockInvs) MaxPayloadLength(pver uint32) uint32 {
|
|
||||||
// Protocol version 4 bytes + num hashes (varInt) + max block locator
|
|
||||||
// hashes + hash stop.
|
|
||||||
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * daghash.HashSize) + daghash.HashSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgGetBlockInvs returns a new bitcoin getblockinvs message that conforms
|
|
||||||
// to the Message interface using the passed parameters and defaults for the
|
|
||||||
// remaining fields.
|
|
||||||
func NewMsgGetBlockInvs(stopHash *daghash.Hash) *MsgGetBlockInvs {
|
|
||||||
return &MsgGetBlockInvs{
|
|
||||||
ProtocolVersion: ProtocolVersion,
|
|
||||||
BlockLocatorHashes: make([]*daghash.Hash, 0, MaxBlockLocatorsPerMsg),
|
|
||||||
StopHash: stopHash,
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,6 @@
|
|||||||
package wire
|
package wire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/daglabs/btcd/util/daghash"
|
"github.com/daglabs/btcd/util/daghash"
|
||||||
@ -28,55 +27,19 @@ import (
|
|||||||
// exponentially decrease the number of hashes the further away from head and
|
// exponentially decrease the number of hashes the further away from head and
|
||||||
// closer to the genesis block you get.
|
// closer to the genesis block you get.
|
||||||
type MsgGetHeaders struct {
|
type MsgGetHeaders struct {
|
||||||
ProtocolVersion uint32
|
StartHash *daghash.Hash
|
||||||
BlockLocatorHashes []*daghash.Hash
|
|
||||||
StopHash *daghash.Hash
|
StopHash *daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBlockLocatorHash adds a new block locator hash to the message.
|
|
||||||
func (msg *MsgGetHeaders) AddBlockLocatorHash(hash *daghash.Hash) error {
|
|
||||||
if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message [max %d]",
|
|
||||||
MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgGetHeaders.AddBlockLocatorHash", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
// This is part of the Message interface implementation.
|
// This is part of the Message interface implementation.
|
||||||
func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32) error {
|
func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
err := ReadElement(r, &msg.ProtocolVersion)
|
msg.StartHash = &daghash.Hash{}
|
||||||
|
err := ReadElement(r, msg.StartHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read num block locator hashes and limit to max.
|
|
||||||
count, err := ReadVarInt(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if count > MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message "+
|
|
||||||
"[count %d, max %d]", count, MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgGetHeaders.BtcDecode", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a contiguous slice of hashes to deserialize into in order to
|
|
||||||
// reduce the number of allocations.
|
|
||||||
locatorHashes := make([]daghash.Hash, count)
|
|
||||||
msg.BlockLocatorHashes = make([]*daghash.Hash, 0, count)
|
|
||||||
for i := uint64(0); i < count; i++ {
|
|
||||||
hash := &locatorHashes[i]
|
|
||||||
err := ReadElement(r, hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msg.AddBlockLocatorHash(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.StopHash = &daghash.Hash{}
|
msg.StopHash = &daghash.Hash{}
|
||||||
return ReadElement(r, msg.StopHash)
|
return ReadElement(r, msg.StopHash)
|
||||||
}
|
}
|
||||||
@ -84,31 +47,11 @@ func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32) error {
|
|||||||
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
// This is part of the Message interface implementation.
|
// This is part of the Message interface implementation.
|
||||||
func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32) error {
|
func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32) error {
|
||||||
// Limit to max block locator hashes per message.
|
err := WriteElement(w, msg.StartHash)
|
||||||
count := len(msg.BlockLocatorHashes)
|
|
||||||
if count > MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message "+
|
|
||||||
"[count %d, max %d]", count, MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgGetHeaders.BtcEncode", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := WriteElement(w, msg.ProtocolVersion)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = WriteVarInt(w, uint64(count))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hash := range msg.BlockLocatorHashes {
|
|
||||||
err := WriteElement(w, hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WriteElement(w, msg.StopHash)
|
return WriteElement(w, msg.StopHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,18 +64,15 @@ func (msg *MsgGetHeaders) Command() string {
|
|||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
// receiver. This is part of the Message interface implementation.
|
// receiver. This is part of the Message interface implementation.
|
||||||
func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 {
|
func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||||
// Version 4 bytes + num block locator hashes (varInt) + max allowed block
|
// start hash + stop hash.
|
||||||
// locators + hash stop.
|
return 2 * daghash.HashSize
|
||||||
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
|
|
||||||
daghash.HashSize) + daghash.HashSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMsgGetHeaders returns a new bitcoin getheaders message that conforms to
|
// NewMsgGetHeaders returns a new bitcoin getheaders message that conforms to
|
||||||
// the Message interface. See MsgGetHeaders for details.
|
// the Message interface. See MsgGetHeaders for details.
|
||||||
func NewMsgGetHeaders() *MsgGetHeaders {
|
func NewMsgGetHeaders(startHash, stopHash *daghash.Hash) *MsgGetHeaders {
|
||||||
return &MsgGetHeaders{
|
return &MsgGetHeaders{
|
||||||
BlockLocatorHashes: make([]*daghash.Hash, 0,
|
StartHash: startHash,
|
||||||
MaxBlockLocatorsPerMsg),
|
StopHash: stopHash,
|
||||||
StopHash: &daghash.ZeroHash,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,76 +20,37 @@ func TestGetHeaders(t *testing.T) {
|
|||||||
|
|
||||||
// Block 99500 hash.
|
// Block 99500 hash.
|
||||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
locatorHash, err := daghash.NewHashFromStr(hashStr)
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
// Ensure the command is expected value.
|
||||||
wantCmd := "getheaders"
|
wantCmd := "getheaders"
|
||||||
msg := NewMsgGetHeaders()
|
msg := NewMsgGetHeaders(startHash, &daghash.ZeroHash)
|
||||||
if cmd := msg.Command(); cmd != wantCmd {
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v",
|
t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v",
|
||||||
cmd, wantCmd)
|
cmd, wantCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
// Ensure max payload is start hash (32 bytes) + stop hash (32 bytes)..
|
||||||
// Protocol version 4 bytes + num hashes (varInt) + max block locator
|
wantPayload := uint32(64)
|
||||||
// hashes + hash stop.
|
|
||||||
wantPayload := uint32(16045)
|
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
if maxPayload != wantPayload {
|
if maxPayload != wantPayload {
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
"protocol version %d - got %v, want %v", pver,
|
"protocol version %d - got %v, want %v", pver,
|
||||||
maxPayload, wantPayload)
|
maxPayload, wantPayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure block locator hashes are added properly.
|
|
||||||
err = msg.AddBlockLocatorHash(locatorHash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("AddBlockLocatorHash: %v", err)
|
|
||||||
}
|
|
||||||
if msg.BlockLocatorHashes[0] != locatorHash {
|
|
||||||
t.Errorf("AddBlockLocatorHash: wrong block locator added - "+
|
|
||||||
"got %v, want %v",
|
|
||||||
spew.Sprint(msg.BlockLocatorHashes[0]),
|
|
||||||
spew.Sprint(locatorHash))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure adding more than the max allowed block locator hashes per
|
// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode.
|
||||||
// message returns an error.
|
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
|
||||||
err = msg.AddBlockLocatorHash(locatorHash)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("AddBlockLocatorHash: expected error on too many " +
|
|
||||||
"block locator hashes not received")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode for various
|
|
||||||
// numbers of block locator hashes and protocol versions.
|
|
||||||
func TestGetHeadersWire(t *testing.T) {
|
func TestGetHeadersWire(t *testing.T) {
|
||||||
// Set protocol inside getheaders message. Use protocol version 1
|
|
||||||
// specifically here instead of the latest because the test data is
|
|
||||||
// using bytes encoded with that protocol version.
|
|
||||||
pver := uint32(1)
|
|
||||||
|
|
||||||
// Block 99499 hash.
|
|
||||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 99500 hash.
|
|
||||||
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
|
||||||
hashLocator2, err := daghash.NewHashFromStr(hashStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block 100000 hash.
|
|
||||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -97,38 +58,29 @@ func TestGetHeadersWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetHeaders message with no block locators or stop hash.
|
// MsgGetHeaders message with no block locators or stop hash.
|
||||||
noLocators := NewMsgGetHeaders()
|
noStartAndStopHash := NewMsgGetHeaders(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||||
noLocators.ProtocolVersion = pver
|
noStartAndStopHashEncoded := []byte{
|
||||||
noLocatorsEncoded := []byte{
|
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0x00, // Varint for number of block locator hashes
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetHeaders message with multiple block locators and a stop hash.
|
// MsgGetHeaders message with multiple block locators and a stop hash.
|
||||||
multiLocators := NewMsgGetHeaders()
|
withStartAndStopHash := NewMsgGetHeaders(startHash, stopHash)
|
||||||
multiLocators.ProtocolVersion = pver
|
withStartAndStopHashEncoded := []byte{
|
||||||
multiLocators.StopHash = stopHash
|
|
||||||
multiLocators.AddBlockLocatorHash(hashLocator2)
|
|
||||||
multiLocators.AddBlockLocatorHash(hashLocator)
|
|
||||||
multiLocatorsEncoded := []byte{
|
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0x02, // Varint for number of block locator hashes
|
|
||||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
|
||||||
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
|
|
||||||
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
|
|
||||||
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
|
|
||||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
|
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -137,19 +89,19 @@ func TestGetHeadersWire(t *testing.T) {
|
|||||||
buf []byte // Wire encoding
|
buf []byte // Wire encoding
|
||||||
pver uint32 // Protocol version for wire encoding
|
pver uint32 // Protocol version for wire encoding
|
||||||
}{
|
}{
|
||||||
// Latest protocol version with no block locators.
|
// Message with no start hash and stop hash.
|
||||||
{
|
{
|
||||||
noLocators,
|
noStartAndStopHash,
|
||||||
noLocators,
|
noStartAndStopHash,
|
||||||
noLocatorsEncoded,
|
noStartAndStopHashEncoded,
|
||||||
ProtocolVersion,
|
ProtocolVersion,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Latest protocol version with multiple block locators.
|
// Message with start hash and stop hash.
|
||||||
{
|
{
|
||||||
multiLocators,
|
withStartAndStopHash,
|
||||||
multiLocators,
|
withStartAndStopHash,
|
||||||
multiLocatorsEncoded,
|
withStartAndStopHashEncoded,
|
||||||
ProtocolVersion,
|
ProtocolVersion,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -188,27 +140,15 @@ func TestGetHeadersWire(t *testing.T) {
|
|||||||
// TestGetHeadersWireErrors performs negative tests against wire encode and
|
// TestGetHeadersWireErrors performs negative tests against wire encode and
|
||||||
// decode of MsgGetHeaders to confirm error paths work correctly.
|
// decode of MsgGetHeaders to confirm error paths work correctly.
|
||||||
func TestGetHeadersWireErrors(t *testing.T) {
|
func TestGetHeadersWireErrors(t *testing.T) {
|
||||||
// Set protocol inside getheaders message. Use protocol version 1
|
// Set protocol inside getheaders message.
|
||||||
// specifically here instead of the latest because the test data is
|
|
||||||
// using bytes encoded with that protocol version.
|
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
wireErr := &MessageError{}
|
|
||||||
|
|
||||||
// Block 99499 hash.
|
|
||||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block 99500 hash.
|
|
||||||
hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
|
||||||
hashLocator2, err := daghash.NewHashFromStr(hashStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block 100000 hash.
|
|
||||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -216,39 +156,16 @@ func TestGetHeadersWireErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetHeaders message with multiple block locators and a stop hash.
|
// MsgGetHeaders message with multiple block locators and a stop hash.
|
||||||
baseGetHeaders := NewMsgGetHeaders()
|
baseGetHeaders := NewMsgGetHeaders(startHash, stopHash)
|
||||||
baseGetHeaders.ProtocolVersion = pver
|
|
||||||
baseGetHeaders.StopHash = stopHash
|
|
||||||
baseGetHeaders.AddBlockLocatorHash(hashLocator2)
|
|
||||||
baseGetHeaders.AddBlockLocatorHash(hashLocator)
|
|
||||||
baseGetHeadersEncoded := []byte{
|
baseGetHeadersEncoded := []byte{
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0x02, // Varint for number of block locator hashes
|
|
||||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
|
||||||
0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
|
|
||||||
0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
|
|
||||||
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
|
|
||||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
|
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Start hash
|
||||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
|
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||||
}
|
|
||||||
|
|
||||||
// Message that forces an error by having more than the max allowed
|
|
||||||
// block locator hashes.
|
|
||||||
maxGetHeaders := NewMsgGetHeaders()
|
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
|
||||||
maxGetHeaders.AddBlockLocatorHash(mainNetGenesisHash)
|
|
||||||
}
|
|
||||||
maxGetHeaders.BlockLocatorHashes = append(maxGetHeaders.BlockLocatorHashes,
|
|
||||||
mainNetGenesisHash)
|
|
||||||
maxGetHeadersEncoded := []byte{
|
|
||||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
|
||||||
0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -259,16 +176,10 @@ func TestGetHeadersWireErrors(t *testing.T) {
|
|||||||
writeErr error // Expected write error
|
writeErr error // Expected write error
|
||||||
readErr error // Expected read error
|
readErr error // Expected read error
|
||||||
}{
|
}{
|
||||||
// Force error in protocol version.
|
// Force error in start hash.
|
||||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
{baseGetHeaders, baseGetHeadersEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
// Force error in block locator hash count.
|
|
||||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 4, io.ErrShortWrite, io.EOF},
|
|
||||||
// Force error in block locator hashes.
|
|
||||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 5, io.ErrShortWrite, io.EOF},
|
|
||||||
// Force error in stop hash.
|
// Force error in stop hash.
|
||||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 69, io.ErrShortWrite, io.EOF},
|
{baseGetHeaders, baseGetHeadersEncoded, pver, 32, io.ErrShortWrite, io.EOF},
|
||||||
// Force error with greater than max block locator hashes.
|
|
||||||
{maxGetHeaders, maxGetHeadersEncoded, pver, 7, wireErr, wireErr},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
@ -12,8 +12,7 @@ import (
|
|||||||
// sendheaders message. It is used to request the peer send block headers
|
// sendheaders message. It is used to request the peer send block headers
|
||||||
// rather than inventory vectors.
|
// rather than inventory vectors.
|
||||||
//
|
//
|
||||||
// This message has no payload and was not added until protocol versions
|
// This message has no payload.
|
||||||
// starting with SendHeadersVersion.
|
|
||||||
type MsgSendHeaders struct{}
|
type MsgSendHeaders struct{}
|
||||||
|
|
||||||
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user