871 | @@ -872,8 +872,12 @@ class MinerImpl : public Mining
872 |
873 | bool testBlockValidity(const CBlock& block, bool check_merkle_root, BlockValidationState& state) override
874 | {
875 | - LOCK(::cs_main);
876 | - return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, chainman().ActiveChain().Tip(), /*fCheckPOW=*/false, check_merkle_root);
877 | + LOCK(cs_main);
878 | + CBlockIndex* tip{chainman().ActiveChain().Tip()};
879 | + // Fail if the tip updated before the lock was taken
880 | + if (block.hashPrevBlock != tip->GetBlockHash()) return false;
nit: In the RPC error you'll give state.ToString() as the failure reason, but if this tip check fails that'll be the default BLOCK_RESULT_UNSET.
better suggestion #30335 (review) Seems correct to fail if the tip has updated to avoid building a fork on an older block, but I can't see a BlockValidationResult that would make sense here other than maybe stretching the meaning of BLOCK_INVALID_HEADER -- perhaps you could add another that only makes sense in a templating context like BLOCK_NOT_TIP (or wrap BlockValidationState in something like BlockTemplateValidationState allowing for other template-specific reults but that might be too messy?)
Currently if this happens as you said #30335 (review) it looks like we'd have crashed which is fun