While reviewing #34254, I asked Claude to examine for any multi-threaded issues with InvalidateBlock. It came up with the following crash which I verified:
- [rpc thread] in
InvalidateBlock, populate the list ofhighpow_outofchain_headershere and then release cs_main. These headers are all better than the new tip we should be getting and will be used to updatem_best_headerlater. - [other thread] acquire cs_main and call
AddToBlockIndexwith a header that is not better thanm_best_headerbut is better than every header inhighpow_outofchain_headers. This meansAddToBlockIndexwon't updatem_best_headerhere - [rpc thread] acquire cs_main in the loop and update
m_best_headerbased on the candidates inhighpow_outofchain_headershere - [rpc thread] calls
CheckBlockIndexwhich fails on the assert since there is actually a header with more work thanm_best_header: https://github.com/bitcoin/bitcoin/blob/05e49b342faa1412266951429c135e9f5daa30c2/src/validation.cpp#L5317-L5318
Haven't really thought about how to fix this too much.
Vibe-coded functional test: https://github.com/Crypt-iQ/bitcoin/tree/08282026/rpc_invalidateblock_crash
Run with: <build_dir>/test/functional/test_runner.py rpc_invalidateblock.py --race-attempts=5000 and inspect stderr of node2 in the tmp test directory.