Port getheaders
JSON-RPC command from dcrd
This commit is contained in:
parent
765ca28711
commit
7c44b6472f
9 changed files with 254 additions and 103 deletions
|
@ -90,6 +90,27 @@ func NewGetCurrentNetCmd() *GetCurrentNetCmd {
|
||||||
return &GetCurrentNetCmd{}
|
return &GetCurrentNetCmd{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeadersCmd defines the getheaders JSON-RPC command.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrd/dcrjson.
|
||||||
|
type GetHeadersCmd struct {
|
||||||
|
BlockLocators []string `json:"blocklocators"`
|
||||||
|
HashStop string `json:"hashstop"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGetHeadersCmd returns a new instance which can be used to issue a
|
||||||
|
// getheaders JSON-RPC command.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrd/dcrjson.
|
||||||
|
func NewGetHeadersCmd(blockLocators []string, hashStop string) *GetHeadersCmd {
|
||||||
|
return &GetHeadersCmd{
|
||||||
|
BlockLocators: blockLocators,
|
||||||
|
HashStop: hashStop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VersionCmd defines the version JSON-RPC command.
|
// VersionCmd defines the version JSON-RPC command.
|
||||||
//
|
//
|
||||||
// NOTE: This is a btcsuite extension ported from
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
@ -112,5 +133,6 @@ func init() {
|
||||||
MustRegisterCmd("generate", (*GenerateCmd)(nil), flags)
|
MustRegisterCmd("generate", (*GenerateCmd)(nil), flags)
|
||||||
MustRegisterCmd("getbestblock", (*GetBestBlockCmd)(nil), flags)
|
MustRegisterCmd("getbestblock", (*GetBestBlockCmd)(nil), flags)
|
||||||
MustRegisterCmd("getcurrentnet", (*GetCurrentNetCmd)(nil), flags)
|
MustRegisterCmd("getcurrentnet", (*GetCurrentNetCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getheaders", (*GetHeadersCmd)(nil), flags)
|
||||||
MustRegisterCmd("version", (*VersionCmd)(nil), flags)
|
MustRegisterCmd("version", (*VersionCmd)(nil), flags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,46 @@ func TestBtcdExtCmds(t *testing.T) {
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getcurrentnet","params":[],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getcurrentnet","params":[],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetCurrentNetCmd{},
|
unmarshalled: &btcjson.GetCurrentNetCmd{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "getheaders",
|
||||||
|
newCmd: func() (interface{}, error) {
|
||||||
|
return btcjson.NewCmd("getheaders", []string{}, "")
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
return btcjson.NewGetHeadersCmd(
|
||||||
|
[]string{},
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"getheaders","params":[[],""],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.GetHeadersCmd{
|
||||||
|
BlockLocators: []string{},
|
||||||
|
HashStop: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "getheaders - with arguments",
|
||||||
|
newCmd: func() (interface{}, error) {
|
||||||
|
return btcjson.NewCmd("getheaders", []string{"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", "0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10"}, "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7")
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
return btcjson.NewGetHeadersCmd(
|
||||||
|
[]string{
|
||||||
|
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||||
|
"0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10",
|
||||||
|
},
|
||||||
|
"000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"getheaders","params":[["000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16","0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10"],"000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.GetHeadersCmd{
|
||||||
|
BlockLocators: []string{
|
||||||
|
"000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16",
|
||||||
|
"0000000000000000026f4b7f56eef057b32167eb5ad9ff62006f1807b7336d10",
|
||||||
|
},
|
||||||
|
HashStop: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "version",
|
name: "version",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2016 The btcsuite developers
|
// Copyright (c) 2016-2017 The btcsuite developers
|
||||||
// Copyright (c) 2015-2016 The Decred developers
|
// Copyright (c) 2015-2017 The Decred developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2016 The btcsuite developers
|
// Copyright (c) 2016-2017 The btcsuite developers
|
||||||
// Copyright (c) 2015-2016 The Decred developers
|
// Copyright (c) 2015-2016 The Decred developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
|
@ -84,35 +84,45 @@ func NewHash(newHash []byte) (*Hash, error) {
|
||||||
// the hexadecimal string of a byte-reversed hash, but any missing characters
|
// the hexadecimal string of a byte-reversed hash, but any missing characters
|
||||||
// result in zero padding at the end of the Hash.
|
// result in zero padding at the end of the Hash.
|
||||||
func NewHashFromStr(hash string) (*Hash, error) {
|
func NewHashFromStr(hash string) (*Hash, error) {
|
||||||
// Return error if hash string is too long.
|
ret := new(Hash)
|
||||||
if len(hash) > MaxHashStringSize {
|
err := Decode(ret, hash)
|
||||||
return nil, ErrHashStrSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hex decoder expects the hash to be a multiple of two.
|
|
||||||
if len(hash)%2 != 0 {
|
|
||||||
hash = "0" + hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert string hash to bytes.
|
|
||||||
buf, err := hex.DecodeString(hash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return ret, nil
|
||||||
// Un-reverse the decoded bytes, copying into in leading bytes of a
|
}
|
||||||
// Hash. There is no need to explicitly pad the result as any
|
|
||||||
// missing (when len(buf) < HashSize) bytes from the decoded hex string
|
// Decode decodes the byte-reversed hexadecimal string encoding of a Hash to a
|
||||||
// will remain zeros at the end of the Hash.
|
// destination.
|
||||||
var ret Hash
|
func Decode(dst *Hash, src string) error {
|
||||||
blen := len(buf)
|
// Return error if hash string is too long.
|
||||||
mid := blen / 2
|
if len(src) > MaxHashStringSize {
|
||||||
if blen%2 != 0 {
|
return ErrHashStrSize
|
||||||
mid++
|
}
|
||||||
}
|
|
||||||
blen--
|
// Hex decoder expects the hash to be a multiple of two. When not, pad
|
||||||
for i, b := range buf[:mid] {
|
// with a leading zero.
|
||||||
ret[i], ret[blen-i] = buf[blen-i], b
|
var srcBytes []byte
|
||||||
}
|
if len(src)%2 == 0 {
|
||||||
return &ret, nil
|
srcBytes = []byte(src)
|
||||||
|
} else {
|
||||||
|
srcBytes = make([]byte, 1+len(src))
|
||||||
|
srcBytes[0] = '0'
|
||||||
|
copy(srcBytes[1:], src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex decode the source bytes to a temporary destination.
|
||||||
|
var reversedHash Hash
|
||||||
|
_, err := hex.Decode(reversedHash[HashSize-hex.DecodedLen(len(srcBytes)):], srcBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse copy from the temporary hash to destination. Because the
|
||||||
|
// temporary was zeroed, the written result will be correctly padded.
|
||||||
|
for i, b := range reversedHash[:HashSize/2] {
|
||||||
|
dst[i], dst[HashSize-1-i] = reversedHash[HashSize-1-i], b
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -584,6 +584,7 @@ The following is an overview of the RPC methods which are implemented by btcd, b
|
||||||
|5|[node](#node)|N|Attempts to add or remove a peer. |None|
|
|5|[node](#node)|N|Attempts to add or remove a peer. |None|
|
||||||
|6|[generate](#generate)|N|When in simnet or regtest mode, generate a set number of blocks. |None|
|
|6|[generate](#generate)|N|When in simnet or regtest mode, generate a set number of blocks. |None|
|
||||||
|7|[version](#version)|Y|Returns the JSON-RPC API version.|
|
|7|[version](#version)|Y|Returns the JSON-RPC API version.|
|
||||||
|
|8|[getheaders](#getheaders)|Y|Returns block headers starting with the first known block hash from the request.|
|
||||||
|
|
||||||
|
|
||||||
<a name="ExtMethodDetails" />
|
<a name="ExtMethodDetails" />
|
||||||
|
@ -678,6 +679,19 @@ The following is an overview of the RPC methods which are implemented by btcd, b
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
<a name="getheaders"/>
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
|Method|getheaders|
|
||||||
|
|Parameters|1. Block Locators (JSON array, required)<br /> `[ (json array of strings)`<br /> `"blocklocator", (string) the known block hash`<br /> `...`<br /> `]`<br />2. hashstop (string) - last desired block's hash|
|
||||||
|
|Description|Returns block headers starting with the first known block hash from the request.|
|
||||||
|
|Returns|`[ (json array of strings)`<br /> `"blockheader",`<br /> `...`<br />`]`|
|
||||||
|
|Example Return|`[`<br /> `"0000002099417930b2ae09feda10e38b58c0f6bb44b4d60fa33f0e000000000000000000d53...",`<br /> `"000000203ba25a173bfd24d09e0c76002a910b685ca297bd09a17b020000000000000000702..."`<br />`]`|
|
||||||
|
[Return to Overview](#MethodOverview)<br />
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
<a name="WSExtMethods" />
|
<a name="WSExtMethods" />
|
||||||
### 7. Websocket Extension Methods (Websocket-specific)
|
### 7. Websocket Extension Methods (Websocket-specific)
|
||||||
|
|
||||||
|
|
69
rpcserver.go
69
rpcserver.go
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
// Copyright (c) 2013-2017 The btcsuite developers
|
||||||
// Copyright (c) 2015-2016 The Decred developers
|
// Copyright (c) 2015-2017 The Decred developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ import (
|
||||||
|
|
||||||
// API version constants
|
// API version constants
|
||||||
const (
|
const (
|
||||||
jsonrpcSemverString = "1.0.0"
|
jsonrpcSemverString = "1.1.0"
|
||||||
jsonrpcSemverMajor = 1
|
jsonrpcSemverMajor = 1
|
||||||
jsonrpcSemverMinor = 0
|
jsonrpcSemverMinor = 1
|
||||||
jsonrpcSemverPatch = 0
|
jsonrpcSemverPatch = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -163,6 +163,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
|
||||||
"getdifficulty": handleGetDifficulty,
|
"getdifficulty": handleGetDifficulty,
|
||||||
"getgenerate": handleGetGenerate,
|
"getgenerate": handleGetGenerate,
|
||||||
"gethashespersec": handleGetHashesPerSec,
|
"gethashespersec": handleGetHashesPerSec,
|
||||||
|
"getheaders": handleGetHeaders,
|
||||||
"getinfo": handleGetInfo,
|
"getinfo": handleGetInfo,
|
||||||
"getmempoolinfo": handleGetMempoolInfo,
|
"getmempoolinfo": handleGetMempoolInfo,
|
||||||
"getmininginfo": handleGetMiningInfo,
|
"getmininginfo": handleGetMiningInfo,
|
||||||
|
@ -270,6 +271,7 @@ var rpcLimited = map[string]struct{}{
|
||||||
"getblockhash": {},
|
"getblockhash": {},
|
||||||
"getcurrentnet": {},
|
"getcurrentnet": {},
|
||||||
"getdifficulty": {},
|
"getdifficulty": {},
|
||||||
|
"getheaders": {},
|
||||||
"getinfo": {},
|
"getinfo": {},
|
||||||
"getnettotals": {},
|
"getnettotals": {},
|
||||||
"getnetworkhashps": {},
|
"getnetworkhashps": {},
|
||||||
|
@ -2148,6 +2150,65 @@ func handleGetHashesPerSec(s *rpcServer, cmd interface{}, closeChan <-chan struc
|
||||||
return int64(s.server.cpuMiner.HashesPerSecond()), nil
|
return int64(s.server.cpuMiner.HashesPerSecond()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleGetHeaders implements the getheaders command.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrd.
|
||||||
|
func handleGetHeaders(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
|
c := cmd.(*btcjson.GetHeadersCmd)
|
||||||
|
blockLocators := make([]*chainhash.Hash, len(c.BlockLocators))
|
||||||
|
for i := range c.BlockLocators {
|
||||||
|
blockLocator, err := chainhash.NewHashFromStr(c.BlockLocators[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidParameter,
|
||||||
|
Message: "Failed to decode block locator: " +
|
||||||
|
err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockLocators[i] = blockLocator
|
||||||
|
}
|
||||||
|
var hashStop chainhash.Hash
|
||||||
|
if c.HashStop != "" {
|
||||||
|
err := chainhash.Decode(&hashStop, c.HashStop)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidParameter,
|
||||||
|
Message: "Failed to decode hashstop: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockHashes, err := s.server.locateBlocks(blockLocators, &hashStop)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Failed to fetch hashes of block " +
|
||||||
|
"headers: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockHeaders, err := fetchHeaders(s.server.db, blockHashes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCDatabase,
|
||||||
|
Message: "Failed to fetch headers of located blocks: " +
|
||||||
|
err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hexBlockHeaders := make([]string, len(blockHeaders))
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, h := range blockHeaders {
|
||||||
|
err := h.Serialize(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, internalRPCError(err.Error(),
|
||||||
|
"Failed to serialize block header")
|
||||||
|
}
|
||||||
|
hexBlockHeaders[i] = hex.EncodeToString(buf.Bytes())
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
return hexBlockHeaders, nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleGetInfo implements the getinfo command. We only return the fields
|
// handleGetInfo implements the getinfo command. We only return the fields
|
||||||
// that are not related to wallet functionality.
|
// that are not related to wallet functionality.
|
||||||
func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) 2015-2016 The btcsuite developers
|
// Copyright (c) 2015-2017 The btcsuite developers
|
||||||
// Copyright (c) 2015-2016 The Decred developers
|
// Copyright (c) 2015-2017 The Decred developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -360,6 +360,12 @@ var helpDescsEnUS = map[string]string{
|
||||||
"infowalletresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB",
|
"infowalletresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB",
|
||||||
"infowalletresult-errors": "Any current errors",
|
"infowalletresult-errors": "Any current errors",
|
||||||
|
|
||||||
|
// 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-hashstop": "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.
|
// GetInfoCmd help.
|
||||||
"getinfo--synopsis": "Returns a JSON object containing various state info.",
|
"getinfo--synopsis": "Returns a JSON object containing various state info.",
|
||||||
|
|
||||||
|
@ -646,6 +652,7 @@ var rpcResultTypes = map[string][]interface{}{
|
||||||
"getdifficulty": {(*float64)(nil)},
|
"getdifficulty": {(*float64)(nil)},
|
||||||
"getgenerate": {(*bool)(nil)},
|
"getgenerate": {(*bool)(nil)},
|
||||||
"gethashespersec": {(*float64)(nil)},
|
"gethashespersec": {(*float64)(nil)},
|
||||||
|
"getheaders": {(*[]string)(nil)},
|
||||||
"getinfo": {(*btcjson.InfoChainResult)(nil)},
|
"getinfo": {(*btcjson.InfoChainResult)(nil)},
|
||||||
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
|
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
|
||||||
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
|
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
|
||||||
|
|
129
server.go
129
server.go
|
@ -1,4 +1,5 @@
|
||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
// Copyright (c) 2013-2017 The btcsuite developers
|
||||||
|
// Copyright (c) 2015-2017 The Decred developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -640,58 +641,30 @@ func (sp *serverPeer) OnGetBlocks(_ *peer.Peer, msg *wire.MsgGetBlocks) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnGetHeaders is invoked when a peer receives a getheaders bitcoin
|
// locateBlocks returns the hashes of the blocks after the first known block in
|
||||||
// message.
|
// locators, until hashStop is reached, or up to a max of
|
||||||
func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) {
|
// wire.MaxBlockHeadersPerMsg block hashes. This implements the search
|
||||||
// Ignore getheaders requests if not in sync.
|
// algorithm used by getheaders.
|
||||||
if !sp.server.blockManager.IsCurrent() {
|
func (s *server) locateBlocks(locators []*chainhash.Hash, hashStop *chainhash.Hash) ([]chainhash.Hash, error) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to look up the height of the provided stop hash.
|
// Attempt to look up the height of the provided stop hash.
|
||||||
chain := sp.server.blockManager.chain
|
chain := s.blockManager.chain
|
||||||
endIdx := int32(math.MaxInt32)
|
endIdx := int32(math.MaxInt32)
|
||||||
height, err := chain.BlockHeightByHash(&msg.HashStop)
|
height, err := chain.BlockHeightByHash(hashStop)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
endIdx = height + 1
|
endIdx = height + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are no block locators so a specific header is being requested
|
// There are no block locators so a specific header is being requested
|
||||||
// as identified by the stop hash.
|
// as identified by the stop hash.
|
||||||
if len(msg.BlockLocatorHashes) == 0 {
|
if len(locators) == 0 {
|
||||||
// No blocks with the stop hash were found so there is nothing
|
// No blocks with the stop hash were found so there is nothing
|
||||||
// to do. Just return. This behavior mirrors the reference
|
// to do. Just return. This behavior mirrors the reference
|
||||||
// implementation.
|
// implementation.
|
||||||
if endIdx == math.MaxInt32 {
|
if endIdx == math.MaxInt32 {
|
||||||
return
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the raw block header bytes from the database.
|
return []chainhash.Hash{*hashStop}, nil
|
||||||
var headerBytes []byte
|
|
||||||
err := sp.server.db.View(func(dbTx database.Tx) error {
|
|
||||||
var err error
|
|
||||||
headerBytes, err = dbTx.FetchBlockHeader(&msg.HashStop)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
peerLog.Warnf("Lookup of known block hash failed: %v",
|
|
||||||
err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the block header.
|
|
||||||
var header wire.BlockHeader
|
|
||||||
err = header.Deserialize(bytes.NewReader(headerBytes))
|
|
||||||
if err != nil {
|
|
||||||
peerLog.Warnf("Block header deserialize failed: %v",
|
|
||||||
err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
headersMsg := wire.NewMsgHeaders()
|
|
||||||
headersMsg.AddBlockHeader(&header)
|
|
||||||
sp.QueueMessage(headersMsg, nil)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the most recent known block based on the block locator.
|
// Find the most recent known block based on the block locator.
|
||||||
|
@ -700,8 +673,8 @@ func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) {
|
||||||
// over with the genesis block if unknown block locators are provided.
|
// over with the genesis block if unknown block locators are provided.
|
||||||
// This mirrors the behavior in the reference implementation.
|
// This mirrors the behavior in the reference implementation.
|
||||||
startIdx := int32(1)
|
startIdx := int32(1)
|
||||||
for _, hash := range msg.BlockLocatorHashes {
|
for _, loc := range locators {
|
||||||
height, err := chain.BlockHeightByHash(hash)
|
height, err := chain.BlockHeightByHash(loc)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Start with the next hash since we know this one.
|
// Start with the next hash since we know this one.
|
||||||
startIdx = height + 1
|
startIdx = height + 1
|
||||||
|
@ -709,43 +682,67 @@ func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't attempt to fetch more than we can put into a single message.
|
// Don't attempt to fetch more than we can put into a single wire
|
||||||
|
// message.
|
||||||
if endIdx-startIdx > wire.MaxBlockHeadersPerMsg {
|
if endIdx-startIdx > wire.MaxBlockHeadersPerMsg {
|
||||||
endIdx = startIdx + wire.MaxBlockHeadersPerMsg
|
endIdx = startIdx + wire.MaxBlockHeadersPerMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the inventory from the block database.
|
// Fetch the inventory from the block database.
|
||||||
hashList, err := chain.HeightRange(startIdx, endIdx)
|
return chain.HeightRange(startIdx, endIdx)
|
||||||
if err != nil {
|
}
|
||||||
peerLog.Warnf("Header lookup failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate headers message and send it.
|
// fetchHeaders fetches and decodes headers from the db for each hash in
|
||||||
headersMsg := wire.NewMsgHeaders()
|
// blockHashes.
|
||||||
err = sp.server.db.View(func(dbTx database.Tx) error {
|
func fetchHeaders(db database.DB, blockHashes []chainhash.Hash) ([]*wire.BlockHeader, error) {
|
||||||
for i := range hashList {
|
headers := make([]*wire.BlockHeader, 0, len(blockHashes))
|
||||||
headerBytes, err := dbTx.FetchBlockHeader(&hashList[i])
|
err := db.View(func(dbTx database.Tx) error {
|
||||||
if err != nil {
|
rawHeaders, err := dbTx.FetchBlockHeaders(blockHashes)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
var header wire.BlockHeader
|
for _, headerBytes := range rawHeaders {
|
||||||
err = header.Deserialize(bytes.NewReader(headerBytes))
|
h := new(wire.BlockHeader)
|
||||||
if err != nil {
|
err = h.Deserialize(bytes.NewReader(headerBytes))
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
headersMsg.AddBlockHeader(&header)
|
}
|
||||||
|
headers = append(headers, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
return headers, err
|
||||||
peerLog.Warnf("Failed to build headers: %v", err)
|
}
|
||||||
|
|
||||||
|
// OnGetHeaders is invoked when a peer receives a getheaders bitcoin
|
||||||
|
// message.
|
||||||
|
func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) {
|
||||||
|
// Ignore getheaders requests if not in sync.
|
||||||
|
if !sp.server.blockManager.IsCurrent() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sp.QueueMessage(headersMsg, nil)
|
blockHashes, err := sp.server.locateBlocks(msg.BlockLocatorHashes,
|
||||||
|
&msg.HashStop)
|
||||||
|
if err != nil {
|
||||||
|
peerLog.Errorf("OnGetHeaders: failed to fetch hashes: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
blockHeaders, err := fetchHeaders(sp.server.db, blockHashes)
|
||||||
|
if err != nil {
|
||||||
|
peerLog.Errorf("OnGetHeaders: failed to fetch block headers: "+
|
||||||
|
"%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blockHeaders) > wire.MaxBlockHeadersPerMsg {
|
||||||
|
peerLog.Warnf("OnGetHeaders: fetched more block headers than " +
|
||||||
|
"allowed per message")
|
||||||
|
// Can still recover from this error, just slice off the extra
|
||||||
|
// headers and continue queing the message.
|
||||||
|
blockHeaders = blockHeaders[:wire.MaxBlockHeaderPayload]
|
||||||
|
}
|
||||||
|
sp.QueueMessage(&wire.MsgHeaders{Headers: blockHeaders}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// enforceNodeBloomFlag disconnects the peer if the server is not configured to
|
// enforceNodeBloomFlag disconnects the peer if the server is not configured to
|
||||||
|
|
Loading…
Reference in a new issue