aboutsummaryrefslogtreecommitdiff
path: root/src/node
diff options
context:
space:
mode:
authorAva Chow <github@achow101.com>2024-08-27 12:29:20 -0400
committerAva Chow <github@achow101.com>2024-08-27 12:29:20 -0400
commit78567b052d7f541fc1d24a2199d980dedb3305f4 (patch)
tree2dd7fef67842afc317834292deb4eb1a02ded1ed /src/node
parentc6d2d1cb66b8c0c15b1a6a712e196869b4bf3040 (diff)
parent1b41d45d462d856a9d0b44ae0039bbb2cd78407c (diff)
Merge bitcoin/bitcoin#30697: Bugfix: Ensure Atomicity in Wallet Settings Updates from Chain Interface
1b41d45d462d856a9d0b44ae0039bbb2cd78407c wallet: bugfix: ensure atomicity in settings updates (ismaelsadeeq) Pull request description: This PR fixes #30620. As outlined in the issue, creating two wallets with `load_on_startup=true` simultaneously results in only one wallet being added to the startup file. The current issue arises because the wallet settings update process involves: 1. Obtaining the settings value while acquiring the settings lock. 2. Modifying the settings value. 3. Overwriting the settings value while acquiring the settings lock again. This sequence is not thread-safe. Different threads could modify the same base value simultaneously, overwriting data from other workers without realizing it. The PR attempts to fix this by modifying the chain interface's `updateRwSetting` method to accept a function that will be called with the settings reference. This function will either update or delete the setting and return an enum indicating whether the settings need to be overwritten in this or not. Additionally, this PR introduces two new methods to the chain interface: - `overwriteRwSetting`: This method replaces the setting with a new value. Used in `VerifyWallets` - `deleteRwSettings`: This method completely erases a specified setting. This method is currently used only in `overwriteRwSetting`. These changes ensure that updates are race-free across all clients. ACKs for top commit: achow101: ACK 1b41d45d462d856a9d0b44ae0039bbb2cd78407c furszy: self-code-ACK https://github.com/bitcoin/bitcoin/commit/1b41d45d462d856a9d0b44ae0039bbb2cd78407c Tree-SHA512: 50cda612b782aeb5e03e2cf63cc44779a013de1c535b883b57af4de22f24b0de80b4edecbcda235413baec0a12bdf0e5750fb6731c9e67d32e742d8c63f08c13
Diffstat (limited to 'src/node')
-rw-r--r--src/node/interfaces.cpp30
1 files changed, 24 insertions, 6 deletions
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 9fe08eb3dd..54b986c926 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -814,14 +814,32 @@ public:
});
return result;
}
- bool updateRwSetting(const std::string& name, const common::SettingsValue& value, bool write) override
+ bool updateRwSetting(const std::string& name,
+ const interfaces::SettingsUpdate& update_settings_func) override
{
+ std::optional<interfaces::SettingsAction> action;
args().LockSettings([&](common::Settings& settings) {
- if (value.isNull()) {
- settings.rw_settings.erase(name);
- } else {
- settings.rw_settings[name] = value;
- }
+ auto* ptr_value = common::FindKey(settings.rw_settings, name);
+ // Create value if it doesn't exist
+ auto& value = ptr_value ? *ptr_value : settings.rw_settings[name];
+ action = update_settings_func(value);
+ });
+ if (!action) return false;
+ // Now dump value to disk if requested
+ return *action == interfaces::SettingsAction::SKIP_WRITE || args().WriteSettingsFile();
+ }
+ bool overwriteRwSetting(const std::string& name, common::SettingsValue& value, bool write) override
+ {
+ if (value.isNull()) return deleteRwSettings(name, write);
+ return updateRwSetting(name, [&](common::SettingsValue& settings) {
+ settings = std::move(value);
+ return write ? interfaces::SettingsAction::WRITE : interfaces::SettingsAction::SKIP_WRITE;
+ });
+ }
+ bool deleteRwSettings(const std::string& name, bool write) override
+ {
+ args().LockSettings([&](common::Settings& settings) {
+ settings.rw_settings.erase(name);
});
return !write || args().WriteSettingsFile();
}