/// <inheritdoc /> public async Task <ClassicTumblerParameters> ConnectToTumblerAsync(Uri serverAddress) { // TODO this method will probably need to change as the connection to a tumbler is currently done during configuration // of the TumblebitRuntime. This method can then be modified to potentially be a convenience method // where a user wants to check a tumbler's paramters before commiting to tumbling (and therefore before configuring the runtime). // TODO: Temporary measure string[] args = { "-testnet" }; var config = new TumblerClientConfiguration(); config.LoadArgs(args); // AcceptAllClientConfiguration should be used if the interaction is null this.runtime = TumblerClientRuntime.FromConfiguration(config, null); //this.tumblerService = new TumblerService(serverAddress); //this.TumblerParameters = await this.tumblerService.GetClassicTumblerParametersAsync(); this.TumblerParameters = runtime.TumblerParameters; if (this.TumblerParameters.Network != this.network) { throw new Exception($"The tumbler is on network {this.TumblerParameters.Network} while the wallet is on network {this.network}."); } // Load the current tumbling state fromt the file system this.tumblingState.LoadStateFromMemory(); // Update and save the state this.tumblingState.TumblerUri = serverAddress; this.tumblingState.TumblerParameters = this.TumblerParameters; this.tumblingState.Save(); return(this.TumblerParameters); }
public bool HasEnoughFundsForCycle(bool firstCycle, string originWalletName) { Money walletBalance = this.runtime.Services.WalletService.GetBalance(originWalletName); FeeRate networkFeeRate = this.runtime.Services.FeeService.GetFeeRateAsync().GetAwaiter().GetResult(); return(TumblerClientRuntime.HasEnoughFundsForCycle(firstCycle, walletBalance, networkFeeRate, TumblerParameters.Denomination, TumblerParameters.Fee)); }
public async Task RedeemEscrows(TumblerClientRuntime runtime) { Logs.Client.LogInformation("Rescanning redeems"); var rpc = ((RPCBlockExplorerService)runtime.Services.BlockExplorerService).RPCClient; var rate = await runtime.Services.FeeService.GetFeeRateAsync(); foreach (var unspent in await rpc.ListUnspentAsync(0, 99999999)) { foreach (var record in runtime.Tracker.Search(unspent.ScriptPubKey)) { if (record.TransactionType == Services.TransactionType.ClientEscrow) { Logs.Client.LogInformation("Client Escrow found " + record.TransactionId); var cycle = runtime.TumblerParameters.CycleGenerator.GetCycle(record.Cycle); var machineState = runtime.CreateStateMachineJob().GetPaymentStateMachineState(cycle); var solver = new PaymentStateMachine(runtime, machineState).SolverClientSession; var broadcast = solver.CreateRedeemTransaction(rate); runtime.Services.TrustedBroadcastService.Broadcast(cycle.Start, Services.TransactionType.ClientRedeem, new CorrelationId(solver.Id), broadcast); } } } var lockedOutpoint = await rpc.ListLockUnspentAsync(); await rpc.UnlockUnspentAsync(lockedOutpoint); Logs.Client.LogInformation("Unlocking " + lockedOutpoint.Length + " outpoints"); }
public ClientInteractiveRuntime(TumblerClientRuntime runtime) { if (runtime == null) { throw new ArgumentNullException("runtime"); } InnerRuntime = runtime; }
private async Task <Result <ClassicTumblerParameters> > TryUseServer() { logger.LogInformation($"Attempting connection to the masternode at address {this.TumblerAddress}"); this.TumblingState.TumblerUri = new Uri(this.TumblerAddress); FullNodeTumblerClientConfiguration config; if (!UseTor) { config = new FullNodeTumblerClientConfiguration(this.TumblingState, onlyMonitor: false, connectionTest: true, useProxy: false); } else { config = new FullNodeTumblerClientConfiguration(this.TumblingState, onlyMonitor: false, connectionTest: true, useProxy: true); } TumblerClientRuntime rt = null; try { rt = await TumblerClientRuntime.FromConfigurationAsync(config, TumblerProtocol, connectionTest : true) .ConfigureAwait(false); // This is overwritten by the tumble method, but it is needed at the beginning of that method for the balance check this.TumblerParameters = rt.TumblerParameters; //Check if the Server parameters are standard and do not connect if the parameters are non-standard if (!rt.TumblerParameters.IsStandard()) { this.logger.LogDebug($"Refusing to connect to the non-standard MasterNode {this.TumblerAddress}"); return(Result.Fail <ClassicTumblerParameters>($"Cannot connect to the MasterNode server {this.TumblerAddress} because its parameters are non-standard.", PostResultActionType.CanContinue)); } return(Result.Ok(rt.TumblerParameters)); } catch (PrivacyProtocolConfigException e) { this.logger.LogError(e, "Privacy protocol exception: {0}", e.Message); return(Result.Fail <ClassicTumblerParameters>("TOR is required for connectivity to an active Stratis Masternode. Please restart Breeze Wallet with Privacy Protocol and ensure that an instance of TOR is running.", PostResultActionType.ShouldStop)); } catch (ConfigException e) { this.logger.LogError(e, "Privacy protocol config exception: {0}", e.Message); return(Result.Fail <ClassicTumblerParameters>(e.Message, PostResultActionType.CanContinue)); } catch (Exception e) { this.logger.LogError(e, "Error obtaining tumbler parameters: {0}", e.Message); return(Result.Fail <ClassicTumblerParameters>("Error obtaining tumbler parameters", PostResultActionType.CanContinue)); } finally { rt?.Dispose(); } }
public void Run(string[] args) { var argsConf = new TextFileConfiguration(args); var debug = argsConf.GetOrDefault <bool>("debug", false); ConsoleLoggerProcessor loggerProcessor = new ConsoleLoggerProcessor(); Logs.Configure(new FuncLoggerFactory(i => new CustomerConsoleLogger(i, Logs.SupportDebug(debug), false, loggerProcessor))); using (var interactive = new Interactive()) { try { var config = new TumblerClientConfiguration(); config.LoadArgs(args); var runtime = TumblerClientRuntime.FromConfiguration(config, new TextWriterClientInteraction(Console.Out, Console.In)); interactive.Runtime = new ClientInteractiveRuntime(runtime); var broadcaster = runtime.CreateBroadcasterJob(); broadcaster.Start(); interactive.Services.Add(broadcaster); //interactive.Services.Add(new CheckIpService(runtime)); //interactive.Services.Last().Start(); if (!config.OnlyMonitor) { var stateMachine = runtime.CreateStateMachineJob(); stateMachine.Start(); interactive.Services.Add(stateMachine); } interactive.StartInteractive(); } catch (ClientInteractionException ex) { if (!string.IsNullOrEmpty(ex.Message)) { Logs.Configuration.LogError(ex.Message); } } catch (ConfigException ex) { if (!string.IsNullOrEmpty(ex.Message)) { Logs.Configuration.LogError(ex.Message); } } catch (InterruptedConsoleException) { } catch (Exception ex) { Logs.Configuration.LogError(ex.Message); Logs.Configuration.LogDebug(ex.StackTrace); } } }
public async Task Initialize() { // Start broadcasterJob (onlymonitor mode) if (this.broadcasterJob == null || !this.broadcasterJob.Started) { var config = new FullNodeTumblerClientConfiguration(this.TumblingState, onlyMonitor: true); this.runtime = await TumblerClientRuntime.FromConfigurationAsync(config).ConfigureAwait(false); this.broadcasterJob = this.runtime.CreateBroadcasterJob(); this.broadcasterJob.Start(); } }
private async Task <Result <ClassicTumblerParameters> > TryUseServer() { logger.LogInformation($"Attempting connection to the masternode at address {this.TumblerAddress}"); this.tumblingState.TumblerUri = new Uri(this.TumblerAddress); FullNodeTumblerClientConfiguration config; if (this.TumblerAddress.Contains("127.0.0.1")) { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: true, useProxy: false); } else { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: true, useProxy: true); } TumblerClientRuntime rt = null; try { rt = await TumblerClientRuntime.FromConfigurationAsync(config, connectionTest : true) .ConfigureAwait(false); // This is overwritten by the tumble method, but it is needed at the beginning of that method for the balance check this.TumblerParameters = rt.TumblerParameters; return(Result.Ok(rt.TumblerParameters)); } catch (PrivacyProtocolConfigException e) { this.logger.LogError(e, "Privacy protocol exception: {0}", e.Message); return(Result.Fail <ClassicTumblerParameters>("TOR is required for connectivity to an active Stratis Masternode. Please restart Breeze Wallet with Privacy Protocol and ensure that an instance of TOR is running.", false)); } catch (ConfigException e) { this.logger.LogError(e, "Privacy protocol config exception: {0}", e.Message); return(Result.Fail <ClassicTumblerParameters>(e.Message, true)); } catch (Exception e) { this.logger.LogError(e, "Error obtaining tumbler parameters: {0}", e.Message); return(Result.Fail <ClassicTumblerParameters>("Error obtaining tumbler parameters", true)); } finally { rt?.Dispose(); } }
public TumblerServerTester(string directory, bool shouldBeStandard) { try { var rootTestData = "TestData"; directory = rootTestData + "/" + directory; _Directory = directory; if (!Directory.Exists(rootTestData)) { Directory.CreateDirectory(rootTestData); } if (!TryDelete(directory, false)) { foreach (var process in Process.GetProcessesByName("bitcoind")) { if (process.MainModule.FileName.Replace("\\", "/").StartsWith(Path.GetFullPath(rootTestData).Replace("\\", "/"), StringComparison.Ordinal)) { process.Kill(); process.WaitForExit(); } } TryDelete(directory, true); } _NodeBuilder = NodeBuilder.Create(directory); _NodeBuilder.ConfigParameters.Add("prematurewitness", "1"); _NodeBuilder.ConfigParameters.Add("walletprematurewitness", "1"); _TumblerNode = _NodeBuilder.CreateNode(false); _AliceNode = _NodeBuilder.CreateNode(false); _BobNode = _NodeBuilder.CreateNode(false); Directory.CreateDirectory(directory); _NodeBuilder.StartAll(); //Activate segwit SyncNodes(); _TumblerNode.Generate(440); _TumblerNode.CreateRPCClient().SendToAddress(_AliceNode.CreateRPCClient().GetNewAddress(), Money.Coins(100m)); _TumblerNode.Generate(1); SyncNodes(); var conf = new TumblerConfiguration(); conf.DataDir = Path.Combine(directory, "server"); Directory.CreateDirectory(conf.DataDir); File.WriteAllBytes(Path.Combine(conf.DataDir, "Tumbler.pem"), TestKeys.Default.ToBytes()); File.WriteAllBytes(Path.Combine(conf.DataDir, "Voucher.pem"), TestKeys.Default2.ToBytes()); conf.RPC.Url = TumblerNode.CreateRPCClient().Address; var creds = ExtractCredentials(File.ReadAllText(_TumblerNode.Config)); conf.RPC.User = creds.Item1; conf.RPC.Password = creds.Item2; conf.TorMandatory = false; conf.Network = Network.RegTest; conf.Listen = new System.Net.IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000); conf.AllowInsecure = !shouldBeStandard; conf.NoRSAProof = !shouldBeStandard; if (!shouldBeStandard) { conf.ClassicTumblerParameters.FakePuzzleCount = 10; conf.ClassicTumblerParameters.FakeTransactionCount = 10; conf.ClassicTumblerParameters.RealTransactionCount = 10; conf.ClassicTumblerParameters.RealPuzzleCount = 2; conf.ClassicTumblerParameters.CycleGenerator.FirstCycle.Start = 105; } else { var standard = new StandardCycles(conf.Network).Shorty2x; conf.ClassicTumblerParameters.CycleGenerator = standard.Generator; conf.ClassicTumblerParameters.Denomination = standard.Denomination; } var runtime = TumblerRuntime.FromConfiguration(conf, new AcceptAllClientInteraction()); _Host = new WebHostBuilder() .UseAppConfiguration(runtime) .UseContentRoot(Path.GetFullPath(directory)) .UseStartup <Startup>() .Build(); _Host.Start(); ServerRuntime = runtime; //Overrides server fee ((RPCFeeService)runtime.Services.FeeService).FallBackFeeRate = new FeeRate(Money.Satoshis(100), 1); ((RPCWalletService)runtime.Services.WalletService).BatchInterval = TimeSpan.FromMilliseconds(10); ((RPCWalletService)runtime.Services.WalletService).AddressGenerationBatchInterval = TimeSpan.FromMilliseconds(10); ((RPCBroadcastService)runtime.Services.BroadcastService).BatchInterval = TimeSpan.FromMilliseconds(10); ((RPCBlockExplorerService)runtime.Services.BlockExplorerService).BatchInterval = TimeSpan.FromMilliseconds(10); var clientConfig = new TumblerClientConfiguration(); clientConfig.DataDir = Path.Combine(directory, "client"); clientConfig.AllowInsecure = !shouldBeStandard; Directory.CreateDirectory(clientConfig.DataDir); clientConfig.Network = conf.Network; clientConfig.CheckIp = false; clientConfig.TorMandatory = false; clientConfig.OutputWallet.KeyPath = new KeyPath("0"); clientConfig.OutputWallet.RootKey = new ExtKey().Neuter().GetWif(conf.Network); clientConfig.RPCArgs.Url = AliceNode.CreateRPCClient().Address; creds = ExtractCredentials(File.ReadAllText(AliceNode.Config)); clientConfig.RPCArgs.User = creds.Item1; clientConfig.RPCArgs.Password = creds.Item2; clientConfig.TumblerServer = runtime.TumblerUris.First(); ClientRuntime = TumblerClientRuntime.FromConfiguration(clientConfig, new AcceptAllClientInteraction()); //Overrides client fee ((RPCFeeService)ClientRuntime.Services.FeeService).FallBackFeeRate = new FeeRate(Money.Satoshis(50), 1); } catch { Dispose(); throw; } }
public CheckIpService(TumblerClientRuntime runtime) { this.runtime = runtime; }
/// <inheritdoc /> public async Task TumbleAsync(string originWalletName, string destinationWalletName, string originWalletPassword) { // Make sure it won't start new tumbling round if already started if (this.State == TumbleState.Tumbling) { this.logger.LogDebug("Tumbler is already running"); throw new Exception("Tumbling is already running"); } this.tumblingState.TumblerUri = new Uri(this.TumblerAddress); // Check if in initial block download if (!this.chain.IsDownloaded()) { this.logger.LogDebug("Chain is still being downloaded: " + this.chain.Tip); throw new Exception("Chain is still being downloaded"); } Wallet destinationWallet = this.walletManager.GetWallet(destinationWalletName); Wallet originWallet = this.walletManager.GetWallet(originWalletName); // Check if origin wallet has a sufficient balance to begin tumbling at least 1 cycle Money originBalance = this.walletManager.GetSpendableTransactionsInWallet(this.tumblingState.OriginWalletName) .Sum(s => s.Transaction.Amount); // Should ideally take network's transaction fee into account too, but that is dynamic if (originBalance <= (this.TumblerParameters.Denomination + this.TumblerParameters.Fee)) { this.logger.LogDebug("Insufficient funds in origin wallet"); throw new Exception("Insufficient funds in origin wallet"); } // Check if password is valid before starting any cycles try { HdAddress tempAddress = originWallet.GetAccountsByCoinType(this.tumblingState.CoinType).First() .GetFirstUnusedReceivingAddress(); originWallet.GetExtendedPrivateKeyForAddress(originWalletPassword, tempAddress); } catch (Exception) { this.logger.LogDebug("Origin wallet password appears to be invalid"); throw new Exception("Origin wallet password appears to be invalid"); } // Update the state and save this.tumblingState.DestinationWallet = destinationWallet ?? throw new Exception($"Destination wallet not found. Have you created a wallet with name {destinationWalletName}?"); this.tumblingState.DestinationWalletName = destinationWalletName; this.tumblingState.OriginWallet = originWallet ?? throw new Exception($"Origin wallet not found. Have you created a wallet with name {originWalletName}?"); this.tumblingState.OriginWalletName = originWalletName; this.tumblingState.OriginWalletPassword = originWalletPassword; var accounts = this.tumblingState.DestinationWallet.GetAccountsByCoinType(this.tumblingState.CoinType); // TODO: Possibly need to preserve destination account name in tumbling state. Default to first account for now string accountName = accounts.First().Name; HdAccount destAccount = this.tumblingState.DestinationWallet.GetAccountByCoinType(accountName, this.tumblingState.CoinType); string key = destAccount.ExtendedPubKey; KeyPath keyPath = new KeyPath("0"); // Stop and dispose onlymonitor if (this.broadcasterJob != null && this.broadcasterJob.Started) { await this.broadcasterJob.Stop().ConfigureAwait(false); } this.runtime?.Dispose(); // Bypass Tor for integration tests FullNodeTumblerClientConfiguration config; if (this.TumblerAddress.Contains("127.0.0.1")) { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: false, useProxy: false); } else { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: false, useProxy: true); } this.runtime = await TumblerClientRuntime.FromConfigurationAsync(config).ConfigureAwait(false); BitcoinExtPubKey extPubKey = new BitcoinExtPubKey(key, this.runtime.Network); if (key != null) { this.runtime.DestinationWallet = new ClientDestinationWallet(extPubKey, keyPath, this.runtime.Repository, this.runtime.Network); } this.TumblerParameters = this.runtime.TumblerParameters; // Run onlymonitor mode this.broadcasterJob = this.runtime.CreateBroadcasterJob(); this.broadcasterJob.Start(); // Run tumbling mode this.stateMachine = new StateMachinesExecutor(this.runtime); this.stateMachine.Start(); this.State = TumbleState.Tumbling; }
public ClientInteractiveRuntime(TumblerClientRuntime runtime) { InnerRuntime = runtime ?? throw new ArgumentNullException("runtime"); }
/// <inheritdoc /> public async Task TumbleAsync(string originWalletName, string destinationWalletName, string originWalletPassword) { // Make sure it won't start new tumbling round if already started if (this.State == TumbleState.Tumbling) { this.logger.LogDebug("Tumbler is already running"); throw new Exception("Tumbling is already running"); } this.tumblingState.TumblerUri = new Uri(this.TumblerAddress); // Check if in initial block download if (!this.chain.IsDownloaded()) { this.logger.LogDebug("Chain is still being downloaded: " + this.chain.Tip); throw new Exception("Chain is still being downloaded"); } Wallet destinationWallet = this.walletManager.GetWallet(destinationWalletName); Wallet originWallet = this.walletManager.GetWallet(originWalletName); // Check if origin wallet has a balance WalletBalanceModel model = new WalletBalanceModel(); var originWalletAccounts = this.walletManager.GetAccounts(originWallet.Name).ToList(); var originConfirmed = new Money(0); var originUnconfirmed = new Money(0); foreach (var originAccount in originWallet.GetAccountsByCoinType(this.tumblingState.CoinType)) { var result = originAccount.GetSpendableAmount(); originConfirmed += result.ConfirmedAmount; originUnconfirmed += result.UnConfirmedAmount; } // Should ideally take network transaction fee into account too, but that is dynamic if ((originConfirmed + originUnconfirmed) <= (this.TumblerParameters.Denomination + this.TumblerParameters.Fee)) { this.logger.LogDebug("Insufficient funds in origin wallet"); throw new Exception("Insufficient funds in origin wallet"); } // TODO: Check if password is valid before starting any cycles // Update the state and save this.tumblingState.DestinationWallet = destinationWallet ?? throw new Exception($"Destination wallet not found. Have you created a wallet with name {destinationWalletName}?"); this.tumblingState.DestinationWalletName = destinationWalletName; this.tumblingState.OriginWallet = originWallet ?? throw new Exception($"Origin wallet not found. Have you created a wallet with name {originWalletName}?"); this.tumblingState.OriginWalletName = originWalletName; this.tumblingState.OriginWalletPassword = originWalletPassword; var accounts = this.tumblingState.DestinationWallet.GetAccountsByCoinType(this.tumblingState.CoinType); // TODO: Possibly need to preserve destination account name in tumbling state. Default to first account for now string accountName = null; foreach (var account in accounts) { if (account.Index == 0) { accountName = account.Name; } } var destAccount = this.tumblingState.DestinationWallet.GetAccountByCoinType(accountName, this.tumblingState.CoinType); var key = destAccount.ExtendedPubKey; var keyPath = new KeyPath("0"); // Stop and dispose onlymonitor if (this.broadcasterJob != null && this.broadcasterJob.Started) { await this.broadcasterJob.Stop().ConfigureAwait(false); } this.runtime?.Dispose(); // Bypass Tor for integration tests FullNodeTumblerClientConfiguration config; if (this.TumblerAddress.Contains("127.0.0.1")) { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: false, useProxy: false); } else { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: false, useProxy: true); } this.runtime = await TumblerClientRuntime.FromConfigurationAsync(config).ConfigureAwait(false); var extPubKey = new BitcoinExtPubKey(key, this.runtime.Network); if (key != null) { this.runtime.DestinationWallet = new ClientDestinationWallet(extPubKey, keyPath, this.runtime.Repository, this.runtime.Network); } this.TumblerParameters = this.runtime.TumblerParameters; // Run onlymonitor mode this.broadcasterJob = this.runtime.CreateBroadcasterJob(); this.broadcasterJob.Start(); // Run tumbling mode this.stateMachine = new StateMachinesExecutor(this.runtime); this.stateMachine.Start(); this.State = TumbleState.Tumbling; return; }
/// <inheritdoc /> public async Task <Result <ClassicTumblerParameters> > ConnectToTumblerAsync() { // Temporary hardcoding for testnet if (this.TumblerAddress == null) { this.TumblerAddress = "ctb://6cvi6ulcd4qn42mi.onion?h=95cb936fde9ae1856bcfd953746c26724a25dc46"; } // If the -ppuri command line option wasn't used to bypass the registration store lookup if (this.TumblerAddress == null) { List <RegistrationRecord> registrations = this.registrationStore.GetAll(); if (registrations.Count < MINIMUM_MASTERNODE_COUNT) { this.logger.LogDebug("Not enough masternode registrations downloaded yet: " + registrations.Count); return(null); } RegistrationRecord record = null; RegistrationToken registrationToken = null; bool validRegistrationFound = false; // TODO: Search the registration store more robustly for (int i = 1; i < 10; i++) { record = registrations[random.Next(registrations.Count)]; registrationToken = record.Record; // Implicitly, the registration feature will have deleted the registration if the collateral // requirement was not met within 30 blocks if ((this.walletManager.LastBlockHeight() - record.BlockReceived) >= 32) { validRegistrationFound = true; break; } } if (!validRegistrationFound) { this.logger.LogDebug("Did not find a valid registration"); return(Result.Fail <ClassicTumblerParameters>("Did not find a valid registration")); } this.TumblerAddress = "ctb://" + registrationToken.OnionAddress + ".onion?h=" + registrationToken.ConfigurationHash; } this.tumblingState.TumblerUri = new Uri(this.TumblerAddress); FullNodeTumblerClientConfiguration config; if (this.TumblerAddress.Contains("127.0.0.1")) { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: true, useProxy: false); } else { config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: true, useProxy: true); } TumblerClientRuntime rt = null; try { rt = await TumblerClientRuntime.FromConfigurationAsync(config, connectionTest : true) .ConfigureAwait(false); // This is overwritten by the tumble method, but it is needed at the beginning of that method for the balance check this.TumblerParameters = rt.TumblerParameters; return(Result.Ok(rt.TumblerParameters)); } catch (Exception cex) when(cex is PrivacyProtocolConfigException || cex is ConfigException) { this.logger.LogError("Error obtaining tumbler parameters: " + cex); return(Result.Fail <ClassicTumblerParameters>( cex is PrivacyProtocolConfigException ? "Tor is required for connectivity to an active Stratis Masternode. Please restart Breeze Wallet with Privacy Protocol and ensure that an instance of Tor is running." : cex.Message)); } catch (Exception e) { this.logger.LogError("Error obtaining tumbler parameters: " + e); return(Result.Fail <ClassicTumblerParameters>("Error obtaining tumbler parameters")); } finally { rt?.Dispose(); } }
/// <inheritdoc /> public async Task <ClassicTumblerParameters> ConnectToTumblerAsync() { if (this.network == Network.TestNet) { this.TumblerAddress = "ctb://sz64kj6ev5576w34.onion?h=ceced829426faf63cb906b99e5ee1ff83f001a95"; } else { this.TumblerAddress = "ctb://frspe6yz6en4wbrt.onion?h=14dad7205ff3632f5ab903b9052116397bf7302f"; } // If the -ppuri command line option wasn't used to bypass the registration store lookup if (this.TumblerAddress == null) { List <RegistrationRecord> registrations = this.registrationStore.GetAll(); if (registrations.Count < MINIMUM_MASTERNODE_COUNT) { this.logger.LogDebug("Not enough masternode registrations downloaded yet: " + registrations.Count); return(null); } RegistrationRecord record = null; RegistrationToken registrationToken = null; bool validRegistrationFound = false; // TODO: Search the registration store more robustly for (int i = 1; i < 10; i++) { record = registrations[random.Next(registrations.Count)]; registrationToken = record.Record; // Implicitly, the registration feature will have deleted the registration if the collateral // requirement was not met within 30 blocks if ((this.walletManager.LastBlockHeight() - record.BlockReceived) >= 32) { validRegistrationFound = true; break; } } if (!validRegistrationFound) { this.logger.LogDebug("Did not find a valid registration"); return(null); } this.TumblerAddress = "ctb://" + registrationToken.OnionAddress + ".onion?h=" + registrationToken.ConfigurationHash; } this.tumblingState.TumblerUri = new Uri(this.TumblerAddress); var config = new FullNodeTumblerClientConfiguration(this.tumblingState, onlyMonitor: false, connectionTest: true); TumblerClientRuntime rt = null; try { rt = await TumblerClientRuntime.FromConfigurationAsync(config, connectionTest : true).ConfigureAwait(false); // This is overwritten by the tumble method, but it is needed at the beginning of that method for the balance check this.TumblerParameters = rt.TumblerParameters; return(rt.TumblerParameters); } catch (Exception e) { this.logger.LogError("Error obtaining tumbler parameters: " + e); return(null); } finally { rt?.Dispose(); } }
public TumblerServerTester(string directory) { try { var rootTestData = "TestData"; directory = rootTestData + "/" + directory; _Directory = directory; if (!Directory.Exists(rootTestData)) { Directory.CreateDirectory(rootTestData); } if (!TryDelete(directory, false)) { foreach (var process in Process.GetProcessesByName("bitcoind")) { if (process.MainModule.FileName.Replace("\\", "/").StartsWith(Path.GetFullPath(rootTestData).Replace("\\", "/"), StringComparison.Ordinal)) { process.Kill(); process.WaitForExit(); } } TryDelete(directory, true); } _NodeBuilder = NodeBuilder.Create(directory); _TumblerNode = _NodeBuilder.CreateNode(false); _AliceNode = _NodeBuilder.CreateNode(false); _BobNode = _NodeBuilder.CreateNode(false); Directory.CreateDirectory(directory); _NodeBuilder.StartAll(); SyncNodes(); var conf = new TumblerConfiguration(); conf.DataDir = Path.Combine(directory, "server"); Directory.CreateDirectory(conf.DataDir); File.WriteAllBytes(Path.Combine(conf.DataDir, "Tumbler.pem"), TestKeys.Default.ToBytes()); File.WriteAllBytes(Path.Combine(conf.DataDir, "Voucher.pem"), TestKeys.Default2.ToBytes()); conf.RPC.Url = TumblerNode.CreateRPCClient().Address; var creds = ExtractCredentials(File.ReadAllText(_TumblerNode.Config)); conf.RPC.User = creds.Item1; conf.RPC.Password = creds.Item2; conf.Network = Network.RegTest; conf.Listen.Add(new System.Net.IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000)); conf.ClassicTumblerParameters.FakePuzzleCount /= 4; conf.ClassicTumblerParameters.FakeTransactionCount /= 4; conf.ClassicTumblerParameters.RealTransactionCount /= 4; conf.ClassicTumblerParameters.RealPuzzleCount /= 4; conf.ClassicTumblerParameters.CycleGenerator.FirstCycle.Start = 105; var runtime = TumblerRuntime.FromConfiguration(conf, new AcceptAllClientInteraction()); _Host = new WebHostBuilder() .UseKestrel() .UseAppConfiguration(runtime) .UseContentRoot(Path.GetFullPath(directory)) .UseStartup <Startup>() .UseUrls(conf.GetUrls()) .Build(); _Host.Start(); ServerRuntime = runtime; //Overrides server fee ((RPCFeeService)runtime.Services.FeeService).FallBackFeeRate = new FeeRate(Money.Satoshis(100), 1); var clientConfig = new TumblerClientConfiguration(); clientConfig.DataDir = Path.Combine(directory, "client"); clientConfig.AllowInsecure = true; Directory.CreateDirectory(clientConfig.DataDir); clientConfig.Network = conf.Network; clientConfig.CheckIp = false; clientConfig.OutputWallet.KeyPath = new KeyPath("0"); clientConfig.OutputWallet.RootKey = new ExtKey().Neuter().GetWif(conf.Network); clientConfig.RPCArgs.Url = AliceNode.CreateRPCClient().Address; creds = ExtractCredentials(File.ReadAllText(AliceNode.Config)); clientConfig.RPCArgs.User = creds.Item1; clientConfig.RPCArgs.Password = creds.Item2; clientConfig.TumblerServer = Address; ClientRuntime = TumblerClientRuntime.FromConfiguration(clientConfig, new AcceptAllClientInteraction()); //Overrides client fee ((RPCFeeService)ClientRuntime.Services.FeeService).FallBackFeeRate = new FeeRate(Money.Satoshis(50), 1); } catch { Dispose(); throw; } }