Skip to content

Commit 7f4db9a

Browse files
committed
Merge #13151: net: Serve blocks directly from disk when possible
0bf4318 net: Serve blocks directly from disk when possible (Wladimir J. van der Laan) Pull request description: In `ProcessGetBlockData`, send the block data directly from disk if type MSG_WITNESS_BLOCK is requested. This is a valid shortcut as the on-disk format matches the network format. This is expected to increase performance because a deserialization and subsequent serialization roundtrip is avoided. Tree-SHA512: 9a9500b4c1354eaae1a6f1c6ef2416c1c1985029852589266f3a70e808f6c7482c135e9ab251a527566935378ab7c32dba4ed43ba5451e802d8e72b77d1ba472
2 parents 3c2a41a + 0bf4318 commit 7f4db9a

File tree

3 files changed

+107
-47
lines changed

3 files changed

+107
-47
lines changed

src/net_processing.cpp

Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,12 +1070,13 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma
10701070
connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
10711071
}
10721072

1073-
void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensusParams, const CInv& inv, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
1073+
void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
10741074
{
10751075
bool send = false;
10761076
std::shared_ptr<const CBlock> a_recent_block;
10771077
std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
10781078
bool fWitnessesPresentInARecentCompactBlock;
1079+
const Consensus::Params& consensusParams = chainparams.GetConsensus();
10791080
{
10801081
LOCK(cs_most_recent_block);
10811082
a_recent_block = most_recent_block;
@@ -1142,60 +1143,71 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus
11421143
std::shared_ptr<const CBlock> pblock;
11431144
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
11441145
pblock = a_recent_block;
1146+
} else if (inv.type == MSG_WITNESS_BLOCK) {
1147+
// Fast-path: in this case it is possible to serve the block directly from disk,
1148+
// as the network format matches the format on disk
1149+
std::vector<uint8_t> block_data;
1150+
if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) {
1151+
assert(!"cannot load block from disk");
1152+
}
1153+
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
1154+
// Don't set pblock as we've sent the block
11451155
} else {
11461156
// Send block from disk
11471157
std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
11481158
if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams))
11491159
assert(!"cannot load block from disk");
11501160
pblock = pblockRead;
11511161
}
1152-
if (inv.type == MSG_BLOCK)
1153-
connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
1154-
else if (inv.type == MSG_WITNESS_BLOCK)
1155-
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
1156-
else if (inv.type == MSG_FILTERED_BLOCK)
1157-
{
1158-
bool sendMerkleBlock = false;
1159-
CMerkleBlock merkleBlock;
1162+
if (pblock) {
1163+
if (inv.type == MSG_BLOCK)
1164+
connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
1165+
else if (inv.type == MSG_WITNESS_BLOCK)
1166+
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
1167+
else if (inv.type == MSG_FILTERED_BLOCK)
11601168
{
1161-
LOCK(pfrom->cs_filter);
1162-
if (pfrom->pfilter) {
1163-
sendMerkleBlock = true;
1164-
merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
1169+
bool sendMerkleBlock = false;
1170+
CMerkleBlock merkleBlock;
1171+
{
1172+
LOCK(pfrom->cs_filter);
1173+
if (pfrom->pfilter) {
1174+
sendMerkleBlock = true;
1175+
merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
1176+
}
1177+
}
1178+
if (sendMerkleBlock) {
1179+
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
1180+
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
1181+
// This avoids hurting performance by pointlessly requiring a round-trip
1182+
// Note that there is currently no way for a node to request any single transactions we didn't send here -
1183+
// they must either disconnect and retry or request the full block.
1184+
// Thus, the protocol spec specified allows for us to provide duplicate txn here,
1185+
// however we MUST always provide at least what the remote peer needs
1186+
typedef std::pair<unsigned int, uint256> PairType;
1187+
for (PairType& pair : merkleBlock.vMatchedTxn)
1188+
connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
11651189
}
1190+
// else
1191+
// no response
11661192
}
1167-
if (sendMerkleBlock) {
1168-
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
1169-
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
1170-
// This avoids hurting performance by pointlessly requiring a round-trip
1171-
// Note that there is currently no way for a node to request any single transactions we didn't send here -
1172-
// they must either disconnect and retry or request the full block.
1173-
// Thus, the protocol spec specified allows for us to provide duplicate txn here,
1174-
// however we MUST always provide at least what the remote peer needs
1175-
typedef std::pair<unsigned int, uint256> PairType;
1176-
for (PairType& pair : merkleBlock.vMatchedTxn)
1177-
connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
1178-
}
1179-
// else
1180-
// no response
1181-
}
1182-
else if (inv.type == MSG_CMPCT_BLOCK)
1183-
{
1184-
// If a peer is asking for old blocks, we're almost guaranteed
1185-
// they won't have a useful mempool to match against a compact block,
1186-
// and we don't feel like constructing the object for them, so
1187-
// instead we respond with the full, non-compact block.
1188-
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
1189-
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
1190-
if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
1191-
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
1192-
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
1193+
else if (inv.type == MSG_CMPCT_BLOCK)
1194+
{
1195+
// If a peer is asking for old blocks, we're almost guaranteed
1196+
// they won't have a useful mempool to match against a compact block,
1197+
// and we don't feel like constructing the object for them, so
1198+
// instead we respond with the full, non-compact block.
1199+
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
1200+
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
1201+
if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
1202+
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
1203+
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
1204+
} else {
1205+
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
1206+
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
1207+
}
11931208
} else {
1194-
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
1195-
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
1209+
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
11961210
}
1197-
} else {
1198-
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
11991211
}
12001212
}
12011213

@@ -1213,7 +1225,7 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus
12131225
}
12141226
}
12151227

1216-
void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
1228+
void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
12171229
{
12181230
AssertLockNotHeld(cs_main);
12191231

@@ -1262,7 +1274,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
12621274
const CInv &inv = *it;
12631275
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
12641276
it++;
1265-
ProcessGetBlockData(pfrom, consensusParams, inv, connman, interruptMsgProc);
1277+
ProcessGetBlockData(pfrom, chainparams, inv, connman, interruptMsgProc);
12661278
}
12671279
}
12681280

@@ -1972,7 +1984,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
19721984
}
19731985

19741986
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
1975-
ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc);
1987+
ProcessGetData(pfrom, chainparams, connman, interruptMsgProc);
19761988
}
19771989

19781990

@@ -2942,7 +2954,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
29422954
bool fMoreWork = false;
29432955

29442956
if (!pfrom->vRecvGetData.empty())
2945-
ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc);
2957+
ProcessGetData(pfrom, chainparams, connman, interruptMsgProc);
29462958

29472959
if (pfrom->fDisconnect)
29482960
return false;

src/validation.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,52 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
11371137
return true;
11381138
}
11391139

1140+
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start)
1141+
{
1142+
CDiskBlockPos hpos = pos;
1143+
hpos.nPos -= 8; // Seek back 8 bytes for meta header
1144+
CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
1145+
if (filein.IsNull()) {
1146+
return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
1147+
}
1148+
1149+
try {
1150+
CMessageHeader::MessageStartChars blk_start;
1151+
unsigned int blk_size;
1152+
1153+
filein >> blk_start >> blk_size;
1154+
1155+
if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
1156+
return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
1157+
HexStr(blk_start, blk_start + CMessageHeader::MESSAGE_START_SIZE),
1158+
HexStr(message_start, message_start + CMessageHeader::MESSAGE_START_SIZE));
1159+
}
1160+
1161+
if (blk_size > MAX_SIZE) {
1162+
return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(),
1163+
blk_size, MAX_SIZE);
1164+
}
1165+
1166+
block.resize(blk_size); // Zeroing of memory is intentional here
1167+
filein.read((char*)block.data(), blk_size);
1168+
} catch(const std::exception& e) {
1169+
return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
1170+
}
1171+
1172+
return true;
1173+
}
1174+
1175+
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
1176+
{
1177+
CDiskBlockPos block_pos;
1178+
{
1179+
LOCK(cs_main);
1180+
block_pos = pindex->GetBlockPos();
1181+
}
1182+
1183+
return ReadRawBlockFromDisk(block, block_pos, message_start);
1184+
}
1185+
11401186
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
11411187
{
11421188
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;

src/validation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ void InitScriptExecutionCache();
398398
/** Functions for disk access for blocks */
399399
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
400400
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
401+
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start);
402+
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
401403

402404
/** Functions for validating blocks and updating the block tree */
403405

0 commit comments

Comments
 (0)