Skip to content

Commit bfc30b3

Browse files
committed
Ignore unrequested blocks too far ahead of tip
1 parent 9d60602 commit bfc30b3

File tree

2 files changed

+63
-11
lines changed

2 files changed

+63
-11
lines changed

qa/rpc-tests/p2p-acceptblock.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
it's missing an intermediate block.
4141
Node1 should reorg to this longer chain.
4242
43+
4b.Send 288 more blocks on the longer chain.
44+
Node0 should process all but the last block (too far ahead in height).
45+
Send all headers to Node1, and then send the last block in that chain.
46+
Node1 should accept the block because it's coming from a whitelisted peer.
47+
4348
5. Send a duplicate of the block in #3 to Node0.
4449
Node0 should not process the block because it is unrequested, and stay on
4550
the shorter chain.
@@ -126,13 +131,15 @@ def run_test(self):
126131
# 2. Send one block that builds on each tip.
127132
# This should be accepted.
128133
blocks_h2 = [] # the height 2 blocks on each node's chain
134+
block_time = time.time() + 1
129135
for i in xrange(2):
130-
blocks_h2.append(create_block(tips[i], create_coinbase(), time.time()+1))
136+
blocks_h2.append(create_block(tips[i], create_coinbase(), block_time))
131137
blocks_h2[i].solve()
138+
block_time += 1
132139
test_node.send_message(msg_block(blocks_h2[0]))
133140
white_node.send_message(msg_block(blocks_h2[1]))
134141

135-
time.sleep(1)
142+
time.sleep(0.5)
136143
assert_equal(self.nodes[0].getblockcount(), 2)
137144
assert_equal(self.nodes[1].getblockcount(), 2)
138145
print "First height 2 block accepted by both nodes"
@@ -145,7 +152,7 @@ def run_test(self):
145152
test_node.send_message(msg_block(blocks_h2f[0]))
146153
white_node.send_message(msg_block(blocks_h2f[1]))
147154

148-
time.sleep(1) # Give time to process the block
155+
time.sleep(0.5) # Give time to process the block
149156
for x in self.nodes[0].getchaintips():
150157
if x['hash'] == blocks_h2f[0].hash:
151158
assert_equal(x['status'], "headers-only")
@@ -164,7 +171,7 @@ def run_test(self):
164171
test_node.send_message(msg_block(blocks_h3[0]))
165172
white_node.send_message(msg_block(blocks_h3[1]))
166173

167-
time.sleep(1)
174+
time.sleep(0.5)
168175
# Since the earlier block was not processed by node0, the new block
169176
# can't be fully validated.
170177
for x in self.nodes[0].getchaintips():
@@ -182,6 +189,45 @@ def run_test(self):
182189
assert_equal(self.nodes[1].getblockcount(), 3)
183190
print "Successfully reorged to length 3 chain from whitelisted peer"
184191

192+
# 4b. Now mine 288 more blocks and deliver; all should be processed but
193+
# the last (height-too-high) on node0. Node1 should process the tip if
194+
# we give it the headers chain leading to the tip.
195+
tips = blocks_h3
196+
headers_message = msg_headers()
197+
all_blocks = [] # node0's blocks
198+
for j in xrange(2):
199+
for i in xrange(288):
200+
next_block = create_block(tips[j].sha256, create_coinbase(), tips[j].nTime+1)
201+
next_block.solve()
202+
if j==0:
203+
test_node.send_message(msg_block(next_block))
204+
all_blocks.append(next_block)
205+
else:
206+
headers_message.headers.append(CBlockHeader(next_block))
207+
tips[j] = next_block
208+
209+
time.sleep(2)
210+
for x in all_blocks:
211+
try:
212+
self.nodes[0].getblock(x.hash)
213+
if x == all_blocks[287]:
214+
raise AssertionError("Unrequested block too far-ahead should have been ignored")
215+
except:
216+
if x == all_blocks[287]:
217+
print "Unrequested block too far-ahead not processed"
218+
else:
219+
raise AssertionError("Unrequested block with more work should have been accepted")
220+
221+
headers_message.headers.pop() # Ensure the last block is unrequested
222+
white_node.send_message(headers_message) # Send headers leading to tip
223+
white_node.send_message(msg_block(tips[1])) # Now deliver the tip
224+
try:
225+
time.sleep(0.5)
226+
self.nodes[1].getblock(tips[1].hash)
227+
print "Unrequested block far ahead of tip accepted from whitelisted peer"
228+
except:
229+
raise AssertionError("Unrequested block from whitelisted peer not accepted")
230+
185231
# 5. Test handling of unrequested block on the node that didn't process
186232
# Should still not be processed (even though it has a child that has more
187233
# work).
@@ -204,21 +250,20 @@ def run_test(self):
204250
test_node.last_getdata = None
205251
test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
206252

207-
time.sleep(1)
253+
time.sleep(0.5)
208254
with mininode_lock:
209255
getdata = test_node.last_getdata
210256

211-
# Check that the getdata is for the right block
212-
assert_equal(len(getdata.inv), 1)
257+
# Check that the getdata includes the right block
213258
assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)
214259
print "Inv at tip triggered getdata for unprocessed block"
215260

216261
# 7. Send the missing block for the third time (now it is requested)
217262
test_node.send_message(msg_block(blocks_h2f[0]))
218263

219-
time.sleep(1)
220-
assert_equal(self.nodes[0].getblockcount(), 3)
221-
print "Successfully reorged to length 3 chain from non-whitelisted peer"
264+
time.sleep(2)
265+
assert_equal(self.nodes[0].getblockcount(), 290)
266+
print "Successfully reorged to longer chain from non-whitelisted peer"
222267

223268
[ c.disconnect_node() for c in connections ]
224269

src/main.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2841,16 +2841,23 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
28412841

28422842
// Try to process all requested blocks that we don't have, but only
28432843
// process an unrequested block if it's new and has enough work to
2844-
// advance our tip.
2844+
// advance our tip, and isn't too many blocks ahead.
28452845
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
28462846
bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true);
2847+
// Blocks that are too out-of-order needlessly limit the effectiveness of
2848+
// pruning, because pruning will not delete block files that contain any
2849+
// blocks which are too close in height to the tip. Apply this test
2850+
// regardless of whether pruning is enabled; it should generally be safe to
2851+
// not process unrequested blocks.
2852+
bool fTooFarAhead = (pindex->nHeight - chainActive.Height()) > MIN_BLOCKS_TO_KEEP;
28472853

28482854
// TODO: deal better with return value and error conditions for duplicate
28492855
// and unrequested blocks.
28502856
if (fAlreadyHave) return true;
28512857
if (!fRequested) { // If we didn't ask for it:
28522858
if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
28532859
if (!fHasMoreWork) return true; // Don't process less-work chains
2860+
if (fTooFarAhead) return true; // Block height is too high
28542861
}
28552862

28562863
if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {

0 commit comments

Comments
 (0)