public static async Task InitializeWalletServiceAsync(KeyManager keyManager) { if (Config.UseTor.Value) { ChaumianClient = new CcjClient(Synchronizer, Network, keyManager, () => Config.GetCurrentBackendUri(), Config.GetTorSocks5EndPoint()); } else { ChaumianClient = new CcjClient(Synchronizer, Network, keyManager, Config.GetFallbackBackendUri(), null); } WalletService = new WalletService(keyManager, Synchronizer, ChaumianClient, MemPoolService, Nodes, DataDir, Config.ServiceConfiguration); ChaumianClient.Start(); Logger.LogInfo("Start Chaumian CoinJoin service..."); using (CancelWalletServiceInitialization = new CancellationTokenSource()) { Logger.LogInfo("Starting WalletService..."); await WalletService.InitializeAsync(CancelWalletServiceInitialization.Token); Logger.LogInfo("WalletService started."); } CancelWalletServiceInitialization = null; // Must make it null explicitly, because dispose won't make it null. WalletService.Coins.CollectionChanged += Coins_CollectionChanged; }
private static async Task TryDesperateDequeueAllCoinsAsync() { try { if (Interlocked.Read(ref _triedDesperateDequeuing) == 1) { return; } else { Interlocked.Increment(ref _triedDesperateDequeuing); } if (WalletService is null || ChaumianClient is null) { return; } SmartCoin[] enqueuedCoins = WalletService.Coins.Where(x => x.CoinJoinInProgress).ToArray(); if (enqueuedCoins.Any()) { Logger.LogWarning("Unregistering coins in CoinJoin process.", nameof(Global)); await ChaumianClient.DequeueCoinsFromMixAsync(enqueuedCoins); } } catch (Exception ex) { Logger.LogWarning(ex, nameof(Global)); } }
public static async Task DisposeInWalletDependentServicesAsync() { if (WalletService != null) { WalletService.Coins.CollectionChanged -= Coins_CollectionChanged; } CancelWalletServiceInitialization?.Cancel(); CancelWalletServiceInitialization = null; if (WalletService != null) { if (WalletService.KeyManager != null) // This should not ever happen. { string backupWalletFilePath = Path.Combine(WalletBackupsDir, Path.GetFileName(WalletService.KeyManager.FilePath)); WalletService.KeyManager?.ToFile(backupWalletFilePath); Logger.LogInfo($"{nameof(KeyManager)} backup saved to {backupWalletFilePath}.", nameof(Global)); } WalletService?.Dispose(); WalletService = null; Logger.LogInfo($"{nameof(WalletService)} is stopped.", nameof(Global)); } if (ChaumianClient != null) { await ChaumianClient.StopAsync(); ChaumianClient = null; Logger.LogInfo($"{nameof(ChaumianClient)} is stopped.", nameof(Global)); } }
public static async Task DisposeAsync() { CancelWalletServiceInitialization?.Cancel(); WalletService?.Dispose(); Logger.LogInfo($"{nameof(WalletService)} is stopped.", nameof(Global)); UpdateChecker?.Dispose(); Logger.LogInfo($"{nameof(UpdateChecker)} is stopped.", nameof(Global)); IndexDownloader?.Dispose(); Logger.LogInfo($"{nameof(IndexDownloader)} is stopped.", nameof(Global)); Directory.CreateDirectory(Path.GetDirectoryName(AddressManagerFilePath)); AddressManager?.SavePeerFile(AddressManagerFilePath, Config.Network); Logger.LogInfo($"{nameof(AddressManager)} is saved to `{AddressManagerFilePath}`.", nameof(Global)); Nodes?.Dispose(); Logger.LogInfo($"{nameof(Nodes)} are disposed.", nameof(Global)); if (RegTestMemPoolServingNode != null) { RegTestMemPoolServingNode.Disconnect(); Logger.LogInfo($"{nameof(RegTestMemPoolServingNode)} is disposed.", nameof(Global)); } if (ChaumianClient != null) { await ChaumianClient.StopAsync(); } Logger.LogInfo($"{nameof(ChaumianClient)} is stopped.", nameof(Global)); }
/// <inheritdoc/> public async Task StartAsync(CancellationToken cancel) { try { InitializingChanged?.Invoke(null, true); if (!Synchronizer.IsRunning) { throw new NotSupportedException($"{nameof(Synchronizer)} is not running."); } while (!BitcoinStore.IsInitialized) { await Task.Delay(100).ConfigureAwait(false); cancel.ThrowIfCancellationRequested(); } await RuntimeParams.LoadAsync(); ChaumianClient.Start(); using (await HandleFiltersLock.LockAsync()) { await LoadWalletStateAsync(cancel); await LoadDummyMempoolAsync(); } } finally { InitializingChanged?.Invoke(null, false); } }
public static async Task DesperateDequeueAllCoinsAsync() { if (WalletService is null || ChaumianClient is null) { return; } SmartCoin[] enqueuedCoins = WalletService.Coins.Where(x => x.CoinJoinInProgress).ToArray(); if (enqueuedCoins.Any()) { Logger.LogWarning("Unregistering coins in CoinJoin process.", nameof(Global)); await ChaumianClient.DequeueCoinsFromMixAsync(enqueuedCoins, "Process was signaled to kill."); } }
public async Task DesperateDequeueAllCoinsAsync() { if (WalletService is null || ChaumianClient is null) { return; } SmartCoin[] enqueuedCoins = WalletService.Coins.CoinJoinInProcess().ToArray(); if (enqueuedCoins.Any()) { Logger.LogWarning("Unregistering coins in CoinJoin process."); await ChaumianClient.DequeueCoinsFromMixAsync(enqueuedCoins, DequeueReason.ApplicationExit); } }
public static async Task DisposeInWalletDependentServicesAsync() { CancelWalletServiceInitialization?.Cancel(); CancelWalletServiceInitialization = null; WalletService?.Dispose(); WalletService = null; Logger.LogInfo($"{nameof(WalletService)} is stopped.", nameof(Global)); if (ChaumianClient != null) { await ChaumianClient.StopAsync(); ChaumianClient = null; } Logger.LogInfo($"{nameof(ChaumianClient)} is stopped.", nameof(Global)); }
public async Task InitializeWalletServiceAsync(KeyManager keyManager) { using (_cancelWalletServiceInitialization = new CancellationTokenSource()) { var token = _cancelWalletServiceInitialization.Token; while (!Initialized) { await Task.Delay(100, token); } if (Config.UseTor) { ChaumianClient = new CoinJoinClient(Synchronizer, Network, keyManager, () => Config.GetCurrentBackendUri(), Config.TorSocks5EndPoint); } else { ChaumianClient = new CoinJoinClient(Synchronizer, Network, keyManager, Config.GetFallbackBackendUri(), null); } try { keyManager.CorrectBlockHeights(BitcoinStore.HashChain); // Block heights are wrong sometimes. It's a hack. We have to retroactively fix existing wallets, but also we have to figure out where we ruin the block heights. } catch (Exception ex) // Whatever this is not critical, but let's log it. { Logger.LogWarning(ex); } WalletService = new WalletService(BitcoinStore, keyManager, Synchronizer, ChaumianClient, Nodes, DataDir, Config.ServiceConfiguration, FeeProviders, BitcoinCoreNode?.RpcClient); ChaumianClient.Start(); Logger.LogInfo("Start Chaumian CoinJoin service..."); Logger.LogInfo($"Starting {nameof(WalletService)}..."); await WalletService.InitializeAsync(token); Logger.LogInfo($"{nameof(WalletService)} started."); token.ThrowIfCancellationRequested(); WalletService.TransactionProcessor.CoinReceived += CoinReceived; TransactionBroadcaster.AddWalletService(WalletService); } _cancelWalletServiceInitialization = null; // Must make it null explicitly, because dispose won't make it null. }
private async void TransactionProcessor_WalletRelevantTransactionProcessedAsync(object sender, ProcessedResult e) { try { foreach (var coin in e.NewlySpentCoins.Concat(e.ReplacedCoins).Concat(e.SuccessfullyDoubleSpentCoins).Distinct()) { ChaumianClient.ExposedLinks.TryRemove(coin.GetTxoRef(), out _); } } catch (Exception ex) { Logger.LogError(ex); } try { IEnumerable <SmartCoin> newCoins = e.NewlyReceivedCoins.Concat(e.RestoredCoins).Distinct(); if (newCoins.Any()) { if (ChaumianClient.State.Contains(e.Transaction.Transaction.Inputs.Select(x => x.PrevOut.ToTxoRef()))) { var coinsToQueue = new HashSet <SmartCoin>(); foreach (var newCoin in newCoins) { // If it's being mixed and anonset is not sufficient, then queue it. if (newCoin.Unspent && ChaumianClient.HasIngredients && newCoin.AnonymitySet < ServiceConfiguration.MixUntilAnonymitySet) { coinsToQueue.Add(newCoin); } } await ChaumianClient.QueueCoinsToMixAsync(coinsToQueue).ConfigureAwait(false); } } } catch (Exception ex) { Logger.LogError(ex); } }
public async Task DisposeInWalletDependentServicesAsync() { if (WalletService != null) { WalletService.Coins.CollectionChanged -= Coins_CollectionChanged; } try { _cancelWalletServiceInitialization?.Cancel(); } catch (ObjectDisposedException) { Logger.LogWarning($"{nameof(_cancelWalletServiceInitialization)} is disposed. This can occur due to an error while processing the wallet.", nameof(Global)); } _cancelWalletServiceInitialization = null; if (WalletService != null) { if (WalletService.KeyManager != null) // This should not ever happen. { string backupWalletFilePath = Path.Combine(WalletBackupsDir, Path.GetFileName(WalletService.KeyManager.FilePath)); WalletService.KeyManager?.ToFile(backupWalletFilePath); Logger.LogInfo($"{nameof(KeyManager)} backup saved to `{backupWalletFilePath}`.", nameof(Global)); } if (WalletService != null) { await WalletService.StopAsync(); } WalletService = null; Logger.LogInfo($"{nameof(WalletService)} is stopped.", nameof(Global)); } if (ChaumianClient != null) { await ChaumianClient.StopAsync(); ChaumianClient = null; Logger.LogInfo($"{nameof(ChaumianClient)} is stopped.", nameof(Global)); } }
public async Task InitializeWalletServiceAsync(KeyManager keyManager) { using (_cancelWalletServiceInitialization = new CancellationTokenSource()) { var token = _cancelWalletServiceInitialization.Token; while (!Initialized) { await Task.Delay(100, token); } if (Config.UseTor.Value) { ChaumianClient = new CcjClient(Synchronizer, Network, keyManager, () => Config.GetCurrentBackendUri(), Config.GetTorSocks5EndPoint()); } else { ChaumianClient = new CcjClient(Synchronizer, Network, keyManager, Config.GetFallbackBackendUri(), null); } keyManager.CorrectBlockHeights(BitcoinStore.HashChain); // Block heights are wrong sometimes. It's a hack. We have to retroactively fix existing wallets, but also we have to figure out where we ruin the block heights. WalletService = new WalletService(BitcoinStore, keyManager, Synchronizer, ChaumianClient, MemPoolService, Nodes, DataDir, Config.ServiceConfiguration); ChaumianClient.Start(); Logger.LogInfo("Start Chaumian CoinJoin service..."); Logger.LogInfo("Starting WalletService..."); await WalletService.InitializeAsync(token); Logger.LogInfo("WalletService started."); token.ThrowIfCancellationRequested(); WalletService.Coins.CollectionChanged += Coins_CollectionChanged; } _cancelWalletServiceInitialization = null; // Must make it null explicitly, because dispose won't make it null. }
private void ProcessTransaction(SmartTransaction tx, List <HdPubKey> keys = null) { if (tx.Height != Height.MemPool && tx.Height != Height.Unknown) { MemPool.TransactionHashes.TryRemove(tx.GetHash()); } //iterate tx // if already have the coin // if NOT mempool // update height //if double spend // if mempool // if all double spent coins are mempool and RBF // remove double spent coins(if other coin spends it, remove that too and so on) // will add later if they came to our keys // else // return // else // new confirmation always enjoys priority // remove double spent coins recursively(if other coin spends it, remove that too and so on)// will add later if they came to our keys //iterate tx // if came to our keys // add coin // If key list is not provided refresh the key list. if (keys == null) { keys = KeyManager.GetKeys().ToList(); } for (var i = 0; i < tx.Transaction.Outputs.Count; i++) { // If we already had it, just update the height. Maybe got from mempool to block or reorged. var foundCoin = Coins.FirstOrDefault(x => x.TransactionId == tx.GetHash() && x.Index == i); if (foundCoin != default) { // If tx height is mempool then don't, otherwise update the height. if (tx.Height != Height.MemPool) { foundCoin.Height = tx.Height; } } } // If double spend: List <SmartCoin> doubleSpends = Coins .Where(x => tx.Transaction.Inputs.Any(y => x.SpentOutputs.Select(z => z.ToOutPoint()).Contains(y.PrevOut))) .ToList(); if (doubleSpends.Any()) { if (tx.Height == Height.MemPool) { // if all double spent coins are mempool and RBF if (doubleSpends.All(x => x.Height == Height.MemPool && x.RBF)) { // remove double spent coins(if other coin spends it, remove that too and so on) // will add later if they came to our keys foreach (var doubleSpentCoin in doubleSpends) { RemoveCoinRecursively(doubleSpentCoin); } } else { return; } } else // new confirmation always enjoys priority { // remove double spent coins recursively (if other coin spends it, remove that too and so on), will add later if they came to our keys foreach (var doubleSpentCoin in doubleSpends) { RemoveCoinRecursively(doubleSpentCoin); } } } for (var i = 0U; i < tx.Transaction.Outputs.Count; i++) { // If transaction received to any of the wallet keys: var output = tx.Transaction.Outputs[i]; HdPubKey foundKey = keys.SingleOrDefault(x => x.GetP2wpkhScript() == output.ScriptPubKey); if (foundKey != default) { foundKey.SetKeyState(KeyState.Used, KeyManager); List <SmartCoin> spentOwnCoins = Coins.Where(x => tx.Transaction.Inputs.Any(y => y.PrevOut.Hash == x.TransactionId && y.PrevOut.N == x.Index)).ToList(); var mixin = tx.Transaction.GetMixin(i); if (spentOwnCoins.Count != 0) { mixin += spentOwnCoins.Min(x => x.Mixin); } var coin = new SmartCoin(tx.GetHash(), i, output.ScriptPubKey, output.Value, tx.Transaction.Inputs.ToTxoRefs().ToArray(), tx.Height, tx.Transaction.RBF, mixin, foundKey.Label, spenderTransactionId: null, locked: false); // Don't inherit locked status from key, that's different. Coins.TryAdd(coin); if (coin.Unspent && coin.Label == "ZeroLink Change" && ChaumianClient.OnePiece != null) { Task.Run(async() => { try { await ChaumianClient.QueueCoinsToMixAsync(ChaumianClient.OnePiece, coin); } catch (Exception ex) { Logger.LogError <WalletService>(ex); } }); } // Make sure there's always 21 clean keys generated and indexed. if (AssertCleanKeysIndexed(21, foundKey.IsInternal())) { // If it generated a new key refresh the keys: keys = KeyManager.GetKeys().ToList(); } } } // If spends any of our coin for (var i = 0; i < tx.Transaction.Inputs.Count; i++) { var input = tx.Transaction.Inputs[i]; var foundCoin = Coins.FirstOrDefault(x => x.TransactionId == input.PrevOut.Hash && x.Index == input.PrevOut.N); if (foundCoin != null) { foundCoin.SpenderTransactionId = tx.GetHash(); CoinSpentOrSpenderConfirmed?.Invoke(this, foundCoin); } } }