Skip to content

Commit 7d2812d

Browse files
chopperbrianoclaude
andcommitted
PSP: replace dead on-chain matching with /permanent/<page>.json fetcher (win.30)
The C++ mctrivia pool implemented an on-chain fee-matching protocol that was never deployed server-side - the 14 pay-in addresses it watched have never been paid in 4M blocks of chain history. And mctrivia's actual payment- registration endpoint (POST /list/<floor>.json) has been returning HTTP 500 since ~July 2024, so no client of any stripe is getting paid. The pool operator is uncontactable. Gut serializeMetaProcessor to return "" unconditionally and strip the 14-address list + bytes budget math. Add a background permanentFetcherTask that walks GET /permanent/<page>.json, pins every CID via local IPFS, and persists progress to psp1permanentpage in config.cfg. A probeListEndpoint fires every ~30 min against /list so the dashboard auto-recovers if the server is ever fixed. Dashboard rewritten in plain English: "Hosting pool files (unpaid)" instead of "pinning, payment offline", and a new Payment row that explicitly tells the user whether DGB is flowing. main.cpp emits a one-shot info block on startup explaining the state. Bug fixes along the way: - RPC Server row was stuck on "Off" even though the server was listening. The win.26 TCP-connect probe used non-blocking connect() + select() on writability, which on Windows localhost sometimes times out (200ms) even though the 3-way handshake completed. Replaced with a simple pointer check - main.cpp pins RPC::Server via shared_ptr for the process lifetime and the accept loop catches its own exceptions, so the "dangling pointer" scenario the probe was guarding against hasn't happened in practice. Also added a "Checking..." tri-state so the row doesn't briefly flash red on startup. - _secretCode now read from psp1secret and persisted on first run instead of regenerating every startup. - updateBadList had an unconditional second push_back after dedupe that grew _badAssets/_badFiles unbounded on every poll. - _pspStatus / _pspNodeCount now mutex-guarded. Detached background thread was racing with render. - Config::refresh/write now preserve comments, blank lines, and original key ordering via a _rawLines vector. Previously Config round-trips would silently strip user comments. - getIPFS() throws on nullptr; replaced with getIPFSIfSet() in mctrivia.cpp background paths so we don't crash on shutdown races. - getPermanentStoragePoolListIfSet() added; dashboard first render runs BEFORE main.cpp constructs the PSP list, so the throwing getter used to fatal at "Not available". - Rolling back the permanent page on error created a re-pin loop; now stays at the current page and retries. - probeListEndpoint call wrapped in try/catch so a curl exception can't take down the fetcher thread. Payment is still offline. This commit cannot fix that - it's a server-side bug on uncontactable infrastructure. Users running this node are contributing storage to the DigiByte ecosystem without compensation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a543a5e commit 7d2812d

11 files changed

Lines changed: 623 additions & 152 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ SET(PATCH_VERSION 0)
4040
SET(SO_VERSION 0)
4141

4242
# Windows port build number (increment for each Windows-specific release)
43-
SET(WIN_BUILD 28)
43+
SET(WIN_BUILD 30)
4444

4545
# Add source directory
4646
include_directories(src)

src/AppMain.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class AppMain {
7979
ChainAnalyzer* getChainAnalyzerIfSet() { return _analyzer; }
8080
RPC::Server* getRpcServerIfSet() { return _rpcServer; }
8181
WebServer* getWebServerIfSet() { return _webServer; }
82+
PermanentStoragePoolList* getPermanentStoragePoolListIfSet() { return _psp; }
8283

8384
void reset();
8485
};

src/Config.cpp

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Config::Config() {
1616

1717
void Config::clear() {
1818
_values.clear();
19+
_rawLines.clear();
20+
_keyToLineIndex.clear();
1921
}
2022

2123
void Config::refresh() {
@@ -27,17 +29,26 @@ void Config::refresh() {
2729
myfile.open(_fileName);
2830
if (myfile.fail()) throw exceptionConfigFileMissing(); //does not exist
2931

30-
//check lines and save to correct value
32+
//read every line in order (preserving comments and blank lines), and for
33+
//each `key=value` line also populate the parsed map and the index lookup.
3134
string line;
3235
while (getline(myfile, line)) {
36+
// Strip trailing \r so CRLF-terminated files don't accumulate a \r per
37+
// line on the next write.
38+
if (!line.empty() && line.back() == '\r') line.pop_back();
39+
size_t idx = _rawLines.size();
40+
_rawLines.push_back(line);
41+
42+
if (line.empty()) continue; //blank line - keep in _rawLines, skip parse
43+
if (line[0] == '#') continue; //comment - keep in _rawLines, skip parse
44+
3345
istringstream is_line(line);
34-
if (line.empty()) continue; //ignore blank lines
35-
if (line[0] == '#') continue; //ignore notes
3646
string key;
3747
if (getline(is_line, key, '=')) {
3848
string value;
3949
if (getline(is_line, value)) {
4050
_values[key] = value;
51+
_keyToLineIndex[key] = idx;
4152
}
4253
}
4354
}
@@ -47,14 +58,27 @@ void Config::refresh() {
4758
/**
4859
* writes the config contents to a file. If name not set will write over existign config file
4960
* @param fileName
61+
*
62+
* Writes _rawLines verbatim so comments, blank lines, and original ordering
63+
* are preserved. setString/setInteger/setBool update BOTH _values and the
64+
* corresponding entry in _rawLines, so the raw-lines view is authoritative.
5065
*/
5166
void Config::write(string fileName) const {
5267
if (fileName.empty()) fileName = _fileName;
5368

5469
ofstream myfile(fileName);
5570
if (!myfile.is_open()) throw exceptionConfigFileCouldNotBeWritten();
56-
for (const auto& pair: _values) {
57-
myfile << pair.first << "=" << pair.second << "\n";
71+
72+
if (_rawLines.empty()) {
73+
// Fallback path — Config was constructed via the default ctor with no
74+
// file, or refresh() was never called. Dump _values as before.
75+
for (const auto& pair: _values) {
76+
myfile << pair.first << "=" << pair.second << "\n";
77+
}
78+
} else {
79+
for (const auto& line: _rawLines) {
80+
myfile << line << "\n";
81+
}
5882
}
5983
myfile.close();
6084
}
@@ -217,10 +241,22 @@ map<string, bool> Config::getBoolMap(const string& keyPrefix) const {
217241

218242
void Config::setString(const string& key, const string& value) {
219243
_values[key] = value;
244+
245+
// Keep the raw-line view in sync. If the key was already in the file,
246+
// update its existing line in place (preserving position relative to
247+
// comments and other keys). If it's new, append a fresh line.
248+
const string newLine = key + "=" + value;
249+
auto it = _keyToLineIndex.find(key);
250+
if (it != _keyToLineIndex.end()) {
251+
_rawLines[it->second] = newLine;
252+
} else {
253+
_keyToLineIndex[key] = _rawLines.size();
254+
_rawLines.push_back(newLine);
255+
}
220256
}
221257

222258
void Config::setInteger(const string& key, int value) {
223-
_values[key] = to_string(value);
259+
setString(key, to_string(value));
224260
}
225261

226262
void Config::setBool(const string& key, bool value) {
@@ -229,18 +265,18 @@ void Config::setBool(const string& key, bool value) {
229265

230266
void Config::setStringMap(const string& key, const map<string, string>& values) {
231267
for (const auto& entry: values) {
232-
_values[key + entry.first] = entry.second;
268+
setString(key + entry.first, entry.second);
233269
}
234270
}
235271

236272
void Config::setIntegerMap(const string& key, const map<string, int>& values) {
237273
for (const auto& entry: values) {
238-
_values[key + entry.first] = to_string(entry.second);
274+
setString(key + entry.first, to_string(entry.second));
239275
}
240276
}
241277

242278
void Config::setBoolMap(const string& key, const map<string, bool>& values) {
243279
for (const auto& entry: values) {
244-
_values[key + entry.first] = to_string(entry.second);
280+
setString(key + entry.first, to_string(entry.second));
245281
}
246282
}

src/Config.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ using namespace std;
1313

1414
class Config {
1515
map<string, string> _values;
16+
// _rawLines stores the file's contents in original order, INCLUDING comment
17+
// lines (`#...`) and blank lines. _keyToLineIndex maps each known key to
18+
// the index in _rawLines where it lives. On setString we update that line
19+
// in place instead of rewriting the whole file from _values, so comments
20+
// and user-intended ordering survive a write().
21+
vector<string> _rawLines;
22+
map<string, size_t> _keyToLineIndex;
1623
string _fileName;
1724
static bool isInteger(const string& value);
1825
static bool isBool(const string& value);

0 commit comments

Comments
 (0)