fix(storage): acquire wallet lock before load and before CloseWallet
Three improvements to WalletLock integration: 1. OpenExisting acquires the lock before decrypting the file — no point attempting a potentially expensive PBKDF2 derivation for a wallet already held by another instance. 2. OpenFromPath acquires the lock before CloseWallet — if the new wallet is unavailable, the current session stays intact instead of being silently destroyed. 3. OpenLoaded now receives an already-acquired WalletLock from the caller instead of acquiring it internally; TryAcquireWalletLock is removed. Callers that fail to open (wrong password, I/O error) dispose the lock in their catch blocks. Also fix CoinAmount.Of() to throw ArgumentException for unknown units instead of silently falling back to PLM.
This commit is contained in:
@@ -874,7 +874,9 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
for (var n = 2; WalletStore.Exists(path); n++)
|
for (var n = 2; WalletStore.Exists(path); n++)
|
||||||
path = Path.Combine(AppPaths.WalletsDir(Net), $"wallet-{n}.wallet.json");
|
path = Path.Combine(AppPaths.WalletsDir(Net), $"wallet-{n}.wallet.json");
|
||||||
WalletStore.Save(doc, path, password);
|
WalletStore.Save(doc, path, password);
|
||||||
OpenLoaded(doc, account, path, password);
|
var newLock = WalletLock.TryAcquire(path);
|
||||||
|
if (newLock is null) { StatusMessage = Loc.Tr("msg.wallet.locked"); return; }
|
||||||
|
OpenLoaded(doc, account, path, password, newLock);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -885,24 +887,33 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenExisting()
|
private void OpenExisting()
|
||||||
{
|
{
|
||||||
|
var path = _pendingOpenPath ?? AppPaths.DefaultWalletPath(Net);
|
||||||
|
var password = string.IsNullOrEmpty(PasswordInput) ? null : PasswordInput;
|
||||||
|
|
||||||
|
// Il lock viene acquisito prima del caricamento: se il wallet è già aperto
|
||||||
|
// altrove, non ha senso nemmeno tentare la decifrazione.
|
||||||
|
var newLock = WalletLock.TryAcquire(path);
|
||||||
|
if (newLock is null) { StatusMessage = Loc.Tr("msg.wallet.locked"); return; }
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var path = _pendingOpenPath ?? AppPaths.DefaultWalletPath(Net);
|
|
||||||
var password = string.IsNullOrEmpty(PasswordInput) ? null : PasswordInput;
|
|
||||||
var doc = WalletStore.Load(path, password);
|
var doc = WalletStore.Load(path, password);
|
||||||
_pendingOpenPath = null;
|
_pendingOpenPath = null;
|
||||||
OpenLoaded(doc, WalletLoader.ToAccount(doc), path, password);
|
OpenLoaded(doc, WalletLoader.ToAccount(doc), path, password, newLock);
|
||||||
}
|
}
|
||||||
catch (WrongPasswordException)
|
catch (WrongPasswordException)
|
||||||
{
|
{
|
||||||
|
newLock.Dispose();
|
||||||
StatusMessage = Loc.Tr("msg.wrongpassword");
|
StatusMessage = Loc.Tr("msg.wrongpassword");
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException)
|
catch (UnauthorizedAccessException)
|
||||||
{
|
{
|
||||||
|
newLock.Dispose();
|
||||||
StatusMessage = Loc.Tr("msg.wallet.noaccess");
|
StatusMessage = Loc.Tr("msg.wallet.noaccess");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
newLock.Dispose();
|
||||||
StatusMessage = $"Errore: {ex.Message}";
|
StatusMessage = $"Errore: {ex.Message}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -910,16 +921,22 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
/// <summary>Apertura di un file wallet qualunque (menu File → Apri, multi-wallet §8).</summary>
|
/// <summary>Apertura di un file wallet qualunque (menu File → Apri, multi-wallet §8).</summary>
|
||||||
public void OpenFromPath(string path)
|
public void OpenFromPath(string path)
|
||||||
{
|
{
|
||||||
|
// Il lock viene acquisito prima di chiudere l'eventuale wallet aperto:
|
||||||
|
// se il nuovo wallet non è disponibile, la sessione corrente resta intatta.
|
||||||
|
var newLock = WalletLock.TryAcquire(path);
|
||||||
|
if (newLock is null) { StatusMessage = Loc.Tr("msg.wallet.locked"); return; }
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var doc = WalletStore.Load(path);
|
||||||
if (IsWalletOpen)
|
if (IsWalletOpen)
|
||||||
CloseWallet();
|
CloseWallet();
|
||||||
var doc = WalletStore.Load(path);
|
OpenLoaded(doc, WalletLoader.ToAccount(doc), path, password: null, newLock);
|
||||||
OpenLoaded(doc, WalletLoader.ToAccount(doc), path, password: null);
|
|
||||||
}
|
}
|
||||||
catch (WrongPasswordException)
|
catch (WrongPasswordException)
|
||||||
{
|
{
|
||||||
// Cifrato: si chiede la password nel passo di apertura del wizard.
|
// Cifrato: si chiede la password nel passo di apertura del wizard.
|
||||||
|
newLock.Dispose();
|
||||||
if (IsWalletOpen)
|
if (IsWalletOpen)
|
||||||
CloseWallet();
|
CloseWallet();
|
||||||
_pendingOpenPath = path;
|
_pendingOpenPath = path;
|
||||||
@@ -930,10 +947,12 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException)
|
catch (UnauthorizedAccessException)
|
||||||
{
|
{
|
||||||
|
newLock.Dispose();
|
||||||
StatusMessage = Loc.Tr("msg.wallet.noaccess");
|
StatusMessage = Loc.Tr("msg.wallet.noaccess");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
newLock.Dispose();
|
||||||
StatusMessage = $"Errore: {ex.Message}";
|
StatusMessage = $"Errore: {ex.Message}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -948,23 +967,10 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
StatusMessage = "";
|
StatusMessage = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryAcquireWalletLock(string path)
|
private void OpenLoaded(WalletDocument doc, HdAccount account, string path, string? password, WalletLock walletLock)
|
||||||
{
|
{
|
||||||
var acquired = WalletLock.TryAcquire(path);
|
|
||||||
if (acquired is null)
|
|
||||||
{
|
|
||||||
StatusMessage = Loc.Tr("msg.wallet.locked");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_walletLock?.Dispose();
|
_walletLock?.Dispose();
|
||||||
_walletLock = acquired;
|
_walletLock = walletLock;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenLoaded(WalletDocument doc, HdAccount account, string path, string? password)
|
|
||||||
{
|
|
||||||
if (!TryAcquireWalletLock(path))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// La rete del wallet comanda (registry, pin TLS, indirizzi).
|
// La rete del wallet comanda (registry, pin TLS, indirizzi).
|
||||||
SelectedNetwork = doc.Network;
|
SelectedNetwork = doc.Network;
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ public static class CoinAmount
|
|||||||
/// <summary>(satoshi per unità, decimali mostrati) di ciascuna unità.</summary>
|
/// <summary>(satoshi per unità, decimali mostrati) di ciascuna unità.</summary>
|
||||||
private static (long Factor, int Decimals) Of(string unit) => unit switch
|
private static (long Factor, int Decimals) Of(string unit) => unit switch
|
||||||
{
|
{
|
||||||
|
"PLM" => (SatsPerCoin, 8),
|
||||||
"mPLM" => (100_000, 5),
|
"mPLM" => (100_000, 5),
|
||||||
"µPLM" => (100, 2),
|
"µPLM" => (100, 2),
|
||||||
"sat" => (1, 0),
|
"sat" => (1, 0),
|
||||||
_ => (SatsPerCoin, 8), // PLM
|
_ => throw new ArgumentException($"Unknown coin unit: {unit}", nameof(unit)),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string Format(long sats, string unit = "") =>
|
public static string Format(long sats, string unit = "") =>
|
||||||
|
|||||||
Reference in New Issue
Block a user