mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 14:16:43 +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
|
||||
}
|
265
blockdag/dag.go
265
blockdag/dag.go
@ -31,21 +31,6 @@ const (
|
||||
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
|
||||
// is a normal block plus an expiration time to prevent caching the orphan
|
||||
// forever.
|
||||
@ -1520,90 +1505,6 @@ func (dag *BlockDAG) HeaderByHash(hash *daghash.Hash) (*wire.BlockHeader, error)
|
||||
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
|
||||
// hash in the DAG.
|
||||
//
|
||||
@ -1708,67 +1609,13 @@ func (dag *BlockDAG) IntervalBlockHashes(endHash *daghash.Hash, interval uint64,
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
// locateInventory returns the node of the block after the first known block in
|
||||
// the locator along with the number of subsequent nodes needed to either reach
|
||||
// the provided stop hash or the provided max number of entries.
|
||||
//
|
||||
// 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.
|
||||
// getBlueBlocksHashesBetween returns the hashes of the blocks after the provided
|
||||
// start hash until the provided stop hash is reached, or up to the
|
||||
// provided max number of block hashes.
|
||||
//
|
||||
// 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) {
|
||||
// There are no block locators so a specific block is being requested
|
||||
// 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)
|
||||
func (dag *BlockDAG) getBlueBlocksHashesBetween(startHash, stopHash *daghash.Hash, maxHashes uint64) []*daghash.Hash {
|
||||
nodes := dag.getBlueBlocksBetween(startHash, stopHash, maxHashes)
|
||||
hashes := make([]*daghash.Hash, len(nodes))
|
||||
for i, node := range nodes {
|
||||
hashes[i] = node.hash
|
||||
@ -1776,67 +1623,65 @@ func (dag *BlockDAG) locateBlocks(locator BlockLocator, stopHash *daghash.Hash,
|
||||
return hashes
|
||||
}
|
||||
|
||||
func (dag *BlockDAG) locateBlockNodes(locator BlockLocator, stopHash *daghash.Hash, maxEntries uint32) []*blockNode {
|
||||
// Find the first known block in the locator and the estimated number of
|
||||
// nodes after it needed while respecting the stop hash and max entries.
|
||||
node, estimatedEntries := dag.locateInventory(locator, stopHash, maxEntries)
|
||||
if estimatedEntries == 0 {
|
||||
func (dag *BlockDAG) getBlueBlocksBetween(startHash, stopHash *daghash.Hash, maxEntries uint64) []*blockNode {
|
||||
startNode := dag.index.LookupNode(startHash)
|
||||
if startNode == nil {
|
||||
return nil
|
||||
}
|
||||
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.
|
||||
nodes := make([]*blockNode, 0, estimatedEntries)
|
||||
queue := newUpHeap()
|
||||
queue.pushSet(node.children)
|
||||
|
||||
visited := newSet()
|
||||
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)
|
||||
}
|
||||
nodes := make([]*blockNode, 0, stopNode.blueScore-startNode.blueScore+1)
|
||||
nodes = append(nodes, stopNode)
|
||||
for current := stopNode; current != startNode; current = current.selectedParent {
|
||||
for _, blue := range current.blues {
|
||||
nodes = append(nodes, blue)
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
reversedNodes := make([]*blockNode, len(nodes))
|
||||
for i, node := range nodes {
|
||||
reversedNodes[len(reversedNodes)-i-1] = node
|
||||
}
|
||||
return reversedNodes
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
// GetBlueBlocksHashesBetween returns the hashes of the blue blocks after the
|
||||
// provided start hash until the provided stop hash is reached, or up to the
|
||||
// provided max number of block hashes.
|
||||
//
|
||||
// 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()
|
||||
hashes := dag.locateBlocks(locator, stopHash, maxHashes)
|
||||
hashes := dag.getBlueBlocksHashesBetween(startHash, stopHash, maxHashes)
|
||||
dag.dagLock.RUnlock()
|
||||
return hashes
|
||||
}
|
||||
|
||||
// locateHeaders returns the headers 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 headers.
|
||||
//
|
||||
// See the comment on the exported function for more details on special cases.
|
||||
// getBlueBlocksHeadersBetween returns the headers of the blue blocks after the
|
||||
// provided start hash until the provided stop hash is reached, or up to the
|
||||
// provided max number of block headers.
|
||||
//
|
||||
// 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 {
|
||||
nodes := dag.locateBlockNodes(locator, stopHash, maxHeaders)
|
||||
func (dag *BlockDAG) getBlueBlocksHeadersBetween(startHash, stopHash *daghash.Hash, maxHeaders uint64) []*wire.BlockHeader {
|
||||
nodes := dag.getBlueBlocksBetween(startHash, stopHash, maxHeaders)
|
||||
headers := make([]*wire.BlockHeader, len(nodes))
|
||||
for i, node := range nodes {
|
||||
headers[i] = node.Header()
|
||||
@ -1880,22 +1725,14 @@ func (dag *BlockDAG) RUnlock() {
|
||||
dag.dagLock.RUnlock()
|
||||
}
|
||||
|
||||
// LocateHeaders returns the headers of the blocks after the first known block
|
||||
// in the locator until the provided stop hash is reached, or up to a max of
|
||||
// wire.MaxBlockHeadersPerMsg 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
|
||||
// GetBlueBlocksHeadersBetween returns the headers of the blocks after the provided
|
||||
// start hash until the provided stop hash is reached, or up to the
|
||||
// provided max number of block headers.
|
||||
//
|
||||
// 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()
|
||||
headers := dag.locateHeaders(locator, stopHash, wire.MaxBlockHeadersPerMsg)
|
||||
headers := dag.getBlueBlocksHeadersBetween(startHash, stopHash, wire.MaxBlockHeadersPerMsg)
|
||||
dag.dagLock.RUnlock()
|
||||
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
|
||||
func TestSubnetworkRegistry(t *testing.T) {
|
||||
params := dagconfig.SimNetParams
|
||||
|
@ -108,8 +108,8 @@ func NewGetTopHeadersCmd(startHash *string) *GetTopHeadersCmd {
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
type GetHeadersCmd struct {
|
||||
BlockLocators []string `json:"blockLocators"`
|
||||
StopHash string `json:"stopHash"`
|
||||
StartHash string `json:"startHash"`
|
||||
StopHash string `json:"stopHash"`
|
||||
}
|
||||
|
||||
// NewGetHeadersCmd returns a new instance which can be used to issue a
|
||||
@ -117,10 +117,10 @@ type GetHeadersCmd struct {
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
func NewGetHeadersCmd(blockLocators []string, stopHash string) *GetHeadersCmd {
|
||||
func NewGetHeadersCmd(startHash, stopHash string) *GetHeadersCmd {
|
||||
return &GetHeadersCmd{
|
||||
BlockLocators: blockLocators,
|
||||
StopHash: stopHash,
|
||||
StartHash: startHash,
|
||||
StopHash: stopHash,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,41 +139,35 @@ func TestBtcdExtCmds(t *testing.T) {
|
||||
{
|
||||
name: "getHeaders",
|
||||
newCmd: func() (interface{}, error) {
|
||||
return btcjson.NewCmd("getHeaders", []string{}, "")
|
||||
return btcjson.NewCmd("getHeaders", "", "")
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
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{
|
||||
BlockLocators: []string{},
|
||||
StopHash: "",
|
||||
StartHash: "",
|
||||
StopHash: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getHeaders - with arguments",
|
||||
newCmd: func() (interface{}, error) {
|
||||
return btcjson.NewCmd("getHeaders", []string{"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", "0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10"}, "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7")
|
||||
return btcjson.NewCmd("getHeaders", "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7")
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
return btcjson.NewGetHeadersCmd(
|
||||
[]string{
|
||||
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||
"0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10",
|
||||
},
|
||||
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||
"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{
|
||||
BlockLocators: []string{
|
||||
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||
"0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10",
|
||||
},
|
||||
StopHash: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
||||
StartHash: "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||
StopHash: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -18,6 +18,11 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to generate p2pkh address: %s", err)
|
||||
|
@ -157,7 +157,7 @@ type SyncManager struct {
|
||||
shutdown int32
|
||||
dag *blockdag.BlockDAG
|
||||
txMemPool *mempool.TxPool
|
||||
chainParams *dagconfig.Params
|
||||
dagParams *dagconfig.Params
|
||||
progressLogger *blockProgressLogger
|
||||
msgChan chan interface{}
|
||||
wg sync.WaitGroup
|
||||
@ -177,6 +177,40 @@ type SyncManager struct {
|
||||
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
|
||||
// syncing from a new peer.
|
||||
func (sm *SyncManager) resetHeaderState(newestHash *daghash.Hash, newestHeight uint64) {
|
||||
@ -261,39 +295,16 @@ func (sm *SyncManager) startSync() {
|
||||
// to send.
|
||||
sm.requestedBlocks = make(map[daghash.Hash]struct{})
|
||||
|
||||
locator := sm.dag.LatestBlockLocator()
|
||||
|
||||
log.Infof("Syncing to block %s from peer %s",
|
||||
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 &&
|
||||
sm.dag.ChainHeight() < sm.nextCheckpoint.ChainHeight &&
|
||||
sm.chainParams != &dagconfig.RegressionNetParams { //TODO: (Ori) This is probably wrong. Done only for compilation
|
||||
|
||||
bestPeer.PushGetHeadersMsg(locator, sm.nextCheckpoint.Hash)
|
||||
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
|
||||
sm.dagParams != &dagconfig.RegressionNetParams {
|
||||
//TODO: (Ori) This is probably wrong. Done only for compilation
|
||||
bestPeer.PushGetBlockLocatorMsg(sm.nextCheckpoint.Hash, sm.dagParams.GenesisHash)
|
||||
} else {
|
||||
bestPeer.PushGetBlockInvsMsg(locator, &daghash.ZeroHash)
|
||||
bestPeer.PushGetBlockLocatorMsg(&daghash.ZeroHash, sm.dagParams.GenesisHash)
|
||||
}
|
||||
sm.syncPeer = bestPeer
|
||||
} 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,
|
||||
// however regression test is special in that the regression tool is
|
||||
// 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
|
||||
// or the hostname can't be determined for some reason.
|
||||
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
|
||||
// mode in this case so the chain code is actually fed the
|
||||
// duplicate blocks.
|
||||
if sm.chainParams != &dagconfig.RegressionNetParams {
|
||||
if sm.dagParams != &dagconfig.RegressionNetParams {
|
||||
log.Warnf("Got unrequested block %s from %s -- "+
|
||||
"disconnecting", blockHash, peer.Addr())
|
||||
peer.Disconnect()
|
||||
@ -654,8 +665,7 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
|
||||
parentHash := sm.nextCheckpoint.Hash
|
||||
sm.nextCheckpoint = sm.findNextHeaderCheckpoint(prevHeight)
|
||||
if sm.nextCheckpoint != nil {
|
||||
locator := blockdag.BlockLocator([]*daghash.Hash{parentHash})
|
||||
err := peer.PushGetHeadersMsg(locator, sm.nextCheckpoint.Hash)
|
||||
err := peer.PushGetHeadersMsg(parentHash, sm.nextCheckpoint.Hash)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to send getheaders message to "+
|
||||
"peer %s: %s", peer.Addr(), err)
|
||||
@ -673,8 +683,7 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
|
||||
sm.headersFirstMode = false
|
||||
sm.headerList.Init()
|
||||
log.Infof("Reached the final checkpoint -- switching to normal mode")
|
||||
locator := blockdag.BlockLocator([]*daghash.Hash{blockHash})
|
||||
err = peer.PushGetBlockInvsMsg(locator, &daghash.ZeroHash)
|
||||
err = peer.PushGetBlockInvsMsg(blockHash, &daghash.ZeroHash)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to send getblockinvs message to peer %s: %s",
|
||||
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
|
||||
// headers starting from the latest known header and ending with the
|
||||
// next checkpoint.
|
||||
locator := blockdag.BlockLocator([]*daghash.Hash{finalHash})
|
||||
err := peer.PushGetHeadersMsg(locator, sm.nextCheckpoint.Hash)
|
||||
err := peer.PushGetHeadersMsg(finalHash, sm.nextCheckpoint.Hash)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to send getheaders message to "+
|
||||
"peer %s: %s", peer.Addr(), err)
|
||||
@ -1015,10 +1023,8 @@ func (sm *SyncManager) handleInvMsg(imsg *invMsg) {
|
||||
if i == lastBlock && peer == sm.syncPeer {
|
||||
// Request blocks after the first block's ancestor that exists
|
||||
// in the selected path chain, one up to the
|
||||
// final one the remote peer knows about (zero
|
||||
// stop hash).
|
||||
locator := sm.dag.BlockLocatorFromHash(iv.Hash)
|
||||
peer.PushGetBlockInvsMsg(locator, &daghash.ZeroHash)
|
||||
// final one the remote peer knows about.
|
||||
peer.PushGetBlockLocatorMsg(iv.Hash, &daghash.ZeroHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1408,7 +1414,7 @@ func New(config *Config) (*SyncManager, error) {
|
||||
peerNotifier: config.PeerNotifier,
|
||||
dag: config.DAG,
|
||||
txMemPool: config.TxMemPool,
|
||||
chainParams: config.ChainParams,
|
||||
dagParams: config.ChainParams,
|
||||
rejectedTxns: make(map[daghash.TxID]struct{}),
|
||||
requestedTxns: make(map[daghash.TxID]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/daglabs/btcd/logger"
|
||||
"github.com/daglabs/btcd/txscript"
|
||||
"github.com/daglabs/btcd/util/daghash"
|
||||
"github.com/daglabs/btcd/util/panics"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
@ -90,16 +89,6 @@ func invSummary(invList []*wire.InvVect) string {
|
||||
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
|
||||
// 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
|
||||
@ -179,10 +168,22 @@ func messageSummary(msg wire.Message) string {
|
||||
return invSummary(msg.InvList)
|
||||
|
||||
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:
|
||||
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:
|
||||
return fmt.Sprintf("num %d", len(msg.Headers))
|
||||
|
126
peer/peer.go
126
peer/peer.go
@ -137,6 +137,12 @@ type MessageListeners struct {
|
||||
// OnInv is invoked when a peer receives an inv bitcoin message.
|
||||
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 func(p *Peer, msg *wire.MsgHeaders)
|
||||
|
||||
@ -445,10 +451,10 @@ type Peer struct {
|
||||
|
||||
knownInventory *mruInventoryMap
|
||||
prevGetBlockInvsMtx sync.Mutex
|
||||
prevGetBlockInvsBegin *daghash.Hash
|
||||
prevGetBlockInvsStart *daghash.Hash
|
||||
prevGetBlockInvsStop *daghash.Hash
|
||||
prevGetHdrsMtx sync.Mutex
|
||||
prevGetHdrsBegin *daghash.Hash
|
||||
prevGetHdrsStart *daghash.Hash
|
||||
prevGetHdrsStop *daghash.Hash
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
// and stop hash. It will ignore back-to-back duplicate requests.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (p *Peer) PushGetBlockInvsMsg(locator blockdag.BlockLocator, 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]
|
||||
}
|
||||
|
||||
func (p *Peer) PushGetBlockInvsMsg(startHash, stopHash *daghash.Hash) error {
|
||||
// Filter duplicate getblockinvs requests.
|
||||
p.prevGetBlockInvsMtx.Lock()
|
||||
isDuplicate := p.prevGetBlockInvsStop != nil && p.prevGetBlockInvsBegin != nil &&
|
||||
beginHash != nil && stopHash.IsEqual(p.prevGetBlockInvsStop) &&
|
||||
beginHash.IsEqual(p.prevGetBlockInvsBegin)
|
||||
isDuplicate := p.prevGetBlockInvsStop != nil && p.prevGetBlockInvsStart != nil &&
|
||||
startHash != nil && stopHash.IsEqual(p.prevGetBlockInvsStop) &&
|
||||
startHash.IsEqual(p.prevGetBlockInvsStart)
|
||||
p.prevGetBlockInvsMtx.Unlock()
|
||||
|
||||
if isDuplicate {
|
||||
log.Tracef("Filtering duplicate [getblockinvs] with begin "+
|
||||
"hash %s, stop hash %s", beginHash, stopHash)
|
||||
log.Tracef("Filtering duplicate [getblockinvs] with start "+
|
||||
"hash %s, stop hash %s", startHash, stopHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
err := msg.AddBlockLocatorHash(hash)
|
||||
if err != nil {
|
||||
@ -892,13 +913,6 @@ func (p *Peer) PushGetBlockInvsMsg(locator blockdag.BlockLocator, stopHash *dagh
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -906,42 +920,28 @@ func (p *Peer) PushGetBlockInvsMsg(locator blockdag.BlockLocator, stopHash *dagh
|
||||
// and stop hash. It will ignore back-to-back duplicate requests.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (p *Peer) PushGetHeadersMsg(locator blockdag.BlockLocator, 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]
|
||||
}
|
||||
|
||||
func (p *Peer) PushGetHeadersMsg(startHash, stopHash *daghash.Hash) error {
|
||||
// Filter duplicate getheaders requests.
|
||||
p.prevGetHdrsMtx.Lock()
|
||||
isDuplicate := p.prevGetHdrsStop != nil && p.prevGetHdrsBegin != nil &&
|
||||
beginHash != nil && stopHash.IsEqual(p.prevGetHdrsStop) &&
|
||||
beginHash.IsEqual(p.prevGetHdrsBegin)
|
||||
isDuplicate := p.prevGetHdrsStop != nil && p.prevGetHdrsStart != nil &&
|
||||
startHash != nil && stopHash.IsEqual(p.prevGetHdrsStop) &&
|
||||
startHash.IsEqual(p.prevGetHdrsStart)
|
||||
p.prevGetHdrsMtx.Unlock()
|
||||
|
||||
if isDuplicate {
|
||||
log.Tracef("Filtering duplicate [getheaders] with begin hash %s",
|
||||
beginHash)
|
||||
log.Tracef("Filtering duplicate [getheaders] with start hash %s",
|
||||
startHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the getheaders request and queue it to be sent.
|
||||
msg := wire.NewMsgGetHeaders()
|
||||
msg.StopHash = stopHash
|
||||
for _, hash := range locator {
|
||||
err := msg.AddBlockLocatorHash(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
msg := wire.NewMsgGetHeaders(startHash, stopHash)
|
||||
p.QueueMessage(msg, nil)
|
||||
|
||||
// Update the previous getheaders request information for filtering
|
||||
// duplicates.
|
||||
p.prevGetHdrsMtx.Lock()
|
||||
p.prevGetHdrsBegin = beginHash
|
||||
p.prevGetHdrsStart = startHash
|
||||
p.prevGetHdrsStop = stopHash
|
||||
p.prevGetHdrsMtx.Unlock()
|
||||
return nil
|
||||
@ -1517,6 +1517,16 @@ out:
|
||||
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:
|
||||
if p.cfg.Listeners.OnGetBlockInvs != nil {
|
||||
p.cfg.Listeners.OnGetBlockInvs(p, msg)
|
||||
@ -1748,26 +1758,6 @@ cleanup:
|
||||
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
|
||||
// goroutine. It uses a buffered channel to serialize output messages while
|
||||
// allowing the sender to continue running asynchronously.
|
||||
@ -1789,10 +1779,8 @@ out:
|
||||
err := p.writeMessage(msg.msg)
|
||||
if err != nil {
|
||||
p.Disconnect()
|
||||
if p.shouldLogWriteError(err) {
|
||||
log.Errorf("Failed to send message to "+
|
||||
"%s: %s", p, err)
|
||||
}
|
||||
log.Errorf("Failed to send message to "+
|
||||
"%s: %s", p, err)
|
||||
if msg.doneChan != nil {
|
||||
msg.doneChan <- struct{}{}
|
||||
}
|
||||
|
@ -514,11 +514,11 @@ func TestPeerListeners(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"OnGetBlockInvs",
|
||||
wire.NewMsgGetBlockInvs(&daghash.Hash{}),
|
||||
wire.NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{}),
|
||||
},
|
||||
{
|
||||
"OnGetHeaders",
|
||||
wire.NewMsgGetHeaders(),
|
||||
wire.NewMsgGetHeaders(&daghash.Hash{}, &daghash.Hash{}),
|
||||
},
|
||||
{
|
||||
"OnGetCFilters",
|
||||
@ -694,7 +694,7 @@ func TestOutboundPeer(t *testing.T) {
|
||||
p2.QueueMessage(wire.NewMsgPing(1), nil)
|
||||
p2.QueueMessage(wire.NewMsgMemPool(), 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.Disconnect()
|
||||
|
@ -216,16 +216,16 @@ func (c *Client) GetTopHeaders(startHash *daghash.Hash) ([]wire.BlockHeader, err
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrrpcclient.
|
||||
func (c *Client) GetHeadersAsync(blockLocators []*daghash.Hash, stopHash *daghash.Hash) FutureGetHeadersResult {
|
||||
locators := make([]string, len(blockLocators))
|
||||
for i := range blockLocators {
|
||||
locators[i] = blockLocators[i].String()
|
||||
func (c *Client) GetHeadersAsync(startHash, stopHash *daghash.Hash) FutureGetHeadersResult {
|
||||
startHashStr := ""
|
||||
if startHash != nil {
|
||||
startHashStr = startHash.String()
|
||||
}
|
||||
hash := ""
|
||||
stopHashStr := ""
|
||||
if stopHash != nil {
|
||||
hash = stopHash.String()
|
||||
stopHashStr = stopHash.String()
|
||||
}
|
||||
cmd := btcjson.NewGetHeadersCmd(locators, hash)
|
||||
cmd := btcjson.NewGetHeadersCmd(startHashStr, stopHashStr)
|
||||
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
|
||||
// github.com/decred/dcrrpcclient.
|
||||
func (c *Client) GetHeaders(blockLocators []*daghash.Hash, stopHash *daghash.Hash) ([]wire.BlockHeader, error) {
|
||||
return c.GetHeadersAsync(blockLocators, stopHash).Receive()
|
||||
func (c *Client) GetHeaders(startHash, stopHash *daghash.Hash) ([]wire.BlockHeader, error) {
|
||||
return c.GetHeadersAsync(startHash, stopHash).Receive()
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
// 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
|
||||
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)
|
||||
|
||||
// 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
|
||||
// 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
|
||||
headers := dag.LocateHeaders(msg.BlockLocatorHashes, msg.StopHash)
|
||||
headers := dag.GetBlueBlocksHeadersBetween(msg.StartHash, msg.StopHash)
|
||||
|
||||
// Send found headers to the requesting peer.
|
||||
blockHeaders := make([]*wire.BlockHeader, len(headers))
|
||||
@ -1771,26 +1828,28 @@ func disconnectPeer(peerList map[int32]*Peer, compareFunc func(*Peer) bool, when
|
||||
func newPeerConfig(sp *Peer) *peer.Config {
|
||||
return &peer.Config{
|
||||
Listeners: peer.MessageListeners{
|
||||
OnVersion: sp.OnVersion,
|
||||
OnMemPool: sp.OnMemPool,
|
||||
OnTx: sp.OnTx,
|
||||
OnBlock: sp.OnBlock,
|
||||
OnInv: sp.OnInv,
|
||||
OnHeaders: sp.OnHeaders,
|
||||
OnGetData: sp.OnGetData,
|
||||
OnGetBlockInvs: sp.OnGetBlockInvs,
|
||||
OnGetHeaders: sp.OnGetHeaders,
|
||||
OnGetCFilters: sp.OnGetCFilters,
|
||||
OnGetCFHeaders: sp.OnGetCFHeaders,
|
||||
OnGetCFCheckpt: sp.OnGetCFCheckpt,
|
||||
OnFeeFilter: sp.OnFeeFilter,
|
||||
OnFilterAdd: sp.OnFilterAdd,
|
||||
OnFilterClear: sp.OnFilterClear,
|
||||
OnFilterLoad: sp.OnFilterLoad,
|
||||
OnGetAddr: sp.OnGetAddr,
|
||||
OnAddr: sp.OnAddr,
|
||||
OnRead: sp.OnRead,
|
||||
OnWrite: sp.OnWrite,
|
||||
OnVersion: sp.OnVersion,
|
||||
OnMemPool: sp.OnMemPool,
|
||||
OnTx: sp.OnTx,
|
||||
OnBlock: sp.OnBlock,
|
||||
OnInv: sp.OnInv,
|
||||
OnHeaders: sp.OnHeaders,
|
||||
OnGetData: sp.OnGetData,
|
||||
OnGetBlockLocator: sp.OnGetBlockLocator,
|
||||
OnBlockLocator: sp.OnBlockLocator,
|
||||
OnGetBlockInvs: sp.OnGetBlockInvs,
|
||||
OnGetHeaders: sp.OnGetHeaders,
|
||||
OnGetCFilters: sp.OnGetCFilters,
|
||||
OnGetCFHeaders: sp.OnGetCFHeaders,
|
||||
OnGetCFCheckpt: sp.OnGetCFCheckpt,
|
||||
OnFeeFilter: sp.OnFeeFilter,
|
||||
OnFilterAdd: sp.OnFilterAdd,
|
||||
OnFilterClear: sp.OnFilterClear,
|
||||
OnFilterLoad: sp.OnFilterLoad,
|
||||
OnGetAddr: sp.OnGetAddr,
|
||||
OnAddr: sp.OnAddr,
|
||||
OnRead: sp.OnRead,
|
||||
OnWrite: sp.OnWrite,
|
||||
|
||||
// Note: The reference client currently bans peers that send alerts
|
||||
// not signed with its key. We could verify against their key, but
|
||||
|
@ -269,12 +269,12 @@ func (b *rpcSyncMgr) SyncPeerID() int32 {
|
||||
return b.syncMgr.SyncPeerID()
|
||||
}
|
||||
|
||||
// LocateBlocks returns the hashes of the blocks after the first known block in
|
||||
// the provided locators until the provided stop hash or the current tip is
|
||||
// reached, up to a max of wire.MaxBlockHeadersPerMsg hashes.
|
||||
// GetBlueBlocksHeadersBetween returns the headers of the blocks after the provided
|
||||
// start hash until the provided stop hash is reached, or up to the
|
||||
// provided max number of block headers.
|
||||
//
|
||||
// This function is safe for concurrent access and is part of the
|
||||
// rpcserverSyncManager interface implementation.
|
||||
func (b *rpcSyncMgr) LocateHeaders(locators []*daghash.Hash, stopHash *daghash.Hash) []*wire.BlockHeader {
|
||||
return b.server.DAG.LocateHeaders(locators, stopHash)
|
||||
func (b *rpcSyncMgr) GetBlueBlocksHeadersBetween(startHash, stopHash *daghash.Hash) []*wire.BlockHeader {
|
||||
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) {
|
||||
c := cmd.(*btcjson.GetHeadersCmd)
|
||||
|
||||
// Fetch the requested headers from chain while respecting the provided
|
||||
// block locators and stop hash.
|
||||
blockLocators := make([]*daghash.Hash, len(c.BlockLocators))
|
||||
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)
|
||||
startHash := &daghash.ZeroHash
|
||||
if c.StartHash != "" {
|
||||
err := daghash.Decode(startHash, c.StartHash)
|
||||
if err != nil {
|
||||
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.
|
||||
hexBlockHeaders := make([]string, len(headers))
|
||||
@ -4238,11 +4235,11 @@ type rpcserverSyncManager interface {
|
||||
// used to sync from or 0 if there is none.
|
||||
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
|
||||
// current tip is reached, up to a max of wire.MaxBlockHeadersPerMsg
|
||||
// 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.
|
||||
|
@ -401,14 +401,14 @@ var helpDescsEnUS = map[string]string{
|
||||
|
||||
// GetTopHeadersCmd help.
|
||||
"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)",
|
||||
|
||||
// GetHeadersCmd help.
|
||||
"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-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--synopsis": "Returns block headers starting with the first known block hash from the request",
|
||||
"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--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)",
|
||||
|
||||
// GetInfoCmd help.
|
||||
"getInfo--synopsis": "Returns a JSON object containing various state info.",
|
||||
|
@ -377,18 +377,12 @@ func BenchmarkWriteBlockHeader(b *testing.B) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Create a message with the maximum number of block locators.
|
||||
pver := ProtocolVersion
|
||||
var m MsgGetHeaders
|
||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
||||
hash, err := daghash.NewHashFromStr(fmt.Sprintf("%x", i))
|
||||
if err != nil {
|
||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
||||
}
|
||||
m.AddBlockLocatorHash(hash)
|
||||
}
|
||||
m.StartHash = &daghash.Hash{1}
|
||||
m.StopHash = &daghash.Hash{1}
|
||||
|
||||
// Serialize it so the bytes are available to test the decode below.
|
||||
var bb bytes.Buffer
|
||||
@ -446,18 +440,12 @@ func BenchmarkDecodeHeaders(b *testing.B) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Create a message with the maximum number of block locators.
|
||||
pver := ProtocolVersion
|
||||
var m MsgGetBlockInvs
|
||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
||||
hash, err := daghash.NewHashFromStr(fmt.Sprintf("%x", i))
|
||||
if err != nil {
|
||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
||||
}
|
||||
m.AddBlockLocatorHash(hash)
|
||||
}
|
||||
m.StartHash = &daghash.Hash{1}
|
||||
m.StopHash = &daghash.Hash{1}
|
||||
|
||||
// Serialize it so the bytes are available to test the decode below.
|
||||
var bb bytes.Buffer
|
||||
|
@ -28,35 +28,37 @@ const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
|
||||
|
||||
// Commands used in bitcoin message headers which describe the type of message.
|
||||
const (
|
||||
CmdVersion = "version"
|
||||
CmdVerAck = "verack"
|
||||
CmdGetAddr = "getaddr"
|
||||
CmdAddr = "addr"
|
||||
CmdGetBlockInvs = "getblockinvs"
|
||||
CmdInv = "inv"
|
||||
CmdGetData = "getdata"
|
||||
CmdNotFound = "notfound"
|
||||
CmdBlock = "block"
|
||||
CmdTx = "tx"
|
||||
CmdGetHeaders = "getheaders"
|
||||
CmdHeaders = "headers"
|
||||
CmdPing = "ping"
|
||||
CmdPong = "pong"
|
||||
CmdAlert = "alert"
|
||||
CmdMemPool = "mempool"
|
||||
CmdFilterAdd = "filteradd"
|
||||
CmdFilterClear = "filterclear"
|
||||
CmdFilterLoad = "filterload"
|
||||
CmdMerkleBlock = "merkleblock"
|
||||
CmdReject = "reject"
|
||||
CmdSendHeaders = "sendheaders"
|
||||
CmdFeeFilter = "feefilter"
|
||||
CmdGetCFilters = "getcfilters"
|
||||
CmdGetCFHeaders = "getcfheaders"
|
||||
CmdGetCFCheckpt = "getcfcheckpt"
|
||||
CmdCFilter = "cfilter"
|
||||
CmdCFHeaders = "cfheaders"
|
||||
CmdCFCheckpt = "cfcheckpt"
|
||||
CmdVersion = "version"
|
||||
CmdVerAck = "verack"
|
||||
CmdGetAddr = "getaddr"
|
||||
CmdAddr = "addr"
|
||||
CmdGetBlockInvs = "getblockinvs"
|
||||
CmdInv = "inv"
|
||||
CmdGetData = "getdata"
|
||||
CmdNotFound = "notfound"
|
||||
CmdBlock = "block"
|
||||
CmdTx = "tx"
|
||||
CmdGetHeaders = "getheaders"
|
||||
CmdHeaders = "headers"
|
||||
CmdPing = "ping"
|
||||
CmdPong = "pong"
|
||||
CmdAlert = "alert"
|
||||
CmdMemPool = "mempool"
|
||||
CmdFilterAdd = "filteradd"
|
||||
CmdFilterClear = "filterclear"
|
||||
CmdFilterLoad = "filterload"
|
||||
CmdMerkleBlock = "merkleblock"
|
||||
CmdReject = "reject"
|
||||
CmdSendHeaders = "sendheaders"
|
||||
CmdFeeFilter = "feefilter"
|
||||
CmdGetCFilters = "getcfilters"
|
||||
CmdGetCFHeaders = "getcfheaders"
|
||||
CmdGetCFCheckpt = "getcfcheckpt"
|
||||
CmdCFilter = "cfilter"
|
||||
CmdCFHeaders = "cfheaders"
|
||||
CmdCFCheckpt = "cfcheckpt"
|
||||
CmdGetBlockLocator = "getlocator"
|
||||
CmdBlockLocator = "locator"
|
||||
)
|
||||
|
||||
// Message is an interface that describes a bitcoin message. A type that
|
||||
@ -99,6 +101,12 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||
case CmdGetData:
|
||||
msg = &MsgGetData{}
|
||||
|
||||
case CmdGetBlockLocator:
|
||||
msg = &MsgGetBlockLocator{}
|
||||
|
||||
case CmdBlockLocator:
|
||||
msg = &MsgBlockLocator{}
|
||||
|
||||
case CmdNotFound:
|
||||
msg = &MsgNotFound{}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func TestMessage(t *testing.T) {
|
||||
msgVerack := NewMsgVerAck()
|
||||
msgGetAddr := NewMsgGetAddr(false, nil)
|
||||
msgAddr := NewMsgAddr(false, nil)
|
||||
msgGetBlockInvs := NewMsgGetBlockInvs(&daghash.Hash{})
|
||||
msgGetBlockInvs := NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{})
|
||||
msgBlock := &blockOne
|
||||
msgInv := NewMsgInv()
|
||||
msgGetData := NewMsgGetData()
|
||||
@ -59,7 +59,9 @@ func TestMessage(t *testing.T) {
|
||||
msgTx := NewNativeMsgTx(1, nil, nil)
|
||||
msgPing := NewMsgPing(123123)
|
||||
msgPong := NewMsgPong(123123)
|
||||
msgGetHeaders := NewMsgGetHeaders()
|
||||
msgGetHeaders := NewMsgGetHeaders(&daghash.Hash{}, &daghash.Hash{})
|
||||
msgGetBlockLocator := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||
msgBlockLocator := NewMsgBlockLocator()
|
||||
msgSendHeaders := NewMsgSendHeaders()
|
||||
msgFeeFilter := NewMsgFeeFilter(123456)
|
||||
msgHeaders := NewMsgHeaders()
|
||||
@ -90,7 +92,7 @@ func TestMessage(t *testing.T) {
|
||||
{msgVerack, msgVerack, pver, MainNet, 24},
|
||||
{msgGetAddr, msgGetAddr, pver, MainNet, 26},
|
||||
{msgAddr, msgAddr, pver, MainNet, 27},
|
||||
{msgGetBlockInvs, msgGetBlockInvs, pver, MainNet, 61},
|
||||
{msgGetBlockInvs, msgGetBlockInvs, pver, MainNet, 88},
|
||||
{msgBlock, msgBlock, pver, MainNet, 372},
|
||||
{msgInv, msgInv, pver, MainNet, 25},
|
||||
{msgGetData, msgGetData, pver, MainNet, 25},
|
||||
@ -98,7 +100,9 @@ func TestMessage(t *testing.T) {
|
||||
{msgTx, msgTx, pver, MainNet, 58},
|
||||
{msgPing, msgPing, 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},
|
||||
{msgFeeFilter, msgFeeFilter, pver, MainNet, 32},
|
||||
{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
|
||||
|
||||
import (
|
||||
@ -14,42 +10,29 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// TestGetBlockInvs tests the MsgGetBlockInvs API.
|
||||
func TestGetBlockInvs(t *testing.T) {
|
||||
// TestBlockLocator tests the MsgBlockLocator API.
|
||||
func TestBlockLocator(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// Block 99500 hash.
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
locatorHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Block 100000 hash.
|
||||
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)
|
||||
}
|
||||
msg := NewMsgBlockLocator()
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "getblockinvs"
|
||||
wantCmd := "locator"
|
||||
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)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
// Protocol version 4 bytes + num hashes (varInt) + max block locator
|
||||
// hashes + hash stop.
|
||||
wantPayload := uint32(16045)
|
||||
// Num hashes (varInt) + max block locator
|
||||
// hashes.
|
||||
wantPayload := uint32(16009)
|
||||
maxPayload := msg.MaxPayloadLength(pver)
|
||||
if maxPayload != wantPayload {
|
||||
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
|
||||
// numbers of block locator hashes and protocol versions.
|
||||
func TestGetBlockInvsWire(t *testing.T) {
|
||||
// Set protocol inside getblockinvs message.
|
||||
pver := uint32(1)
|
||||
|
||||
// Block 99499 hash.
|
||||
// TestBlockLocatorWire tests the MsgBlockLocator wire encode and decode for various
|
||||
// numbers of block locator hashes.
|
||||
func TestBlockLocatorWire(t *testing.T) {
|
||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
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"
|
||||
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
|
||||
// MsgBlockLocator message with no block locators.
|
||||
noLocators := NewMsgBlockLocator()
|
||||
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, // Hash stop
|
||||
}
|
||||
|
||||
// MsgGetBlockInvs message with multiple block locators and a stop hash.
|
||||
multiLocators := NewMsgGetBlockInvs(stopHash)
|
||||
// MsgBlockLocator message with multiple block locators.
|
||||
multiLocators := NewMsgBlockLocator()
|
||||
multiLocators.AddBlockLocatorHash(hashLocator2)
|
||||
multiLocators.AddBlockLocatorHash(hashLocator)
|
||||
multiLocators.ProtocolVersion = pver
|
||||
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
|
||||
0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // first hash
|
||||
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, // 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
|
||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // second hash
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgGetBlockInvs // Message to encode
|
||||
out *MsgGetBlockInvs // Expected decoded message
|
||||
in *MsgBlockLocator // Message to encode
|
||||
out *MsgBlockLocator // Expected decoded message
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
}{
|
||||
@ -180,7 +139,7 @@ func TestGetBlockInvsWire(t *testing.T) {
|
||||
}
|
||||
|
||||
// Decode the message from wire format.
|
||||
var msg MsgGetBlockInvs
|
||||
var msg MsgBlockLocator
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
err = msg.BtcDecode(rbuf, test.pver)
|
||||
if err != nil {
|
||||
@ -195,43 +154,32 @@ func TestGetBlockInvsWire(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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. Use protocol version 1
|
||||
// TestBlockLocatorWireErrors performs negative tests against wire encode and
|
||||
// decode of MsgBlockLocator to confirm error paths work correctly.
|
||||
func TestBlockLocatorWireErrors(t *testing.T) {
|
||||
// Set protocol inside locator 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)
|
||||
wireErr := &MessageError{}
|
||||
|
||||
// Block 99499 hash.
|
||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
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"
|
||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// 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
|
||||
// MsgBlockLocator message with multiple block locators and a stop hash.
|
||||
baseGetBlocks := NewMsgBlockLocator()
|
||||
baseGetBlocks.AddBlockLocatorHash(hashLocator2)
|
||||
baseGetBlocks.AddBlockLocatorHash(hashLocator)
|
||||
baseGetBlocksEncoded := []byte{
|
||||
0x02, // Varint for number of block locator hashes
|
||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
||||
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,
|
||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||
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
|
||||
// block locator hashes.
|
||||
maxGetBlockInvs := NewMsgGetBlockInvs(stopHash)
|
||||
maxGetBlocks := NewMsgBlockLocator()
|
||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
||||
maxGetBlockInvs.AddBlockLocatorHash(mainNetGenesisHash)
|
||||
maxGetBlocks.AddBlockLocatorHash(mainNetGenesisHash)
|
||||
}
|
||||
maxGetBlockInvs.BlockLocatorHashes = append(maxGetBlockInvs.BlockLocatorHashes,
|
||||
maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes,
|
||||
mainNetGenesisHash)
|
||||
maxGetBlockInvsEncoded := []byte{
|
||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
||||
maxGetBlocksEncoded := []byte{
|
||||
0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgGetBlockInvs // Value to encode
|
||||
in *MsgBlockLocator // 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 protocol version.
|
||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||
// 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.
|
||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 5, io.ErrShortWrite, io.EOF},
|
||||
// Force error in stop hash.
|
||||
{baseGetBlockInvs, baseGetBlockInvsEncoded, pver, 69, io.ErrShortWrite, io.EOF},
|
||||
{baseGetBlocks, baseGetBlocksEncoded, pver, 1, io.ErrShortWrite, io.EOF},
|
||||
// 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))
|
||||
@ -302,7 +241,7 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
// Decode from wire format.
|
||||
var msg MsgGetBlockInvs
|
||||
var msg MsgBlockLocator
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
err = msg.BtcDecode(r, test.pver)
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/daglabs/btcd/util/daghash"
|
||||
@ -28,55 +27,19 @@ import (
|
||||
// exponentially decrease the number of hashes the further away from head and
|
||||
// closer to the genesis block you get.
|
||||
type MsgGetHeaders struct {
|
||||
ProtocolVersion uint32
|
||||
BlockLocatorHashes []*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
|
||||
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 *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 {
|
||||
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{}
|
||||
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.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgGetHeaders) 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("MsgGetHeaders.BtcEncode", str)
|
||||
}
|
||||
|
||||
err := WriteElement(w, msg.ProtocolVersion)
|
||||
err := WriteElement(w, msg.StartHash)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -121,18 +64,15 @@ func (msg *MsgGetHeaders) Command() string {
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||
// Version 4 bytes + num block locator hashes (varInt) + max allowed block
|
||||
// locators + hash stop.
|
||||
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
|
||||
daghash.HashSize) + daghash.HashSize
|
||||
// start hash + stop hash.
|
||||
return 2 * daghash.HashSize
|
||||
}
|
||||
|
||||
// NewMsgGetHeaders returns a new bitcoin getheaders message that conforms to
|
||||
// the Message interface. See MsgGetHeaders for details.
|
||||
func NewMsgGetHeaders() *MsgGetHeaders {
|
||||
func NewMsgGetHeaders(startHash, stopHash *daghash.Hash) *MsgGetHeaders {
|
||||
return &MsgGetHeaders{
|
||||
BlockLocatorHashes: make([]*daghash.Hash, 0,
|
||||
MaxBlockLocatorsPerMsg),
|
||||
StopHash: &daghash.ZeroHash,
|
||||
StartHash: startHash,
|
||||
StopHash: stopHash,
|
||||
}
|
||||
}
|
||||
|
@ -20,76 +20,37 @@ func TestGetHeaders(t *testing.T) {
|
||||
|
||||
// Block 99500 hash.
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
locatorHash, err := daghash.NewHashFromStr(hashStr)
|
||||
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "getheaders"
|
||||
msg := NewMsgGetHeaders()
|
||||
msg := NewMsgGetHeaders(startHash, &daghash.ZeroHash)
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
// Protocol version 4 bytes + num hashes (varInt) + max block locator
|
||||
// hashes + hash stop.
|
||||
wantPayload := uint32(16045)
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode.
|
||||
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"
|
||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
||||
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
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"
|
||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
@ -97,38 +58,29 @@ func TestGetHeadersWire(t *testing.T) {
|
||||
}
|
||||
|
||||
// MsgGetHeaders message with no block locators or stop hash.
|
||||
noLocators := NewMsgGetHeaders()
|
||||
noLocators.ProtocolVersion = pver
|
||||
noLocatorsEncoded := []byte{
|
||||
0x01, 0x00, 0x00, 0x00, // Protocol version 1
|
||||
0x00, // Varint for number of block locator hashes
|
||||
noStartAndStopHash := NewMsgGetHeaders(&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, // 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.
|
||||
multiLocators := NewMsgGetHeaders()
|
||||
multiLocators.ProtocolVersion = pver
|
||||
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
|
||||
withStartAndStopHash := NewMsgGetHeaders(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, // Block 99499 hash
|
||||
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, // Hash stop
|
||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@ -137,19 +89,19 @@ func TestGetHeadersWire(t *testing.T) {
|
||||
buf []byte // 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,
|
||||
noLocators,
|
||||
noLocatorsEncoded,
|
||||
noStartAndStopHash,
|
||||
noStartAndStopHash,
|
||||
noStartAndStopHashEncoded,
|
||||
ProtocolVersion,
|
||||
},
|
||||
|
||||
// Latest protocol version with multiple block locators.
|
||||
// Message with start hash and stop hash.
|
||||
{
|
||||
multiLocators,
|
||||
multiLocators,
|
||||
multiLocatorsEncoded,
|
||||
withStartAndStopHash,
|
||||
withStartAndStopHash,
|
||||
withStartAndStopHashEncoded,
|
||||
ProtocolVersion,
|
||||
},
|
||||
}
|
||||
@ -188,27 +140,15 @@ func TestGetHeadersWire(t *testing.T) {
|
||||
// TestGetHeadersWireErrors performs negative tests against wire encode and
|
||||
// decode of MsgGetHeaders to confirm error paths work correctly.
|
||||
func TestGetHeadersWireErrors(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.
|
||||
// Set protocol inside getheaders message.
|
||||
pver := ProtocolVersion
|
||||
wireErr := &MessageError{}
|
||||
|
||||
// Block 99499 hash.
|
||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||
hashLocator, err := daghash.NewHashFromStr(hashStr)
|
||||
startHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
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"
|
||||
stopHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
@ -216,39 +156,16 @@ func TestGetHeadersWireErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
// MsgGetHeaders message with multiple block locators and a stop hash.
|
||||
baseGetHeaders := NewMsgGetHeaders()
|
||||
baseGetHeaders.ProtocolVersion = pver
|
||||
baseGetHeaders.StopHash = stopHash
|
||||
baseGetHeaders.AddBlockLocatorHash(hashLocator2)
|
||||
baseGetHeaders.AddBlockLocatorHash(hashLocator)
|
||||
baseGetHeaders := NewMsgGetHeaders(startHash, stopHash)
|
||||
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,
|
||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||
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,
|
||||
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
|
||||
// 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)
|
||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Stop hash
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@ -259,16 +176,10 @@ func TestGetHeadersWireErrors(t *testing.T) {
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Force error in protocol version.
|
||||
// Force error in start hash.
|
||||
{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.
|
||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 69, io.ErrShortWrite, io.EOF},
|
||||
// Force error with greater than max block locator hashes.
|
||||
{maxGetHeaders, maxGetHeadersEncoded, pver, 7, wireErr, wireErr},
|
||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 32, io.ErrShortWrite, io.EOF},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
|
@ -12,8 +12,7 @@ import (
|
||||
// sendheaders message. It is used to request the peer send block headers
|
||||
// rather than inventory vectors.
|
||||
//
|
||||
// This message has no payload and was not added until protocol versions
|
||||
// starting with SendHeadersVersion.
|
||||
// This message has no payload.
|
||||
type MsgSendHeaders struct{}
|
||||
|
||||
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||
|
Loading…
x
Reference in New Issue
Block a user