feat(wallet): per-wallet data isolation foundation (P1)
First phase of multi-wallet support: keep each wallet's data separate so loading
a different wallet no longer shows the previous one's data, and lay the metadata
groundwork for the wallet-files list.
- Address book scoped per wallet: AddressBookEntry gains a "scope" ("global" or a
wallet-identity hash); the Contacts tab shows global + current-wallet contacts
(App::activeWalletIdentityHash) with a "show in every wallet" toggle + globe
badge. Legacy entries migrate to "global". Scope-aware de-dup allows the same
address across different wallets.
- Wallet metadata index (data/wallet_index -> wallets.json): file-keyed cache of
balance / address count / identity / size / last-opened / synced-here, since
those can't be read off a wallet.dat without loading it. Populated on connect +
address refresh (change-detecting upsert). Plus an active_wallet_file setting
for the -wallet=<name> switch coming in P2.
Unit-tested (testAddressBookScope, testWalletIndex): migration, visibility filter,
scope-aware de-dup, upsert change-detection, save/reload round-trip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,10 +17,21 @@ struct AddressBookEntry {
|
||||
std::string label;
|
||||
std::string address;
|
||||
std::string notes;
|
||||
|
||||
// Per-wallet visibility scope: "global" (shown in every wallet) or a wallet-identity hash
|
||||
// (shown only when that wallet is the active one). Empty is treated as "global" so legacy
|
||||
// entries — written before multi-wallet scoping — keep showing everywhere.
|
||||
std::string scope = "global";
|
||||
|
||||
AddressBookEntry() = default;
|
||||
AddressBookEntry(const std::string& l, const std::string& a, const std::string& n = "")
|
||||
: label(l), address(a), notes(n) {}
|
||||
AddressBookEntry(const std::string& l, const std::string& a, const std::string& n = "",
|
||||
const std::string& s = "global")
|
||||
: label(l), address(a), notes(n), scope(s) {}
|
||||
|
||||
bool isGlobal() const { return scope.empty() || scope == "global"; }
|
||||
// Visible under the wallet whose identity hash is walletHash (or globally).
|
||||
bool visibleInWallet(const std::string& walletHash) const {
|
||||
return isGlobal() || scope == walletHash;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -73,12 +84,21 @@ public:
|
||||
bool removeEntry(size_t index);
|
||||
|
||||
/**
|
||||
* @brief Find entry by address
|
||||
* @brief Find entry by address (any scope). Used for contact-label lookups.
|
||||
* @param address Address to search for
|
||||
* @return Index or -1 if not found
|
||||
*/
|
||||
int findByAddress(const std::string& address) const;
|
||||
|
||||
/**
|
||||
* @brief Is there an existing entry with this address that would be visible ALONGSIDE a new
|
||||
* entry of the given scope? (Same wallet, or either side global.) Used to reject
|
||||
* duplicates within a wallet while still allowing the same address in different wallets.
|
||||
* @param excludeIndex Storage index to skip (for edit), or -1.
|
||||
*/
|
||||
bool hasVisibleDuplicate(const std::string& address, const std::string& scope,
|
||||
int excludeIndex = -1) const;
|
||||
|
||||
/**
|
||||
* @brief Get all entries
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user