Skip to content

Commit 874c8bd

Browse files
committed
Merge bitcoin/bitcoin#29144: init: handle empty settings file gracefully
e901404 settings: add auto-generated warning msg for editing the file manually (furszy) 966f5de init: improve corrupted/empty settings file error msg (furszy) Pull request description: Small and simple issue reported [here](https://community.umbrel.com/t/bitcoin-docker-container-keeps-restarting/2144). Improving a confusing situation reported by users who did not understand why a settings parsing error occurred when the file was empty and did not know how to solve it. Empty setting file could be due (1) corruption or (2) an user manually cleaning up the file content. In both scenarios, the 'Unable to parse settings file' error does not help the user move forward. ACKs for top commit: achow101: ACK e901404 hebasto: re-ACK e901404. ryanofsky: Code review ACK e901404. Just whitespace formatting changes and shortening a test string literal since last review shaavan: Code review ACK e901404 Tree-SHA512: 2910654c6b9e9112de391eedb8e46980280f822fa3059724dd278db7436804dd27fae628d2003f2c6ac1599b07ac5c589af016be693486e949f558515e662bec
2 parents 6f732ff + e901404 commit 874c8bd

File tree

4 files changed

+26
-6
lines changed

4 files changed

+26
-6
lines changed

src/common/settings.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
#include <common/settings.h>
66

7+
#if defined(HAVE_CONFIG_H)
8+
#include <config/bitcoin-config.h>
9+
#endif
10+
711
#include <tinyformat.h>
812
#include <univalue.h>
913
#include <util/fs.h>
@@ -81,7 +85,9 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
8185

8286
SettingsValue in;
8387
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
84-
errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
88+
errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, "
89+
"and can be fixed by removing the file, which will reset settings to default values.",
90+
fs::PathToString(path)));
8591
return false;
8692
}
8793

@@ -114,6 +120,13 @@ bool WriteSettings(const fs::path& path,
114120
std::vector<std::string>& errors)
115121
{
116122
SettingsValue out(SettingsValue::VOBJ);
123+
// Add auto-generated warning comment only if it does not exist
124+
if (!values.contains("_warning_")) {
125+
out.pushKV("_warning_", strprintf("This file is automatically generated and updated by %s. Please do not edit this file while the node "
126+
"is running, as any changes might be ignored or overwritten.",
127+
PACKAGE_NAME));
128+
}
129+
// Push settings values
117130
for (const auto& value : values) {
118131
out.pushKVEnd(value.first, value.second);
119132
}

src/qt/test/optiontests.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ void OptionTests::migrateSettings()
6161
QVERIFY(!settings.contains("addrSeparateProxyTor"));
6262

6363
std::ifstream file(gArgs.GetDataDirNet() / "settings.json");
64+
std::string default_warning = strprintf("This file is automatically generated and updated by %s. Please do not edit this file while the node "
65+
"is running, as any changes might be ignored or overwritten.",
66+
PACKAGE_NAME);
6467
QCOMPARE(std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()).c_str(), "{\n"
68+
" \"_warning_\": \""+ default_warning+"\",\n"
6569
" \"dbcache\": \"600\",\n"
6670
" \"listen\": false,\n"
6771
" \"onion\": \"onion:234\",\n"

src/test/settings_tests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ BOOST_AUTO_TEST_CASE(ReadWrite)
9999
// Check invalid json not allowed
100100
WriteText(path, R"(invalid json)");
101101
BOOST_CHECK(!common::ReadSettings(path, values, errors));
102-
std::vector<std::string> fail_parse = {strprintf("Unable to parse settings file %s", fs::PathToString(path))};
102+
std::vector<std::string> fail_parse = {strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, "
103+
"and can be fixed by removing the file, which will reset settings to default values.",
104+
fs::PathToString(path))};
103105
BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), fail_parse.begin(), fail_parse.end());
104106
}
105107

test/functional/feature_settings.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ def run_test(self):
2323
settings = node.chain_path / "settings.json"
2424
conf = node.datadir_path / "bitcoin.conf"
2525

26-
# Assert empty settings file was created
26+
# Assert default settings file was created
2727
self.stop_node(0)
28+
default_settings = {"_warning_": "This file is automatically generated and updated by Bitcoin Core. Please do not edit this file while the node is running, as any changes might be ignored or overwritten."}
2829
with settings.open() as fp:
29-
assert_equal(json.load(fp), {})
30+
assert_equal(json.load(fp), default_settings)
3031

3132
# Assert settings are parsed and logged
3233
with settings.open("w") as fp:
@@ -48,12 +49,12 @@ def run_test(self):
4849

4950
# Assert settings are unchanged after shutdown
5051
with settings.open() as fp:
51-
assert_equal(json.load(fp), {"string": "string", "num": 5, "bool": True, "null": None, "list": [6, 7]})
52+
assert_equal(json.load(fp), {**default_settings, **{"string": "string", "num": 5, "bool": True, "null": None, "list": [6, 7]}})
5253

5354
# Test invalid json
5455
with settings.open("w") as fp:
5556
fp.write("invalid json")
56-
node.assert_start_raises_init_error(expected_msg='Unable to parse settings file', match=ErrorMatch.PARTIAL_REGEX)
57+
node.assert_start_raises_init_error(expected_msg='does not contain valid JSON. This is probably caused by disk corruption or a crash', match=ErrorMatch.PARTIAL_REGEX)
5758

5859
# Test invalid json object
5960
with settings.open("w") as fp:

0 commit comments

Comments
 (0)