From 47214121a721fe3c98c23beaa6bf4e40bf0d56f6 Mon Sep 17 00:00:00 2001 From: Dan Aharoni Date: Wed, 20 Nov 2019 12:04:22 +0200 Subject: [PATCH] [NOD-423] Implement get selected tip RPC command (#469) * [NOD-423] Rename BestBlock to SelectedTip * [NOD-423] Implement GetSelectedTip RPC command * [NOD-423] Add help to getSelectedTip command * [NOD-423] Fix getSelectedTip test * [NOD-423] Fix tests so they would compile. These tests will need to be rewriten at some point. * [NOD-423] Make integration test compile. Test need to be revisited * [NOD-423] Rename variables * [NOD-423] Change comment s about best block to selected tip. * [NOD-423] Update comment * [NOD-423] Change height to bluescore --- btcjson/btcdextcmds.go | 20 ++++-- btcjson/btcdextcmds_test.go | 13 ++-- btcjson/dagsvrcmds.go | 14 ++-- btcjson/dagsvrcmds_test.go | 10 +-- btcjson/dagsvrresults.go | 8 +-- btcjson/jsonrpcerr.go | 2 +- integration/csv_test.go | 2 +- integration/rpcserver_test.go | 12 ++-- integration/rpctest/rpc_harness.go | 19 +++-- integration/rpctest/utils.go | 15 ++-- rpcclient/dag.go | 24 +++---- rpcclient/extensions.go | 83 +++++++++++++++------- server/rpc/handle_get_best_block.go | 15 ---- server/rpc/handle_get_best_block_hash.go | 6 -- server/rpc/handle_get_block.go | 4 +- server/rpc/handle_get_selected_tip.go | 46 ++++++++++++ server/rpc/handle_get_selected_tip_hash.go | 6 ++ server/rpc/handle_get_tx_out.go | 8 +-- server/rpc/rpcserver.go | 8 +-- server/rpc/rpcserverhelp.go | 29 ++++---- 20 files changed, 217 insertions(+), 127 deletions(-) delete mode 100644 server/rpc/handle_get_best_block.go delete mode 100644 server/rpc/handle_get_best_block_hash.go create mode 100644 server/rpc/handle_get_selected_tip.go create mode 100644 server/rpc/handle_get_selected_tip_hash.go diff --git a/btcjson/btcdextcmds.go b/btcjson/btcdextcmds.go index b013e7bb9..da8bdf30a 100644 --- a/btcjson/btcdextcmds.go +++ b/btcjson/btcdextcmds.go @@ -72,13 +72,19 @@ func NewGenerateCmd(numBlocks uint32) *GenerateCmd { } } -// GetBestBlockCmd defines the getBestBlock JSON-RPC command. -type GetBestBlockCmd struct{} +// GetSelectedTipCmd defines the getSelectedTip JSON-RPC command. +type GetSelectedTipCmd struct { + Verbose *bool `jsonrpcdefault:"true"` + VerboseTx *bool `jsonrpcdefault:"false"` +} -// NewGetBestBlockCmd returns a new instance which can be used to issue a -// getBestBlock JSON-RPC command. -func NewGetBestBlockCmd() *GetBestBlockCmd { - return &GetBestBlockCmd{} +// NewGetSelectedTipCmd returns a new instance which can be used to issue a +// getSelectedTip JSON-RPC command. +func NewGetSelectedTipCmd(verbose, verboseTx *bool) *GetSelectedTipCmd { + return &GetSelectedTipCmd{ + Verbose: verbose, + VerboseTx: verboseTx, + } } // GetCurrentNetCmd defines the getCurrentNet JSON-RPC command. @@ -144,7 +150,7 @@ func init() { MustRegisterCmd("debugLevel", (*DebugLevelCmd)(nil), flags) MustRegisterCmd("node", (*NodeCmd)(nil), flags) MustRegisterCmd("generate", (*GenerateCmd)(nil), flags) - MustRegisterCmd("getBestBlock", (*GetBestBlockCmd)(nil), flags) + MustRegisterCmd("getSelectedTip", (*GetSelectedTipCmd)(nil), flags) MustRegisterCmd("getCurrentNet", (*GetCurrentNetCmd)(nil), flags) MustRegisterCmd("getHeaders", (*GetHeadersCmd)(nil), flags) MustRegisterCmd("getTopHeaders", (*GetTopHeadersCmd)(nil), flags) diff --git a/btcjson/btcdextcmds_test.go b/btcjson/btcdextcmds_test.go index 62c04d1d2..896347581 100644 --- a/btcjson/btcdextcmds_test.go +++ b/btcjson/btcdextcmds_test.go @@ -115,15 +115,18 @@ func TestBtcdExtCmds(t *testing.T) { }, }, { - name: "getBestBlock", + name: "getSelectedTip", newCmd: func() (interface{}, error) { - return btcjson.NewCmd("getBestBlock") + return btcjson.NewCmd("getSelectedTip") }, staticCmd: func() interface{} { - return btcjson.NewGetBestBlockCmd() + return btcjson.NewGetSelectedTipCmd(nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getSelectedTip","params":[],"id":1}`, + unmarshalled: &btcjson.GetSelectedTipCmd{ + Verbose: btcjson.Bool(true), + VerboseTx: btcjson.Bool(false), }, - marshalled: `{"jsonrpc":"1.0","method":"getBestBlock","params":[],"id":1}`, - unmarshalled: &btcjson.GetBestBlockCmd{}, }, { name: "getCurrentNet", diff --git a/btcjson/dagsvrcmds.go b/btcjson/dagsvrcmds.go index c1e4c00bf..90ea81d38 100644 --- a/btcjson/dagsvrcmds.go +++ b/btcjson/dagsvrcmds.go @@ -124,13 +124,13 @@ func NewGetAllManualNodesInfoCmd(details *bool) *GetAllManualNodesInfoCmd { } } -// GetBestBlockHashCmd defines the getBestBlockHash JSON-RPC command. -type GetBestBlockHashCmd struct{} +// GetSelectedTipHashCmd defines the getSelectedTipHash JSON-RPC command. +type GetSelectedTipHashCmd struct{} -// NewGetBestBlockHashCmd returns a new instance which can be used to issue a -// getBestBlockHash JSON-RPC command. -func NewGetBestBlockHashCmd() *GetBestBlockHashCmd { - return &GetBestBlockHashCmd{} +// NewGetSelectedTipHashCmd returns a new instance which can be used to issue a +// getSelectedTipHash JSON-RPC command. +func NewGetSelectedTipHashCmd() *GetSelectedTipHashCmd { + return &GetSelectedTipHashCmd{} } // GetBlockCmd defines the getBlock JSON-RPC command. @@ -740,7 +740,7 @@ func init() { MustRegisterCmd("decodeRawTransaction", (*DecodeRawTransactionCmd)(nil), flags) MustRegisterCmd("decodeScript", (*DecodeScriptCmd)(nil), flags) MustRegisterCmd("getAllManualNodesInfo", (*GetAllManualNodesInfoCmd)(nil), flags) - MustRegisterCmd("getBestBlockHash", (*GetBestBlockHashCmd)(nil), flags) + MustRegisterCmd("getSelectedTipHash", (*GetSelectedTipHashCmd)(nil), flags) MustRegisterCmd("getBlock", (*GetBlockCmd)(nil), flags) MustRegisterCmd("getBlocks", (*GetBlocksCmd)(nil), flags) MustRegisterCmd("getBlockDagInfo", (*GetBlockDAGInfoCmd)(nil), flags) diff --git a/btcjson/dagsvrcmds_test.go b/btcjson/dagsvrcmds_test.go index 7b576b136..206644070 100644 --- a/btcjson/dagsvrcmds_test.go +++ b/btcjson/dagsvrcmds_test.go @@ -115,15 +115,15 @@ func TestDAGSvrCmds(t *testing.T) { unmarshalled: &btcjson.GetAllManualNodesInfoCmd{Details: btcjson.Bool(true)}, }, { - name: "getBestBlockHash", + name: "getSelectedTipHash", newCmd: func() (interface{}, error) { - return btcjson.NewCmd("getBestBlockHash") + return btcjson.NewCmd("getSelectedTipHash") }, staticCmd: func() interface{} { - return btcjson.NewGetBestBlockHashCmd() + return btcjson.NewGetSelectedTipHashCmd() }, - marshalled: `{"jsonrpc":"1.0","method":"getBestBlockHash","params":[],"id":1}`, - unmarshalled: &btcjson.GetBestBlockHashCmd{}, + marshalled: `{"jsonrpc":"1.0","method":"getSelectedTipHash","params":[],"id":1}`, + unmarshalled: &btcjson.GetSelectedTipHashCmd{}, }, { name: "getBlock", diff --git a/btcjson/dagsvrresults.go b/btcjson/dagsvrresults.go index 9dabdafc3..f3456cc22 100644 --- a/btcjson/dagsvrresults.go +++ b/btcjson/dagsvrresults.go @@ -283,7 +283,7 @@ type GetSubnetworkResult struct { // GetTxOutResult models the data from the gettxout command. type GetTxOutResult struct { - BestBlock string `json:"bestBlock"` + SelectedTip string `json:"selectedTip"` Confirmations *uint64 `json:"confirmations,omitempty"` IsInMempool bool `json:"isInMempool"` Value float64 `json:"value"` @@ -485,12 +485,6 @@ type ValidateAddressResult struct { Address string `json:"address,omitempty"` } -// GetBestBlockResult models the data from the getbestblock command. -type GetBestBlockResult struct { - Hash string `json:"hash"` - Height uint64 `json:"height"` -} - // ChainBlock models a block that is part of the selected parent chain. type ChainBlock struct { Hash string `json:"hash"` diff --git a/btcjson/jsonrpcerr.go b/btcjson/jsonrpcerr.go index 20533dc33..6e83c4b1a 100644 --- a/btcjson/jsonrpcerr.go +++ b/btcjson/jsonrpcerr.go @@ -54,7 +54,7 @@ const ( const ( ErrRPCBlockNotFound RPCErrorCode = -5 ErrRPCBlockCount RPCErrorCode = -5 - ErrRPCBestBlockHash RPCErrorCode = -5 + ErrRPCSelectedTipHash RPCErrorCode = -5 ErrRPCDifficulty RPCErrorCode = -5 ErrRPCOutOfRange RPCErrorCode = -1 ErrRPCNoTxInfo RPCErrorCode = -5 diff --git a/integration/csv_test.go b/integration/csv_test.go index 9ede59d21..19d44ce55 100644 --- a/integration/csv_test.go +++ b/integration/csv_test.go @@ -435,7 +435,7 @@ func TestBIP0068AndCsv(t *testing.T) { // Now mine 10 additional blocks giving the inputs generated above a // age of 11. Space out each block 10 minutes after the previous block. - parentBlockHash, err := r.Node.GetBestBlockHash() + parentBlockHash, err := r.Node.GetSelectedTipHash() if err != nil { t.Fatalf("unable to get prior block hash: %v", err) } diff --git a/integration/rpcserver_test.go b/integration/rpcserver_test.go index 816716282..013d3a1e9 100644 --- a/integration/rpcserver_test.go +++ b/integration/rpcserver_test.go @@ -18,10 +18,10 @@ import ( "github.com/daglabs/btcd/integration/rpctest" ) -func testGetBestBlock(r *rpctest.Harness, t *testing.T) { - _, prevbestHeight, err := r.Node.GetBestBlock() +func testGetSelectedTip(r *rpctest.Harness, t *testing.T) { + _, prevbestHeight, err := r.Node.GetSelectedTip() if err != nil { - t.Fatalf("Call to `getbestblock` failed: %v", err) + t.Fatalf("Call to `GetSelectedTip` failed: %v", err) } // Create a new block connecting to the current tip. @@ -30,9 +30,9 @@ func testGetBestBlock(r *rpctest.Harness, t *testing.T) { t.Fatalf("Unable to generate block: %v", err) } - bestHash, bestHeight, err := r.Node.GetBestBlock() + bestHash, bestHeight, err := r.Node.GetSelectedTip() if err != nil { - t.Fatalf("Call to `getbestblock` failed: %v", err) + t.Fatalf("Call to `GetSelectedTip` failed: %v", err) } // Hash should be the same as the newly submitted block. @@ -95,7 +95,7 @@ func testGetBlockHash(r *rpctest.Harness, t *testing.T) { } var rpcTestCases = []rpctest.HarnessTestCase{ - testGetBestBlock, + testGetSelectedTip, testGetBlockCount, testGetBlockHash, } diff --git a/integration/rpctest/rpc_harness.go b/integration/rpctest/rpc_harness.go index 7e9cb67df..d221df278 100644 --- a/integration/rpctest/rpc_harness.go +++ b/integration/rpctest/rpc_harness.go @@ -245,14 +245,15 @@ func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error { // Block until the wallet has fully synced up to the tip of the main // chain. - _, height, err := h.Node.GetBestBlock() + selectedTip, err := h.Node.GetSelectedTip() if err != nil { return err } + blueScore := selectedTip.BlueScore ticker := time.NewTicker(time.Millisecond * 100) for range ticker.C { walletHeight := h.wallet.SyncedHeight() - if walletHeight == height { + if walletHeight == blueScore { break } } @@ -429,16 +430,24 @@ func (h *Harness) GenerateAndSubmitBlockWithCustomCoinbaseOutputs( blockVersion = BlockVersion } - parentBlockHash, parentBlockHeight, err := h.Node.GetBestBlock() + selectedTip, err := h.Node.GetSelectedTip() if err != nil { return nil, err } - mBlock, err := h.Node.GetBlock(parentBlockHash, nil) + + selectedTipHash, err := daghash.NewHashFromStr(selectedTip.Hash) if err != nil { return nil, err } + + selectedTipBlueScore := selectedTip.BlueScore + mBlock, err := h.Node.GetBlock(selectedTipHash, nil) + if err != nil { + return nil, err + } + parentBlock := util.NewBlock(mBlock) - parentBlock.SetChainHeight(parentBlockHeight) + parentBlock.SetChainHeight(selectedTipBlueScore) // Create a new block including the specified transactions newBlock, err := CreateBlock(parentBlock, txns, blockVersion, diff --git a/integration/rpctest/utils.go b/integration/rpctest/utils.go index d46c94a87..66a7ab685 100644 --- a/integration/rpctest/utils.go +++ b/integration/rpctest/utils.go @@ -81,19 +81,26 @@ func syncBlocks(nodes []*Harness) error { retry: for !blocksMatch { var parentHash *daghash.Hash - var prevHeight uint64 + var prevBlueScore uint64 for _, node := range nodes { - blockHash, blockHeight, err := node.Node.GetBestBlock() + selectedTip, err := node.Node.GetSelectedTip() if err != nil { return err } + + blockHash, err := daghash.NewHashFromStr(selectedTip.Hash) + if err != nil { + return err + } + blueScore := selectedTip.BlueScore + if parentHash != nil && (*blockHash != *parentHash || - blockHeight != prevHeight) { + blueScore != prevBlueScore) { time.Sleep(time.Millisecond * 100) continue retry } - parentHash, prevHeight = blockHash, blockHeight + parentHash, prevBlueScore = blockHash, blueScore } blocksMatch = true diff --git a/rpcclient/dag.go b/rpcclient/dag.go index 40f774deb..4b30791cd 100644 --- a/rpcclient/dag.go +++ b/rpcclient/dag.go @@ -15,13 +15,13 @@ import ( "github.com/daglabs/btcd/wire" ) -// FutureGetBestBlockHashResult is a future promise to deliver the result of a -// GetBestBlockAsync RPC invocation (or an applicable error). -type FutureGetBestBlockHashResult chan *response +// FutureGetSelectedTipHashResult is a future promise to deliver the result of a +// GetSelectedTipAsync RPC invocation (or an applicable error). +type FutureGetSelectedTipHashResult chan *response // Receive waits for the response promised by the future and returns the hash of // the best block in the longest block dag. -func (r FutureGetBestBlockHashResult) Receive() (*daghash.Hash, error) { +func (r FutureGetSelectedTipHashResult) Receive() (*daghash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -36,20 +36,20 @@ func (r FutureGetBestBlockHashResult) Receive() (*daghash.Hash, error) { return daghash.NewHashFromStr(txHashStr) } -// GetBestBlockHashAsync returns an instance of a type that can be used to get +// GetSelectedTipHashAsync 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 GetBestBlockHash for the blocking version and more details. -func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult { - cmd := btcjson.NewGetBestBlockHashCmd() +// See GetSelectedTipHash for the blocking version and more details. +func (c *Client) GetSelectedTipHashAsync() FutureGetSelectedTipHashResult { + cmd := btcjson.NewGetSelectedTipHashCmd() return c.sendCmd(cmd) } -// GetBestBlockHash returns the hash of the best block in the longest block -// dag. -func (c *Client) GetBestBlockHash() (*daghash.Hash, error) { - return c.GetBestBlockHashAsync().Receive() +// GetSelectedTipHash returns the hash of the selected tip of the +// Block DAG. +func (c *Client) GetSelectedTipHash() (*daghash.Hash, error) { + return c.GetSelectedTipHashAsync().Receive() } // FutureGetBlockResult is a future promise to deliver the result of a diff --git a/rpcclient/extensions.go b/rpcclient/extensions.go index a30aae89d..8d571639e 100644 --- a/rpcclient/extensions.go +++ b/rpcclient/extensions.go @@ -62,52 +62,87 @@ func (c *Client) DebugLevel(levelSpec string) (string, error) { return c.DebugLevelAsync(levelSpec).Receive() } -// FutureGetBestBlockResult is a future promise to deliver the result of a -// GetBestBlockAsync RPC invocation (or an applicable error). -type FutureGetBestBlockResult chan *response +// FutureGetSelectedTipResult is a future promise to deliver the result of a +// GetSelectedTipAsync RPC invocation (or an applicable error). +type FutureGetSelectedTipResult chan *response -// Receive waits for the response promised by the future and returns the hash -// and height of the block in the longest (best) chain. -func (r FutureGetBestBlockResult) Receive() (*daghash.Hash, uint64, error) { +// Receive waits for the response promised by the future and returns the +// selected tip block. +func (r FutureGetSelectedTipResult) Receive() (*wire.MsgBlock, error) { res, err := receiveFuture(r) if err != nil { - return nil, 0, err + return nil, err } - // Unmarshal result as a getbestblock result object. - var bestBlock btcjson.GetBestBlockResult - err = json.Unmarshal(res, &bestBlock) + // Unmarshal result as a string. + var blockHex string + err = json.Unmarshal(res, &blockHex) if err != nil { - return nil, 0, err + return nil, err } - // Convert to hash from string. - hash, err := daghash.NewHashFromStr(bestBlock.Hash) + // Decode the serialized block hex to raw bytes. + serializedBlock, err := hex.DecodeString(blockHex) if err != nil { - return nil, 0, err + return nil, err } - return hash, bestBlock.Height, nil + // Deserialize the block and return it. + var msgBlock wire.MsgBlock + err = msgBlock.Deserialize(bytes.NewReader(serializedBlock)) + if err != nil { + return nil, err + } + return &msgBlock, nil } -// GetBestBlockAsync returns an instance of a type that can be used to get the +// GetSelectedTipAsync 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 GetBestBlock for the blocking version and more details. +// See GetSelectedTip for the blocking version and more details. // // NOTE: This is a btcd extension. -func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { - cmd := btcjson.NewGetBestBlockCmd() +func (c *Client) GetSelectedTipAsync() FutureGetSelectedTipResult { + cmd := btcjson.NewGetSelectedTipCmd(btcjson.Bool(false), btcjson.Bool(false)) return c.sendCmd(cmd) } -// GetBestBlock returns the hash and height of the block in the longest (best) -// chain. -// +// GetSelectedTip returns the block of the selected DAG tip // NOTE: This is a btcd extension. -func (c *Client) GetBestBlock() (*daghash.Hash, uint64, error) { - return c.GetBestBlockAsync().Receive() +func (c *Client) GetSelectedTip() (*btcjson.GetBlockVerboseResult, error) { + return c.GetSelectedTipVerboseAsync().Receive() +} + +// FutureGetSelectedTipVerboseResult is a future promise to deliver the result of a +// GetSelectedTipVerboseAsync RPC invocation (or an applicable error). +type FutureGetSelectedTipVerboseResult chan *response + +// Receive waits for the response promised by the future and returns the data +// structure from the server with information about the requested block. +func (r FutureGetSelectedTipVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal the raw result into a BlockResult. + var blockResult btcjson.GetBlockVerboseResult + err = json.Unmarshal(res, &blockResult) + if err != nil { + return nil, err + } + return &blockResult, nil +} + +// GetSelectedTipVerboseAsync 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 GeSelectedTipBlockVerbose for the blocking version and more details. +func (c *Client) GetSelectedTipVerboseAsync() FutureGetSelectedTipVerboseResult { + cmd := btcjson.NewGetSelectedTipCmd(btcjson.Bool(true), btcjson.Bool(false)) + return c.sendCmd(cmd) } // FutureGetCurrentNetResult is a future promise to deliver the result of a diff --git a/server/rpc/handle_get_best_block.go b/server/rpc/handle_get_best_block.go deleted file mode 100644 index 008f1af49..000000000 --- a/server/rpc/handle_get_best_block.go +++ /dev/null @@ -1,15 +0,0 @@ -package rpc - -import "github.com/daglabs/btcd/btcjson" - -// handleGetBestBlock implements the getBestBlock command. -func handleGetBestBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - // All other "get block" commands give either the height, the - // hash, or both but require the block SHA. This gets both for - // the best block. - result := &btcjson.GetBestBlockResult{ - Hash: s.cfg.DAG.SelectedTipHash().String(), - Height: s.cfg.DAG.ChainHeight(), //TODO: (Ori) This is probably wrong. Done only for compilation - } - return result, nil -} diff --git a/server/rpc/handle_get_best_block_hash.go b/server/rpc/handle_get_best_block_hash.go deleted file mode 100644 index 053f02065..000000000 --- a/server/rpc/handle_get_best_block_hash.go +++ /dev/null @@ -1,6 +0,0 @@ -package rpc - -// handleGetBestBlockHash implements the getBestBlockHash command. -func handleGetBestBlockHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return s.cfg.DAG.SelectedTipHash().String(), nil -} diff --git a/server/rpc/handle_get_block.go b/server/rpc/handle_get_block.go index 98c09531f..ae6644735 100644 --- a/server/rpc/handle_get_block.go +++ b/server/rpc/handle_get_block.go @@ -80,8 +80,8 @@ func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte } } - // When the verbose flag isn't set, simply return the serialized block - // as a hex-encoded string. + // When the verbose flag is set to false, simply return the serialized block + // as a hex-encoded string (verbose flag is on by default). if c.Verbose != nil && !*c.Verbose { return hex.EncodeToString(blkBytes), nil } diff --git a/server/rpc/handle_get_selected_tip.go b/server/rpc/handle_get_selected_tip.go new file mode 100644 index 000000000..1ff825b6a --- /dev/null +++ b/server/rpc/handle_get_selected_tip.go @@ -0,0 +1,46 @@ +package rpc + +import ( + "encoding/hex" + "github.com/daglabs/btcd/btcjson" + "github.com/daglabs/btcd/database" + "github.com/daglabs/btcd/util" +) + +// handleGetSelectedTip implements the getSelectedTip command. +func handleGetSelectedTip(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + getSelectedTipCmd := cmd.(*btcjson.GetSelectedTipCmd) + selectedTipHash := s.cfg.DAG.SelectedTipHash() + + var blockBytes []byte + err := s.cfg.DB.View(func(dbTx database.Tx) error { + var err error + blockBytes, err = dbTx.FetchBlock(selectedTipHash) + return err + }) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCBlockNotFound, + Message: "Block not found", + } + } + + // When the verbose flag is set to false, simply return the serialized block + // as a hex-encoded string (verbose flag is on by default). + if getSelectedTipCmd.Verbose != nil && !*getSelectedTipCmd.Verbose { + return hex.EncodeToString(blockBytes), nil + } + + // Deserialize the block. + blk, err := util.NewBlockFromBytes(blockBytes) + if err != nil { + context := "Failed to deserialize block" + return nil, internalRPCError(err.Error(), context) + } + + blockVerboseResult, err := buildGetBlockVerboseResult(s, blk, getSelectedTipCmd.VerboseTx == nil || !*getSelectedTipCmd.VerboseTx) + if err != nil { + return nil, err + } + return blockVerboseResult, nil +} diff --git a/server/rpc/handle_get_selected_tip_hash.go b/server/rpc/handle_get_selected_tip_hash.go new file mode 100644 index 000000000..211cef935 --- /dev/null +++ b/server/rpc/handle_get_selected_tip_hash.go @@ -0,0 +1,6 @@ +package rpc + +// handleGetSelectedTipHash implements the getSelectedTipHash command. +func handleGetSelectedTipHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return s.cfg.DAG.SelectedTipHash().String(), nil +} diff --git a/server/rpc/handle_get_tx_out.go b/server/rpc/handle_get_tx_out.go index 9a76fd7ef..ca2c854e6 100644 --- a/server/rpc/handle_get_tx_out.go +++ b/server/rpc/handle_get_tx_out.go @@ -22,7 +22,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte // If requested and the tx is available in the mempool try to fetch it // from there, otherwise attempt to fetch from the block database. - var bestBlockHash string + var selectedTipHash string var confirmations *uint64 var value uint64 var scriptPubKey []byte @@ -56,7 +56,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte return nil, internalRPCError(errStr, "") } - bestBlockHash = s.cfg.DAG.SelectedTipHash().String() + selectedTipHash = s.cfg.DAG.SelectedTipHash().String() value = txOut.Value scriptPubKey = txOut.ScriptPubKey isCoinbase = mtx.IsCoinBase() @@ -86,7 +86,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte confirmations = &txConfirmations } - bestBlockHash = s.cfg.DAG.SelectedTipHash().String() + selectedTipHash = s.cfg.DAG.SelectedTipHash().String() value = entry.Amount() scriptPubKey = entry.ScriptPubKey() isCoinbase = entry.IsCoinbase() @@ -108,7 +108,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte } txOutReply := &btcjson.GetTxOutResult{ - BestBlock: bestBlockHash, + SelectedTip: selectedTipHash, Confirmations: confirmations, IsInMempool: isInMempool, Value: util.Amount(value).ToBTC(), diff --git a/server/rpc/rpcserver.go b/server/rpc/rpcserver.go index 62536da89..e1077865d 100644 --- a/server/rpc/rpcserver.go +++ b/server/rpc/rpcserver.go @@ -66,8 +66,8 @@ var rpcHandlersBeforeInit = map[string]commandHandler{ "decodeScript": handleDecodeScript, "generate": handleGenerate, "getAllManualNodesInfo": handleGetAllManualNodesInfo, - "getBestBlock": handleGetBestBlock, - "getBestBlockHash": handleGetBestBlockHash, + "getSelectedTip": handleGetSelectedTip, + "getSelectedTipHash": handleGetSelectedTipHash, "getBlock": handleGetBlock, "getBlocks": handleGetBlocks, "getBlockDagInfo": handleGetBlockDAGInfo, @@ -140,8 +140,8 @@ var rpcLimited = map[string]struct{}{ "createRawTransaction": {}, "decodeRawTransaction": {}, "decodeScript": {}, - "getBestBlock": {}, - "getBestBlockHash": {}, + "getSelectedTip": {}, + "getSelectedTipHash": {}, "getBlock": {}, "getBlocks": {}, "getBlockCount": {}, diff --git a/server/rpc/rpcserverhelp.go b/server/rpc/rpcserverhelp.go index a4ab5e404..5db0c9c72 100644 --- a/server/rpc/rpcserverhelp.go +++ b/server/rpc/rpcserverhelp.go @@ -149,17 +149,22 @@ var helpDescsEnUS = map[string]string{ "getManualNodeInfo--condition1": "details=true", "getManualNodeInfo--result0": "List of added peers", - // GetBestBlockResult help. - "getBestBlockResult-hash": "Hex-encoded bytes of the best block hash", - "getBestBlockResult-height": "Height of the best block", + // GetSelectedTipResult help. + "getSelectedTipResult-hash": "Hex-encoded bytes of the best block hash", + "getSelectedTipResult-height": "Height of the best block", - // GetBestBlockCmd help. - "getBestBlock--synopsis": "Get block height and hash of best block in the main chain.", - "getBestBlock--result0": "Get block height and hash of best block in the main chain.", + // GetSelectedTipCmd help. + "getSelectedTip--synopsis": "Returns information about the selected tip of the blockDAG.", + "getSelectedTip-verbose": "Specifies the block is returned as a JSON object instead of hex-encoded string", + "getSelectedTip-verboseTx": "Specifies that each transaction is returned as a JSON object and only applies if the verbose flag is true (btcd extension)", + "getSelectedTip--condition0": "verbose=false", + "getSelectedTip--condition1": "verbose=true", + "getSelectedTip-acceptedTx": "Specifies if the transaction got accepted", + "getSelectedTip--result0": "Hex-encoded bytes of the serialized block", - // GetBestBlockHashCmd help. - "getBestBlockHash--synopsis": "Returns the hash of the of the best (most recent) block in the longest block chain.", - "getBestBlockHash--result0": "The hex-encoded block hash", + // GetSelectedTipHashCmd help. + "getSelectedTipHash--synopsis": "Returns the hash of the of the selected tip of the blockDAG.", + "getSelectedTipHash--result0": "The hex-encoded block hash", // GetBlockCmd help. "getBlock--synopsis": "Returns information about a block given its hash.", @@ -519,7 +524,7 @@ var helpDescsEnUS = map[string]string{ "getSubnetworkResult-gasLimit": "The gas limit of the subnetwork", // GetTxOutResult help. - "getTxOutResult-bestBlock": "The block hash that contains the transaction output", + "getTxOutResult-selectedTip": "The block hash that contains the transaction output", "getTxOutResult-confirmations": "The number of confirmations (Will be 'null' if txindex is not disabled)", "getTxOutResult-isInMempool": "Whether the transaction is in the mempool", "getTxOutResult-value": "The transaction amount in BTC", @@ -675,8 +680,8 @@ var rpcResultTypes = map[string][]interface{}{ "decodeScript": {(*btcjson.DecodeScriptResult)(nil)}, "generate": {(*[]string)(nil)}, "getAllManualNodesInfo": {(*[]string)(nil), (*[]btcjson.GetManualNodeInfoResult)(nil)}, - "getBestBlock": {(*btcjson.GetBestBlockResult)(nil)}, - "getBestBlockHash": {(*string)(nil)}, + "getSelectedTip": {(*btcjson.GetBlockVerboseResult)(nil)}, + "getSelectedTipHash": {(*string)(nil)}, "getBlock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)}, "getBlocks": {(*btcjson.GetBlocksResult)(nil)}, "getBlockCount": {(*int64)(nil)},