diff options
author | Ava Chow <github@achow101.com> | 2024-03-12 13:04:29 -0400 |
---|---|---|
committer | Ava Chow <github@achow101.com> | 2024-03-12 13:17:57 -0400 |
commit | bde3db40f6d5bd5ad499cd9b9c6e8352e713de55 (patch) | |
tree | 2a09838f727ed81eb6792dee3af28a897e7c2427 /src | |
parent | bef99176e638688360020152579b92008c857688 (diff) | |
parent | e710cefd5701cd33d1e55034b3e37cea78582733 (diff) |
Merge bitcoin/bitcoin#26415: rpc,rest,zmq: faster getblock, NotifyBlock and rest_block by reading raw block
e710cefd5701cd33d1e55034b3e37cea78582733 rest: read raw block in rest_block and deserialize for json (Andrew Toth)
95ce0783a6dab325038a64d6c529c9e7816e3072 rpc: read raw block in getblock and deserialize for verbosity > 0 (Andrew Toth)
0865ab8712429761bc69f09d93760f8c6081c99c test: check more details on zmq raw block response (Andrew Toth)
38265cc14e7d646bf27882329d374d42167eb49f zmq: read raw block with ReadRawBlockFromDisk (Andrew Toth)
da338aada7943c392013c36c542af621fbc6edd1 blockstorage: check nPos in ReadRawBlockFromDisk before seeking back (Andrew Toth)
Pull request description:
For the `getblock` endpoint with `verbosity=0`, the `rest_block` REST endpoint for `bin` and `hex`, and zmq `NotifyBlock` we don't have to deserialize the block since we're just sending the raw data. This PR uses `ReadRawBlockFromDisk` instead of `ReadBlockFromDisk` to serve these requests, and only deserializes for `verbosity > 0` and `json` REST requests. See benchmarks in https://github.com/bitcoin/bitcoin/pull/26684.
Benchmarked using ApacheBench. Requesting block 750,000 in binary 10k times on a single core (set `-rest=1` in config):
`ab -n 10000 -c 1 "http://127.0.0.1:8332/rest/block/0000000000000000000592a974b1b9f087cb77628bb4a097d5c2c11b3476a58e.bin"`
On master, mean time 15ms.
On this branch, mean time 1ms.
For RPC
```
echo '{"jsonrpc": "1.0", "id": "curltest", "method": "getblock", "params": ["0000000000000000000592a974b1b9f087cb77628bb4a097d5c2c11b3476a58e", 0]}' > /tmp/data.json
ab -p /tmp/data.json -n 1000 -c 1 -A user:password "http://127.0.0.1:8332/"
```
On master, mean time 32ms
On this branch, mean time 13ms
ACKs for top commit:
achow101:
re-ACK e710cefd5701cd33d1e55034b3e37cea78582733
Tree-SHA512: 4cea13c7b563b2139d041b1fdcfdb793c8cc688654ae08db07e7ee6b875c5e582b8185db3ae603abbfb06d2164724f29205774620b48c493726b991999af289e
Diffstat (limited to 'src')
-rw-r--r-- | src/init.cpp | 4 | ||||
-rw-r--r-- | src/node/blockstorage.cpp | 6 | ||||
-rw-r--r-- | src/rest.cpp | 20 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 34 | ||||
-rw-r--r-- | src/zmq/zmqnotificationinterface.cpp | 2 | ||||
-rw-r--r-- | src/zmq/zmqnotificationinterface.h | 3 | ||||
-rw-r--r-- | src/zmq/zmqpublishnotifier.cpp | 7 | ||||
-rw-r--r-- | src/zmq/zmqpublishnotifier.h | 6 |
8 files changed, 56 insertions, 26 deletions
diff --git a/src/init.cpp b/src/init.cpp index 36557387f1..0ecfba18c1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1454,9 +1454,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) #if ENABLE_ZMQ g_zmq_notification_interface = CZMQNotificationInterface::Create( - [&chainman = node.chainman](CBlock& block, const CBlockIndex& index) { + [&chainman = node.chainman](std::vector<uint8_t>& block, const CBlockIndex& index) { assert(chainman); - return chainman->m_blockman.ReadBlockFromDisk(block, index); + return chainman->m_blockman.ReadRawBlockFromDisk(block, WITH_LOCK(cs_main, return index.GetBlockPos())); }); if (g_zmq_notification_interface) { diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 211d557826..d0ddfeb0f1 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -1088,6 +1088,12 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) co bool BlockManager::ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos) const { FlatFilePos hpos = pos; + // If nPos is less than 8 the pos is null and we don't have the block data + // Return early to prevent undefined behavior of unsigned int underflow + if (hpos.nPos < 8) { + LogError("%s: OpenBlockFile failed for %s\n", __func__, pos.ToString()); + return false; + } hpos.nPos -= 8; // Seek back 8 bytes for meta header AutoFile filein{OpenBlockFile(hpos, true)}; if (filein.IsNull()) { diff --git a/src/rest.cpp b/src/rest.cpp index 91184745c8..89c033b8a3 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -13,6 +13,7 @@ #include <chain.h> #include <chainparams.h> #include <core_io.h> +#include <flatfile.h> #include <httpserver.h> #include <index/blockfilterindex.h> #include <index/txindex.h> @@ -34,7 +35,7 @@ #include <validation.h> #include <any> -#include <string> +#include <vector> #include <univalue.h> @@ -295,7 +296,7 @@ static bool rest_block(const std::any& context, if (!ParseHashStr(hashStr, hash)) return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); - CBlock block; + FlatFilePos pos{}; const CBlockIndex* pblockindex = nullptr; const CBlockIndex* tip = nullptr; ChainstateManager* maybe_chainman = GetChainman(context, req); @@ -311,32 +312,33 @@ static bool rest_block(const std::any& context, if (chainman.m_blockman.IsBlockPruned(*pblockindex)) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); } + pos = pblockindex->GetBlockPos(); } - if (!chainman.m_blockman.ReadBlockFromDisk(block, *pblockindex)) { + std::vector<uint8_t> block_data{}; + if (!chainman.m_blockman.ReadRawBlockFromDisk(block_data, pos)) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } switch (rf) { case RESTResponseFormat::BINARY: { - DataStream ssBlock; - ssBlock << TX_WITH_WITNESS(block); - std::string binaryBlock = ssBlock.str(); + const std::string binaryBlock{block_data.begin(), block_data.end()}; req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryBlock); return true; } case RESTResponseFormat::HEX: { - DataStream ssBlock; - ssBlock << TX_WITH_WITNESS(block); - std::string strHex = HexStr(ssBlock) + "\n"; + const std::string strHex{HexStr(block_data) + "\n"}; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); return true; } case RESTResponseFormat::JSON: { + CBlock block{}; + DataStream block_stream{block_data}; + block_stream >> TX_WITH_WITNESS(block); UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity); std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index dfdddeacea..a1135c27d4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -17,6 +17,7 @@ #include <core_io.h> #include <deploymentinfo.h> #include <deploymentstatus.h> +#include <flatfile.h> #include <hash.h> #include <index/blockfilterindex.h> #include <index/coinstatsindex.h> @@ -595,6 +596,28 @@ static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex& blockin return block; } +static std::vector<uint8_t> GetRawBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex) +{ + std::vector<uint8_t> data{}; + FlatFilePos pos{}; + { + LOCK(cs_main); + if (blockman.IsBlockPruned(blockindex)) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); + } + pos = blockindex.GetBlockPos(); + } + + if (!blockman.ReadRawBlockFromDisk(data, pos)) { + // Block not found on disk. This could be because we have the block + // header in our index but not yet have the block or did not accept the + // block. Or if the block was pruned right after we released the lock above. + throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); + } + + return data; +} + static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex& blockindex) { CBlockUndo blockUndo; @@ -735,15 +758,16 @@ static RPCHelpMan getblock() } } - const CBlock block{GetBlockChecked(chainman.m_blockman, *pblockindex)}; + const std::vector<uint8_t> block_data{GetRawBlockChecked(chainman.m_blockman, *pblockindex)}; if (verbosity <= 0) { - DataStream ssBlock; - ssBlock << TX_WITH_WITNESS(block); - std::string strHex = HexStr(ssBlock); - return strHex; + return HexStr(block_data); } + DataStream block_stream{block_data}; + CBlock block{}; + block_stream >> TX_WITH_WITNESS(block); + TxVerbosity tx_verbosity; if (verbosity == 1) { tx_verbosity = TxVerbosity::SHOW_TXID; diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index 63c2737706..d10db046f5 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -41,7 +41,7 @@ std::list<const CZMQAbstractNotifier*> CZMQNotificationInterface::GetActiveNotif return result; } -std::unique_ptr<CZMQNotificationInterface> CZMQNotificationInterface::Create(std::function<bool(CBlock&, const CBlockIndex&)> get_block_by_index) +std::unique_ptr<CZMQNotificationInterface> CZMQNotificationInterface::Create(std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> get_block_by_index) { std::map<std::string, CZMQNotifierFactory> factories; factories["pubhashblock"] = CZMQAbstractNotifier::Create<CZMQPublishHashBlockNotifier>; diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index 45d0982bd3..c879fdd0dd 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -12,6 +12,7 @@ #include <functional> #include <list> #include <memory> +#include <vector> class CBlock; class CBlockIndex; @@ -25,7 +26,7 @@ public: std::list<const CZMQAbstractNotifier*> GetActiveNotifiers() const; - static std::unique_ptr<CZMQNotificationInterface> Create(std::function<bool(CBlock&, const CBlockIndex&)> get_block_by_index); + static std::unique_ptr<CZMQNotificationInterface> Create(std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> get_block_by_index); protected: bool Initialize(); diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index 0f20706364..608870c489 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -243,16 +243,13 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex) { LogPrint(BCLog::ZMQ, "Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address); - DataStream ss; - CBlock block; + std::vector<uint8_t> block{}; if (!m_get_block_by_index(block, *pindex)) { zmqError("Can't read block from disk"); return false; } - ss << TX_WITH_WITNESS(block); - - return SendZmqMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size()); + return SendZmqMessage(MSG_RAWBLOCK, block.data(), block.size()); } bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction) diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h index a5cd433761..cc941a899c 100644 --- a/src/zmq/zmqpublishnotifier.h +++ b/src/zmq/zmqpublishnotifier.h @@ -10,8 +10,8 @@ #include <cstddef> #include <cstdint> #include <functional> +#include <vector> -class CBlock; class CBlockIndex; class CTransaction; @@ -49,10 +49,10 @@ public: class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier { private: - const std::function<bool(CBlock&, const CBlockIndex&)> m_get_block_by_index; + const std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> m_get_block_by_index; public: - CZMQPublishRawBlockNotifier(std::function<bool(CBlock&, const CBlockIndex&)> get_block_by_index) + CZMQPublishRawBlockNotifier(std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> get_block_by_index) : m_get_block_by_index{std::move(get_block_by_index)} {} bool NotifyBlock(const CBlockIndex *pindex) override; }; |