Skip to content

Commit a7f7651

Browse files
committed
Merge #9352: Attempt reconstruction from all compact block announcements
813ede9 [qa] Update compactblocks test for multi-peer reconstruction (Suhas Daftuar) 7017298 Allow compactblock reconstruction when block is in flight (Suhas Daftuar)
2 parents b416095 + 813ede9 commit a7f7651

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

qa/rpc-tests/p2p-compactblocks.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,54 @@ def request_cb_announcements(self, peer, node, version):
764764
msg.announce = True
765765
peer.send_and_ping(msg)
766766

767+
def test_compactblock_reconstruction_multiple_peers(self, node, stalling_peer, delivery_peer):
768+
assert(len(self.utxos))
769+
770+
def announce_cmpct_block(node, peer):
771+
utxo = self.utxos.pop(0)
772+
block = self.build_block_with_transactions(node, utxo, 5)
773+
774+
cmpct_block = HeaderAndShortIDs()
775+
cmpct_block.initialize_from_block(block)
776+
msg = msg_cmpctblock(cmpct_block.to_p2p())
777+
peer.send_and_ping(msg)
778+
with mininode_lock:
779+
assert(peer.last_getblocktxn is not None)
780+
return block, cmpct_block
781+
782+
block, cmpct_block = announce_cmpct_block(node, stalling_peer)
783+
784+
for tx in block.vtx[1:]:
785+
delivery_peer.send_message(msg_tx(tx))
786+
delivery_peer.sync_with_ping()
787+
mempool = node.getrawmempool()
788+
for tx in block.vtx[1:]:
789+
assert(tx.hash in mempool)
790+
791+
delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
792+
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
793+
794+
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
795+
796+
# Now test that delivering an invalid compact block won't break relay
797+
798+
block, cmpct_block = announce_cmpct_block(node, stalling_peer)
799+
for tx in block.vtx[1:]:
800+
delivery_peer.send_message(msg_tx(tx))
801+
delivery_peer.sync_with_ping()
802+
803+
cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [ CTxInWitness() ]
804+
cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
805+
806+
cmpct_block.use_witness = True
807+
delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
808+
assert(int(node.getbestblockhash(), 16) != block.sha256)
809+
810+
msg = msg_blocktxn()
811+
msg.block_transactions.blockhash = block.sha256
812+
msg.block_transactions.transactions = block.vtx[1:]
813+
stalling_peer.send_and_ping(msg)
814+
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
767815

768816
def run_test(self):
769817
# Setup the p2p connections and start up the network thread.
@@ -848,6 +896,10 @@ def run_test(self):
848896
self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, False)
849897
self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, False)
850898

899+
print("\tTesting reconstructing compact blocks from all peers...")
900+
self.test_compactblock_reconstruction_multiple_peers(self.nodes[1], self.segwit_node, self.old_node)
901+
sync_blocks(self.nodes)
902+
851903
# Advance to segwit activation
852904
print ("\nAdvancing to segwit activation\n")
853905
self.activate_segwit(self.nodes[1])

src/net_processing.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
17821782
}
17831783
}
17841784

1785+
// Keep a CBlock for "optimistic" compactblock reconstructions (see
1786+
// below)
1787+
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
1788+
bool fBlockReconstructed = false;
1789+
17851790
LOCK(cs_main);
17861791
// If AcceptBlockHeader returned true, it set pindex
17871792
assert(pindex);
@@ -1870,6 +1875,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
18701875
req.blockhash = pindex->GetBlockHash();
18711876
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
18721877
}
1878+
} else {
1879+
// This block is either already in flight from a different
1880+
// peer, or this peer has too many blocks outstanding to
1881+
// download from.
1882+
// Optimistically try to reconstruct anyway since we might be
1883+
// able to without any round trips.
1884+
PartiallyDownloadedBlock tempBlock(&mempool);
1885+
ReadStatus status = tempBlock.InitData(cmpctblock);
1886+
if (status != READ_STATUS_OK) {
1887+
// TODO: don't ignore failures
1888+
return true;
1889+
}
1890+
std::vector<CTransactionRef> dummy;
1891+
status = tempBlock.FillBlock(*pblock, dummy);
1892+
if (status == READ_STATUS_OK) {
1893+
fBlockReconstructed = true;
1894+
}
18731895
}
18741896
} else {
18751897
if (fAlreadyInFlight) {
@@ -1889,6 +1911,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
18891911
return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman);
18901912
}
18911913
}
1914+
1915+
if (fBlockReconstructed) {
1916+
// If we got here, we were able to optimistically reconstruct a
1917+
// block that is in flight from some other peer.
1918+
{
1919+
LOCK(cs_main);
1920+
mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
1921+
}
1922+
bool fNewBlock = false;
1923+
ProcessNewBlock(chainparams, pblock, true, &fNewBlock);
1924+
if (fNewBlock)
1925+
pfrom->nLastBlockTime = GetTime();
1926+
1927+
LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid()
1928+
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) {
1929+
// Clear download state for this block, which is in
1930+
// process from some other peer. We do this after calling
1931+
// ProcessNewBlock so that a malleated cmpctblock announcement
1932+
// can't be used to interfere with block relay.
1933+
MarkBlockAsReceived(pblock->GetHash());
1934+
}
1935+
}
1936+
18921937
}
18931938

18941939
else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing

0 commit comments

Comments
 (0)