wallet: default -autoshield ON only when the HD seed is known-recoverable

Autoshield moves mined coinbase into a z-address that only this wallet's HD seed
can re-derive. Defaulting that ON is only defensible where the user can actually
restore that seed.

Two cases fail that test. A seedless legacy wallet has a seed minted onto it
silently at first start, so no backup the user already holds contains it. And a
wallet seeded by an earlier build predates provenance recording, so we cannot
tell which case it was. Both are now classified as not-known-recoverable and
autoshield stays off there until the operator backs the seed up and passes
-autoshield=1. An explicit -autoshield=0/1 still wins in either direction.

Wallets this software created on an empty datadir, or restored from a
user-supplied -mnemonic/-hdseed, keep the ON default: in both cases the user
either has the phrase or supplied the seed themselves.

Verified on real wallets: a wallet carrying no origin record is classified
unknown and logs "autoshield left OFF by default: HD seed origin 4"; a wallet
created by the previous commit logs "autoshield enabled" with no
re-classification, confirming the record persists rather than being recomputed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 03:22:55 +02:00
parent a494eabdce
commit e2f88175ab

View File

@@ -489,7 +489,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-zsweepexternal", _("Enable sweeping to an external wallet (default false)"));
strUsage += HelpMessageOpt("-zsweepexclude", _("Addresses to exclude from sweeping (default none)"));
strUsage += HelpMessageOpt("-autoshield", _("Automatically shield matured coinbase (mining rewards) into a wallet z-address (default: true). No-op when not mining or wallet is locked."));
strUsage += HelpMessageOpt("-autoshield", _("Automatically shield matured coinbase (mining rewards) into a seed-derived wallet z-address (default: true for wallets created or restored by this software, false when the HD seed provenance is unknown). No-op when not mining or wallet is locked."));
strUsage += HelpMessageOpt("-autoshieldinterval", strprintf(_("Block interval between automatic coinbase-shielding rounds (default: %i, min 5)"), 25));
strUsage += HelpMessageOpt("-autoshieldaddress=<zaddr>", _("Destination Sapling z-address for auto-shielded coinbase (default: reuse or create a wallet z-address). Must be spendable by this wallet."));
strUsage += HelpMessageOpt("-autoshieldfee", strprintf(_("Fee in puposhis for automatic coinbase-shielding transactions (default: %i)"), 10000));
@@ -2499,7 +2499,21 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
//Set Automatic Coinbase Shielding (default ON, conditional: self-guards
//on nodes where it cannot act - no owned coinbase, external mineraddress,
//or locked wallet). Closes the transparent-coinbase leak for miners.
pwalletMain->fAutoShieldEnabled = GetBoolArg("-autoshield", true);
// Default ON only when this wallet's HD seed provenance says the
// destination is genuinely recoverable. Autoshield sends mined coinbase to
// a seed-derived z-address (resolveDestination), so for a seed retrofitted
// onto a pre-existing wallet - or one predating provenance recording - we
// cannot assume the user holds it. Those wallets opt in with -autoshield=1
// after backing the seed up.
const bool fAutoShieldSeedKnown =
(pwalletMain->hdSeedOrigin == CWallet::HDSEED_ORIGIN_CREATED ||
pwalletMain->hdSeedOrigin == CWallet::HDSEED_ORIGIN_RESTORED);
pwalletMain->fAutoShieldEnabled = GetBoolArg("-autoshield", fAutoShieldSeedKnown);
if (!fAutoShieldSeedKnown && !mapArgs.count("-autoshield")) {
LogPrintf("%s: autoshield left OFF by default: HD seed origin %d is not known-recoverable. "
"Back the seed up (z_exportwallet, or z_exportmnemonic on a mnemonic wallet) and "
"pass -autoshield=1 to enable.\n", __func__, pwalletMain->hdSeedOrigin);
}
if (pwalletMain->fAutoShieldEnabled) {
int autoShieldInterval = GetArg("-autoshieldinterval", 25);
if (autoShieldInterval < 5) {