Implement mix to another wallet with Daemon
MODIFY
WalletWasabi.Gui/CommandLine/Daemon.cs
WalletWasabi.Gui/CommandLine/Daemon.cs
+using NBitcoin;
using System;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.CoinJoin.Client.Clients.Queuing;
@@ -20,7 +22,7 @@ namespace WalletWasabi.Gui.CommandLine
private WalletService WalletService { get; set; }
- internal async Task RunAsync(string walletName, bool mixAll, bool keepMixAlive)
+ internal async Task RunAsync(string walletName, string destinationWalletName, bool mixAll, bool keepMixAlive)
{
try
{
@@ -84,9 +86,15 @@ namespace WalletWasabi.Gui.CommandLine
return;
}
- await TryQueueCoinsToMixAsync(mixAll, password);
+ KeyManager destinationKeyManager = TryGetKeyManagerFromWalletName(destinationWalletName);
+ if (keyManager.ExtPubKey != destinationKeyManager.ExtPubKey)
+ {
+ await Global.WalletManager.CreateAndStartWalletServiceAsync(destinationKeyManager);
+ }
+
+ // Enqueue coins up to the anonset target, except if mixall, because then enqueue all coins.
+ await TryQueueCoinsToMixAsync(password, maxAnonset: mixAll ? int.MaxValue : WalletService.ServiceConfiguration.MixUntilAnonymitySet - 1);
- bool mixing;
do
{
if (Global.KillRequested)
@@ -95,26 +103,29 @@ namespace WalletWasabi.Gui.CommandLine
}
await Task.Delay(3000);
+
if (Global.KillRequested)
{
break;
}
- bool anyCoinsQueued = WalletService.ChaumianClient.State.AnyCoinsQueued();
- if (!anyCoinsQueued && keepMixAlive) // If no coins queued and mixing is asked to be kept alive then try to queue coins.
+ // If no coins enqueued then enqueue the large anonset coins and mix to another wallet.
+ if (!AnyCoinsQueued())
{
- // Don't do mixall here, the mixall says all the coins has to be mixed once, it doesn't says it has to be requeued all the time.
- await TryQueueCoinsToMixAsync(mixAll: false, password);
+ WalletService.ChaumianClient.DestinationKeyManager = destinationKeyManager;
+ await TryQueueCoinsToMixAsync(password, minAnonset: WalletService.ServiceConfiguration.MixUntilAnonymitySet);
}
- if (Global.KillRequested)
+ // If no coins were queued then try to queue coins those have less anonset and mix it into the same wallet.
+ if (!AnyCoinsQueued())
{
- break;
+ // Don't do mixall here, the mixall says all the coins has to be mixed once, it doesn't says it has to be requeued all the time.
+ WalletService.ChaumianClient.DestinationKeyManager = WalletService.ChaumianClient.KeyManager;
+ await TryQueueCoinsToMixAsync(password, maxAnonset: WalletService.ServiceConfiguration.MixUntilAnonymitySet - 1);
}
-
- mixing = anyCoinsQueued || keepMixAlive;
}
- while (mixing);
+ // Keep this loop alive as long as a coin is queued or keepalive was specified.
+ while (keepMixAlive || AnyCoinsQueued());
if (!Global.KillRequested) // This only has to run if it finishes by itself. Otherwise the Ctrl+c runs it.
{
@@ -134,6 +145,11 @@ namespace WalletWasabi.Gui.CommandLine
}
}
+ private bool AnyCoinsQueued()
+ {
+ return WalletService.ChaumianClient.State.AnyCoinsQueued();
+ }
+
public KeyManager TryGetKeyManagerFromWalletName(string walletName)
{
try
@@ -175,17 +191,18 @@ namespace WalletWasabi.Gui.CommandLine
}
}
- private async Task TryQueueCoinsToMixAsync(bool mixAll, string password)
+ private async Task TryQueueCoinsToMixAsync(string password, int minAnonset = int.MinValue, int maxAnonset = int.MaxValue)
{
try
{
- var coinsView = WalletService.Coins;
- var coinsToMix = coinsView.Available();
- if (!mixAll)
+ var coinsToMix = WalletService.Coins.Available().FilterBy(x => x.AnonymitySet <= maxAnonset && minAnonset <= x.AnonymitySet);
+
+ var enqueuedCoins = await WalletService.ChaumianClient.QueueCoinsToMixAsync(password, coinsToMix.ToArray());
+
+ if (enqueuedCoins.Any())
{
- coinsToMix = coinsToMix.FilterBy(x => x.AnonymitySet < WalletService.ServiceConfiguration.MixUntilAnonymitySet);
+ Logger.LogInfo($"Enqueued {Money.Satoshis(enqueuedCoins.Sum(x => x.Amount)).ToString(false, true)} BTC, {enqueuedCoins.Count()} coins with smalles anonset {enqueuedCoins.Min(x => x.AnonymitySet)} and largest anonset {enqueuedCoins.Max(x => x.AnonymitySet)}.");
}
- await WalletService.ChaumianClient.QueueCoinsToMixAsync(password, coinsToMix.ToArray());
}
catch (Exception ex)
{

MODIFY
WalletWasabi.Gui/CommandLine/MixerCommand.cs
WalletWasabi.Gui/CommandLine/MixerCommand.cs
@@ -9,6 +9,7 @@ namespace WalletWasabi.Gui.CommandLine
internal class MixerCommand : Command
{
public string WalletName { get; set; }
+ public string DestinationWalletName { get; set; }
public bool MixAll { get; set; }
public bool KeepMixAlive { get; set; }
public bool ShowHelp { get; set; }
@@ -25,7 +26,8 @@ namespace WalletWasabi.Gui.CommandLine
"Start mixing without the GUI with the specified wallet.",
"eg: ./wassabee mix --wallet:MyWalletName --mixall --keepalive",
{ "h|help", "Displays help page and exit.", x => ShowHelp = x != null },
- { "w|wallet=", "The name of the wallet file.", x => WalletName = x },
+ { "w|wallet:", "The name of the wallet file.", x => WalletName = x },
+ { "destination:", "The name of the destination wallet file.", x => DestinationWalletName = x },
{ "mixall", "Mix once even if the coin reached the target anonymity set specified in the config file.", x => MixAll = x != null },
{ "keepalive", "Do not exit the software after mixing has been finished, rather keep mixing when new money arrives.", x => KeepMixAlive = x != null }
};
@@ -44,7 +46,7 @@ namespace WalletWasabi.Gui.CommandLine
if (!error && !ShowHelp)
{
- await Daemon.RunAsync(WalletName, MixAll, KeepMixAlive);
+ await Daemon.RunAsync(WalletName, DestinationWalletName ?? WalletName, MixAll, KeepMixAlive);
}
}
catch (Exception ex)

MODIFY
WalletWasabi/CoinJoin/Client/Clients/CoinJoinClient.cs
WalletWasabi/CoinJoin/Client/Clients/CoinJoinClient.cs
@@ -46,6 +46,7 @@ namespace WalletWasabi.CoinJoin.Client.Clients
{
Network = Guard.NotNull(nameof(network), network);
KeyManager = Guard.NotNull(nameof(keyManager), keyManager);
+ DestinationKeyManager = KeyManager;
Synchronizer = Guard.NotNull(nameof(synchronizer), synchronizer);
CcjHostUriAction = Synchronizer.WasabiClient.TorClient.DestinationUriAction;
TorSocks5EndPoint = Synchronizer.WasabiClient.TorClient.TorSocks5EndPoint;
@@ -76,7 +77,8 @@ namespace WalletWasabi.CoinJoin.Client.Clients
public event EventHandler<DequeueResult> OnDequeue;
public Network Network { get; private set; }
- public KeyManager KeyManager { get; private set; }
+ public KeyManager KeyManager { get; }
+ public KeyManager DestinationKeyManager { get; set; }
public bool IsQuitPending { get; set; }
private ClientRoundRegistration DelayedRoundRegistration { get; set; }
@@ -103,6 +105,8 @@ namespace WalletWasabi.CoinJoin.Client.Clients
public bool HasIngredients => Salt != null && Soup != null;
+ public bool IsDestinationSame => KeyManager.ExtPubKey == DestinationKeyManager.ExtPubKey;
+
private async void Synchronizer_ResponseArrivedAsync(object sender, SynchronizeResponse e)
{
await TryProcessStatusAsync(e?.CcjRoundStates).ConfigureAwait(false);
@@ -431,7 +435,7 @@ namespace WalletWasabi.CoinJoin.Client.Clients
else
{
// Should never happen, but oh well we can autocorrect it so why not.
- ExposedLinks[input] = ExposedLinks[input].Append(new HdPubKeyBlindedPair(KeyManager.GetKeyForScriptPubKey(activeOutput.Address.ScriptPubKey), false));
+ ExposedLinks[input] = ExposedLinks[input].Append(new HdPubKeyBlindedPair(DestinationKeyManager.GetKeyForScriptPubKey(activeOutput.Address.ScriptPubKey) ?? KeyManager.GetKeyForScriptPubKey(activeOutput.Address.ScriptPubKey), false));
}
}
}
@@ -652,8 +656,8 @@ namespace WalletWasabi.CoinJoin.Client.Clients
var keysTryNotToRegister = ExposedLinks.SelectMany(x => x.Value).Select(x => x.Key).Except(keysToSurelyRegister).ToArray();
// Get all locked internal keys we have and assert we have enough.
- KeyManager.AssertLockedInternalKeysIndexed(howMany: maximumMixingLevelCount + 1);
- IEnumerable<HdPubKey> allLockedInternalKeys = KeyManager.GetKeys(x => x.IsInternal && x.KeyState == KeyState.Locked && !keysTryNotToRegister.Contains(x));
+ DestinationKeyManager.AssertLockedInternalKeysIndexed(howMany: maximumMixingLevelCount + 1);
+ IEnumerable<HdPubKey> allLockedInternalKeys = DestinationKeyManager.GetKeys(x => x.IsInternal && x.KeyState == KeyState.Locked && !keysTryNotToRegister.Contains(x));
// If any of our inputs have exposed address relationship then prefer that.
allLockedInternalKeys = keysToSurelyRegister.Concat(allLockedInternalKeys).Distinct();
@@ -667,7 +671,7 @@ namespace WalletWasabi.CoinJoin.Client.Clients
var newKeys = new List<HdPubKey>();
for (int i = allLockedInternalKeys.Count(); i <= maximumMixingLevelCount + 1; i++)
{
- HdPubKey k = KeyManager.GenerateNewKey(SmartLabel.Empty, KeyState.Locked, isInternal: true, toFile: false);
+ HdPubKey k = DestinationKeyManager.GenerateNewKey(SmartLabel.Empty, KeyState.Locked, isInternal: true, toFile: false);
newKeys.Add(k);
}
allLockedInternalKeys = allLockedInternalKeys.Concat(newKeys);
@@ -721,7 +725,7 @@ namespace WalletWasabi.CoinJoin.Client.Clients
}
// Save our modifications in the keymanager before we give back the selected keys.
- KeyManager.ToFile();
+ DestinationKeyManager.ToFile();
return (change, actives);
}

Implement mix to another wallet with Daemon