Skip to content

Commit

Permalink
Merge #9357: [0.13 backport] Attempt reconstruction from all compact …
Browse files Browse the repository at this point in the history
…block announcements

53b656f [qa] Update compactblocks test for multi-peer reconstruction (Suhas Daftuar)
4ced313 Allow compactblock reconstruction when block is in flight (Suhas Daftuar)
  • Loading branch information
laanwj committed Dec 19, 2016
2 parents fb987b3 + 53b656f commit 03b6f62
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
52 changes: 52 additions & 0 deletions qa/rpc-tests/p2p-compactblocks.py
Expand Up @@ -757,6 +757,54 @@ def request_cb_announcements(self, peer, node, version):
msg.announce = True
peer.send_and_ping(msg)

def test_compactblock_reconstruction_multiple_peers(self, node, stalling_peer, delivery_peer):
assert(len(self.utxos))

def announce_cmpct_block(node, peer):
utxo = self.utxos.pop(0)
block = self.build_block_with_transactions(node, utxo, 5)

cmpct_block = HeaderAndShortIDs()
cmpct_block.initialize_from_block(block)
msg = msg_cmpctblock(cmpct_block.to_p2p())
peer.send_and_ping(msg)
with mininode_lock:
assert(peer.last_getblocktxn is not None)
return block, cmpct_block

block, cmpct_block = announce_cmpct_block(node, stalling_peer)

for tx in block.vtx[1:]:
delivery_peer.send_message(msg_tx(tx))
delivery_peer.sync_with_ping()
mempool = node.getrawmempool()
for tx in block.vtx[1:]:
assert(tx.hash in mempool)

delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
assert_equal(int(node.getbestblockhash(), 16), block.sha256)

self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])

# Now test that delivering an invalid compact block won't break relay

block, cmpct_block = announce_cmpct_block(node, stalling_peer)
for tx in block.vtx[1:]:
delivery_peer.send_message(msg_tx(tx))
delivery_peer.sync_with_ping()

cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [ CTxInWitness() ]
cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]

cmpct_block.use_witness = True
delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
assert(int(node.getbestblockhash(), 16) != block.sha256)

msg = msg_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = block.vtx[1:]
stalling_peer.send_and_ping(msg)
assert_equal(int(node.getbestblockhash(), 16), block.sha256)

def run_test(self):
# Setup the p2p connections and start up the network thread.
Expand Down Expand Up @@ -841,6 +889,10 @@ def run_test(self):
self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, False)
self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, False)

print("\tTesting reconstructing compact blocks from all peers...")
self.test_compactblock_reconstruction_multiple_peers(self.nodes[1], self.segwit_node, self.old_node)
sync_blocks(self.nodes)

# Advance to segwit activation
print ("\nAdvancing to segwit activation\n")
self.activate_segwit(self.nodes[1])
Expand Down
49 changes: 49 additions & 0 deletions src/main.cpp
Expand Up @@ -5650,6 +5650,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CBlockHeaderAndShortTxIDs cmpctblock;
vRecv >> cmpctblock;

// Keep a CBlock for "optimistic" compactblock reconstructions (see
// below)
CBlock block;
bool fBlockReconstructed = false;

LOCK(cs_main);

if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) {
Expand Down Expand Up @@ -5758,6 +5763,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
req.blockhash = pindex->GetBlockHash();
pfrom->PushMessage(NetMsgType::GETBLOCKTXN, req);
}
} else {
// This block is either already in flight from a different
// peer, or this peer has too many blocks outstanding to
// download from.
// Optimistically try to reconstruct anyway since we might be
// able to without any round trips.
PartiallyDownloadedBlock tempBlock(&mempool);
ReadStatus status = tempBlock.InitData(cmpctblock);
if (status != READ_STATUS_OK) {
// TODO: don't ignore failures
return true;
}
std::vector<CTransaction> dummy;
status = tempBlock.FillBlock(block, dummy);
if (status == READ_STATUS_OK) {
fBlockReconstructed = true;
}
}
} else {
if (fAlreadyInFlight) {
Expand All @@ -5778,6 +5800,33 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}

if (fBlockReconstructed) {
// If we got here, we were able to optimistically reconstruct a
// block that is in flight from some other peer. However, this
// cmpctblock may be invalid. In particular, while we've checked
// that the block merkle root commits to the transaction ids, we
// haven't yet checked that tx witnesses are properly committed to
// in the coinbase witness commitment.
//
// ProcessNewBlock will call MarkBlockAsReceived(), which will
// clear any in-flight compact block state that might be present
// from some other peer. We don't want a malleated compact block
// request to interfere with block relay, so we don't want to call
// ProcessNewBlock until we've already checked that the witness
// commitment is correct.
{
LOCK(cs_main);
CValidationState dummy;
if (!ContextualCheckBlock(block, dummy, pindex->pprev)) {
// TODO: could send reject message to peer?
return true;
}
}
CValidationState state;
ProcessNewBlock(state, chainparams, pfrom, &block, true, NULL, false);
// TODO: could send reject message if block is invalid?
}

CheckBlockIndex(chainparams.GetConsensus());
}

Expand Down

0 comments on commit 03b6f62

Please sign in to comment.