Skip to content

Conversation

andrewtoth
Copy link
Contributor

@andrewtoth andrewtoth commented Oct 28, 2022

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 #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

@andrewtoth andrewtoth marked this pull request as draft October 28, 2022 15:58
@andrewtoth andrewtoth force-pushed the read-raw-block branch 2 times, most recently from 4986d7a to 01999de Compare October 28, 2022 18:05
@andrewtoth andrewtoth marked this pull request as ready for review October 28, 2022 18:05
@andrewtoth andrewtoth changed the title rpc,rest: read raw block in getblock verbosity 0 and rest_block bin and hex rpc,rest: faster getblock and rest_block by reading raw block Oct 28, 2022
@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 28, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK achow101
Concept ACK RandyMcMillan, kashifs, pablomartin4btc
Stale ACK furszy, TheCharlatan

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

No conflicts as of last run.

@andrewtoth
Copy link
Contributor Author

andrewtoth commented Oct 30, 2022

For some more context, here is the PR that introduced ReadRawBlockFromDisk #13151.

I see that this was attempted in #21319 as well.

@andrewtoth
Copy link
Contributor Author

@luke-jr I wasn't aware of #21319 until now, but I can't comment there because it's locked. If you want to reopen that I can review and remove the change to RPC in this PR so it will only be for REST.

Comment on lines +600 to +609
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();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we only care about the data existence here, what about doing a more general check:

if (!(blockindex.nStatus & BLOCK_HAVE_DATA)) {
    throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}

(Unless you want to keep the same error message as before)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this check is specifically done to see if the block is pruned, hence the error message (pruned data). This is taken verbatim from the above function, GetBlockChecked, which all it does inside the locked block is check if it is pruned or not. Here we also extract the FlatFilePos. However, if blockindex.nStatus & BLOCK_HAVE_DATA is false then the call to ReadRawBlockFromDisk will fail and throw the message Block not available. The first commit addresses the undefined behavior part of calling ReadRawBlockFromDisk with a null FlatFilePos, which is safe to do now.

@andrewtoth
Copy link
Contributor Author

@furszy @TheCharlatan updated with both your suggestions, thanks!

Copy link
Member

@furszy furszy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code ACK 4fc91b3

Copy link
Contributor

@TheCharlatan TheCharlatan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-ACK 4fc91b3

@achow101
Copy link
Member

achow101 commented Mar 12, 2024

ACK 4fc91b3

@DrahtBot DrahtBot requested a review from achow101 March 12, 2024 16:20
// 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) {
return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 3f086a9 "blockstorage: check nPos in ReadRawBlockFromDisk before seeking back"

Silent merge conflict here:

../../../src/node/blockstorage.cpp: In member function ‘bool node::BlockManager::ReadRawBlockFromDisk(std::vector<unsigned char>&, const FlatFilePos&) const’:
../../../src/node/blockstorage.cpp:1094:16: error: ‘error’ was not declared in this scope; did you mean ‘perror’?
 1094 |         return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
      |                ^~~~~
      |                perror

error() was removed in #29236

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@achow101 resolved, thanks.

ReadRawBlockFromDisk assumes a non-null pos that has an nPos >= 8.
This simple check makes the function safer to call in the future,
so callers don't need to worry about causing UB if the pos is null.
Note that for speed this commit also removes the proof of work and
signet signature checks before returning the block in getblock.
It is assumed if a block is stored it will be valid.
Note that for speed this commit also removes the proof of work and
signet signature checks before returning the block in getblock.
It is assumed if a block is stored it will be valid.
@achow101
Copy link
Member

re-ACK e710cef

Verified rebase diff.

@DrahtBot DrahtBot requested review from TheCharlatan and furszy March 12, 2024 17:01
@achow101 achow101 merged commit bde3db4 into bitcoin:master Mar 12, 2024
@andrewtoth andrewtoth deleted the read-raw-block branch March 12, 2024 17:45
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Jun 13, 2024
ReadRawBlockFromDisk assumes a non-null pos that has an nPos >= 8.
This simple check makes the function safer to call in the future,
so callers don't need to worry about causing UB if the pos is null.

Github-Pull: bitcoin#26415
Rebased-From: da338aa
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Jun 13, 2024
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Jun 13, 2024
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Jun 13, 2024
Note that for speed this commit also removes the proof of work and
signet signature checks before returning the block in getblock.
It is assumed if a block is stored it will be valid.

Github-Pull: bitcoin#26415
Rebased-From: 95ce078
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Jun 13, 2024
Note that for speed this commit also removes the proof of work and
signet signature checks before returning the block in getblock.
It is assumed if a block is stored it will be valid.

Github-Pull: bitcoin#26415
Rebased-From: e710cef
@bitcoin bitcoin locked and limited conversation to collaborators Mar 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants