kernel: Remove dependency on CScheduler #28960

pull TheCharlatan wants to merge 7 commits into bitcoin:master from TheCharlatan:noGlobalSignals changing 40 files +365 −289
  1. TheCharlatan commented at 4:27 pm on November 28, 2023: contributor

    By defining a virtual interface class for the scheduler client, users of the kernel can now define their own event consuming infrastructure, without having to spawn threads or rely on the scheduler design.

    Removing CScheduler also allows removing the thread and exception modules from the kernel library.

    To make the CMainSignals class easier to use from a kernel library perspective, remove its global instantiation and adopt RAII practices.

    Renames CMainSignals to ValidationSignals, which more accurately describes its purpose and scope.

    Also make the ValidationSignals in the ChainstateManager and CTxMemPool` optional. This could be useful in the future for using or testing these classes without having to instantiate any form of signal handling.


    This PR is part of the libbitcoinkernel project. It improves the kernel API and removes two modules from the kernel library.

  2. DrahtBot commented at 4:27 pm on November 28, 2023: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK ryanofsky, vasild, furszy, maflcko
    Concept ACK dergoegge, darosior
    Stale ACK jamesob

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #29538 (refactor: Improve naming of CBlock::GetHash() -> GetHeaderHash() by fjahr)
    • #29415 (Broadcast own transactions only via short-lived Tor or I2P connections by vasild)
    • #29278 (Wallet: Add maxfeerate wallet startup option by ismaelsadeeq)
    • #29242 (Mempool util: Add RBF diagram checks for single chunks against clusters of size 2 by instagibbs)
    • #29236 (log: Nuke error(…) by maflcko)
    • #29015 (kernel: Streamline util library by ryanofsky)
    • #28984 (Cluster size 2 package rbf by instagibbs)
    • #28830 ([refactor] Check CTxMemPool options in ctor by TheCharlatan)
    • #28676 ([WIP] Cluster mempool implementation by sdaftuar)
    • #28608 (assumeutxo state and locking cleanup by ryanofsky)
    • #28228 (kernel: Run sanity checks on context construction by TheCharlatan)
    • #28031 (Package Relay 1/3: Introduce TxDownloadManager and improve orphan-handling by glozow)
    • #27277 (Move log messages: tx enqueue to mempool, allocation to blockstorage by Sjors)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  3. DrahtBot added the label Validation on Nov 28, 2023
  4. TheCharlatan force-pushed on Nov 28, 2023
  5. TheCharlatan force-pushed on Nov 28, 2023
  6. TheCharlatan force-pushed on Nov 28, 2023
  7. DrahtBot added the label CI failed on Nov 28, 2023
  8. DrahtBot removed the label CI failed on Nov 29, 2023
  9. dergoegge commented at 10:17 am on November 29, 2023: member
    Concept ACK
  10. DrahtBot added the label Needs rebase on Nov 30, 2023
  11. TheCharlatan force-pushed on Nov 30, 2023
  12. DrahtBot removed the label Needs rebase on Nov 30, 2023
  13. DrahtBot added the label Needs rebase on Dec 1, 2023
  14. TheCharlatan force-pushed on Dec 1, 2023
  15. DrahtBot removed the label Needs rebase on Dec 1, 2023
  16. in src/bitcoin-chainstate.cpp:126 in f86c3e858f outdated
    122@@ -113,7 +123,7 @@ int main(int argc, char* argv[])
    123 
    124 
    125     // SETUP: Chainstate
    126-    auto chainparams = CChainParams::Main();
    127+    auto chainparams = CChainParams::SigNet({});
    


    darosior commented at 12:16 pm on December 2, 2023:
    Why?

    TheCharlatan commented at 12:32 pm on December 2, 2023:
    Just not done testing yet and SigNet is easier.
  17. TheCharlatan force-pushed on Dec 2, 2023
  18. TheCharlatan marked this as ready for review on Dec 2, 2023
  19. TheCharlatan commented at 10:16 pm on December 2, 2023: contributor
    Undrafting this now. I tested this approach extensively against my proof of concept C FFI and proof of concept Rust library, and think it’s not that bad an interface to have. Not sure why the msvc build is failing (only seems to be the Qt build?).
  20. hebasto commented at 10:33 pm on December 2, 2023: member

    Not sure why the msvc build is failing (only seems to be the Qt build?).

    cc @sipsorcery

  21. sipsorcery commented at 11:29 pm on December 6, 2023: member

    Maybe something in txmempool.h that the msvc compiler doesn’t like?

    0D:\a\bitcoin\bitcoin\src\txmempool.h(457,60): error C2143: syntax error: missing ')' before 'public' [D:\a\bitcoin\bitcoin\build_msvc\libbitcoin_qt\libbitcoin_qt.vcxproj]
    1D:\a\bitcoin\bitcoin\src\txmempool.h(457,60): error C2143: syntax error: missing ';' before 'public' [D:\a\bitcoin\bitcoin\build_msvc\libbitcoin_qt\libbitcoin_qt.vcxproj]
    2D:\a\bitcoin\bitcoin\src\txmempool.h(457,67): error C2059: syntax error: ')' [D:\a\bitcoin\bitcoin\build_msvc\libbitcoin_qt\libbitcoin_qt.vcxproj]
    

    Given it’s a compiler, rather than a linker, error should be easy enough to replicate. I’ll try and do so 2morrow

  22. sipsorcery commented at 11:32 pm on December 8, 2023: member

    It’s something to do with CMainSignals&. Wherever it’s used in libbitcoin_qt the msvc compiler generates a syntax error of:

    Error C2238 unexpected token(s) preceding ';'

  23. TheCharlatan force-pushed on Dec 9, 2023
  24. hebasto commented at 4:19 pm on December 9, 2023: member

    It’s something to do with CMainSignals&. Wherever it’s used in libbitcoin_qt the msvc compiler generates a syntax error of:

    Error C2238 unexpected token(s) preceding ';'

    Fixed in #29044.

  25. fanquake closed this on Dec 11, 2023

  26. fanquake closed this on Dec 11, 2023

  27. darosior commented at 11:31 am on December 11, 2023: member
    This shouldn’t have been closed i think @fanquake.
  28. fanquake reopened this on Dec 11, 2023

  29. DrahtBot added the label Needs rebase on Dec 11, 2023
  30. TheCharlatan force-pushed on Dec 11, 2023
  31. DrahtBot removed the label Needs rebase on Dec 11, 2023
  32. TheCharlatan force-pushed on Dec 14, 2023
  33. darosior commented at 4:30 pm on December 18, 2023: member

    Concept ACK.

    The interface looks good to me. Removing the dependency on CScheduler is also potentially helpful for fuzzing. I’ve for instance rebased on top of this PR a fuzz target for ChainstateManager i’ve been working on and being able to get rid of spawning and joining a thread on every run in favour of a global DummyQueue sped up the target by a 1.5 factor.

  34. in src/node/interfaces.cpp:466 in 4b9a37e461 outdated
    458@@ -458,17 +459,20 @@ class NotificationsProxy : public CValidationInterface
    459 
    460 class NotificationsHandlerImpl : public Handler
    461 {
    462+private:
    463+    CMainSignals& m_signals;
    


    darosior commented at 5:59 pm on December 30, 2023:
    Technically i think this reference can be dangling at Shutdown() (if there is still some wallets loaded) since we don’t reset() the node.wallet_loader before resetting node.main_signals.

    TheCharlatan commented at 11:00 am on January 3, 2024:
    Mmh, the destruction of wallet_loader is a bit weird, but doesn’t it get reset when node.chain_clients.clear() is called at Shutdown(...)?

    darosior commented at 11:32 am on January 3, 2024:

    Ah, right, the ownership of the loader is in node.chain_clients: https://github.com/bitcoin/bitcoin/blob/65c05db660b2ca1d0076b0d8573a6760b3228068/src/wallet/init.cpp#L129-L131

    So it’s dropped before node.main_signals is.

  35. in src/validationinterface.h:160 in 4b9a37e461 outdated
    154@@ -189,16 +155,53 @@ class CValidationInterface {
    155     friend class ValidationInterfaceTest;
    156 };
    157 
    158-class MainSignalsImpl;
    159+/**
    160+ * MainSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.
    


    maflcko commented at 9:44 am on January 3, 2024:
    why is this moved to the header?

    TheCharlatan commented at 10:54 am on January 3, 2024:
    Felt more natural to give a CMainSignals ownership of MainSignalsImpl instead of a std::unique_ptr<MainSignalsImpl, so it needs the full declaration of the class.

    maflcko commented at 11:07 am on January 3, 2024:
    ok, but in the commit 4b9a37e461aee0c24f798123cdb023a783a130c6 I was looking at, the unique_ptr remains unchanged

    TheCharlatan commented at 8:22 am on January 17, 2024:
    Ah, I think we need the full declaration here, because other modules are now instantiating the CMainSignals.
  36. DrahtBot added the label Needs rebase on Jan 3, 2024
  37. TheCharlatan force-pushed on Jan 3, 2024
  38. TheCharlatan commented at 3:13 pm on January 3, 2024: contributor

    Rebased 717093f24b1ad65bc18b23eab492cfdcc2511e69 -> d63b2e88780dc78fd531b053653361a0bf3fcbea (noGlobalSignals_0 -> noGlobalSignals_1, compare)

  39. DrahtBot removed the label Needs rebase on Jan 3, 2024
  40. in src/bitcoin-chainstate.cpp:78 in d63b2e8878 outdated
    91+            return 0;
    92+        }
    93+    };
    94 
    95-    // Gather some entropy once per minute.
    96-    scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
    


    jamesob commented at 7:46 pm on January 12, 2024:

    https://github.com/bitcoin/bitcoin/pull/28960/commits/d63b2e88780dc78fd531b053653361a0bf3fcbea

    I guess this periodic call was originally included to demonstrate how you might use the scheduler from the libbitcoinkernel interface, but now that we’ve removed the scheduler from the interface there isn’t really an equivalent. Clients would be expected to run their own async event managers and periodic schedulers, which makes sense - and I guess is the whole point of this change!

  41. jamesob approved
  42. jamesob commented at 8:06 pm on January 12, 2024: member

    ACK d63b2e88780dc78fd531b053653361a0bf3fcbea (jamesob/ackr/28960.1.TheCharlatan.kernel_remove_dependency)

    Reviewed and ran tests locally. Changes are pretty straightforward and common-sense.

    Putting event queue execution into the hands of libbitcoinkernel users seems like the right design choice, and it is especially motivating that this change helps novel fuzz testing (#29158).

    As an aside, it seems like the simplest option for users of libbitcoinkernel would be to execute the contents of the queue synchronously (i.e. drain the queue as soon as anything is pushed to it). That should be a perfectly safe way to use the library, right?

  43. DrahtBot requested review from darosior on Jan 12, 2024
  44. DrahtBot requested review from dergoegge on Jan 12, 2024
  45. in src/validationinterface.h:229 in cfb94ef2b3 outdated
    224+    // unregistration is nonblocking and can return before the last notification is
    225+    // processed.
    226+    /** Unregister subscriber */
    227+    void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks);
    228+
    229+    void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface>);
    


    maflcko commented at 12:49 pm on January 16, 2024:
    in the first commit: Any reason to re-order this and remove the doc comment?
  46. in src/init.cpp:294 in cfb94ef2b3 outdated
    290@@ -291,7 +291,7 @@ void Shutdown(NodeContext& node)
    291 
    292     // Because these depend on each-other, we make sure that neither can be
    293     // using the other before destroying them.
    294-    if (node.peerman) UnregisterValidationInterface(node.peerman.get());
    295+    if (node.peerman) node.main_signals->UnregisterValidationInterface(node.peerman.get());
    


    maflcko commented at 1:08 pm on January 16, 2024:
    first commit: Why check below, but not here, for nullptr?
  47. in src/validation.cpp:437 in cfb94ef2b3 outdated
    434         m_pool(mempool),
    435         m_view(&m_dummy),
    436         m_viewmempool(&active_chainstate.CoinsTip(), m_pool),
    437-        m_active_chainstate(active_chainstate)
    438+        m_active_chainstate(active_chainstate),
    439+        m_signals{signals}
    


    maflcko commented at 1:16 pm on January 16, 2024:
    nit in the first commit: Could drop signals and use active_chainstate.GetMainSignals() instead?
  48. DrahtBot added the label CI failed on Jan 16, 2024
  49. in src/node/interfaces.cpp:773 in cfb94ef2b3 outdated
    770     }
    771     void waitForNotificationsIfTipChanged(const uint256& old_tip) override
    772     {
    773         if (!old_tip.IsNull() && old_tip == WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip()->GetBlockHash())) return;
    774-        SyncWithValidationInterfaceQueue();
    775+        m_node.main_signals->SyncWithValidationInterfaceQueue();
    


    maflcko commented at 2:34 pm on January 16, 2024:
    Wouldn’t it be better to use existing chainman references to get the signals reference, instead of using unchecked pointer dereferences? (Same in all other places)
  50. in src/node/context.h:76 in cfb94ef2b3 outdated
    72@@ -72,6 +73,7 @@ struct NodeContext {
    73     std::function<void()> rpc_interruption_point = [] {};
    74     std::unique_ptr<KernelNotifications> notifications;
    75     std::atomic<int> exit_status{EXIT_SUCCESS};
    76+    std::unique_ptr<CMainSignals> main_signals;
    


    maflcko commented at 2:40 pm on January 16, 2024:
    nit: Any reason to call it “main”, when main.cpp has long been split up and renamed to validation.cpp? Could call it validation_signals, or leave it as is.
  51. maflcko commented at 2:42 pm on January 16, 2024: member
    Left some questions, feel free to ignore.
  52. TheCharlatan force-pushed on Jan 17, 2024
  53. TheCharlatan commented at 8:21 am on January 17, 2024: contributor

    Thank you for the review @maflcko,

    Updated d63b2e88780dc78fd531b053653361a0bf3fcbea -> 5755454fb5cc4067fc94e2682116e0fc5c9dfc58 (noGlobalSignals_1 -> noGlobalSignals_2, compare)

    • Added a new first scripted-diff commit for renaming MainSignals to ValidationSignals as suggested by @maflcko’s comment_1 and comment_2. Since most of the touched lines in the scripted-diff end up being touched anyway, this seems like a good opportunity for a rename.
    • Addressed @maflcko’s comment, making the diffs a bit more move-only.
    • Addressed @maflcko’s comment, consistently checking if node.main_signals is initialized in init.cpp’s Shutdown().
    • Addressed @maflcko’s comment, dropping m_signals member in MemPoolAccept and instead using active_chainstate.GetValidationSignals().
    • Addressed @maflcko’s comment, using chainman.m_signals reference instead of the m_node.main_signals unique_ptr. Also applied this suggestion in some other places where references to the signals are available.
  54. DrahtBot removed the label CI failed on Jan 17, 2024
  55. in src/index/base.cpp:402 in a8267363f2 outdated
    398@@ -399,7 +399,7 @@ bool BaseIndex::StartBackgroundSync()
    399 
    400 void BaseIndex::Stop()
    401 {
    402-    UnregisterValidationInterface(this);
    403+    m_chain->context()->validation_signals->UnregisterValidationInterface(this);
    


    maflcko commented at 1:21 pm on January 17, 2024:
    second commit: I still don’t understand why sometimes a nullptr check is done and sometimes not

    TheCharlatan commented at 2:43 pm on January 17, 2024:
    I’m also not sure what to do with this one. We seem to be fine with not doing nullptr checks for very similar code, e.g. base.cpp:89, so I did not include any checks here. Maybe it is better to add a reference to the signals to BaseIndex?

    maflcko commented at 2:48 pm on January 17, 2024:

    We seem to be fine with not doing nullptr checks for very similar code, e.g. base.cpp:89

    That isn’t run during shutdown, but during start, so the code path should be exercised in tests. Also, it is only “temporary”, so will be removed in any case. Whereas the code here is run during shutdown, for which it is hard (impossible?) to test all code path permutations. Also it isn’t temporary, is it?


    TheCharlatan commented at 3:07 pm on January 17, 2024:
    Right, not temporary, and in the Stop case we should guard against early destruction of the signals in the Shutdown procedure. I’ll add an Assert for this case.
  56. in src/init.cpp:1512 in a8267363f2 outdated
    1508@@ -1504,9 +1509,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    1509     LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
    1510 
    1511     for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
    1512-        node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
    1513+        node.mempool = std::make_unique<CTxMemPool>(mempool_opts, *Assert(node.validation_signals));
    


    maflcko commented at 2:16 pm on January 17, 2024:
    What is the point of asserting here, when previously it wasn’t (and already ran into UB)?

    maflcko commented at 2:17 pm on January 17, 2024:
    Maybe assert in the first line of this function and create a reference?
  57. maflcko commented at 2:17 pm on January 17, 2024: member
    The assertions still seem a bit arbitrary
  58. TheCharlatan force-pushed on Jan 17, 2024
  59. TheCharlatan commented at 4:34 pm on January 17, 2024: contributor

    5755454fb5cc4067fc94e2682116e0fc5c9dfc58 -> 487b12e18ce007379a997da48b5ee97f459f6342 (noGlobalSignals_2 -> noGlobalSignals_3, compare)

    • Addressed @maflcko’s comment, adding Assert to guard against early destruction of the signals in base.cpp.
    • Addressed @maflcko’s comment, creating a reference of the signals at the top of AppInitMain.
    • Applied the same pattern to the scheduler in AppInitMain.
  60. ryanofsky commented at 9:57 pm on January 17, 2024: contributor

    Concept ACK. This change removes removes g_signals and lets kernel applications call validation functions with separate schedulers so signals from different functions don’t need to interfere with each other. It also lets kernel applications define their own scheduling logic, which may be useful if they want to control when notifications are processed.

    I think naming and organization could be improved though. Like kernel::ValidationInterfaceQueue seems seems like it should be called util::SchedulerInterface (since it is an abstract class and doesn’t have to do with validation), and DummyQueue seems like it should be called util::ImmediateScheduler (since it is a fully functional class and not a dummy object).

    I’m not totally sure that ImmediateScheduler will be sufficient for libbitcoinkernel applications, and seems possible application may want to use SingleThreadedSchedulerClient instead (which I would call SerialScheduler) instead of an immediate scheduler, to avoiding having to spawn their own threads and deal with potential deadlocks. But it seems fine to exclude for now since we can always include it later if there is a use-case.

    EDIT: If we want to avoid “Scheduler” in the interface name because the interface doesn’t let the caller control scheduling (the only scheduling option for callers is whenever) you could replace Scheduler in suggestions above with something like TaskQueue, WorkQueue, CallbackRunner, etc. But I think Scheduler is ok because the class controls scheduling even if the caller doesn’t.

  61. TheCharlatan force-pushed on Jan 18, 2024
  62. TheCharlatan commented at 10:32 am on January 18, 2024: contributor

    Thank you for looking this over @ryanofsky,

    Updated 487b12e18ce007379a997da48b5ee97f459f6342 -> e3592ee55756c08bda3075b54cbff719d3fb964f (noGlobalSignals_3 -> noGlobalSignals_4, compare)

    • Addressed @ryanofsky’s comment, renaming ValidationInterfaceQueue to TaskQueueInterface and DummyQueue to ImmediateTaskQueue.
    • Addressed @ryanofsky’s comment, moving ImmediateTaskQueue and TaskQueueInterface to their own modules in util/.
  63. in src/bitcoin-chainstate.cpp:80 in 0fd494c4a8 outdated
    76@@ -77,7 +77,7 @@ int main(int argc, char* argv[])
    77     // Gather some entropy once per minute.
    78     scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
    79 
    80-    GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
    81+    GetValidationSignals().RegisterBackgroundSignalScheduler(scheduler);
    


    ryanofsky commented at 3:45 pm on January 18, 2024:

    In commit “scripted-diff: Rename MainSignals to ValidationSignals” (0fd494c4a856d7aefd4fd94a4d5f18ab5e59c2a2)

    Not important, but I think it would be a little nicer if scripted diff was last commit instead of first commit, because it’s hard to tell whether renames are good or bad when the code is in flux, and better when you can see that names match state of the final code. Moving the scripted diff would also reduce churn within the PR so commit diffs are smaller and lines like this (which are being completely replaced later) do not change multiple times.

  64. in src/bench/wallet_balance.cpp:42 in 6ffa572edd outdated
    38@@ -39,7 +39,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
    39         generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
    40         generatetoaddress(test_setup->m_node, ADDRESS_WATCHONLY);
    41     }
    42-    SyncWithValidationInterfaceQueue();
    43+    test_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
    


    ryanofsky commented at 3:58 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    These wallet tests should be calling wallet.chain().waitForNotificationsIfTipChanged({}) instead of test_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue. This would make them more realistic and less tied to test and node internals.

  65. in src/wallet/test/util.cpp:74 in 6ffa572edd outdated
    68@@ -69,9 +69,9 @@ std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
    69     return TestLoadWallet(std::move(database), context, options.create_flags);
    70 }
    71 
    72-void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
    73+void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet, ValidationSignals& validation_signals)
    74 {
    75-    SyncWithValidationInterfaceQueue();
    76+    validation_signals.SyncWithValidationInterfaceQueue();
    


    ryanofsky commented at 4:02 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Mentioned earlier, but you should drop the new parameter here and just call wallet->chain().waitForNotificationsIfTipChanged({}) instead of SyncWithValidationInterfaceQueue, for the same reasons

  66. in src/bitcoin-chainstate.cpp:131 in 6ffa572edd outdated
    127@@ -126,7 +128,7 @@ int main(int argc, char* argv[])
    128         .notifications = chainman_opts.notifications,
    129     };
    130     util::SignalInterrupt interrupt;
    131-    ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
    132+    ChainstateManager chainman{interrupt, chainman_opts, blockman_opts, validation_signals};
    


    ryanofsky commented at 4:05 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Would suggest organizing the parameters {validation_signals, interrupt, chainman_opts, blockman_opts} so ChainstateManager dependencies are first and ChainstateManager options are last. Also so dependencies and options are both ordered from high level to low level.

  67. in src/index/base.cpp:92 in 6ffa572edd outdated
    88@@ -89,7 +89,7 @@ bool BaseIndex::Init()
    89         return &m_chain->context()->chainman->GetChainstateForIndexing());
    90     // Register to validation interface before setting the 'm_synced' flag, so that
    91     // callbacks are not missed once m_synced is true.
    92-    RegisterValidationInterface(this);
    93+    m_chain->context()->validation_signals->RegisterValidationInterface(this);
    


    ryanofsky commented at 4:08 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Note: This is ugly, but #24230 replaces it with m_chain->attachChain(). Other changes to indexing code in this PR, which also don’t look great, have similar replacements.

  68. in src/init.cpp:1513 in 6ffa572edd outdated
    1509@@ -1504,9 +1510,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    1510     LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
    1511 
    1512     for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
    1513-        node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
    1514+        node.mempool = std::make_unique<CTxMemPool>(mempool_opts, validation_signals);
    


    ryanofsky commented at 4:13 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Slight preference for passing validation_signals dependency parameter first and mempool_opts options parameter second, for consistency with ChainstateManager, and since signals dependency is required in theory it should be possible to omit the options parameter.

    It might also be worth asking whether the signals parameter actually should be required, since mempool class does not need to send signals to function. We could add a ValidationSignals* option to CTxMemPool::Options that is allowed to be null, as an alternative to making this a required parameter.


    TheCharlatan commented at 11:26 am on January 19, 2024:

    It might also be worth asking whether the signals parameter actually should be required.

    It is used in CTxMemPool methods, so should be required, no? Or are you saying these classes should be given the option to not be sending signals in the first place? In that case, I don’t see what the difference between the mempool and the other validation classes is. The signals could be optional in the chainman too.


    ryanofsky commented at 12:23 pm on January 19, 2024:

    re: #28960 (review)

    The signals could be optional in the chainman too.

    Yes, absolutely. The question is in cases where libbitcoinkernel applications don’t care about receiving signals (if maybe just they are just validating a block), should they be forced to declare ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()}; and pass a validation_signals parameter to the ChainstateManager object that won’t do any real work. Eliminating this requirement might be good for libbitocoinkernel usability, but it would make validation code a little more verbose, because it would need to check if the signals object is null before sending signal. I don’t have a real opinion about this, so I think the current approach seems pretty reasonable.

    The mempool case is slightly different though. It sends fewer signals than validation code, and maybe requiring a new CTxMempool constructor argument will add pointless verbosity to mempool tests. So I think the mempool case is more worth thinking about and maybe getting some feedback from others familiar the mempool code. They may prefer to let this be optional.

  69. in src/node/interfaces.cpp:102 in 6ffa572edd outdated
     98@@ -99,6 +99,7 @@ class NodeImpl : public Node
     99         if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false;
    100         if (!AppInitParameterInteraction(args())) return false;
    101 
    102+        m_context->validation_signals = std::make_unique<ValidationSignals>();
    


    ryanofsky commented at 4:17 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Note: At some point we should see if it makes sense to deduplicate this function with AppInit and BasicTestingSetup::BasicTestingSetup since all three functions are doing very similar things.

  70. in src/node/interfaces.cpp:467 in 6ffa572edd outdated
    464+    ValidationSignals& m_signals;
    465+
    466 public:
    467-    explicit NotificationsHandlerImpl(std::shared_ptr<Chain::Notifications> notifications)
    468-        : m_proxy(std::make_shared<NotificationsProxy>(std::move(notifications)))
    469+    explicit NotificationsHandlerImpl(std::shared_ptr<Chain::Notifications> notifications, ValidationSignals& signals)
    


    ryanofsky commented at 4:20 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Would again suggest making make signals parameter first. signals is the hard dependency and input, notifications is the option and output.

  71. in src/node/interfaces.cpp:464 in 6ffa572edd outdated
    459@@ -459,17 +460,20 @@ class NotificationsProxy : public CValidationInterface
    460 
    461 class NotificationsHandlerImpl : public Handler
    462 {
    463+private:
    464+    ValidationSignals& m_signals;
    


    ryanofsky commented at 4:28 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    m_signals declaration should be moved directly above m_proxy declaration. IMO it is a bad practice to mix up method declarations and variable declarations in a class definition. The most important thing you can know about a class is what it information contains and what it represents, and that is difficult to do if you can’t easily see a list of its member variables.

    Additionally, all members of these classes should all be public to reduce boilerplate:

    https://github.com/bitcoin/bitcoin/blob/6ffa572edd9693a3217eb201b9d147b44c5b4eb5/src/node/interfaces.cpp#L75-L76

  72. in src/test/validationinterface_tests.cpp:75 in 6ffa572edd outdated
    72-        GetValidationSignals().BlockChecked(block, state);
    73+        m_signal.BlockChecked(block, state);
    74     }
    75     std::function<void()> m_on_call;
    76     std::function<void()> m_on_destroy;
    77+    ValidationSignals& m_signal;
    


    ryanofsky commented at 4:37 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Any particular reason this is called m_signal not m_signals like the rest of the code? It is true that the Call method only sends one signal, but that is a property of the Call method, not the class. The Call method could easily send multiple signals, or another method could.

  73. in src/txmempool.h:401 in 6ffa572edd outdated
    396@@ -396,6 +397,9 @@ class CTxMemPool
    397     using Limits = kernel::MemPoolLimits;
    398 
    399     uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
    400+
    401+    ValidationSignals& m_signals;
    


    ryanofsky commented at 4:44 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    I think it would make sense to move this member variable declaration after mapDeltas and before m_max_size_bytes member variable declarations below. Both m_signals and m_max_size_bytes are public members that are externally controlled (passed in as constructor arguments) so they would seem to belong together.

  74. in src/validation.cpp:1223 in 6ffa572edd outdated
    1219@@ -1220,7 +1220,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
    1220                                                        args.m_bypass_limits, args.m_package_submission,
    1221                                                        IsCurrentForFeeEstimation(m_active_chainstate),
    1222                                                        m_pool.HasNoInputsOf(tx));
    1223-        GetValidationSignals().TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
    1224+        m_active_chainstate.GetValidationSignals().TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
    


    ryanofsky commented at 4:48 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    It seems like it would be less verbose and more appropriate to use m_pool.m_signals instead of m_active_chainstate.GetValidationSignals() here and below on line 1272

  75. in src/validation.cpp:1708 in 6ffa572edd outdated
    1704@@ -1705,6 +1705,11 @@ const CBlockIndex* Chainstate::SnapshotBase()
    1705     return m_cached_snapshot_base;
    1706 }
    1707 
    1708+ValidationSignals& Chainstate::GetValidationSignals()
    


    ryanofsky commented at 4:53 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    Maybe consider eliminating this and just using m_chainman.m_signals directly. IMO that would be clearer and more consistent than having code occasionally call this method, and this method isn’t actually called very many places.

  76. in src/validationinterface.h:166 in 6ffa572edd outdated
    162+ * A std::unordered_map is used to track what callbacks are currently
    163+ * registered, and a std::list is used to store the callbacks that are
    164+ * currently registered as well as any callbacks that are just unregistered
    165+ * and about to be deleted when they are done executing.
    166+ */
    167+class ValidationSignalsImpl
    


    ryanofsky commented at 5:00 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    I don’t see a reason this class should be to be moved to the header, instead of just forward-declared like previously.

    I’m guessing maybe you were seeing link errors due to the std::unique_ptr<ValidationSignalsImpl> member below? If so this can be fixed by adding ValidationSignals(); ~ValidationSignals(); to the header file and definitions to the cpp file. That way the compiler will not try to generate constructors and destructors while processing the header file and complain about the incomplete ValidationSignalsImpl definition.

  77. in src/validationinterface.h:178 in 6ffa572edd outdated
    212@@ -209,6 +213,43 @@ class ValidationSignals {
    213 
    214     size_t CallbacksPending();
    215 
    216+    /** Register subscriber */
    


    ryanofsky commented at 5:15 pm on January 18, 2024:

    In commit “[refactor] De-globalize ValidationSignals” (6ffa572edd9693a3217eb201b9d147b44c5b4eb5)

    This commit is very large, and would suggest splitting it up. The changes to validationinterface.h and validationinterface.cpp are unlike the other changes in the commit, and it would be nice if they could be done separately in an earlier commit. All the other changes are basically small 1-line replacements and insertions, while the validationinterface.h and validationinterface.cpp changes are moving around larger blocks of code.

    It should be pretty easy to split this commit up by adding the new ValidationSignals methods with documention in an the earlier commit, while keeping the old standalone functions as one-line wrappers without documentation, and then deleting the wrappers in the next commit.

  78. in src/validationinterface.h:204 in 06191f3899 outdated
    200@@ -201,13 +201,11 @@ class ValidationSignalsImpl
    201 
    202 class ValidationSignals {
    203 private:
    204-    std::unique_ptr<ValidationSignalsImpl> m_internals;
    205+    ValidationSignalsImpl m_internals;
    


    ryanofsky commented at 5:34 pm on January 18, 2024:

    In commit “[refactor] Make MainSignalsImpl RAII styled” (06191f38997a02460482efa6b519607406fe5c44)

    Ok, looking at this diff, now I understand why ValidationSignalsImpl was moved from the .cpp to the .h in the previous commit.

    But I would not recommend this change, because it makes the previous diff and this diff both substantially larger than they need to be, and it makes the code organization nonsensical.

    Previously, the code was using a pointer-to-implementation pattern to keep the external class definition smaller and more stable. Arguably there’s isn’t a reason to do this anymore, because the external class isn’t a global variable that is initialized before main() runs anymore. But the logical way to clean up it would be to consolidate the two class definitions into a single class, not just embed one class inside the other class.

    I think the best thing to do here would be to keep this m_internals as a unique_ptr to reduce the size of this PR and not make unnecessary changes. You can still achieve the goal of making ValidationSignals class an RAII class by just doing m_internals = std::make_unique<ValidationSignalsImpl>(scheduler); in the constructor rather than in a separate RegisterBackgroundSignalScheduler` method. This would eliminate most of the diffs in this commit and keep the ValidationSignals implementation coherent.

  79. in src/validationinterface.h:201 in 3a6241064e outdated
    199     ValidationSignalsImpl m_internals;
    200 
    201+    // We are not allowed to assume the scheduler only runs in one thread,
    202+    // but must ensure all callbacks happen in-order, so we end up creating
    203+    // our own queue here :(
    204+    SingleThreadedSchedulerClient m_schedulerClient;
    


    ryanofsky commented at 5:49 pm on January 18, 2024:

    In commit “[refactor] Move scheduler client to CMainSignals” (3a6241064ed8bc35d7e159978d40049ed3680a44)

    I don’t think this change makes sense, and would suggest reverting. The point of the internals class is to contain the internals, following the pointer-to-implementation pattern (https://en.cppreference.com/w/cpp/language/pimpl) and keeping the ValidationSignals internal reliance on the scheduder out of the header, and out of view of callers.

    If some of the internals are inside the internals class and other internals are outside of the internals class, it creates an arbitrary and hard to explain inconsistency. There should be some real benefits to this change for it to make sense.

  80. in src/util/immediate_task_queue.h:15 in e3592ee557 outdated
    10+#include <cstddef>
    11+#include <functional>
    12+
    13+namespace util {
    14+
    15+class ImmediateTaskQueue : public TaskQueueInterface
    


    ryanofsky commented at 5:58 pm on January 18, 2024:

    In commit “kernel: Remove dependency on CScheduler” (e3592ee55756c08bda3075b54cbff719d3fb964f)

    I think immediate_task_queue.h and task_queue_interface.h should be consolidated into a single task_queue.h so functionality provided is clear and discoverable. Both classes are very small and lightweight, and the interface class is header-only, so it should not introduce any build overhead or any significant compile overhead to combine them. It would also be good to mention in a comment that other implementations of the task queue interface are possible, such as the SingleThreadedSchedulerClient implementation in scheduler.cpp.

    Thinking more about naming, I think having “queue” in the name is not great because it implies an order of execution and existence of a queue which may not exist. I think a better name would be TaskRunner as the only thing the interface provides is a way to run tasks without control over when and how they will be run.

    I think there should also be a scripted diff renaming SingleThreadedSchedulerClient to SerialTaskRunner to keep class names consistent. And renaming AddToProcessQueue to Insert or insert, EmptyQueue to Clear or clear and CallbacksPending to Size or size to be consistent with c++ container conventions.

  81. in contrib/devtools/test_deterministic_coverage.sh:20 in e3592ee557 outdated
    16@@ -17,21 +17,21 @@ NON_DETERMINISTIC_TESTS=(
    17     "blockfilter_index_tests/blockfilter_index_initial_sync"  # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... }
    18     "coinselector_tests/knapsack_solver_test"                 # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2))
    19     "fs_tests/fsbridge_fstream"                               # deterministic test failure?
    20-    "miner_tests/CreateNewBlock_validity"                     # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
    21+    "miner_tests/CreateNewBlock_validity"                     # validation.cpp: if (GetValidationSignals().CallbacksPending() > 10)
    


    ryanofsky commented at 6:35 pm on January 18, 2024:
    Probably not important, but the comments in this file become slightly out of date because the final line in validation.cpp looks like if (signals.CallbacksPending() > 10)
  82. ryanofsky approved
  83. ryanofsky commented at 6:49 pm on January 18, 2024: contributor

    Code review ACK e3592ee55756c08bda3075b54cbff719d3fb964f. Nice improvement eliminating g_signals and letting libbitcoinkernel application code control where signals are sent and how they are processed. (I think this could be mentioned in the commit description. I think it is a bigger accomplishment than removing a small dependency on CScheduler code).

    This PR is definitely an improvement over status the quo, but I did make a number of suggestions below to improve internal consistency and naming in the new code, so it would be great if you could consider these.

  84. DrahtBot requested review from jamesob on Jan 18, 2024
  85. TheCharlatan force-pushed on Jan 19, 2024
  86. TheCharlatan commented at 11:22 am on January 19, 2024: contributor

    Thank you for the review and detailed comments @ryanofsky!

    Rebased e3592ee55756c08bda3075b54cbff719d3fb964f -> 2ef630f3e49f11cbae7abe2dc31c2a908bb5e6ba (noGlobalSignals_4 -> noGlobalSignals_5, compare)

    Updated 2ef630f3e49f11cbae7abe2dc31c2a908bb5e6ba -> 492ff2c504d1da3f4ae8704a5485a286ee76f0d9 (noGlobalSignals_5 -> noGlobalSignals_6, compare)

    • Addressed @ryanofsky’s comment, removing the renaming of GetMainSignals(), but leaving the current commit order intact. Not renaming GetMainSignals() reduces churn and is not useful in any case, since it is removed later on.
    • Addressed @ryanofsky’s comment_1 and comment_2, using wallet->chain().waitForNotificationsIfTipChanged() instead of test_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue().
    • Addressed @ryanofsky’s comment_1, comment_2, comment_3, comment_4, comment_5, fixing ordering of the various signals declarations.
    • Addressed @ryanofsky’s comment, fixing naming from m_signal to m_signals in the validationinterface_tests.
    • Addressed @ryanofsky’s comment_1, comment_2, removing the GetValidationSignals() helper in ChainState and replacing it with direct access to available m_signals references.
    • Addressed @ryanofsky’s comment_1, comment_2, no longer moving ValidationSignalsImpl to the header.
    • Addressed @ryanofsky’s comment, splitting the second commit de-globalizing the signals into two commits; one for moving functions into the ValidationSignals class, and another for actually removing the g_signals.
    • Addressed @ryanofsky’s comment, dropping the commit that moved the scheduler client to the ValidationSignals. Note that this patch set still retains the move of the m_serial_task_runner to the ValidationSignals in the last commit. This should be fine, since no additional details are leaked, and it makes the responsibilities of ValidationSignalsImpl more singular.
    • Addressed @ryanofsky’s comment, adding a scripted-diff commit for renaming the SingleThreadedScheduler to SerialTaskRunner and correspondingly adjust its related class methods.
    • Addressed @ryanofsky’s comment, merging the two previously introduced headers into one util/task_queue.h.
  87. DrahtBot added the label CI failed on Jan 19, 2024
  88. DrahtBot commented at 12:24 pm on January 19, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/20654065729

  89. TheCharlatan force-pushed on Jan 19, 2024
  90. DrahtBot removed the label CI failed on Jan 19, 2024
  91. in src/init.cpp:1517 in e7c0608662 outdated
    1515@@ -1506,7 +1516,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    1516 
    1517             // Drain the validation interface queue to ensure that the old indexes
    1518             // don't have any pending work.
    1519-            SyncWithValidationInterfaceQueue();
    1520+            Assert(node.validation_signals)->SyncWithValidationInterfaceQueue();
    


    ryanofsky commented at 2:36 pm on January 19, 2024:

    In commit “refactor: De-globalize g_signals” (e7c060866267fb99c77f80a959bfeb5204d1d3a5)

    I think it would be more consistent to use the local validation_signals variable here instead of the node variable. Same on line 1608 below


    TheCharlatan commented at 4:11 pm on January 19, 2024:
    Would have to capture it then in the lambda, which seems less clear to me.
  92. in src/node/context.h:77 in e7c0608662 outdated
    72@@ -72,6 +73,7 @@ struct NodeContext {
    73     std::function<void()> rpc_interruption_point = [] {};
    74     std::unique_ptr<KernelNotifications> notifications;
    75     std::atomic<int> exit_status{EXIT_SUCCESS};
    76+    std::unique_ptr<ValidationSignals> validation_signals;
    


    ryanofsky commented at 2:51 pm on January 19, 2024:

    In commit “refactor: De-globalize g_signals” (e7c060866267fb99c77f80a959bfeb5204d1d3a5)

    Maybe move this declaration up next to the notifications member since both are ways of receiving notifications from validation code.

    • NodeContext::notifications sends high level messages about sync status and errors and warnings, which are blocking and only received by a single recipient (the UI).
    • NodeContext::validation_signals sends lower messages about blocks and transactions, which are non-blocking, and are sent to multiple recipients (wallets, indexes, and the fee estimator).

    Might be useful to compare these in a code comment.

  93. in src/test/validationinterface_tests.cpp:55 in e7c0608662 outdated
    51@@ -52,8 +52,8 @@ BOOST_AUTO_TEST_CASE(unregister_validation_interface_race)
    52 class TestInterface : public CValidationInterface
    53 {
    54 public:
    55-    TestInterface(std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr)
    56-        : m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy))
    57+    TestInterface(ValidationSignals& signal, std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr)
    


    ryanofsky commented at 2:55 pm on January 19, 2024:

    In commit “refactor: De-globalize g_signals” (e7c060866267fb99c77f80a959bfeb5204d1d3a5)

    Possible inconsistency between signal/signals here

  94. in src/txmempool.h:400 in e7c0608662 outdated
    396@@ -396,6 +397,7 @@ class CTxMemPool
    397     using Limits = kernel::MemPoolLimits;
    398 
    399     uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
    400+
    


    ryanofsky commented at 2:57 pm on January 19, 2024:

    In commit “refactor: De-globalize g_signals” (e7c060866267fb99c77f80a959bfeb5204d1d3a5)

    Maybe some unintended whitespace changes here and below

  95. in src/init.cpp:1143 in 552a653b99 outdated
    1141@@ -1142,19 +1142,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    1142         return InitError(strprintf(_("Unable to allocate memory for -maxsigcachesize: '%s' MiB"), args.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_BYTES >> 20)));
    1143     }
    1144 
    1145-    assert(!node.scheduler);
    1146-    node.scheduler = std::make_unique<CScheduler>();
    1147-
    1148     // Start the lightweight task scheduler thread
    1149-    node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { node.scheduler->serviceQueue(); });
    


    ryanofsky commented at 3:02 pm on January 19, 2024:

    In commit “refactor: De-globalize g_signals” (e7c060866267fb99c77f80a959bfeb5204d1d3a5)

    I see 2 changes to this function which are necessary, and 9 changes which are not needed. It’s not important, but I’d suggest reverting all the changes in this function except the on lines 1145-6 above and lines 1167-8 below. Moving the std::make_unique<CScheduler>() call is the most important change here, and the other changes do not seem like a significant cleanup and are distracting.


    TheCharlatan commented at 4:45 pm on January 19, 2024:
    Leaving this for now. The pattern was suggested here #28960 (review) and I think it makes sense to apply it to the scheduler as well.
  96. in src/scheduler.cpp:185 in 46defde7a6 outdated
    181@@ -182,7 +182,7 @@ void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func
    182     MaybeScheduleProcessQueue();
    183 }
    184 
    185-void SingleThreadedSchedulerClient::EmptyQueue()
    186+void SerialTaskRunner::clear()
    


    ryanofsky commented at 3:11 pm on January 19, 2024:

    In commit “scripted-diff: Rename SingleThreadedSchedulerClient to SerialTaskRunner” (46defde7a60afe186629481b738225919f874342)

    I think I made a bad suggestion earlier to call this method “clear”. I mistakenly thought this was discarding callbacks, but it appears to wait for all the callbacks to run. In light of this, I think it would be better to call this method “flush” or “wait”.

  97. in src/util/immediate_task_runner.cpp:13 in 492ff2c504 outdated
     8+
     9+namespace util {
    10+
    11+void ImmediateTaskRunner::insert(std::function<void()> func)
    12+{
    13+    func();
    


    ryanofsky commented at 3:17 pm on January 19, 2024:

    In commit “kernel: Remove dependency on CScheduler” (492ff2c504d1da3f4ae8704a5485a286ee76f0d9)

    I think it would be clearer just to inline these methods and eliminate this file. If not eliminating this file, I would also consider it renaming it to just util/task_runner.cpp so it easier to find next to the header file.

  98. in src/validationinterface.h:168 in 492ff2c504 outdated
    161@@ -160,8 +162,13 @@ class ValidationSignals {
    162 private:
    163     std::unique_ptr<ValidationSignalsImpl> m_internals;
    164 
    165+    // We are not allowed to assume the scheduler only runs in one thread,
    166+    // but must ensure all callbacks happen in-order, so we end up creating
    167+    // our own queue here :(
    168+    std::unique_ptr<util::TaskRunnerInterface> m_serial_task_runner;
    


    ryanofsky commented at 3:24 pm on January 19, 2024:

    In commit “kernel: Remove dependency on CScheduler” (492ff2c504d1da3f4ae8704a5485a286ee76f0d9)

    I’d consider the leaving this declaration inside the ValidationSignalsImpl class instead of moving it here. This would adhere more closely to the pointer-to-implementation pattern and ensure future ValidationSignalsImpl methods can access all private members of the class, not just some of them. It would also just move less code around unnecessarily.


    TheCharlatan commented at 4:19 pm on January 19, 2024:

    ensure future ValidationSignalsImpl methods can access all private members of the class, not just some of them.

    I don’t follow here. ValidationSignalsImpl is not a friend of ValidationSignals, so I’m not sure which access you are talking about here. Moving it just seems more SOLID to me and I am not sure how this is violating the pimpl pattern, but I feel like I am missing something.


    ryanofsky commented at 11:26 pm on January 19, 2024:

    re: #28960 (review)

    Pointer-to-implementation is a c++ specific pattern. It’s a simple trick to divide a single class into two halves so that the private parts of the class are in the .cpp file rather than the .h file. It doesn’t have to do with SOLID principles or object oriented design.

    The typical way to define a class in c++ is

    0class MyClass
    1{
    2public:
    3   // MyClass public methods and members
    4private:
    5   // MyClass private methods and members
    6};
    

    This works well in most cases, but in some cases you don’t want to expose private parts of the class in the header. For example if you are trying to avoid adding private dependencies to the header, or trying to define a stable ABI. In these cases, you can use the pointer to implementation trick to divide MyClass into two parts, with the public part in the header file:

    0class MyClass
    1{
    2public:
    3   // MyClass public methods only
    4private:
    5   std::unique_ptr<MyClassImpl> m_impl;
    6};
    

    And the private part in the cpp file:

    0class MyClassImpl
    1{
    2       // MyClass private members and methods.
    3};
    

    The problem with f442a3a5b2a0a58bc263fbb9c87e8e4715de103a is that it is not following either of these two approaches, but instead taking a hybrid approach when private state is split up across both the public and private classes. This is bad, because if it means that the private methods only have access to part of the private state, not all of it.

    In OO terms, ValidationSignals and ValidationSignalsImpl do not represent separate classes. They just represent the public and private parts of a single class, that are declared in separate halves because of C++ headers weirdness that doesn’t exist in other languages.

    If you actually think that ValidationSignals and ValidationSignalsImpl should be separate OO classes, I think you will want to rename the ValidationSignalsImpl to reflect the actual role of this class rather than taking the name of the class which contains it.

  99. ryanofsky approved
  100. ryanofsky commented at 3:31 pm on January 19, 2024: contributor
    Code review ACK 492ff2c504d1da3f4ae8704a5485a286ee76f0d9. This looks great, and thanks for taking so many suggestions. I did make a few more suggestions, but none are important so feel free to ignore them.
  101. TheCharlatan force-pushed on Jan 19, 2024
  102. TheCharlatan commented at 5:02 pm on January 19, 2024: contributor

    Updated 492ff2c504d1da3f4ae8704a5485a286ee76f0d9 -> f442a3a5b2a0a58bc263fbb9c87e8e4715de103a (noGlobalSignals_6 -> noGlobalSignals_7, compare)

    • Addressed @ryanofsky’s comment, using appropriate variable for accessing signals in commit “De-globalize g_signals”
    • Addressed @ryanofsky’s comment, moving the declartion of validation_signals within NodeContext and adding some docstrings to the notifications and validation_signals fields.
    • Addressed @ryanofsky’s comment, missing consistency with naming signals in validationinterface_tests.
    • Addressed @ryanofsky’s comment, reverting unintended whitespace change
    • Addressed @ryanofsky’s comment, renaming SerialTaskRunner’s clear to flush.
    • Addressed @ryanofsky’s comment, removing immediate_task_runner and instead inlining its content.
  103. DrahtBot added the label CI failed on Jan 19, 2024
  104. ryanofsky approved
  105. ryanofsky commented at 11:29 pm on January 19, 2024: contributor
    Code review ACK f442a3a5b2a0a58bc263fbb9c87e8e4715de103a, but note that linter is failing in https://cirrus-ci.com/task/5386260651442176, I think due to a problem with the scripted-diff command. Only changes since last review were suggested changes (thanks!)
  106. TheCharlatan force-pushed on Jan 20, 2024
  107. TheCharlatan commented at 9:35 am on January 20, 2024: contributor

    Thank you @ryanofsky,

    Updated f442a3a5b2a0a58bc263fbb9c87e8e4715de103a -> 38e25f67b4b2aeb3dd13f4f9ffa689f47718aa17 (noGlobalSignals_7 -> noGlobalSignals_8, compare)

    • Fixup scripted-diff
    • Addressed @ryanofsky’s comment, moved TaskRunnerInterface unique pointer member to implementation class.
    • Reworded docstrings for task_runner.h a bit.
  108. DrahtBot removed the label CI failed on Jan 20, 2024
  109. TheCharlatan force-pushed on Jan 20, 2024
  110. TheCharlatan commented at 10:43 am on January 20, 2024: contributor

    Updated 38e25f67b4b2aeb3dd13f4f9ffa689f47718aa17 -> 22048c19e236e4b683f1c8192883545d5c68f793 (noGlobalSignals_8 -> noGlobalSignals_9, compare)

    • Improved scripted-diff to exclude renaming a method.
  111. furszy commented at 12:30 pm on January 20, 2024: member
    Concept ACK, will review soon.
  112. in src/validationinterface.cpp:284 in 4083720b33 outdated
    276@@ -273,3 +277,33 @@ void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::s
    277     LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
    278     m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
    279 }
    280+
    281+// These functions are temporary and will be removed in the following commit
    282+void RegisterValidationInterface(CValidationInterface* callbacks)
    283+{
    284+    GetMainSignals().RegisterValidationInterface(callbacks);
    


    ryanofsky commented at 3:17 pm on January 22, 2024:

    In commit “[refactor] Prepare for g_signals de-globalization” (4083720b33763ad2547a3e1ef9bb16cc979f5d31)

    I was confused about how this was compiling given “scripted-diff: Rename MainSignals to ValidationSignals” in the previous commit. But I guess it that commit did not actually rename the GetMainSignals function.

    Would maybe suggest clarifying that commit message to mention it only renames the main signals class not the function. And again, not important, but I think it would be a little better if the script diff renames were done last to avoid confusing intermediate states, and make it easier to determine whether new names make sense in the context of the final code.

  113. in src/test/util/setup_common.cpp:141 in 35ee50f762 outdated
    137@@ -138,7 +138,8 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
    138     InitLogging(*m_node.args);
    139     AppInitParameterInteraction(*m_node.args);
    140     LogInstance().StartLogging();
    141-    m_node.validation_signals = std::make_unique<ValidationSignals>();
    142+    m_node.scheduler = std::make_unique<CScheduler>();
    


    ryanofsky commented at 3:31 pm on January 22, 2024:

    In commit “[refactor] Make MainSignalsImpl RAII styled” (35ee50f762aee78944f563b74a5e9a677980400d)

    This change is moving the scheduler creation and RegisterBackgroundSignalScheduler assignment from ChainTestingSetup to BasicTestingSetup, but it is leaving the m_service_thread scheduler thread creation in ChainTestingSetup. This makes me wonder whether the validation_signals instance created here works, and whether it should be created in ChainTestingSetup instead of BasicTestingSetup. That would seem cleaner. Or if that is not possible, it would be helpful to have a comment here saying why the scheduler is needed here, how it works without a thread, and mentioning that ChainTestingSetup may create a thread for it later


    TheCharlatan commented at 3:56 pm on January 24, 2024:
    Good catch! I think the real problem here is though that the scheduler is now created too early, so I’ll revert moving its initialization out of AppInitMain and will instead leave it there and initialize the signals right after it. Then we can have all these components be initialized in ChainTestingSetup.
  114. in src/scheduler.cpp:132 in 70793430bb outdated
    128@@ -129,7 +129,7 @@ bool CScheduler::AreThreadsServicingQueue() const
    129 }
    130 
    131 
    132-void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue()
    133+void SerialTaskRunner::MaybeScheduleProcessQueue()
    


    ryanofsky commented at 3:35 pm on January 22, 2024:

    In commit “scripted-diff: Rename SingleThreadedSchedulerClient to SerialTaskRunner” (70793430bb072ceae589f4d44c49ce2c5db23216)

    Harmless, but s 'SinglethreadedSchedulerClient' 'SerialTaskRunner' '' is repeated twice in the scripted diff script


    TheCharlatan commented at 12:31 pm on January 24, 2024:
    This is on purpose (note the lower case t) to fix a documentation typo. I could have added a regex for doing it in one line, but that seems even less readable.
  115. ryanofsky approved
  116. ryanofsky commented at 4:10 pm on January 22, 2024: contributor

    Code review ACK 22048c19e236e4b683f1c8192883545d5c68f793

    Reflecting more on #28960 (review), I lean more towards opinion that ValidationSignals parameters passed to ChainstateManager and CTxMemPool should be optional rather than required. Two reasons:

    • Validation code is a hairball of interdependencies and classes that don’t really play the roles you would expect based on their names, so a change that lets them have fewer hard dependencies seems like it would make them easier to understand and keep separated.

    • Making validation signals optional would allow the signals pointer to be a member of mempool and chainstatemanager options structs, and avoid adding new constructor arguments. This seems nicer for using these classes in tests and potential libbitcoinkernel applications.

    But that is not a strong opinion, and I have no problem with the way the PR is written now. Assuming the PR will not be changed, I think it would be good to say something about making validation signals required in the PR description so reviewers do not take it as a foregone conclusion, and so there is something to point to if the issue is revisited later. Would suggest adding something like: “Design considerations: This PR is adding required ValidationSignals& parameters to ChainstateManager and CTxMemPool constructors, rather than optional ValidationSignals* pointers in their options structs. Both approaches would have been feasible, but making validation signals required keeps the implementations of the classes a little simpler, and avoids potential bugs if the options are not set when they should be.”

  117. DrahtBot requested review from furszy on Jan 22, 2024
  118. TheCharlatan force-pushed on Jan 24, 2024
  119. TheCharlatan commented at 3:56 pm on January 24, 2024: contributor

    Thank you for the continued feedback @ryanofsky!

    Updated 22048c19e236e4b683f1c8192883545d5c68f793 -> bdd985af62ff9e94490ecc701d6063eaab172add (noGlobalSignals_9 -> noGlobalSignals_10, compare)

    • Addressed @ryanofsky’s comment, moving the scripted-diff renaming MainSignals to ValidationSignals to after the de-globalization and RAII-styling.
    • Addressed @ryanofsky’s comment, moving the MainSignals initialization to AppInitMain, which is closer to where the other validation components are initialized as well. This also reverts moving the CScheduler initialization out of AppInitMain.
    • Addressed @ryanofsky’s comment, adding a new first commit for making the signals in CTxMemPool and ChainstateManager optional. I convinced myself over the past few days that this might be more desirable.
  120. in src/test/util/setup_common.cpp:171 in dddaa962cf outdated
    167@@ -168,12 +168,11 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
    168 {
    169     const CChainParams& chainparams = Params();
    170 
    171+    m_node.scheduler = std::make_unique<CScheduler>();
    


    ryanofsky commented at 5:06 pm on January 24, 2024:

    In commit “[refactor] Make MainSignals RAII styled” (dddaa962cfbeb96a8148535e74e1b53c9c717376)

    Maybe unintended diff moving this line

  121. ryanofsky approved
  122. ryanofsky commented at 7:25 pm on January 24, 2024: contributor
    Code review ACK bdd985af62ff9e94490ecc701d6063eaab172add. Letting signals be optional and moving more initialization to AppInitMain seem like nice changes among other cleanups since last review
  123. TheCharlatan force-pushed on Jan 24, 2024
  124. TheCharlatan commented at 8:55 pm on January 24, 2024: contributor

    Updated bdd985af62ff9e94490ecc701d6063eaab172add -> 536429372dfd9759dc8f089962f3a15a94b54751 (noGlobalSignals_10 -> noGlobalSignals_11, compare)

    • Addresesd @ryanofsky’s comment, reverting unintended diff
    • Fixed a small formatting issue
  125. ryanofsky approved
  126. ryanofsky commented at 5:14 pm on January 25, 2024: contributor
    Code review ACK 536429372dfd9759dc8f089962f3a15a94b54751, just the minor changes mentioned above since last review.
  127. Julio-Rats referenced this in commit ba5f16e4a1 on Jan 26, 2024
  128. in src/validation.cpp:1225 in 65f6f8ff93 outdated
    1219@@ -1220,15 +1220,17 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
    1220                                                        args.m_bypass_limits, args.m_package_submission,
    1221                                                        IsCurrentForFeeEstimation(m_active_chainstate),
    1222                                                        m_pool.HasNoInputsOf(tx));
    1223-        GetMainSignals().TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
    1224+        if (m_pool.m_signals) {
    1225+            m_pool.m_signals->TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
    1226+        }
    


    furszy commented at 2:33 pm on January 29, 2024:
    This could be an if (!m_pool.m_signals) continue;, and be placed few lines above, at line 1217 (same for AcceptSingleTransaction).
  129. in src/test/util/txmempool.cpp:25 in 38d32ba2e9 outdated
    21@@ -22,7 +22,7 @@ CTxMemPool::Options MemPoolOptionsForTest(const NodeContext& node)
    22         // Default to always checking mempool regardless of
    23         // chainparams.DefaultConsistencyChecks for tests
    24         .check_ratio = 1,
    25-        .signals = &GetMainSignals(),
    26+        .signals = node.validation_signals.get(),
    


    furszy commented at 1:56 pm on January 31, 2024:

    In 38d32ba2e9:

    nit: I think that you could also remove the <validationinterface.h> include with this change.

  130. in src/test/fuzz/package_eval.cpp:148 in 38d32ba2e9 outdated
    144@@ -145,7 +145,7 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
    145     }
    146 
    147     auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
    148-    RegisterSharedValidationInterface(outpoints_updater);
    149+    g_setup->m_node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
    


    furszy commented at 2:02 pm on January 31, 2024:

    nit:

    0    node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
    

    (same for the ones below)

  131. in src/node/context.h:76 in 38d32ba2e9 outdated
    70@@ -70,7 +71,10 @@ struct NodeContext {
    71     interfaces::WalletLoader* wallet_loader{nullptr};
    72     std::unique_ptr<CScheduler> scheduler;
    73     std::function<void()> rpc_interruption_point = [] {};
    74+    //! Issues blocking calls about sync status, errors and warnings
    75     std::unique_ptr<KernelNotifications> notifications;
    76+    //! Issues non-blocking calls about blocks and transactions
    


    furszy commented at 2:21 pm on January 31, 2024:

    In 38d32ba2:

    This is not entirely accurate. BlockChecked and NewPoWValidBlock run synchronously.

  132. in src/node/transaction.cpp:107 in 38d32ba2e9 outdated
    100@@ -101,7 +101,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
    101                 // with a transaction to/from their wallet, immediately call some
    102                 // wallet RPC, and get a stale result because callbacks have not
    103                 // yet been processed.
    104-                CallFunctionInValidationInterfaceQueue([&promise] {
    105+                node.validation_signals->CallFunctionInValidationInterfaceQueue([&promise] {
    106                     promise.set_value();
    107                 });
    108                 callback_set = true;
    


    furszy commented at 2:28 pm on January 31, 2024:

    In 38d32ba2:

    What about setting callback_set=false if validation_signals is null?


    TheCharlatan commented at 4:03 pm on January 31, 2024:
    I’m not sure if this should be optional. Was thinking before about asserting here. Wdyt?

    ryanofsky commented at 5:11 pm on January 31, 2024:

    I’m not sure if this should be optional. Was thinking before about asserting here. Wdyt?

    I don’t see a benefit to aborting when the wait_callback option is true and there is no callback. There is no callback if the transaction is already in the mempool, and we are ok with ignoring the wait_callback option in that case. Would suggest changing line 95 to if (wait_callback && node.validation_signals)

  133. in src/test/fuzz/tx_pool.cpp:289 in 38d32ba2e9 outdated
    285@@ -286,7 +286,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
    286         std::set<CTransactionRef> removed;
    287         std::set<CTransactionRef> added;
    288         auto txr = std::make_shared<TransactionsDelta>(removed, added);
    289-        RegisterSharedValidationInterface(txr);
    290+        g_setup->m_node.validation_signals->RegisterSharedValidationInterface(txr);
    


    furszy commented at 2:38 pm on January 31, 2024:

    nit:

    0        node.validation_signals->RegisterSharedValidationInterface(txr);
    

    Same for the others below.

  134. furszy approved
  135. furszy commented at 2:53 pm on January 31, 2024: member
    Code review ACK 53642937. Only nits, nothing blocking. Nice work.
  136. TheCharlatan force-pushed on Jan 31, 2024
  137. TheCharlatan commented at 5:38 pm on January 31, 2024: contributor

    Thank you for the review @furszy!

    Updated 536429372dfd9759dc8f089962f3a15a94b54751 -> cb47c334e0b3d654b5b56ae762aa2a2c33ae2743 (noGlobalSignals_11 -> noGlobalSignals_12, compare)

    • Addressed @furszy’s comment, improving logic around optional m_signals in validation code a bit.
    • Addressed @furszy’s comment, dropping unneeded include
    • Addressed @furszy’s comment_1 and comment_2, using available node reference instead of g_setup->m_node pointer in the fuzz tests.
    • Addressed @furszy’s comment, applying ryanofsky’s suggestion for gating setting the callback.
  138. DrahtBot added the label CI failed on Jan 31, 2024
  139. DrahtBot commented at 7:20 pm on January 31, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/21074804475

  140. TheCharlatan force-pushed on Jan 31, 2024
  141. TheCharlatan commented at 8:43 pm on January 31, 2024: contributor

    Rebased cb47c334e0b3d654b5b56ae762aa2a2c33ae2743 -> 6752d218caeed1111f2520130c156b9ef42ba805 (noGlobalSignals_12 -> noGlobalSignals_13, compare)

  142. DrahtBot removed the label CI failed on Jan 31, 2024
  143. furszy approved
  144. furszy commented at 1:07 pm on February 1, 2024: member
    ACK 6752d218
  145. DrahtBot requested review from ryanofsky on Feb 1, 2024
  146. in contrib/devtools/test_deterministic_coverage.sh:20 in 09310dded4 outdated
    16@@ -17,21 +17,21 @@ NON_DETERMINISTIC_TESTS=(
    17     "blockfilter_index_tests/blockfilter_index_initial_sync"  # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... }
    18     "coinselector_tests/knapsack_solver_test"                 # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2))
    19     "fs_tests/fsbridge_fstream"                               # deterministic test failure?
    20-    "miner_tests/CreateNewBlock_validity"                     # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
    21+    "miner_tests/CreateNewBlock_validity"                     # validation.cpp: if (m_signals.CallbacksPending() > 10)
    


    ryanofsky commented at 2:46 pm on February 1, 2024:

    In commit “refactor: De-globalize g_signals” (09310dded4c063ae2a10d66082ba699a441a604d)

    These changes seem to be part of wrong commit. The lines of code that these are referencing changed in earlier commit fe891bead4fabb945c21a7110459e3e18952a08c, not this commit, and now use signals instead of m_signals.

  147. ryanofsky approved
  148. ryanofsky commented at 3:03 pm on February 1, 2024: contributor

    Code review ACK 6752d218caeed1111f2520130c156b9ef42ba805. Just some cleanups and clarifications suggested by furszy since last review.

    I think this PR is basically ready to merge, but there should be another ACK as it touches validation and some mempool code. The changes here should be very straightforward if any new reviewer wants to take a look.

    Also @jamesob you may want to reack. The PR got a little smaller and simpler since your last review.

  149. DrahtBot added the label Needs rebase on Feb 10, 2024
  150. TheCharlatan force-pushed on Feb 10, 2024
  151. TheCharlatan commented at 7:45 am on February 10, 2024: contributor

    Rebased 6752d218caeed1111f2520130c156b9ef42ba805 -> ed3ca5831515c26cf4242de9d13dbe0eb82f6d03 (noGlobalSignals_13 -> noGlobalSignals_14, compare)

    Updated ed3ca5831515c26cf4242de9d13dbe0eb82f6d03 -> c02fd0379c425f486f1b80a81962c1aa68b8a852 (noGlobalSignals_14 -> noGlobalSignals_15, compare)

    • Addressed @ryanofsky’s comment, fixed variable name in comment and moved the change to the correct commit.
  152. DrahtBot removed the label Needs rebase on Feb 10, 2024
  153. in src/txmempool.h:451 in c02fd0379c outdated
    447@@ -447,6 +448,8 @@ class CTxMemPool
    448 
    449     const Limits m_limits;
    450 
    451+    ValidationSignals* m_signals;
    


    vasild commented at 9:21 am on February 14, 2024:

    The pointer can be const to hint the reader and the compiler that it will never start pointing to something else after it has been initialized in the constructor.

    0    ValidationSignals* const m_signals;
    

    For example constructs like:

    0if (m_signals) {
    1    ...
    2    m_signals->whatever();
    3}
    

    are only safe because m_signals cannot change after the check.

    (but see other suggestions, if this is to be changed to shared_ptr)

  154. in src/validationinterface.cpp:99 in c02fd0379c outdated
     94@@ -94,77 +95,56 @@ class MainSignalsImpl
     95     }
     96 };
     97 
     98-static CMainSignals g_signals;
     99+ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)
    100+    : m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}
    101 
    102-void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler)
    


    vasild commented at 11:11 am on February 14, 2024:

    nit: in commit message of a38dfb797f175aab45352f85fa80371861b22cd5 [refactor] Prepare for g_signals de-globalization:

    s/ValidationSignals/CMainSignals/

  155. in src/bench/wallet_balance.cpp:43 in c02fd0379c outdated
    38@@ -39,7 +39,8 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
    39         generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
    40         generatetoaddress(test_setup->m_node, ADDRESS_WATCHONLY);
    41     }
    42-    SyncWithValidationInterfaceQueue();
    43+    // Calls SyncWithValidationInterfaceQueue
    44+    wallet.chain().waitForNotificationsIfTipChanged({});
    


    vasild commented at 11:57 am on February 14, 2024:

    nit, feel free to ignore: maybe uint256::ZERO is clearer?

    0    wallet.chain().waitForNotificationsIfTipChanged(uint256::ZERO);
    
  156. in src/node/interfaces.cpp:765 in c02fd0379c outdated
    761@@ -761,12 +762,12 @@ class ChainImpl : public Chain
    762     }
    763     std::unique_ptr<Handler> handleNotifications(std::shared_ptr<Notifications> notifications) override
    764     {
    765-        return std::make_unique<NotificationsHandlerImpl>(std::move(notifications));
    766+        return std::make_unique<NotificationsHandlerImpl>(*Assert(chainman().m_options.signals), std::move(notifications));
    


    vasild commented at 1:23 pm on February 14, 2024:

    Here a reference to signals is saved inside NotificationsHandlerImpl and is returned opaquely to the caller as a Handler. The caller has to make sure that the Handler they get does not live longer than chainman().m_options.signals but they have no idea about the latter. I understand that this is ok in the current code, but in general this is a recipe for disaster.

    A cleaner and safer approach is to change the type of ChainstateManagerOpts::signals from ValidationSignals* to std::shared_ptr<ValidationSignals> and just copy it around and save std::shared_ptr<ValidationSignals> inside NotificationsHandlerImpl instead of ValidationSignals&.


    ryanofsky commented at 1:20 pm on February 15, 2024:

    re: #28960 (review) @vasild, this seems like an argument to always use shared_ptr or unique_ptr everywhere and never use plain pointers or references. If you would not want a plain reference to be stored here, is there any case where you would want one to be stored?

    To me, the lifetime of the signals object seems pretty easy to determine and reason about. In bitcoind and bitcoin-qt, there is only one signals object, it is created when the application starts, and destroyed when the application ends. In libbitcoinkernel applications and tests, there may be be more than one signals object or no signal objects, but if signals are used, it should be easy for applications to make sure they outlive other objects like ChainstateManager or CTxMemPool that reference them, and it seems like this would happen naturally as a byproduct of handling the signals.

    I think by contrast using a shared_ptr here would be undesirable, because it would make lifetime of the signals object less predictable. I think it’s nice that NodeContext::validation_signals is a unique_ptr instead of a shared_ptr so we know exactly when it is created and destroyed, and in general I think of shared_ptr’s as something that should be used a last resort when the lifetime of an object truly is unpredictable and needs shared_ptr complexity and semantics. Both ways would work of course, but current approach seems better, IMO


    vasild commented at 4:45 pm on February 16, 2024:

    this seems like an argument to always use shared_ptr or unique_ptr everywhere and never use plain pointers or references.

    Raw pointers and references are ok to be used for arguments to functions. Or as automatic variables that obviously don’t outlive the referenced object (on-stack local variables inside the body of a function).

    If you would not want a plain reference to be stored here, is there any case where you would want one to be stored?

    If you store raw pointers or references as class members that rises the burden of lifetime management. It could quickly become dangerous. Maybe I would “want” to store that if it is super obvious that it is not going to outlive the target. Maybe if both are created and destroyed together. Smart pointers were introduced to solve issues with manual lifetime management.

    To me, the lifetime of the signals object seems pretty easy to determine and reason about …

    To you yes, but what about a random Bitcoin Core developer some time in the future? For me personally, what followed in that paragraph of explanation is not “easy”.

    I think by contrast using a shared_ptr here would be undesirable, because it would make lifetime of the signals object less predictable. I think it’s nice that NodeContext::validation_signals is a unique_ptr instead of a shared_ptr so we know exactly when it is created and destroyed …

    Why do you need to predict it? With a few shared_ptrs you simply don’t care and the code is safe from “use after free” bugs. That’s automatic lifetime management.


    ryanofsky commented at 5:44 pm on February 16, 2024:

    With a few shared_ptrs you simply don’t care and the code is safe from “use after free” bugs. That’s automatic lifetime management.

    Of course, thanks for clarifying. C++ is a very flexible language and you can certainly use it in the style you are suggesting where everything is basically garbage collected, and use after free bugs never happen. There are benefits to doing this, but also drawbacks. IMO, the main drawbacks of using shared_ptr are (1) that it can make lifetime of objects unpredictable, so it is hard to reason about when objects get destroyed and (2) that it tends to be viral, because as soon as one object is wrapped in a shared_ptr, references to outside state in the object also need to be use shared_ptr, because the lifetime of the object is unpredictable and you don’t want use-after-free bugs to happen internally. If you are ok with overhead/complexity/unpredictability of shared_ptr (https://ddanilov.me/shared-ptr-is-evil/) and having it spread, these things are not problems. But it would seem unfortunate to me to force users of the libbitcoinkernel library to use shared_ptrs when there is not an actual need for reference counting here.

    There are specific places where I would agree shared_ptrs and reference counting are very helpful, but unless I am misinterpreting, it sounds like the approach you are advocating is to basically never use raw pointers or references as class and struct members, and only use unique_ptr/shared_ptr/weak_ptr. This is a reasonable approach, but is a little idiosyncratic, not the way most of the bitcoin codebase is written, and does have some tradeoffs.

  157. in src/index/base.cpp:383 in c02fd0379c outdated
    379@@ -380,7 +380,7 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain() const
    380     }
    381 
    382     LogPrintf("%s: %s is catching up on block notifications\n", __func__, GetName());
    383-    SyncWithValidationInterfaceQueue();
    384+    m_chain->context()->validation_signals->SyncWithValidationInterfaceQueue();
    


    vasild commented at 1:27 pm on February 14, 2024:
    Elsewhere (including from BaseIndex::Stop()) there is a check whether validation_signals is null, which I think is missing here and in BaseIndex::Init().

    TheCharlatan commented at 1:31 pm on February 15, 2024:
    See the comments here for the reasoning. Basically we want to guard in the shutdown (Stop) case and it is less important to do so in the rest, since we have tests heavily exercising those paths. Question is also on which de-reference we should be guarding and what to do about the existing unguarded lines that call m_chain and its members.
  158. in src/init.cpp:1168 in c02fd0379c outdated
    1162@@ -1158,7 +1163,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    1163         }
    1164     }, std::chrono::minutes{5});
    1165 
    1166-    GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
    1167+    assert(!node.validation_signals);
    1168+    node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(scheduler));
    1169+    auto& validation_signals = *node.validation_signals;
    


    vasild commented at 2:12 pm on February 14, 2024:

    Before we had a global g_signals and code that wished to access it called GetMainSignals(). This was safe wrt lifetime of the object.

    With this PR it lives in NodeContext::validation_signals as std::unique_ptr<ValidationSignals>. Also (for covenience?) the object being pointed to by the unique_ptr is extracted from the unique_ptr and saved as a raw pointer in ChainstateManager::Options::signals and CTxMemPool::Options::signals. This defeats the idea of unique_ptr because after it goes out of scope and destroys the object, the raw pointers will still exist and point to garbage. Even if not possible with the current code, this pattern better be avoided.

    It would be better to store shared_ptr<ValidationSignals> in all 3 places because that will eliminate any lifetime dependencies - e.g. object A must be destroyed before object B.


    TheCharlatan commented at 12:54 pm on February 15, 2024:
    The pointers in the options and in CTxMemPool are supposed to be optional values (see the first commit message). Using a raw pointer for optional non-owned values seems like an acceptable pattern.

    vasild commented at 4:21 pm on February 16, 2024:
    You can have optional stuff with shared_ptr as well (if it is empty).
  159. in src/scheduler.cpp:176 in c02fd0379c outdated
    172@@ -173,7 +173,7 @@ void SingleThreadedSchedulerClient::ProcessQueue()
    173     callback();
    174 }
    175 
    176-void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func)
    177+void SerialTaskRunner::insert(std::function<void()> func)
    


    vasild commented at 2:49 pm on February 14, 2024:

    Method names should begin with capital letter:

    0void SerialTaskRunner::Insert(std::function<void()> func)
    

    Same for SerialTaskRunner::flush() and SerialTaskRunner::size()


    TheCharlatan commented at 1:42 pm on February 15, 2024:
    I think this is fine here. We use lower case names for similar insert and size methods that mimic the standard library in our codebase already, and we always have this guideline in the dev-notes, that indicates that these kind of interface classes could be lower case.
  160. in src/util/task_runner.h:25 in c02fd0379c outdated
    21+public:
    22+    virtual ~TaskRunnerInterface() {}
    23+
    24+    /**
    25+     * This is called for each subscriber on each validation interface event.
    26+     * The callback can either be queued for later/asynchronous/threaded
    


    vasild commented at 3:58 pm on February 14, 2024:

    I think the terms “subscriber” and “validation interface event” don’t belong here.

    0     * Execute a given task.
    1     * The callback can either be queued for later/asynchronous/threaded
    
  161. in src/util/task_runner.h:19 in c02fd0379c outdated
    14+ * This header provides an interface and simple implementation for a task
    15+ * runner. Another threaded, serial implementation using a queue is available in
    16+ * the scheduler module's SerialTaskRunner.
    17+ */
    18+
    19+class TaskRunnerInterface
    


    vasild commented at 4:07 pm on February 14, 2024:

    Doxygen attaches this comment to the TaskRunnerInterface class in the generated documentation which I guess is not the intention.

     0/** [@file](/bitcoin-bitcoin/contributor/file/)
     1 *
     2 * This header provides an interface and simple implementation for a task
     3 * runner. Another threaded, serial implementation using a queue is available in
     4 * the scheduler module's SerialTaskRunner.
     5 */
     6
     7/**
     8 * Interface for a task runner.
     9 */
    10class TaskRunnerInterface
    

    See https://www.doxygen.nl/manual/commands.html#cmdfile

  162. in src/util/task_runner.h:47 in c02fd0379c outdated
    42+
    43+class ImmediateTaskRunner : public TaskRunnerInterface
    44+{
    45+public:
    46+    void insert(std::function<void()> func) override { func(); }
    47+    void flush() override { return; }
    


    vasild commented at 4:09 pm on February 14, 2024:

    nit:

    0    void flush() override {}
    
  163. in src/validationinterface.cpp:45 in c02fd0379c outdated
    44@@ -45,9 +45,10 @@ class MainSignalsImpl
    45     // We are not allowed to assume the scheduler only runs in one thread,
    46     // but must ensure all callbacks happen in-order, so we end up creating
    47     // our own queue here :(
    48-    SingleThreadedSchedulerClient m_schedulerClient;
    49+    std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
    


    vasild commented at 4:14 pm on February 14, 2024:
    0- We are not allowed to assume the scheduler only runs in one thread
    1+ We are not allowed to assume the task runner has only one thread
    
  164. vasild commented at 4:24 pm on February 14, 2024: contributor

    Approach ACK c02fd0379c425f486f1b80a81962c1aa68b8a852

    Mostly minor stuff below plus a suggestion about the lifetime of the objects: #28960 (review) #28960 (review)

    Comples with clang 19 on FreeBSD, all unit and functional tests pass locally.

  165. [refactor] Make signals optional in mempool and chainman
    This is done in preparation for the next two commits, where the
    CMainSignals are de-globalized.
    
    This avoids adding new constructor arguments to the ChainstateManager
    and CTxMemPool classes over the next two commits.
    
    This could also allow future tests that are only interested in the
    internal behaviour of the classes to forgo instantiating the signals.
    3fba3d5dee
  166. [refactor] Prepare for g_signals de-globalization
    To this end move some functions into the CMainSignals class.
    473dd4b97a
  167. ryanofsky approved
  168. ryanofsky commented at 12:55 pm on February 15, 2024: contributor
    Code review ACK c02fd0379c425f486f1b80a81962c1aa68b8a852. Just rebase and moving test_deterministic_coverage.sh change since last review
  169. DrahtBot requested review from vasild on Feb 15, 2024
  170. DrahtBot requested review from furszy on Feb 15, 2024
  171. furszy commented at 1:26 pm on February 15, 2024: member
    diff ACK c02fd037
  172. refactor: De-globalize g_signals 84f5c135b8
  173. [refactor] Make MainSignals RAII styled 4abde2c4e3
  174. scripted-diff: Rename SingleThreadedSchedulerClient to SerialTaskRunner
    -BEGIN VERIFY SCRIPT-
    s() { git grep -l "$1" src | (grep -v "$3" || cat;) | xargs sed -i "s/$1/$2/g"; }
    
    s 'SingleThreadedSchedulerClient'   'SerialTaskRunner'  ''
    s 'SinglethreadedSchedulerClient'   'SerialTaskRunner'  ''
    s 'm_schedulerClient'               'm_task_runner'     ''
    s 'AddToProcessQueue'               'insert'            ''
    s 'EmptyQueue'                      'flush'             ''
    s 'CallbacksPending'                'size'              'validation'
    sed -i '109s/CallbacksPending/size/' src/validationinterface.cpp
    -END VERIFY SCRIPT-
    
    Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
    0d6d2b650d
  175. scripted-diff: Rename MainSignals to ValidationSignals
    -BEGIN VERIFY SCRIPT-
    s() { git grep -l "$1" src | xargs sed -i "s/$1/$2/g"; }
    
    s 'CMainSignals'    'ValidationSignals'
    s 'MainSignalsImpl' 'ValidationSignalsImpl'
    -END VERIFY SCRIPT-
    06069b3913
  176. TheCharlatan force-pushed on Feb 15, 2024
  177. TheCharlatan commented at 4:05 pm on February 15, 2024: contributor

    Thank you for the review @vasild,

    Updated c02fd0379c425f486f1b80a81962c1aa68b8a852 -> 4e270f3aa70f0bb4e7053d84d5cdd1a1fdcf85c2 (noGlobalSignals_15 -> noGlobalSignals_16, compare)

    • Addressed @vasild’s comment, added const qualifier to m_signals pointer in CTxMemPool.

    • Addressed @vasild’s comment, fixed naming in commit message.

    • Addressed @vasild’s comment, added uint256::Zero as argument to waitForNotificationsIfTipChanged call.

    • Addressed @vasild’s comment, rewording docstring for TaskRunnerInterface’s insert method.

    • Addressed @vasild’s comment, added @file qualifier to docstring for task_runner.h file.

    • Addressed @vasild’s comment, removed unneeded return statement.

    • Addressed @vasild’s comment, fixed re-naming issue in comment describing m_task_runner.

    • Added validation_signals helper in node/interfaces instead of reaching into a ChainstateManager member for accessing the ValidationSignals.

  178. in src/validationinterface.cpp:45 in 4e270f3aa7 outdated
    41@@ -42,12 +42,13 @@ class MainSignalsImpl
    42     std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
    43 
    44 public:
    45-    // We are not allowed to assume the scheduler only runs in one thread,
    46+    // We are not allowed to assume the task runner only runs in one thread,
    47     // but must ensure all callbacks happen in-order, so we end up creating
    48     // our own queue here :(
    49-    SingleThreadedSchedulerClient m_schedulerClient;
    50+    std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
    


    ryanofsky commented at 4:46 pm on February 15, 2024:

    In commit “kernel: Remove dependency on CScheduler” (4e270f3aa70f0bb4e7053d84d5cdd1a1fdcf85c2)

    The comment isn’t accurate if you “change scheduler” to “task runner”. The comment is trying to say “We can’t assume CScheduler only runs in one thread, but must ensure all callbacks happen in-order, so we end up creating our own queue inside SingleThreadedSchedulerClient.”

    Now that SingleThreadedSchedulerClient is replaced by std::unique_ptr<util::TaskRunnerInterface> the comment doesn’t really belong in this file, but I think would be helpful if it were reverted and moved to describe SerialTaskRunner::m_callbacks_pending


    TheCharlatan commented at 5:04 pm on February 15, 2024:
    Right, not sure what I was thinking here. Will revert and apply your suggestion.
  179. TheCharlatan force-pushed on Feb 15, 2024
  180. TheCharlatan commented at 5:11 pm on February 15, 2024: contributor

    Updated 4e270f3aa70f0bb4e7053d84d5cdd1a1fdcf85c2 -> 75931055b84d1f0e9f44d3d3efc28785fd23e66c (noGlobalSignals_16 -> noGlobalSignals_17, compare)

    • Addressed @ryanofsky’s comment, reverted previous comment change and instead moved the comment to describe m_callbacks_pending.
  181. in src/util/task_runner.h:29 in 75931055b8 outdated
    26+     * processing, or be executed immediately for synchronous processing. When
    27+     * used in the ValidationSignals synchronous processing will block
    28+     * validation. Each subscriber will call this on each validation
    29+     * interface event.
    30+     */
    31+    virtual void insert(std::function<void()> func) = 0;
    


    furszy commented at 7:03 pm on February 15, 2024:
    What do you mean with this last sentence? Validation events’ subscribers will not call this function. This function will be called by the event dispatching side.
  182. TheCharlatan force-pushed on Feb 16, 2024
  183. TheCharlatan commented at 9:22 am on February 16, 2024: contributor

    Updated 75931055b84d1f0e9f44d3d3efc28785fd23e66c -> 316c7c845036cbffa22b9e44f31dca8573ffb639 (noGlobalSignals_17 -> noGlobalSignals_18, compare)

  184. in src/util/task_runner.h:29 in 316c7c8450 outdated
    26+     * processing, or be executed immediately for synchronous processing. When
    27+     * used in the ValidationSignals synchronous processing will block
    28+     * validation. It is called for each subscriber on each validation
    29+     * interface event.
    30+     */
    31+    virtual void insert(std::function<void()> func) = 0;
    


    furszy commented at 12:27 pm on February 16, 2024:
    I’m still unsure about the way this is written. Maybe better to write something like “func usually contains a loop that dispatches a single validation event to all subscribers sequentially. This will block validation if it is processed synchronously”.
  185. furszy commented at 12:27 pm on February 16, 2024: member
    ACK 316c7c8450
  186. DrahtBot requested review from ryanofsky on Feb 16, 2024
  187. in contrib/devtools/test_deterministic_coverage.sh:34 in 316c7c8450 outdated
    42+    "wallet_tests/dummy_input_size_test"                      # validation.cpp: if (signals.CallbacksPending() > 10)
    43+    "wallet_tests/importmulti_rescan"                         # validation.cpp: if (signals.CallbacksPending() > 10)
    44+    "wallet_tests/importwallet_rescan"                        # validation.cpp: if (signals.CallbacksPending() > 10)
    45+    "wallet_tests/ListCoins"                                  # validation.cpp: if (signals.CallbacksPending() > 10)
    46+    "wallet_tests/scan_for_wallet_transactions"               # validation.cpp: if (signals.CallbacksPending() > 10)
    47+    "wallet_tests/wallet_disableprivkeys"                     # validation.cpp: if (signals.CallbacksPending() > 10)
    


    maflcko commented at 12:36 pm on February 16, 2024:

    Would be a nice follow-up to remove the non-deterministic scheduler from all tests, unless they are explicitly testing the scheduler.

    cc @aureleoules , who was asking for a solution to this for the corecheck infra

  188. in src/util/task_runner.h:29 in 316c7c8450 outdated
    24+    /**
    25+     * The callback can either be queued for later/asynchronous/threaded
    26+     * processing, or be executed immediately for synchronous processing. When
    27+     * used in the ValidationSignals synchronous processing will block
    28+     * validation. It is called for each subscriber on each validation
    29+     * interface event.
    


    maflcko commented at 12:44 pm on February 16, 2024:
    Are you sure this is correct? Would it be possible that it is called once for each validation interface event, and the inserted function would then iterate over each subscriber?

    furszy commented at 12:48 pm on February 16, 2024:

    ryanofsky commented at 2:14 pm on February 16, 2024:

    re: #28960 (review)

    Yeah the sentence “When used in the ValidationSignals synchronous processing will block validation. It is called for each subscriber on each validation interface event.” is a little misleading because only one task per event is inserted, regardless of number of subscribers. In the future we might want to change this so indexes/wallets don’t block each other, but that is the current implementation.

    I would fix this by deleting this sentence here, because this information is more relevant to ValidationSignals anyway than the TaskRunnerInterface class. A good place to move it would be to the ValidationSignals constructor:

    https://github.com/bitcoin/bitcoin/blob/316c7c845036cbffa22b9e44f31dca8573ffb639/src/validationinterface.h#L166

    And I like furszy’s suggested wording #28960 (review)


    furszy commented at 6:23 pm on February 16, 2024:

    A good place to move it would be to the ValidationSignals constructor

    I would rather place it above the imp class declaration, but just a tiny styling nit.

  189. maflcko commented at 12:45 pm on February 16, 2024: member

    ACK 316c7c845036cbffa22b9e44f31dca8573ffb639 🔁

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: ACK 316c7c845036cbffa22b9e44f31dca8573ffb639 🔁
    31vioJirfjv5T1d+KJlrtR+Bw8ftU7msPN3OGieSZu7FZDuBMu3u642FgGntJXux25f1uaFTvhCgjKjKJDYshCw==
    
  190. kernel: Remove dependency on CScheduler
    By defining a virtual interface class for the scheduler client, users of
    the kernel can now define their own event consuming infrastructure,
    without having to spawn threads or rely on the scheduler design.
    
    Removing CScheduler also allows removing the thread and
    exception modules from the kernel library.
    d5228efb53
  191. TheCharlatan force-pushed on Feb 16, 2024
  192. TheCharlatan commented at 4:19 pm on February 16, 2024: contributor

    Thank you for the comments,

    Updated 316c7c845036cbffa22b9e44f31dca8573ffb639 -> d5228efb5391b31a9a0673019e43e7fa2cd4ac07 (noGlobalSignals_18 -> noGlobalSignals_19, compare)

  193. ryanofsky approved
  194. ryanofsky commented at 4:29 pm on February 16, 2024: contributor
    Code review ACK d5228efb5391b31a9a0673019e43e7fa2cd4ac07. Just comment change since last review.
  195. DrahtBot requested review from furszy on Feb 16, 2024
  196. DrahtBot requested review from maflcko on Feb 16, 2024
  197. vasild approved
  198. vasild commented at 5:02 pm on February 16, 2024: contributor

    ACK d5228efb5391b31a9a0673019e43e7fa2cd4ac07

    I would be happy to re-review if this is changed to use automatic memory management everywhere (https://github.com/bitcoin/bitcoin/pull/28960#discussion_r1489457095 and #28960 (review)). Or at least add a comment next to the raw pointers/references like “this points to NodeContext::validation_signals and should not be used after that is destroyed”.

  199. furszy commented at 6:24 pm on February 16, 2024: member
    diff ACK d5228ef
  200. maflcko commented at 11:46 am on February 17, 2024: member

    re-ACK d5228efb5391b31a9a0673019e43e7fa2cd4ac07 🌄

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: re-ACK d5228efb5391b31a9a0673019e43e7fa2cd4ac07 🌄
    3elAYAr1e+Thfz4bnEGxUZsIrgO73g1NOYOeuWO1064mJWFsjMzzTtufbMX7XQAPS5SAAfAflraHLO49Zr9MCBg==
    
  201. DrahtBot removed review request from maflcko on Feb 17, 2024
  202. maflcko added this to the milestone 28.0 on Feb 22, 2024
  203. maflcko commented at 5:59 pm on February 22, 2024: member
    Added to 28.0, since it looks rfm, but is probably waiting on the branch-off?
  204. achow101 merged this on Mar 9, 2024
  205. achow101 closed this on Mar 9, 2024


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-11-21 09:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me