Currently, this function holds cs_main for its duration, which could be for multiple minutes due to the ComputeUTXOStats call. Holding cs_main used to be necessary (before d43a1f1a2fa3) to avoid advancing validated_cs farther than its target block. Now it should be possible to avoid this, but simply releasing cs_main here would not be possible because this function is invoked by ConnectTip within
| 5984 | // hash, because the UTXO set is only hashed after the historical chainstate |
| 5985 | // reaches its target block and is no longer changing. |
| 5986 | SnapshotCompletionResult ChainstateManager::MaybeValidateSnapshot(Chainstate& validated_cs, Chainstate& unvalidated_cs) |
| 5987 | { |
| 5988 | AssertLockHeld(cs_main); |
| 5989 | |
| 5990 | // If the snapshot does not need to be validated... |
| 5991 | if (unvalidated_cs.m_assumeutxo != Assumeutxo::UNVALIDATED || |
| 5992 | // Or if either chainstate is unusable... |
| 5993 | !unvalidated_cs.m_from_snapshot_blockhash || |
| 5994 | validated_cs.m_assumeutxo != Assumeutxo::VALIDATED || |
| 5995 | !validated_cs.m_chain.Tip() || |
| 5996 | // Or the validated chainstate is not targeting the snapshot block... |
| 5997 | !validated_cs.m_target_blockhash || |
| 5998 | *validated_cs.m_target_blockhash != *unvalidated_cs.m_from_snapshot_blockhash || |
| 5999 | // Or the validated chainstate has not reached the snapshot block yet... |
| 6000 | !validated_cs.ReachedTarget()) { |
| 6001 | // Then the snapshot cannot be validated and there is nothing to do. |
| 6002 | return SnapshotCompletionResult::SKIPPED; |
| 6003 | } |
| 6004 | assert(validated_cs.TargetBlock() == validated_cs.m_chain.Tip()); |
| 6005 | |
| 6006 | auto handle_invalid_snapshot = [&]() EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { |
| 6007 | bilingual_str user_error = strprintf(_( |
| 6008 | "%s failed to validate the -assumeutxo snapshot state. " |
| 6009 | "This indicates a hardware problem, or a bug in the software, or a " |
| 6010 | "bad software modification that allowed an invalid snapshot to be " |
| 6011 | "loaded. As a result of this, the node will shut down and stop using any " |
| 6012 | "state that was built on the snapshot, resetting the chain height " |
| 6013 | "from %d to %d. On the next " |
| 6014 | "restart, the node will resume syncing from %d " |
| 6015 | "without using any snapshot data. " |
| 6016 | "Please report this incident to %s, including how you obtained the snapshot. " |
| 6017 | "The invalid snapshot chainstate will be left on disk in case it is " |
| 6018 | "helpful in diagnosing the issue that caused this error."), |
| 6019 | CLIENT_NAME, unvalidated_cs.m_chain.Height(), |
| 6020 | validated_cs.m_chain.Height(), |
| 6021 | validated_cs.m_chain.Height(), CLIENT_BUGREPORT); |
| 6022 | |
| 6023 | LogError("[snapshot] !!! %s\n", user_error.original); |
| 6024 | LogError("[snapshot] deleting snapshot, reverting to validated chain, and stopping node\n"); |
| 6025 | |
| 6026 | // Reset chainstate target to network tip instead of snapshot block. |
| 6027 | validated_cs.SetTargetBlock(nullptr); |
| 6028 | |
| 6029 | unvalidated_cs.m_assumeutxo = Assumeutxo::INVALID; |
| 6030 | |
| 6031 | auto rename_result = unvalidated_cs.InvalidateCoinsDBOnDisk(); |
| 6032 | if (!rename_result) { |
| 6033 | user_error += Untranslated("\n") + util::ErrorString(rename_result); |
| 6034 | } |
| 6035 | |
| 6036 | GetNotifications().fatalError(user_error); |
| 6037 | }; |
| 6038 | |
| 6039 | CCoinsViewDB& validated_coins_db = validated_cs.CoinsDB(); |
| 6040 | validated_cs.ForceFlushStateToDisk(); |
| 6041 | |
| 6042 | const auto& maybe_au_data = m_options.chainparams.AssumeutxoForHeight(validated_cs.m_chain.Height()); |
| 6043 | if (!maybe_au_data) { |