ipc mining: Prevent `Assertion m_node.chainman' failed`` errors on early startup
This fixes ``Assertion `m_node.chainman' failed`` errors first reported https://github.com/bitcoin/bitcoin/issues/33994#issuecomment-3602551596 when IPC mining methods are called before ChainstateManager is loaded. The fix works by making the `Init.makeMining` method block until chainstate data is loaded.
This commit is contained in:
1 parent
a7cabf92e4
commit
bbc8f1e0a7
6 files changed
+52
-5
No files matched your search
+3
-1
@@ -1207,7 +1207,9 @@ bool AppInitLockDirectories()
|
|||||||
bool AppInitInterfaces(NodeContext& node)
|
bool AppInitInterfaces(NodeContext& node)
|
||||||
{
|
{
|
||||||
node.chain = interfaces::MakeChain(node);
|
node.chain = interfaces::MakeChain(node);
|
||||||
node.mining = interfaces::MakeMining(node);
|
// Specify wait_loaded=false so internal mining interface can be initialized
|
||||||
|
// on early startup and does not need to be tied to chainstate loading.
|
||||||
|
node.mining = interfaces::MakeMining(node, /*wait_loaded=*/false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
//! Return implementation of Mining interface.
|
//! Return implementation of Mining interface.
|
||||||
std::unique_ptr<Mining> MakeMining(node::NodeContext& node);
|
//!
|
||||||
|
//! @param[in] wait_loaded waits for chainstate data to be loaded before
|
||||||
|
//! returning. Used to prevent external clients from
|
||||||
|
//! being able to crash the node during startup.
|
||||||
|
std::unique_ptr<Mining> MakeMining(node::NodeContext& node, bool wait_loaded=true);
|
||||||
|
|
||||||
} // namespace interfaces
|
} // namespace interfaces
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -1017,5 +1017,17 @@ public:
|
|||||||
namespace interfaces {
|
namespace interfaces {
|
||||||
std::unique_ptr<Node> MakeNode(node::NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
|
std::unique_ptr<Node> MakeNode(node::NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
|
||||||
std::unique_ptr<Chain> MakeChain(node::NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
|
std::unique_ptr<Chain> MakeChain(node::NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
|
||||||
std::unique_ptr<Mining> MakeMining(node::NodeContext& context) { return std::make_unique<node::MinerImpl>(context); }
|
std::unique_ptr<Mining> MakeMining(node::NodeContext& context, bool wait_loaded)
|
||||||
|
{
|
||||||
|
if (wait_loaded) {
|
||||||
|
node::KernelNotifications& kernel_notifications(*Assert(context.notifications));
|
||||||
|
util::SignalInterrupt& interrupt(*Assert(context.shutdown_signal));
|
||||||
|
WAIT_LOCK(kernel_notifications.m_tip_block_mutex, lock);
|
||||||
|
kernel_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(kernel_notifications.m_tip_block_mutex) {
|
||||||
|
return kernel_notifications.m_state.chainstate_loaded || interrupt;
|
||||||
|
});
|
||||||
|
if (interrupt) return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_unique<node::MinerImpl>(context);
|
||||||
|
}
|
||||||
} // namespace interfaces
|
} // namespace interfaces
|
||||||
@@ -67,7 +67,7 @@ struct MinerTestingSetup : public TestingSetup {
|
|||||||
}
|
}
|
||||||
std::unique_ptr<Mining> MakeMining()
|
std::unique_ptr<Mining> MakeMining()
|
||||||
{
|
{
|
||||||
return interfaces::MakeMining(m_node);
|
return interfaces::MakeMining(m_node, /*wait_loaded=*/false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace miner_tests
|
} // namespace miner_tests
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace testnet4_miner_tests {
|
|||||||
struct Testnet4MinerTestingSetup : public Testnet4Setup {
|
struct Testnet4MinerTestingSetup : public Testnet4Setup {
|
||||||
std::unique_ptr<Mining> MakeMining()
|
std::unique_ptr<Mining> MakeMining()
|
||||||
{
|
{
|
||||||
return interfaces::MakeMining(m_node);
|
return interfaces::MakeMining(m_node, /*wait_loaded=*/false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace testnet4_miner_tests
|
} // namespace testnet4_miner_tests
|
||||||
|
|||||||
@@ -147,6 +147,34 @@ class IPCMiningTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
asyncio.run(capnp.run(async_routine()))
|
asyncio.run(capnp.run(async_routine()))
|
||||||
|
|
||||||
|
def run_early_startup_test(self):
|
||||||
|
"""Make sure mining.createNewBlock safely returns on early startup as
|
||||||
|
soon as mining interface is available """
|
||||||
|
self.log.info("Running Mining interface early startup test")
|
||||||
|
|
||||||
|
node = self.nodes[0]
|
||||||
|
self.stop_node(node.index)
|
||||||
|
node.start()
|
||||||
|
|
||||||
|
async def async_routine():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
ctx, mining = await self.make_mining_ctx()
|
||||||
|
break
|
||||||
|
except (ConnectionRefusedError, FileNotFoundError):
|
||||||
|
# Poll quickly to connect as soon as socket becomes
|
||||||
|
# available but without using a lot of CPU
|
||||||
|
await asyncio.sleep(0.005)
|
||||||
|
|
||||||
|
opts = self.capnp_modules['mining'].BlockCreateOptions()
|
||||||
|
await mining.createNewBlock(ctx, opts)
|
||||||
|
|
||||||
|
asyncio.run(capnp.run(async_routine()))
|
||||||
|
|
||||||
|
# Reconnect nodes so next tests are happy
|
||||||
|
node.wait_for_rpc_connection()
|
||||||
|
self.connect_nodes(1, 0)
|
||||||
|
|
||||||
def run_block_template_test(self):
|
def run_block_template_test(self):
|
||||||
"""Test BlockTemplate interface methods."""
|
"""Test BlockTemplate interface methods."""
|
||||||
self.log.info("Running BlockTemplate interface test")
|
self.log.info("Running BlockTemplate interface test")
|
||||||
@@ -374,6 +402,7 @@ class IPCMiningTest(BitcoinTestFramework):
|
|||||||
self.miniwallet = MiniWallet(self.nodes[0])
|
self.miniwallet = MiniWallet(self.nodes[0])
|
||||||
self.default_block_create_options = self.capnp_modules['mining'].BlockCreateOptions()
|
self.default_block_create_options = self.capnp_modules['mining'].BlockCreateOptions()
|
||||||
self.run_mining_interface_test()
|
self.run_mining_interface_test()
|
||||||
|
self.run_early_startup_test()
|
||||||
self.run_block_template_test()
|
self.run_block_template_test()
|
||||||
self.run_coinbase_and_submission_test()
|
self.run_coinbase_and_submission_test()
|
||||||
self.run_ipc_option_override_test()
|
self.run_ipc_option_override_test()
|
||||||
|
|||||||
Reference in new issue
Block a user