Skip to content

Commit 17f9dc0

Browse files
committed
Comments on block ptr invalidation, set stop/fault.
1 parent 7feaf49 commit 17f9dc0

File tree

3 files changed

+69
-11
lines changed

3 files changed

+69
-11
lines changed

src/chasers/chaser_confirm.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ void chaser_confirm::do_bump(height_t) NOEXCEPT
240240
return;
241241
}
242242

243+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
244+
// Failure here was previously result of bug in hashmap, which
245+
// caused both iteration across full prevout table and missing
246+
// prevout tx records intermittently in confirmation query.
247+
// This would prevent subsequent confirmation progress. This
248+
// has been resolved.
249+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
243250
notify(ec, chase::unconfirmable, link);
244251
fire(events::block_unconfirmable, height);
245252
LOGR("Unconfirmable block [" << height << "] " << ec.message());

src/chasers/chaser_validate.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,16 @@ void chaser_validate::complete_block(const code& ec, const header_link& link,
316316
notify(ec, chase::unvalid, link);
317317
fire(events::block_unconfirmable, height);
318318
LOGR("Invalid block [" << height << "] " << ec.message());
319+
320+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
321+
// Stop the network in case of an unexpected invalidity (debugging).
322+
// This is considered a bug, not an invalid block arrival (for now).
323+
// This usually manifests as accept failure invalid_witness (!checked),
324+
// in which case the witness data is simply not present, but bip141 is
325+
// active and the output indicates a witness transaction.
326+
fault(ec);
327+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
328+
319329
return;
320330
}
321331

src/protocols/protocol_block_in_31800.cpp

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,21 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
304304
const auto checked = is_under_checkpoint(height) ||
305305
query.is_milestone(link);
306306

307+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
308+
// Failed on block [363353] with block_internal_double_spend (!checked).
309+
// The block object remains in scope during the call, but the pointer owns
310+
// the memory. There is a local pointer object copied above, but it may be
311+
// deleted once it is no longer referenced. This makes the block reference
312+
// below weak. The message pointer object is also held by the calling
313+
// closure, however it can be deleted by the compiler due to subsequent
314+
// non-use. The copied block pointer holds the block object, but it may be
315+
// also deleted if not referenced. Passing a block pointer into check,
316+
// allowing block->check() to be called would resolve that issue, but not
317+
// the issue of calling set_code (see below). But it's not clear how ~block
318+
// could have occured here with query.set_code(*block) pending below.
319+
// It hasn't failed when using a prevout table, which seems unrelated.
320+
// But if it's a race to overwrite freed block memory, it's very possible.
321+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
307322
// Tx commitments and malleation are checked under bypass. Invalidity is
308323
// only stored when a strong header has been stored, later to be found out
309324
// as invalid and not malleable. Stored invalidity prevents repeat
@@ -315,6 +330,11 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
315330
code == system::error::invalid_witness_commitment ||
316331
code == system::error::block_malleated)
317332
{
333+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
334+
// These references to block didn't keep it in scope.
335+
// But because block is const they could precede the check. Could
336+
// be a compiler optimization as these are called by check(true).
337+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
318338
LOGR("Uncommitted block [" << encode_hash(hash) << ":" << height
319339
<< "] from [" << authority() << "] " << code.message()
320340
<< " txs(" << block->transactions() << ")"
@@ -323,22 +343,12 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
323343
return false;
324344
}
325345

326-
// TODO: why were we not setting this block as unconfirmable?
327-
////if (code == system::error::forward_reference)
328-
////{
329-
//// LOGR("Disordered block [" << encode_hash(hash) << ":" << height
330-
//// << " txs(" << block->transactions() << ")"
331-
//// << " segregated(" << block->is_segregated() << ").");
332-
//// stop(code);
333-
//// return false;
334-
////}
335-
336346
// Actual block represented by the hash is unconfirmable.
337347
if (!query.set_block_unconfirmable(link))
338348
{
339349
LOGF("Failure setting block unconfirmable [" << encode_hash(hash)
340350
<< ":" << height << "] from [" << authority() << "].");
341-
fault(error::protocol1);
351+
stop(fault(error::protocol1));
342352
return false;
343353
}
344354

@@ -353,6 +363,18 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
353363
// Commit block.txs.
354364
// ........................................................................
355365

366+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
367+
// For early segwit blocks this often fails to set the witness.
368+
// There is no failure code, the witness is just empty and stored empty.
369+
// That causes a validation failure with invalid_witness (!checked).
370+
// It may have also failed in a way that invalidates confirmation queries.
371+
// Passing a block pointer here would only hold the block in scope until
372+
// it iterates transactions, with no subsequent reference. Then transaction
373+
// pointers could hold themselves in scope, however block owns their memory
374+
// and ~block() frees that memory even if the transaction pointer is alive.
375+
// It hasn't failed when using a prevout table, which seems unrelated.
376+
// But if it's a race to overwrite freed block memory, it's very possible.
377+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
356378
// This invokes set_strong when checked.
357379
if (const auto code = query.set_code(*block, link, checked))
358380
{
@@ -363,6 +385,23 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
363385
return false;
364386
}
365387

388+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
389+
// TODO: verify that ~block() free during contained object access is the
390+
// problem by switching to the default memory allocator w/non-prevout run
391+
// and the code below disabled.
392+
// TODO: pass shared_ptr in to check(block) and set_code(block) from here.
393+
// This may guarantee the pointer (vs. block&) lifetime until return.
394+
// This is an attempt to keep the shared pointer in scope.
395+
// Given that block is const this could also be reordered prior to check().
396+
// But this is not called in this scope, only by message deserialization.
397+
// Passing the block pointer through the store
398+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
399+
if (!block->is_valid())
400+
{
401+
stop(fault(error::protocol2));
402+
return false;
403+
}
404+
366405
// Advance.
367406
// ........................................................................
368407

@@ -372,8 +411,10 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec,
372411
notify(ec, chase::checked, height);
373412
fire(events::block_archived, height);
374413

414+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
375415
// block->serialized_size may keep block in scope during set_code above.
376416
// However the compiler may reorder this calculation since block is const.
417+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
377418
count(block->serialized_size(true));
378419
map_->erase(it);
379420
if (is_idle())

0 commit comments

Comments
 (0)