diff options
-rw-r--r-- | doc/REST-interface.md | 9 | ||||
-rw-r--r-- | doc/release-notes-25412.md | 5 | ||||
-rw-r--r-- | src/rest.cpp | 44 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 2 | ||||
-rwxr-xr-x | test/functional/interface_rest.py | 11 |
5 files changed, 70 insertions, 1 deletions
diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 0035dfcd8e..a704b969df 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -86,6 +86,15 @@ Returns various state info regarding block chain processing. Only supports JSON as output format. Refer to the `getblockchaininfo` RPC help for details. +#### Deployment info +`GET /rest/deploymentinfo.json` +`GET /rest/deploymentinfo/<BLOCKHASH>.json` + +Returns an object containing various state info regarding deployments of +consensus changes at the current chain tip, or at <BLOCKHASH> if provided. +Only supports JSON as output format. +Refer to the `getdeploymentinfo` RPC help for details. + #### Query UTXO set - `GET /rest/getutxos/<TXID>-<N>/<TXID>-<N>/.../<TXID>-<N>.<bin|hex|json>` - `GET /rest/getutxos/checkmempool/<TXID>-<N>/<TXID>-<N>/.../<TXID>-<N>.<bin|hex|json>` diff --git a/doc/release-notes-25412.md b/doc/release-notes-25412.md new file mode 100644 index 0000000000..b11fe73d45 --- /dev/null +++ b/doc/release-notes-25412.md @@ -0,0 +1,5 @@ +New REST endpoint +----------------- + +- A new `/rest/deploymentinfo` endpoint has been added for fetching various + state info regarding deployments of consensus changes. (#25412) diff --git a/src/rest.cpp b/src/rest.cpp index 7f00db2222..a10d8a433f 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -590,6 +590,48 @@ static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std: } } + +RPCHelpMan getdeploymentinfo(); + +static bool rest_deploymentinfo(const std::any& context, HTTPRequest* req, const std::string& str_uri_part) +{ + if (!CheckWarmup(req)) return false; + + std::string hash_str; + const RESTResponseFormat rf = ParseDataFormat(hash_str, str_uri_part); + + switch (rf) { + case RESTResponseFormat::JSON: { + JSONRPCRequest jsonRequest; + jsonRequest.context = context; + jsonRequest.params = UniValue(UniValue::VARR); + + if (!hash_str.empty()) { + uint256 hash; + if (!ParseHashStr(hash_str, hash)) { + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hash_str); + } + + const ChainstateManager* chainman = GetChainman(context, req); + if (!chainman) return false; + if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(ParseHashV(hash_str, "blockhash")))) { + return RESTERR(req, HTTP_BAD_REQUEST, "Block not found"); + } + + jsonRequest.params.pushKV("blockhash", hash_str); + } + + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, getdeploymentinfo().HandleRequest(jsonRequest).write() + "\n"); + return true; + } + default: { + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); + } + } + +} + static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::string& str_uri_part) { if (!CheckWarmup(req)) @@ -939,6 +981,8 @@ static const struct { {"/rest/mempool/", rest_mempool}, {"/rest/headers/", rest_headers}, {"/rest/getutxos", rest_getutxos}, + {"/rest/deploymentinfo/", rest_deploymentinfo}, + {"/rest/deploymentinfo", rest_deploymentinfo}, {"/rest/blockhashbyheight/", rest_blockhash_by_height}, }; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index cddd85bcb5..f8ba822f54 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1307,7 +1307,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& } } // anon namespace -static RPCHelpMan getdeploymentinfo() +RPCHelpMan getdeploymentinfo() { return RPCHelpMan{"getdeploymentinfo", "Returns an object containing various state info regarding deployments of consensus changes.", diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 610a28c56b..24252610be 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -387,6 +387,17 @@ class RESTTest (BitcoinTestFramework): assert_equal(self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 1}), self.test_rest_request(f"/headers/1/{bb_hash}")) assert_equal(self.test_rest_request(f"/blockfilterheaders/basic/{bb_hash}", query_params={"count": 1}), self.test_rest_request(f"/blockfilterheaders/basic/5/{bb_hash}")) + self.log.info("Test the /deploymentinfo URI") + + deployment_info = self.nodes[0].getdeploymentinfo() + assert_equal(deployment_info, self.test_rest_request('/deploymentinfo')) + + non_existing_blockhash = '42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4' + resp = self.test_rest_request(f'/deploymentinfo/{non_existing_blockhash}', ret_type=RetType.OBJ, status=400) + assert_equal(resp.read().decode('utf-8').rstrip(), "Block not found") + + resp = self.test_rest_request(f"/deploymentinfo/{INVALID_PARAM}", ret_type=RetType.OBJ, status=400) + assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid hash: {INVALID_PARAM}") if __name__ == '__main__': RESTTest().main() |