public void RegisterServices( BitcoinStore bitcoinStore, WasabiSynchronizer syncer, NodesGroup nodes, ServiceConfiguration serviceConfiguration, IFeeProvider feeProvider, CoreNode coreNode = null) { if (State != WalletState.Uninitialized) { throw new InvalidOperationException($"{nameof(State)} must be {WalletState.Uninitialized}. Current state: {State}."); } BitcoinStore = Guard.NotNull(nameof(bitcoinStore), bitcoinStore); Nodes = Guard.NotNull(nameof(nodes), nodes); Synchronizer = Guard.NotNull(nameof(syncer), syncer); ServiceConfiguration = Guard.NotNull(nameof(serviceConfiguration), serviceConfiguration); FeeProvider = Guard.NotNull(nameof(feeProvider), feeProvider); CoreNode = coreNode; ChaumianClient = new CoinJoinClient(Synchronizer, Network, KeyManager); TransactionProcessor = new TransactionProcessor(BitcoinStore.TransactionStore, KeyManager, ServiceConfiguration.DustThreshold, ServiceConfiguration.PrivacyLevelStrong); Coins = TransactionProcessor.Coins; TransactionProcessor.WalletRelevantTransactionProcessed += TransactionProcessor_WalletRelevantTransactionProcessedAsync; ChaumianClient.OnDequeue += ChaumianClient_OnDequeue; BitcoinStore.IndexStore.NewFilter += IndexDownloader_NewFilterAsync; BitcoinStore.IndexStore.Reorged += IndexDownloader_ReorgedAsync; BitcoinStore.MempoolService.TransactionReceived += Mempool_TransactionReceived; State = WalletState.Initialized; }
public async Task StartParticipatingAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); var apiClient = new WabiSabiHttpApiClient(HttpClientFactory.NewHttpClientWithDefaultCircuit()); using var roundStateUpdater = new RoundStateUpdater(TimeSpan.FromSeconds(3), apiClient); await roundStateUpdater.StartAsync(cancellationToken).ConfigureAwait(false); var coinJoinClient = new CoinJoinClient( HttpClientFactory, Wallet, Wallet, roundStateUpdater, consolidationMode: true); // Run the coinjoin client task. var walletHdPubKey = new HdPubKey(Wallet.PubKey, KeyPath.Parse("m/84'/0/0/0/0"), SmartLabel.Empty, KeyState.Clean); walletHdPubKey.SetAnonymitySet(1); // bug if not settled if (SplitTransaction is null) { throw new InvalidOperationException($"{nameof(GenerateCoinsAsync)} has to be called first."); } var smartCoins = SplitTransaction.Transaction.Outputs.AsIndexedOutputs() .Select(x => new SmartCoin(SplitTransaction, x.N, walletHdPubKey)) .ToList(); // Run the coinjoin client task. await coinJoinClient.StartCoinJoinAsync(smartCoins, cancellationToken).ConfigureAwait(false); await roundStateUpdater.StopAsync(cancellationToken).ConfigureAwait(false); }
private async Task <TransactionProcessor> CreateTransactionProcessorAsync([CallerMemberName] string callerName = "") { var datadir = EnvironmentHelpers.GetDataDir(Path.Combine("WalletWasabi", "Bench")); var dir = Path.Combine(datadir, callerName, "TransactionStore"); Console.WriteLine(dir); await IoHelpers.DeleteRecursivelyWithMagicDustAsync(dir); // Create the services. // 1. Create connection service. var nodes = new NodesGroup(Network.Main); var bitcoinStore = new BitcoinStore(); var serviceConfiguration = new ServiceConfiguration(2, 2, 21, 50, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 45678), Money.Coins(0.0001m)); // 2. Create wasabi synchronizer service. var synchronizer = new WasabiSynchronizer(Network.Main, bitcoinStore, () => new Uri("http://localhost:35474"), null); synchronizer.Start(requestInterval: TimeSpan.FromDays(1), TimeSpan.FromDays(1), 1000); // 3. Create key manager service. var keyManager = KeyManager.CreateNew(out _, "password"); // 4. Create chaumian coinjoin client. var chaumianClient = new CoinJoinClient(synchronizer, Network.Main, keyManager); // 5. Create wallet service. await bitcoinStore.InitializeAsync(dir, Network.Main); var workDir = Path.Combine(datadir, EnvironmentHelpers.GetMethodName()); var feeProviders = new FeeProviders(new[] { synchronizer }); var wallet = new WalletService(bitcoinStore, keyManager, synchronizer, nodes, workDir, serviceConfiguration, feeProviders); return(wallet.TransactionProcessor); }
public WalletService( BitcoinStore bitcoinStore, KeyManager keyManager, WasabiSynchronizer syncer, CoinJoinClient chaumianClient, NodesGroup nodes, string workFolderDir, ServiceConfiguration serviceConfiguration, IFeeProvider feeProvider, CoreNode coreNode = null) { BitcoinStore = Guard.NotNull(nameof(bitcoinStore), bitcoinStore); KeyManager = Guard.NotNull(nameof(keyManager), keyManager); Nodes = Guard.NotNull(nameof(nodes), nodes); Synchronizer = Guard.NotNull(nameof(syncer), syncer); ChaumianClient = Guard.NotNull(nameof(chaumianClient), chaumianClient); ServiceConfiguration = Guard.NotNull(nameof(serviceConfiguration), serviceConfiguration); FeeProvider = Guard.NotNull(nameof(feeProvider), feeProvider); CoreNode = coreNode; HandleFiltersLock = new AsyncLock(); BlocksFolderPath = Path.Combine(workFolderDir, "Blocks", Network.ToString()); RuntimeParams.SetDataDir(workFolderDir); BlockFolderLock = new AsyncLock(); KeyManager.AssertCleanKeysIndexed(); KeyManager.AssertLockedInternalKeysIndexed(14); TransactionProcessor = new TransactionProcessor(BitcoinStore.TransactionStore, KeyManager, ServiceConfiguration.DustThreshold, ServiceConfiguration.PrivacyLevelStrong); Coins = TransactionProcessor.Coins; TransactionProcessor.WalletRelevantTransactionProcessed += TransactionProcessor_WalletRelevantTransactionProcessedAsync; if (Directory.Exists(BlocksFolderPath)) { if (Synchronizer.Network == Network.RegTest) { Directory.Delete(BlocksFolderPath, true); Directory.CreateDirectory(BlocksFolderPath); } } else { Directory.CreateDirectory(BlocksFolderPath); } var walletName = "UnnamedWallet"; if (!string.IsNullOrWhiteSpace(KeyManager.FilePath)) { walletName = Path.GetFileNameWithoutExtension(KeyManager.FilePath); } BitcoinStore.IndexStore.NewFilter += IndexDownloader_NewFilterAsync; BitcoinStore.IndexStore.Reorged += IndexDownloader_ReorgedAsync; BitcoinStore.MempoolService.TransactionReceived += Mempool_TransactionReceived; }
public Wallet( Network network, BitcoinStore bitcoinStore, KeyManager keyManager, WasabiSynchronizer syncer, NodesGroup nodes, string workFolderDir, ServiceConfiguration serviceConfiguration, IFeeProvider feeProvider, CoreNode coreNode = null) { Network = Guard.NotNull(nameof(network), network); BitcoinStore = Guard.NotNull(nameof(bitcoinStore), bitcoinStore); KeyManager = Guard.NotNull(nameof(keyManager), keyManager); Nodes = Guard.NotNull(nameof(nodes), nodes); Synchronizer = Guard.NotNull(nameof(syncer), syncer); ServiceConfiguration = Guard.NotNull(nameof(serviceConfiguration), serviceConfiguration); FeeProvider = Guard.NotNull(nameof(feeProvider), feeProvider); CoreNode = coreNode; ChaumianClient = new CoinJoinClient(Synchronizer, Network, keyManager); HandleFiltersLock = new AsyncLock(); BlocksFolderPath = Path.Combine(workFolderDir, "Blocks", Network.ToString()); RuntimeParams.SetDataDir(workFolderDir); BlockFolderLock = new AsyncLock(); KeyManager.AssertCleanKeysIndexed(); KeyManager.AssertLockedInternalKeysIndexed(14); TransactionProcessor = new TransactionProcessor(BitcoinStore.TransactionStore, KeyManager, ServiceConfiguration.DustThreshold, ServiceConfiguration.PrivacyLevelStrong); Coins = TransactionProcessor.Coins; TransactionProcessor.WalletRelevantTransactionProcessed += TransactionProcessor_WalletRelevantTransactionProcessedAsync; ChaumianClient.OnDequeue += ChaumianClient_OnDequeue; if (Directory.Exists(BlocksFolderPath)) { if (Network == Network.RegTest) { Directory.Delete(BlocksFolderPath, true); Directory.CreateDirectory(BlocksFolderPath); } } else { Directory.CreateDirectory(BlocksFolderPath); } BitcoinStore.IndexStore.NewFilter += IndexDownloader_NewFilterAsync; BitcoinStore.IndexStore.Reorged += IndexDownloader_ReorgedAsync; BitcoinStore.MempoolService.TransactionReceived += Mempool_TransactionReceived; State = WalletState.Initialized; }
public CoinJoinTracker CreateAndStart(Wallet wallet, IEnumerable <SmartCoin> coinCandidates) { var coinJoinClient = new CoinJoinClient( HttpClientFactory, new KeyChain(wallet.KeyManager, wallet.Kitchen), new InternalDestinationProvider(wallet.KeyManager), RoundStatusUpdater, wallet.KeyManager.MinAnonScoreTarget, doNotRegisterInLastMinuteTimeLimit: TimeSpan.FromMinutes(1)); return(new CoinJoinTracker(wallet, coinJoinClient, coinCandidates, CancellationToken)); }
public CoinJoinTracker( Wallet wallet, CoinJoinClient coinJoinClient, IEnumerable <SmartCoin> coinCandidates, CancellationToken cancellationToken) { Wallet = wallet; CoinJoinClient = coinJoinClient; CoinCandidates = coinCandidates; CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); CoinJoinTask = coinJoinClient.StartCoinJoinAsync(coinCandidates, CancellationTokenSource.Token); }
public void SelectNothingFromEmptySetOfCoins() { // This test is to make sure no coins are selected when there are no coins. var coins = CoinJoinClient.SelectCoinsForRound( coins: Enumerable.Empty <SmartCoin>(), CreateMultipartyTransactionParameters(), consolidationMode: false, anonScoreTarget: 10, ConfigureRng(5)); Assert.Empty(coins); }
public CoinJoinTracker CreateAndStart(Wallet wallet, IEnumerable <SmartCoin> coinCandidates, bool restartAutomatically, bool overridePlebStop) { var coinJoinClient = new CoinJoinClient( HttpClientFactory, new KeyChain(wallet.KeyManager, wallet.Kitchen), new InternalDestinationProvider(wallet.KeyManager), RoundStatusUpdater, wallet.KeyManager.AnonScoreTarget, feeRateMedianTimeFrame: TimeSpan.FromHours(wallet.KeyManager.FeeRateMedianTimeFrameHours), doNotRegisterInLastMinuteTimeLimit: TimeSpan.FromMinutes(1)); return(new CoinJoinTracker(wallet, coinJoinClient, coinCandidates, restartAutomatically, overridePlebStop, CancellationToken)); }
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. }
public CoinJoinTracker( Wallet wallet, CoinJoinClient coinJoinClient, IEnumerable <SmartCoin> coinCandidates, bool stopWhenAllMixed, bool overridePlebStop, CancellationToken cancellationToken) { Wallet = wallet; CoinJoinClient = coinJoinClient; CoinJoinClient.CoinJoinClientProgress += CoinJoinClient_CoinJoinClientProgress; CoinCandidates = coinCandidates; StopWhenAllMixed = stopWhenAllMixed; OverridePlebStop = overridePlebStop; CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); CoinJoinTask = coinJoinClient.StartCoinJoinAsync(coinCandidates, CancellationTokenSource.Token); }
public void SelectNonPrivateCoinFromOneCoinSetOfCoins() { // This test is to make sure that we select the only non-private coin when it is the only coin in the wallet. const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Empty <SmartCoin>() .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet - 1)) .ToList(); var coins = CoinJoinClient.SelectCoinsForRound( coins: coinsToSelectFrom, CreateMultipartyTransactionParameters(), consolidationMode: false, anonScoreTarget: AnonymitySet, ConfigureRng(1)); Assert.Single(coins); }
public void SelectNothingFromFullyPrivateSetOfCoins() { // This test is to make sure no coins are selected when all coins are private. const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Range(0, 10) .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet + 1)) .ToList(); var coins = CoinJoinClient.SelectCoinsForRound( coins: coinsToSelectFrom, CreateMultipartyTransactionParameters(), consolidationMode: false, anonScoreTarget: AnonymitySet, ConfigureRng(5)); Assert.Empty(coins); }
public void TwoNonPrivateCoinInSetOfCoins() { const int MinAnonimitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Empty <SmartCoin>() .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet - 1)) .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet - 1)) .ToList(); var coins = CoinJoinClient.SelectCoinsForRound( coins: coinsToSelectFrom, CreateMultipartyTransactionParameters(), consolidationMode: false, minAnonScoreTarget: MinAnonimitySet, ConfigureRng(1)); Assert.Single(coins); }
public void SelectTwoNonPrivateCoinsFromTwoCoinsSetOfCoinsConsolidationMode() { // This test is to make sure that we select more than one non-private coin. const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Empty <SmartCoin>() .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet - 1)) .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet - 1)) .ToList(); var coins = CoinJoinClient.SelectCoinsForRound( coins: coinsToSelectFrom, CreateMultipartyTransactionParameters(), consolidationMode: true, anonScoreTarget: AnonymitySet, ConfigureRng(1)); Assert.Equal(2, coins.Count); }
public void OnlyOneNonPrivateCoinInBigSetOfCoinsConsolidationMode() { const int MinAnonimitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); SmartCoin smallerAnonCoin = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet - 1); var coinsToSelectFrom = Enumerable .Range(0, 10) .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet + 1)) .Prepend(smallerAnonCoin) .ToList(); var coins = CoinJoinClient.SelectCoinsForRound( coins: coinsToSelectFrom, CreateMultipartyTransactionParameters(), consolidationMode: true, minAnonScoreTarget: MinAnonimitySet, ConfigureRng(5)); Assert.Contains(smallerAnonCoin, coins); Assert.Equal(10, coins.Count); }
public async Task SoloCoinJoinTest() { const int InputCount = 2; // At the end of the test a coinjoin transaction has to be created and broadcasted. var transactionCompleted = new TaskCompletionSource <Transaction>(); // Total test timeout. using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(120)); cts.Token.Register(() => transactionCompleted.TrySetCanceled(), useSynchronizationContext: false); // Create a key manager and use it to create two fake coins. var keyManager = KeyManager.CreateNew(out var _, password: ""); keyManager.AssertCleanKeysIndexed(); var coins = keyManager.GetKeys() .Take(InputCount) .Select(x => new Coin( BitcoinFactory.CreateOutPoint(), new TxOut(Money.Coins(1), x.P2wpkhScript))) .ToArray(); var httpClient = _apiApplicationFactory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { var rpc = BitcoinFactory.GetMockMinimalRpc(); // Make the coordinator to believe that those two coins are real and // that they exist in the blockchain with many confirmations. rpc.OnGetTxOutAsync = (txId, idx, _) => new() { Confirmations = 101, IsCoinBase = false, ScriptPubKeyType = "witness_v0_keyhash", TxOut = coins.Single(x => x.Outpoint.Hash == txId && x.Outpoint.N == idx).TxOut }; // Make the coordinator believe that the transaction is being // broadcasted using the RPC interface. Once we receive this tx // (the `SendRawTransationAsync` was invoked) we stop waiting // and finish the waiting tasks to finish the test successfully. rpc.OnSendRawTransactionAsync = (tx) => { transactionCompleted.SetResult(tx); return(tx.GetHash()); }; // Instruct the coodinator DI container to use these two scoped // services to build everything (wabisabi controller, arena, etc) services.AddScoped <IRPCClient>(s => rpc); services.AddScoped <WabiSabiConfig>(s => new WabiSabiConfig { MaxInputCountByRound = InputCount }); }); }).CreateClient(); // Create the coinjoin client var apiClient = _apiApplicationFactory.CreateWabiSabiHttpApiClient(httpClient); using var roundStateUpdater = new RoundStateUpdater(TimeSpan.FromSeconds(1), apiClient); await roundStateUpdater.StartAsync(CancellationToken.None); var kitchen = new Kitchen(); kitchen.Cook(""); var coinJoinClient = new CoinJoinClient(apiClient, coins, kitchen, keyManager, roundStateUpdater); // Run the coinjoin client task. await coinJoinClient.StartCoinJoinAsync(cts.Token); var boadcastedTx = await transactionCompleted.Task.ConfigureAwait(false); // wait for the transaction to be broadcasted. Assert.NotNull(boadcastedTx); await roundStateUpdater.StopAsync(CancellationToken.None); }