[NOD-88] Add getTopHeaders rpc command (#242)

* [NOD-88] Add getTopHeaders rpc command

* [NOD-88] Fix gofmt error

* [NOD-88] Remove unnecessary variable

* [NOD-88] Remove GetTopHeaders from rpcSyncMgr
This commit is contained in:
Ori Newman 2019-04-04 11:06:19 +03:00 committed by Svarog
parent 7c30bc4301
commit aa22480d5e
6 changed files with 127 additions and 1 deletions

View File

@ -1421,7 +1421,7 @@ func (dag *BlockDAG) locateBlockNodes(locator BlockLocator, hashStop *daghash.Ha
queue.pushSet(node.children)
visited := newSet()
for i := uint32(0); queue.Len() > 0 && i < maxEntries; i++ {
for queue.Len() > 0 && uint32(len(nodes)) < maxEntries {
var current *blockNode
current = queue.pop()
if !visited.contains(current) {
@ -1474,6 +1474,32 @@ func (dag *BlockDAG) locateHeaders(locator BlockLocator, hashStop *daghash.Hash,
return headers
}
// GetTopHeaders returns the top wire.MaxBlockHeadersPerMsg block headers ordered by height.
func (dag *BlockDAG) GetTopHeaders(startHash *daghash.Hash) ([]*wire.BlockHeader, error) {
startNode := &dag.virtual.blockNode
if startHash != nil {
startNode = dag.index.LookupNode(startHash)
if startNode == nil {
return nil, fmt.Errorf("Couldn't find the start hash %s in the dag", startHash)
}
}
headers := make([]*wire.BlockHeader, 0, startNode.blueScore)
queue := NewDownHeap()
queue.pushSet(startNode.parents)
visited := newSet()
for i := uint32(0); queue.Len() > 0 && len(headers) < wire.MaxBlockHeadersPerMsg; i++ {
var current *blockNode
current = queue.pop()
if !visited.contains(current) {
visited.add(current)
headers = append(headers, current.Header())
queue.pushSet(current.parents)
}
}
return headers, nil
}
// RLock locks the DAG's UTXO set for reading.
func (dag *BlockDAG) RLock() {
dag.dagLock.RLock()

View File

@ -90,6 +90,19 @@ func NewGetCurrentNetCmd() *GetCurrentNetCmd {
return &GetCurrentNetCmd{}
}
// GetTopHeadersCmd defined the getTopHeaders JSON-RPC command.
type GetTopHeadersCmd struct {
StartHash *string `json:"startHash"`
}
// NewGetTopHeadersCmd returns a new instance which can be used to issue a
// getTopHeaders JSON-RPC command.
func NewGetTopHeadersCmd(startHash *string) *GetTopHeadersCmd {
return &GetTopHeadersCmd{
StartHash: startHash,
}
}
// GetHeadersCmd defines the getHeaders JSON-RPC command.
//
// NOTE: This is a btcsuite extension ported from
@ -134,5 +147,6 @@ func init() {
MustRegisterCmd("getBestBlock", (*GetBestBlockCmd)(nil), flags)
MustRegisterCmd("getCurrentNet", (*GetCurrentNetCmd)(nil), flags)
MustRegisterCmd("getHeaders", (*GetHeadersCmd)(nil), flags)
MustRegisterCmd("getTopHeaders", (*GetTopHeadersCmd)(nil), flags)
MustRegisterCmd("version", (*VersionCmd)(nil), flags)
}

View File

@ -176,6 +176,34 @@ func TestBtcdExtCmds(t *testing.T) {
HashStop: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7",
},
},
{
name: "getTopHeaders",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getTopHeaders")
},
staticCmd: func() interface{} {
return btcjson.NewGetTopHeadersCmd(
nil,
)
},
marshalled: `{"jsonrpc":"1.0","method":"getTopHeaders","params":[],"id":1}`,
unmarshalled: &btcjson.GetTopHeadersCmd{},
},
{
name: "getTopHeaders - with start hash",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getTopHeaders", "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7")
},
staticCmd: func() interface{} {
return btcjson.NewGetTopHeadersCmd(
btcjson.String("000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"),
)
},
marshalled: `{"jsonrpc":"1.0","method":"getTopHeaders","params":["000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"],"id":1}`,
unmarshalled: &btcjson.GetTopHeadersCmd{
StartHash: btcjson.String("000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"),
},
},
{
name: "version",
newCmd: func() (interface{}, error) {

View File

@ -275,6 +275,24 @@ func (r FutureGetHeadersResult) Receive() ([]wire.BlockHeader, error) {
return headers, nil
}
// GetTopHeadersAsync returns an instance of a type that can be used to get the result
// of the RPC at some future time by invoking the Receive function on the returned instance.
//
// See GetTopHeaders for the blocking version and more details.
func (c *Client) GetTopHeadersAsync(startHash *daghash.Hash) FutureGetHeadersResult {
var hash *string
if startHash != nil {
hash = btcjson.String(startHash.String())
}
cmd := btcjson.NewGetTopHeadersCmd(hash)
return c.sendCmd(cmd)
}
// GetTopHeaders sends a getTopHeaders rpc command to the server.
func (c *Client) GetTopHeaders(startHash *daghash.Hash) ([]wire.BlockHeader, error) {
return c.GetTopHeadersAsync(startHash).Receive()
}
// GetHeadersAsync returns an instance of a type that can be used to get the result
// of the RPC at some future time by invoking the Receive function on the returned instance.
//

View File

@ -161,6 +161,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"getGenerate": handleGetGenerate,
"getHashesPerSec": handleGetHashesPerSec,
"getHeaders": handleGetHeaders,
"getTopHeaders": handleGetTopHeaders,
"getInfo": handleGetInfo,
"getManualNodeInfo": handleGetManualNodeInfo,
"getMempoolInfo": handleGetMempoolInfo,
@ -2289,6 +2290,39 @@ func handleGetHashesPerSec(s *Server, cmd interface{}, closeChan <-chan struct{}
return int64(s.cfg.CPUMiner.HashesPerSecond()), nil
}
// handleGetTopHeaders implements the getTopHeaders command.
func handleGetTopHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.GetTopHeadersCmd)
var startHash *daghash.Hash
if c.StartHash != nil {
startHash = &daghash.Hash{}
err := daghash.Decode(startHash, *c.StartHash)
if err != nil {
return nil, rpcDecodeHexError(*c.StartHash)
}
}
headers, err := s.cfg.DAG.GetTopHeaders(startHash)
if err != nil {
return nil, internalRPCError(err.Error(),
"Failed to get top headers")
}
// Return the serialized block headers as hex-encoded strings.
hexBlockHeaders := make([]string, len(headers))
var buf bytes.Buffer
for i, h := range headers {
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
}
// handleGetHeaders implements the getHeaders command.
//
// NOTE: This is a btcsuite extension originally ported from

View File

@ -402,6 +402,11 @@ var helpDescsEnUS = map[string]string{
"infoWalletResult-relayFee": "The minimum relay fee for non-free transactions in BTC/KB",
"infoWalletResult-errors": "Any current errors",
// 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--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",
@ -707,6 +712,7 @@ var rpcResultTypes = map[string][]interface{}{
"getDifficulty": {(*float64)(nil)},
"getGenerate": {(*bool)(nil)},
"getHashesPerSec": {(*float64)(nil)},
"getTopHeaders": {(*[]string)(nil)},
"getHeaders": {(*[]string)(nil)},
"getInfo": {(*btcjson.InfoDAGResult)(nil)},
"getManualNodeInfo": {(*string)(nil), (*btcjson.GetManualNodeInfoResult)(nil)},