Exemplo n.º 1
0
        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;
        }
Exemplo n.º 2
0
        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));
            }
        }
Exemplo n.º 3
0
        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));
            }
        }
Exemplo n.º 4
0
        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));
        }
Exemplo n.º 5
0
        /// <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);
            }
        }
Exemplo n.º 6
0
        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.");
            }
        }
Exemplo n.º 7
0
        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);
            }
        }
Exemplo n.º 8
0
        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));
        }
Exemplo n.º 9
0
        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.
        }
Exemplo n.º 10
0
        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);
            }
        }
Exemplo n.º 11
0
        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));
            }
        }
Exemplo n.º 12
0
        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.
        }
Exemplo n.º 13
0
        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);
                }
            }
        }