[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:
Ori Newman 2019-08-19 15:35:13 +03:00 committed by stasatdaglabs
parent 70737e4e94
commit d2daf334a5
28 changed files with 1304 additions and 913 deletions

143
blockdag/blocklocator.go Normal file
View 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
}

View File

@ -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
}

View File

@ -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

View File

@ -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,
}
}

View File

@ -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",
},
},
{

View File

@ -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)

View File

@ -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{}),

View File

@ -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))

View File

@ -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{}{}
}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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.

View File

@ -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.",

View File

@ -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

View File

@ -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{}

View File

@ -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
View 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),
}
}

View File

@ -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
View 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,
}
}

View 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
}
}
}
}

View 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,
}
}

View 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
}
}
}
}

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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))

View File

@ -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.