message: Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader
diff: @@ -3383,10 +3383,30 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
  if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
  return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
  - // If the previous block index isn't valid, determine if it descends from any block which
  - // has been found invalid (m_failed_blocks), then mark pindexPrev and any blocks
  - // between them as failed.
  + /* Determine if this block descends from any block which has been found
  + * invalid (m_failed_blocks), then mark pindexPrev and any blocks between
  + * them as failed. For example:
  + *
  + * D3
  + * /
  + * B2 - C2
  + * / \
  + * A D2 - E2 - F2
  + * \
  + * B1 - C1 - D1 - E1
  + *
  + * In the case that we attempted to reorg from E1 to F2, only to find
  + * C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD
  + * but NOT D3 (it was not in any of our candidate sets at the time).
  + *
  + * In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart
  + * in LoadBlockIndex.
  + */
  if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
  + // The above does not mean "invalid": it checks if the previous block
  + // hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance
  + // optimization, in the common case of adding a new block to the tip,
  + // we don't need to iterate over the failed blocks list.
  for (const CBlockIndex* failedit : m_failed_blocks) {
  if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
  assert(failedit->nStatus & BLOCK_FAILED_VALID);
  