- Explorer: the tip check runs every frame the Explorer tab is open,
and it read both tip rows and rewrote them each time (two autocommit
WAL writes per frame). A steady tip is now kept in memory and skips
SQLite. A new tip writes both rows in one savepoint, so a crash
between them can't pair a new height with the old hash. A test
checks that 100 steady checks write nothing.
- Console: while the daemon was in the Error state the Console tab
added its error line on every poll, i.e. every frame, re-laying out
the whole buffer each time. It prints once per error now: on entering
Error, when the message changes, or after another crash
(EmbeddedDaemon::getUnexpectedExitCount(), never reset). setState
writes the message before the state, so a reader that sees Error sees
that error's message. The stray " ===" suffix is gone.
- perf.log: the profiler appended a block every 300 frames on every
run and never rotated the file (tens of MB a day for a wallet left
open). It is now off unless the app starts with DRAGONX_PERFLOG=1;
otherwise its zone hooks are near no-ops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RPC console echoed and stored typed commands verbatim, so `walletpassphrase
<secret>`, `z_importkey <key>`, `encryptwallet <pass>` etc. left the secret in the
visible log AND the 100-entry recall history (copyable). Adds a pure, unit-testable
RedactConsoleCommand()/ConsoleCommandCarriesSecret() (allowlist of 13 secret-bearing
first-tokens) in console_tab_helpers; submitConsoleCommand() now echoes and stores
`> walletpassphrase ****` while still executing the real command unredacted. Bare
secret commands and non-secret commands pass through unchanged.
Output-secret commands (dumpprivkey / z_exportkey / z_exportmnemonic) — whose secret is
in the RESULT — are a separate redaction concern, tracked as a follow-up.
First fix in the wallet-hardening P0-A cluster (see docs/wallet-hardening.md). New
testConsoleSecretRedaction (11 assertions); ctest 1/1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>