Rework the full-node Import Key dialog into a safer, Material-consistent, more capable flow: - Security: the key is masked by default (Password) with a reveal (eye) toggle, a DialogWarningHeader "only import a key you own" note, and the buffer is wiped with sodium_memzero on close and on successful import. - Progress: import triggers a blocking full-chain rescan; the dialog now shows "Importing & rescanning — this can take several minutes" with LoadingDots and disables Import while it runs (no double-submit). Offline shows a "connect a running node" note and disables Import up front. - Viewing keys: the classifier gains isViewingKey / isRecognizedImportKey; importPrivateKey auto-detects a shielded viewing key (zxviews…) and routes to z_importviewingkey (watch-only) vs z_importkey / importprivkey. The live type indicator shows Transparent / Shielded spending / Shielded viewing (watch-only). - Result: on success the imported address is captured from the RPC result and shown via AddressCopyField. - Material restyle (OverlayDialogSpec/BlurFloat + TactileButton + Esc) and full i18n (12 new keys x 8 languages; CJK subset rebuilt). Verified: rendered on dark + light skins; a seeded zxviews… key shows the masked field + "Shielded viewing key (watch-only)" indicator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
3.5 KiB
C++
101 lines
3.5 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
namespace dragonx {
|
|
namespace services {
|
|
|
|
class WalletSecurityController {
|
|
public:
|
|
enum class PinValidationError {
|
|
None,
|
|
Empty,
|
|
Mismatch,
|
|
TooShort,
|
|
NonDigit
|
|
};
|
|
|
|
struct PinValidationResult {
|
|
bool ok = false;
|
|
PinValidationError error = PinValidationError::None;
|
|
const char* message = "";
|
|
};
|
|
|
|
struct DeferredEncryptionSnapshot {
|
|
std::string passphrase;
|
|
std::string pin;
|
|
};
|
|
|
|
class RpcGateway {
|
|
public:
|
|
virtual ~RpcGateway() = default;
|
|
virtual bool encryptWallet(const std::string& passphrase, std::string& error) = 0;
|
|
virtual bool unlockWallet(const std::string& passphrase, int timeoutSeconds, std::string& error) = 0;
|
|
virtual bool exportWallet(const std::string& fileName, long timeoutSeconds, std::string& error) = 0;
|
|
virtual bool importWallet(const std::string& filePath, long timeoutSeconds, std::string& error) = 0;
|
|
};
|
|
|
|
class VaultGateway {
|
|
public:
|
|
virtual ~VaultGateway() = default;
|
|
virtual bool storePin(const std::string& pin, const std::string& passphrase) = 0;
|
|
};
|
|
|
|
enum class KeyKind {
|
|
Transparent,
|
|
Shielded
|
|
};
|
|
|
|
struct DeferredEncryptionResult {
|
|
bool encrypted = false;
|
|
bool pinProvided = false;
|
|
bool pinStored = false;
|
|
bool restartRequired = false;
|
|
std::string error;
|
|
};
|
|
|
|
~WalletSecurityController();
|
|
|
|
void beginDeferredEncryption(std::string passphrase, std::string pin = {});
|
|
bool hasDeferredEncryption() const { return deferred_.pending; }
|
|
DeferredEncryptionSnapshot deferredEncryption() const;
|
|
bool shouldAttemptDeferredConnect(double nowSeconds, double minIntervalSeconds = 3.0);
|
|
void clearDeferredEncryption();
|
|
|
|
DeferredEncryptionResult runDeferredEncryption(DeferredEncryptionSnapshot request,
|
|
RpcGateway& rpc,
|
|
VaultGateway* vault);
|
|
|
|
static PinValidationResult validatePinSetup(const std::string& pin,
|
|
const std::string& confirmation,
|
|
bool allowEmpty = false,
|
|
std::size_t minLength = 4);
|
|
static KeyKind classifyAddress(const std::string& address);
|
|
static KeyKind classifyPrivateKey(const std::string& key);
|
|
// True if `key` is a shielded viewing key (extended full viewing key, "zxview…" — watch-only).
|
|
static bool isViewingKey(const std::string& key);
|
|
// True if `key` looks like a recognized Z (Sapling/Sprout spending) or T (WIF) private key.
|
|
static bool isRecognizedPrivateKey(const std::string& key);
|
|
// As above, but also accepts a recognized shielded viewing key — the import dialog auto-detects
|
|
// both, so this is the single source of truth for its indicator AND its submit guard.
|
|
static bool isRecognizedImportKey(const std::string& key);
|
|
static const char* importSuccessMessage(KeyKind kind);
|
|
static std::string decryptExportFileName(std::uint64_t timestampSeconds);
|
|
static void secureClear(std::string& value);
|
|
|
|
private:
|
|
struct DeferredEncryptionState {
|
|
std::string passphrase;
|
|
std::string pin;
|
|
bool pending = false;
|
|
double lastConnectAttempt = -10.0;
|
|
};
|
|
|
|
DeferredEncryptionState deferred_;
|
|
};
|
|
|
|
} // namespace services
|
|
} // namespace dragonx
|