public void TestMultiClientWithoutTor(int numClients) { // Workaround for segwit not correctly activating Network.RegTest.Consensus.BIP9Deployments[BIP9Deployments.Segwit] = new BIP9DeploymentsParameters(1, 0, DateTime.Now.AddDays(50).ToUnixTimestamp()); NodeBuilder builder = NodeBuilder.Create(version: "0.15.1"); CoreNode coreNode = GetCoreNode(builder); coreNode.Start(); // Replicate portions of BreezeServer's Program.cs. Maybe refactor it into a class/function in future var serviceProvider = new ServiceCollection() .AddLogging() .AddSingleton <Breeze.BreezeServer.Services.ITumblerService, Breeze.BreezeServer.Services.TumblerService>() .BuildServiceProvider(); serviceProvider .GetService <ILoggerFactory>() .AddConsole(LogLevel.Debug); // Skip the registration code - that can be tested separately string configPath = Path.Combine(coreNode.DataFolder, "breeze.conf"); File.WriteAllLines(configPath, this.breezeServerConfig); BreezeConfiguration config = new BreezeConfiguration(configPath); var coreRpc = coreNode.CreateRPCClient(); string ntbServerConfigPath = Path.Combine(coreNode.DataFolder, "server.config"); File.WriteAllLines(ntbServerConfigPath, GetNTBServerConfig(coreRpc)); // We need to start up the masternode prior to creating the SBFN instance so that // we have the URI available for starting the TumbleBit feature // TODO: Also need to see if NTB interactive console interferes with later parts of the test new Thread(delegate() { Thread.CurrentThread.IsBackground = true; // By instantiating the TumblerService directly the registration logic is skipped var tumbler = serviceProvider.GetService <Breeze.BreezeServer.Services.ITumblerService>(); tumbler.StartTumbler(config, false, "server.config", Path.GetFullPath(coreNode.DataFolder), false); }).Start(); // Wait for URI file to be written out by the TumblerService while (!File.Exists(Path.Combine(coreNode.DataFolder, "uri.txt"))) { Thread.Sleep(1000); } Console.WriteLine("* URI file detected *"); Thread.Sleep(5000); var serverAddress = File.ReadAllText(Path.Combine(coreNode.DataFolder, "uri.txt")); // Not used for this test ConfigurationOptionWrapper <object> registrationStoreDirectory = new ConfigurationOptionWrapper <object>("RegistrationStoreDirectory", ""); // Force SBFN to use the temporary hidden service to connect to the server ConfigurationOptionWrapper <object> masternodeUri = new ConfigurationOptionWrapper <object>("MasterNodeUri", serverAddress); ConfigurationOptionWrapper <object>[] configurationOptions = { registrationStoreDirectory, masternodeUri }; List <CoreNode> clientNodes = new List <CoreNode>(); int apiPortNum = 37229; for (int i = 0; i < numClients; i++) { var temp = builder.CreateStratisPowNode(false, fullNodeBuilder => { fullNodeBuilder .UsePowConsensus() .UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .AddMining() .UseWallet() .UseWatchOnlyWallet() .UseApi() .AddRPC() .UseTumbleBit(configurationOptions); }); temp.ConfigParameters.AddOrReplace("apiuri", $"http://localhost:{apiPortNum}"); clientNodes.Add(temp); apiPortNum++; } foreach (var node in clientNodes) { node.Start(); } // Create the source and destination wallets for nodes for (int i = 0; i < numClients; i++) { var wm1 = clientNodes[i].FullNode.NodeService <IWalletManager>() as WalletManager; wm1.CreateWallet("TumbleBit1", $"alice{i}"); wm1.CreateWallet("TumbleBit1", $"bob{i}"); } // Mined coins only mature after 100 blocks on regtest // Additionally, we need to force Segwit to activate in order for NTB to work correctly coreRpc.Generate(450); while (coreRpc.GetBlockCount() < 450) { Thread.Sleep(100); } for (int i = 0; i < numClients; i++) { coreRpc.AddNode(clientNodes[i].Endpoint, false); var rpc = clientNodes[i].CreateRPCClient(); rpc.AddNode(coreNode.Endpoint, false); for (int j = 0; j < numClients; j++) { if (i != j) { rpc.AddNode(clientNodes[j].Endpoint, false); } } } for (int i = 0; i < numClients; i++) { var wm1 = clientNodes[i].FullNode.NodeService <IWalletManager>() as WalletManager; var destination1 = wm1.GetUnusedAddress(new WalletAccountReference($"alice{i}", "account 0")); coreRpc.SendToAddress(BitcoinAddress.Create(destination1.Address, Network.RegTest), new Money(5.0m, MoneyUnit.BTC)); } clientNodes[0].FullNode.Settings.Logger.LogInformation("Waiting for transactions to propagate and finalise"); Thread.Sleep(5000); coreRpc.Generate(1); // Wait for SBFN clients to sync with the core node foreach (var node in clientNodes) { TestHelper.WaitLoop(() => node.CreateRPCClient().GetBestBlockHash() == coreRpc.GetBestBlockHash()); } // Test implementation note: the coins do not seem to immediately appear in the wallet. // This is possibly some sort of race condition between the wallet manager and block generation/sync. // This extra delay seems to ensure that the coins are definitely in the wallet by the time the // transaction count gets logged to the console below. // Wait instead of generating a block Thread.Sleep(5000); for (int i = 0; i < numClients; i++) { var wm1 = clientNodes[i].FullNode.NodeService <IWalletManager>() as WalletManager; //logger1.LogError($"({i}) Number of wallet transactions: " + wm1.GetSpendableTransactionsInWallet($"alice{i}").Count()); // Connect each client to server and start tumbling using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var apiSettings1 = clientNodes[i].FullNode.NodeService <ApiSettings>(); var connectContent = new StringContent(new ConnectRequest { OriginWalletName = $"alice{i}" }.ToString(), Encoding.UTF8, "application/json"); var connectResponse = client.PostAsync(apiSettings1.ApiUri + "api/TumbleBit/connect", connectContent).GetAwaiter().GetResult(); var tumbleContent = new StringContent(new TumbleRequest { OriginWalletName = $"alice{i}", OriginWalletPassword = "******", DestinationWalletName = $"bob{i}" }.ToString(), Encoding.UTF8, "application/json"); var tumbleResponse = client.PostAsync(apiSettings1.ApiUri + "api/TumbleBit/tumble", tumbleContent).GetAwaiter().GetResult(); // Note that the TB client takes about 30 seconds to completely start up, as it has to check the server parameters and RSA key proofs } clientNodes[i].FullNode.Settings.Logger.LogInformation($"Client ({i}) About to start tumbling loop"); } while (true) { for (int i = 0; i < numClients; i++) { clientNodes[i].FullNode.Settings.Logger.LogInformation($"Wallet {i} balance height: " + clientNodes[i].FullNode.Chain.Height); var wm1 = clientNodes[i].FullNode.NodeService <IWalletManager>() as WalletManager; HdAccount alice1 = wm1.GetWalletByName($"alice{i}").GetAccountByCoinType("account 0", (CoinType)Network.RegTest.Consensus.CoinType); clientNodes[i].FullNode.Settings.Logger.LogInformation($"(A{i}) Confirmed: " + alice1.GetSpendableAmount().ConfirmedAmount.ToString()); clientNodes[i].FullNode.Settings.Logger.LogInformation($"(A{i}) Unconfirmed: " + alice1.GetSpendableAmount().UnConfirmedAmount.ToString()); HdAccount bob1 = wm1.GetWalletByName($"bob{i}").GetAccountByCoinType("account 0", (CoinType)Network.RegTest.Consensus.CoinType); clientNodes[i].FullNode.Settings.Logger.LogInformation($"(B{i}) Confirmed: " + bob1.GetSpendableAmount().ConfirmedAmount.ToString()); clientNodes[i].FullNode.Settings.Logger.LogInformation($"(B{i}) Unconfirmed: " + bob1.GetSpendableAmount().UnConfirmedAmount.ToString()); clientNodes[i].FullNode.Settings.Logger.LogInformation("==="); } coreRpc.Generate(1); // Try to ensure the invalid phase error does not occur // (seems to occur when the server has not yet processed a new block and the client has) //TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == coreRpc.GetBestBlockHash()); //TestHelper.WaitLoop(() => rpc2.GetBestBlockHash() == coreRpc.GetBestBlockHash()); /*var mempool = node1.FullNode.NodeService<MempoolManager>(); * var mempoolTx = mempool.GetMempoolAsync().Result; * if (mempoolTx.Count > 0) * { * Console.WriteLine("--- Mempool contents ---"); * foreach (var tx in mempoolTx) * { * var hex = mempool.GetTransaction(tx).Result; * Console.WriteLine(tx + " ->"); * Console.WriteLine(hex); * Console.WriteLine("---"); * } * }*/ Thread.Sleep(20000); } if (builder != null) { builder.Dispose(); } }
public void TestWithoutTor() { // Workaround for segwit not correctly activating Network.RegTest.Consensus.BIP9Deployments[BIP9Deployments.Segwit] = new BIP9DeploymentsParameters(1, 0, DateTime.Now.AddDays(50).ToUnixTimestamp()); NodeBuilder builder = NodeBuilder.Create(version: "0.15.1"); HttpClient client = null; var coreNode = GetCoreNode(builder); coreNode.Start(); // Replicate portions of BreezeServer's Program.cs. Maybe refactor it into a class/function in future var serviceProvider = new ServiceCollection() .AddLogging() .AddSingleton <Breeze.BreezeServer.Services.ITumblerService, Breeze.BreezeServer.Services.TumblerService>() .BuildServiceProvider(); serviceProvider .GetService <ILoggerFactory>() .AddConsole(LogLevel.Debug); // Skip the registration code - that can be tested separately string configPath = Path.Combine(coreNode.DataFolder, "breeze.conf"); File.WriteAllLines(configPath, this.breezeServerConfig); BreezeConfiguration config = new BreezeConfiguration(configPath); var coreRpc = coreNode.CreateRPCClient(); string ntbServerConfigPath = Path.Combine(coreNode.DataFolder, "server.config"); File.WriteAllLines(ntbServerConfigPath, GetNTBServerConfig(coreRpc)); // We need to start up the masternode prior to creating the SBFN instance so that // we have the URI available for starting the TumbleBit feature // TODO: Also need to see if NTB interactive console interferes with later parts of the test new Thread(delegate() { Thread.CurrentThread.IsBackground = true; // By instantiating the TumblerService directly the registration logic is skipped var tumbler = serviceProvider.GetService <Breeze.BreezeServer.Services.ITumblerService>(); tumbler.StartTumbler(config, false, "server.config", Path.GetFullPath(coreNode.DataFolder), false); }).Start(); // Wait for URI file to be written out by the TumblerService while (!File.Exists(Path.Combine(coreNode.DataFolder, "uri.txt"))) { Thread.Sleep(1000); } Console.WriteLine("* URI file detected *"); Thread.Sleep(5000); var serverAddress = File.ReadAllText(Path.Combine(coreNode.DataFolder, "uri.txt")); // Not used for this test ConfigurationOptionWrapper <object> registrationStoreDirectory = new ConfigurationOptionWrapper <object>("RegistrationStoreDirectory", ""); // Force SBFN to connect to the server ConfigurationOptionWrapper <object> masternodeUri = new ConfigurationOptionWrapper <object>("MasterNodeUri", serverAddress); ConfigurationOptionWrapper <object>[] configurationOptions = { registrationStoreDirectory, masternodeUri }; CoreNode node1 = builder.CreateStratisPowNode(true, fullNodeBuilder => { fullNodeBuilder .UsePowConsensus() .UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .AddMining() .UseWallet() .UseWatchOnlyWallet() .UseApi() .AddRPC() .UseTumbleBit(configurationOptions); }); // Logging for NTB client code ConsoleLoggerProcessor loggerProcessor = new ConsoleLoggerProcessor(); Logs.Configure(new FuncLoggerFactory(i => new CustomerConsoleLogger(i, Logs.SupportDebug(true), false, loggerProcessor)), node1.DataFolder); var apiSettings = node1.FullNode.NodeService <ApiSettings>(); // Create the source and destination wallets var wm1 = node1.FullNode.NodeService <IWalletManager>() as WalletManager; //var wm2 = node2.FullNode.NodeService<IWalletManager>() as WalletManager; wm1.CreateWallet("TumbleBit1", "alice"); wm1.CreateWallet("TumbleBit1", "bob"); // Mined coins only mature after 100 blocks on regtest // Additionally, we need to force Segwit to activate in order for NTB to work correctly coreRpc.Generate(450); var rpc1 = node1.CreateRPCClient(); coreRpc.AddNode(node1.Endpoint, false); rpc1.AddNode(coreNode.Endpoint, false); var amount = new Money(5.0m, MoneyUnit.BTC); var destination = wm1.GetUnusedAddress(new WalletAccountReference("alice", "account 0")); coreRpc.SendToAddress(BitcoinAddress.Create(destination.Address, Network.RegTest), amount); Console.WriteLine("Waiting for transaction to propagate and finalise"); Thread.Sleep(5000); coreRpc.Generate(1); // Wait for SBFN to sync with the core node TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == coreRpc.GetBestBlockHash()); // Test implementation note: the coins do not seem to immediately appear in the wallet. // This is possibly some sort of race condition between the wallet manager and block generation/sync. // This extra delay seems to ensure that the coins are definitely in the wallet by the time the // transaction count gets logged to the console below. // Wait instead of generating a block Thread.Sleep(5000); //var log = node1.FullNode.NodeService<ILogger>(); Console.WriteLine("Number of wallet transactions: " + wm1.GetSpendableTransactionsInWallet("alice").Count()); // Connect to server and start tumbling using (client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // Sample returned output // {"tumbler":"ctb://<onionaddress>.onion?h=<confighash>","denomination":"0.01000000","fee":"0.00010000","network":"RegTest","estimate":"22200"} var connectContent = new StringContent(new ConnectRequest { OriginWalletName = "alice" }.ToString(), Encoding.UTF8, "application/json"); var connectResponse = client.PostAsync(apiSettings.ApiUri + "api/TumbleBit/connect", connectContent).GetAwaiter().GetResult(); var tumbleContent = new StringContent(new TumbleRequest { OriginWalletName = "alice", OriginWalletPassword = "******", DestinationWalletName = "bob" }.ToString(), Encoding.UTF8, "application/json"); var tumbleResponse = client.PostAsync(apiSettings.ApiUri + "api/TumbleBit/tumble", tumbleContent).GetAwaiter().GetResult(); // Note that the TB client takes about 30 seconds to completely start up, as it has to check the server parameters and // RSA key proofs //Assert.StartsWith("[{\"", tumbleResponse); } HdAccount alice; HdAccount bob; // TODO: Move forward specific numbers of blocks and check interim states? TB tests already do that for (int i = 0; i < 200; i++) { Console.WriteLine("Wallet balance height: " + node1.FullNode.Chain.Height); alice = wm1.GetWalletByName("alice").GetAccountByCoinType("account 0", (CoinType)Network.RegTest.Consensus.CoinType); Console.WriteLine("(A) Confirmed: " + alice.GetSpendableAmount().ConfirmedAmount.ToString()); Console.WriteLine("(A) Unconfirmed: " + alice.GetSpendableAmount().UnConfirmedAmount.ToString()); bob = wm1.GetWalletByName("bob").GetAccountByCoinType("account 0", (CoinType)Network.RegTest.Consensus.CoinType); Console.WriteLine("(B) Confirmed: " + bob.GetSpendableAmount().ConfirmedAmount.ToString()); Console.WriteLine("(B) Unconfirmed: " + bob.GetSpendableAmount().UnConfirmedAmount.ToString()); coreRpc.Generate(1); builder.SyncNodes(); // Try to ensure the invalid phase error does not occur // (seems to occur when the server has not yet processed a new block and the client has) TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == coreRpc.GetBestBlockHash()); var mempool = node1.FullNode.NodeService <MempoolManager>(); var mempoolTx = mempool.GetMempoolAsync().Result; if (mempoolTx.Count > 0) { Console.WriteLine("--- Mempool contents ---"); foreach (var tx in mempoolTx) { var hex = mempool.GetTransaction(tx).Result; Console.WriteLine(tx + " ->"); Console.WriteLine(hex); Console.WriteLine("---"); } } Thread.Sleep(20000); } // Check destination wallet for tumbled coins // TODO: Need to amend TumblerService so that it can be shut down within the test if (client != null) { client.Dispose(); client = null; } if (builder != null) { builder.Dispose(); } }
public static async Task MainAsync(string[] args) { try { var isTestNet = args.Contains("-testnet"); var isStratis = args.Contains("stratis"); var agent = "Breeze"; // This setting is not in NodeSettings yet, so get it directly from the args ConfigurationOptionWrapper <string> registrationStoreDirectory = new ConfigurationOptionWrapper <string>("RegistrationStoreDirectory", args.GetValueOf("-storedir")); ConfigurationOptionWrapper <string>[] configurationOptions = { registrationStoreDirectory }; NodeSettings nodeSettings; if (isStratis) { //if (NodeSettings.PrintHelp(args, Network.StratisMain)) // return; Network network = isTestNet ? Network.StratisTest : Network.StratisMain; if (isTestNet) { args = args.Append("-addnode=51.141.28.47").ToArray(); // TODO: fix this temp hack } nodeSettings = new NodeSettings(network, ProtocolVersion.ALT_PROTOCOL_VERSION, agent, args: args, loadConfiguration: false); } else { nodeSettings = new NodeSettings(agent: agent, args: args, loadConfiguration: false); } IFullNodeBuilder fullNodeBuilder = null; if (args.Contains("light")) { fullNodeBuilder = new FullNodeBuilder() .UseNodeSettings(nodeSettings) .UseLightWallet() .UseWatchOnlyWallet() .UseBlockNotification() .UseTransactionNotification() .UseApi(); } else { fullNodeBuilder = new FullNodeBuilder() .UseNodeSettings(nodeSettings); if (args.Contains("stratis")) { fullNodeBuilder.UsePosConsensus(); } else { fullNodeBuilder.UsePowConsensus(); } fullNodeBuilder.UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .UseWallet() .UseWatchOnlyWallet(); if (args.Contains("stratis")) { fullNodeBuilder.AddPowPosMining(); } else { fullNodeBuilder.AddMining(); } fullNodeBuilder.AddRPC() .UseApi(); } if (args.Contains("registration")) { //fullNodeBuilder.UseInterNodeCommunication(); fullNodeBuilder.UseRegistration(); } // Need this to happen for both TB and non-TB daemon Logs.Configure(new FuncLoggerFactory(i => new DualLogger(i, (a, b) => true, false))); // Start NTumbleBit logging to the console SetupTumbleBitConsoleLogs(nodeSettings); // Currently TumbleBit is bitcoin only if (args.Contains("-tumblebit")) { // We no longer pass the URI in via the command line, the registration feature selects a random one fullNodeBuilder.UseTumbleBit(configurationOptions); } IFullNode node = fullNodeBuilder.Build(); // Add logging to NLog SetupTumbleBitNLogs(nodeSettings); // Start Full Node - this will also start the API. await node.RunAsync(); } catch (Exception ex) { Console.WriteLine("There was a problem initializing the node. Details: '{0}'", ex.Message); } }
public static IFullNodeBuilder UseTumbleBit(this IFullNodeBuilder fullNodeBuilder, ConfigurationOptionWrapper <string> registrationStoreDirectory) { fullNodeBuilder.ConfigureFeature(features => { features .AddFeature <TumbleBitFeature>() .FeatureServices(services => { JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting = Formatting.Indented, ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List <JsonConverter> { new NetworkConverter(), new PermutationTestProofConverter(), new PoupardSternProofConverter(), new RsaPubKeyConverter() } }; services.AddSingleton <ITumbleBitManager, TumbleBitManager>(); services.AddSingleton <ConfigurationOptionWrapper <string> >(registrationStoreDirectory); services.AddSingleton <TumbleBitController>(); //services.AddSingleton<IWalletFeePolicy, LightWalletFeePolicy>(); }); }); return(fullNodeBuilder); }
public void TestDualClientWithoutTor() { using (NodeBuilder builder = NodeBuilder.Create(version: "0.15.1")) { HttpClient client = null; var coreNode = builder.CreateNode(false); coreNode.ConfigParameters.AddOrReplace("debug", "1"); coreNode.ConfigParameters.AddOrReplace("printtoconsole", "0"); coreNode.ConfigParameters.AddOrReplace("prematurewitness", "1"); coreNode.ConfigParameters.AddOrReplace("walletprematurewitness", "1"); coreNode.ConfigParameters.AddOrReplace("rpcworkqueue", "100"); coreNode.Start(); // Replicate portions of BreezeServer's Program.cs. Maybe refactor it into a class/function in future var serviceProvider = new ServiceCollection() .AddLogging() .AddSingleton <Breeze.BreezeServer.Services.ITumblerService, Breeze.BreezeServer.Services.TumblerService>() .BuildServiceProvider(); serviceProvider .GetService <ILoggerFactory>() .AddConsole(LogLevel.Debug); // Skip the registration code - that can be tested separately string configPath = Path.Combine(coreNode.DataFolder, "breeze.conf"); string[] breezeServerConfig = { "network=regtest", // Only the network setting is currently used from this file "rpc.user=dummy", "rpc.password=dummy", "rpc.url=http://127.0.0.1:26174/", "breeze.ipv4=127.0.0.1", "breeze.ipv6=2001:0db8:85a3:0000:0000:8a2e:0370:7334", "breeze.onion=0123456789ABCDEF", "breeze.port=37123", "breeze.regtxfeevalue=10000", "breeze.regtxoutputvalue=1000", "tumbler.url=http://127.0.0.1:37123/api/v1/", "tumbler.rsakeyfile=/Users/username/.ntumblebitserver/RegTest/Tumbler.pem", "tumbler.ecdsakeyaddress=TVwRFmEKRCnQAgShf3QshBjp1Tmucm1e87" }; File.WriteAllLines(configPath, breezeServerConfig); BreezeConfiguration config = new BreezeConfiguration(configPath); var rpc3 = coreNode.CreateRPCClient(); string ntbServerConfigPath = Path.Combine(coreNode.DataFolder, "server.config"); string[] ntbServerConfig = { "regtest=1", "rpc.url=http://127.0.0.1:" + rpc3.Address.Port + "/", "rpc.user="******"rpc.password="******"cycle=kotori", "tor.enabled=false" }; File.WriteAllLines(ntbServerConfigPath, ntbServerConfig); // We need to start up the masternode prior to creating the SBFN instance so that // we have the URI available for starting the TumbleBit feature // TODO: Also need to see if NTB interactive console interferes with later parts of the test new Thread(delegate() { Thread.CurrentThread.IsBackground = true; // By instantiating the TumblerService directly the registration logic is skipped var tumbler = serviceProvider.GetService <Breeze.BreezeServer.Services.ITumblerService>(); tumbler.StartTumbler(config, false, "server.config", Path.GetFullPath(coreNode.DataFolder), false); }).Start(); // Wait for URI file to be written out by the TumblerService while (!File.Exists(Path.Combine(coreNode.DataFolder, "uri.txt"))) { Thread.Sleep(1000); } Console.WriteLine("* URI file detected *"); Thread.Sleep(5000); var serverAddress = File.ReadAllText(Path.Combine(coreNode.DataFolder, "uri.txt")); // Not used for this test ConfigurationOptionWrapper <string> registrationStoreDirectory = new ConfigurationOptionWrapper <string>("RegistrationStoreDirectory", ""); // Force SBFN to use the temporary hidden service to connect to the server ConfigurationOptionWrapper <string> masternodeUri = new ConfigurationOptionWrapper <string>("MasterNodeUri", serverAddress); ConfigurationOptionWrapper <string>[] configurationOptions = { registrationStoreDirectory, masternodeUri }; // Logging for NTB client code ConsoleLoggerProcessor loggerProcessor = new ConsoleLoggerProcessor(); Logs.Configure(new FuncLoggerFactory(i => new CustomerConsoleLogger(i, Logs.SupportDebug(true), false, loggerProcessor))); CoreNode node1 = builder.CreateStratisPowNode(false, fullNodeBuilder => { fullNodeBuilder .UseConsensus() .UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .AddMining() .UseWallet() .UseWatchOnlyWallet() .UseApi() .AddRPC() .UseTumbleBit(configurationOptions); }); node1.ConfigParameters.AddOrReplace("apiuri", "http://localhost:37229"); CoreNode node2 = builder.CreateStratisPowNode(false, fullNodeBuilder => { fullNodeBuilder .UseConsensus() .UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .AddMining() .UseWallet() .UseWatchOnlyWallet() .UseApi() .AddRPC() .UseTumbleBit(configurationOptions); }); node2.ConfigParameters.AddOrReplace("apiuri", "http://localhost:37228"); var apiSettings1 = node1.FullNode.NodeService <ApiSettings>(); var apiSettings2 = node2.FullNode.NodeService <ApiSettings>(); node1.Start(); node2.Start(); // TODO: See if it is possible to split node1 and node2's logs into separate folders NLog.Config.LoggingConfiguration config1 = LogManager.Configuration; var folder = Path.Combine(node1.DataFolder, "Logs"); var tbTarget = new FileTarget(); tbTarget.Name = "tumblebit"; tbTarget.FileName = Path.Combine(folder, "tumblebit.txt"); tbTarget.ArchiveFileName = Path.Combine(folder, "tb-${date:universalTime=true:format=yyyy-MM-dd}.txt"); tbTarget.ArchiveNumbering = ArchiveNumberingMode.Sequence; tbTarget.ArchiveEvery = FileArchivePeriod.Day; tbTarget.MaxArchiveFiles = 7; tbTarget.Layout = "[${longdate:universalTime=true} ${threadid}${mdlc:item=id}] ${level:uppercase=true}: ${callsite} ${message}"; tbTarget.Encoding = Encoding.UTF8; var ruleTb = new LoggingRule("*", NLog.LogLevel.Debug, tbTarget); config1.LoggingRules.Add(ruleTb); config1.AddTarget(tbTarget); // Apply new rules. LogManager.ReconfigExistingLoggers(); node1.NotInIBD(); node2.NotInIBD(); // Create the source and destination wallets for node 1 var wm1 = node1.FullNode.NodeService <IWalletManager>() as WalletManager; wm1.CreateWallet("TumbleBit1", "alice1"); wm1.CreateWallet("TumbleBit1", "bob1"); // Create the source and destination wallets for node 2 var wm2 = node2.FullNode.NodeService <IWalletManager>() as WalletManager; wm2.CreateWallet("TumbleBit1", "alice2"); wm2.CreateWallet("TumbleBit1", "bob2"); // Mined coins only mature after 100 blocks on regtest // Additionally, we need to force Segwit to activate in order for NTB to work correctly coreNode.FindBlock(450); var rpc1 = node1.CreateRPCClient(); var rpc2 = node2.CreateRPCClient(); rpc3.AddNode(node1.Endpoint, false); rpc3.AddNode(node2.Endpoint, false); rpc1.AddNode(coreNode.Endpoint, false); rpc1.AddNode(node2.Endpoint, false); var amount = new Money(5.0m, MoneyUnit.BTC); var destination1 = wm1.GetUnusedAddress(new WalletAccountReference("alice1", "account 0")); var destination2 = wm2.GetUnusedAddress(new WalletAccountReference("alice2", "account 0")); rpc3.SendToAddress(BitcoinAddress.Create(destination1.Address, Network.RegTest), amount); rpc3.SendToAddress(BitcoinAddress.Create(destination2.Address, Network.RegTest), amount); Console.WriteLine("Waiting for transactions to propagate and finalise"); Thread.Sleep(5000); coreNode.FindBlock(1); // Wait for SBFN to sync with the core node TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc3.GetBestBlockHash()); TestHelper.WaitLoop(() => rpc2.GetBestBlockHash() == rpc3.GetBestBlockHash()); // Test implementation note: the coins do not seem to immediately appear in the wallet. // This is possibly some sort of race condition between the wallet manager and block generation/sync. // This extra delay seems to ensure that the coins are definitely in the wallet by the time the // transaction count gets logged to the console below. // Wait instead of generating a block Thread.Sleep(5000); var loggerFactory1 = node1.FullNode.NodeService <ILoggerFactory>(); var loggerFactory2 = node2.FullNode.NodeService <ILoggerFactory>(); var logger1 = loggerFactory1.CreateLogger(this.GetType().FullName); var logger2 = loggerFactory2.CreateLogger(this.GetType().FullName); logger1.LogError("(1) Number of wallet transactions: " + wm1.GetSpendableTransactionsInWallet("alice1").Count()); logger2.LogError("(2) Number of wallet transactions: " + wm2.GetSpendableTransactionsInWallet("alice2").Count()); // Connect to server and start tumbling using (client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // Sample returned output // {"tumbler":"ctb://<onionaddress>.onion?h=<confighash>","denomination":"0.01000000","fee":"0.00010000","network":"RegTest","estimate":"22200"} var connectResponse = client.GetStringAsync(apiSettings1.ApiUri + "api/TumbleBit/connect").GetAwaiter().GetResult(); //Assert.StartsWith("[{\"", connectResponse); var tumbleModel = new TumbleRequest { OriginWalletName = "alice1", OriginWalletPassword = "******", DestinationWalletName = "bob1" }; var tumbleContent = new StringContent(tumbleModel.ToString(), Encoding.UTF8, "application/json"); var tumbleResponse = client.PostAsync(apiSettings1.ApiUri + "api/TumbleBit/tumble", tumbleContent).GetAwaiter().GetResult(); // Note that the TB client takes about 30 seconds to completely start up, as it has to check the server parameters and // RSA key proofs //Assert.StartsWith("[{\"", tumbleResponse); } using (client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var connectResponse = client.GetStringAsync(apiSettings2.ApiUri + "api/TumbleBit/connect").GetAwaiter().GetResult(); var tumbleModel = new TumbleRequest { OriginWalletName = "alice2", OriginWalletPassword = "******", DestinationWalletName = "bob2" }; var tumbleContent = new StringContent(tumbleModel.ToString(), Encoding.UTF8, "application/json"); var tumbleResponse = client.PostAsync(apiSettings2.ApiUri + "api/TumbleBit/tumble", tumbleContent).GetAwaiter().GetResult(); // Note that the TB client takes about 30 seconds to completely start up, as it has to check the server parameters and // RSA key proofs } logger1.LogError("(1) About to start tumbling loop"); logger2.LogError("(2) About to start tumbling loop"); // TODO: Move forward specific numbers of blocks and check interim states? TB tests already do that for (int i = 0; i < 80; i++) { rpc3.Generate(1); builder.SyncNodes(); // Try to ensure the invalid phase error does not occur // (seems to occur when the server has not yet processed a new block and the client has) TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc3.GetBestBlockHash()); TestHelper.WaitLoop(() => rpc2.GetBestBlockHash() == rpc3.GetBestBlockHash()); /*var mempool = node1.FullNode.NodeService<MempoolManager>(); * var mempoolTx = mempool.GetMempoolAsync().Result; * if (mempoolTx.Count > 0) * { * Console.WriteLine("--- Mempool contents ---"); * foreach (var tx in mempoolTx) * { * var hex = mempool.GetTransaction(tx).Result; * Console.WriteLine(tx + " ->"); * Console.WriteLine(hex); * Console.WriteLine("---"); * } * }*/ Thread.Sleep(10000); } // Check destination wallet for tumbled coins // TODO: Need to amend TumblerService so that it can be shut down within the test if (client != null) { client.Dispose(); client = null; } } }
public static async Task MainAsync(string[] args) { try { var comparer = new CommandlineArgumentComparer(); var isRegTest = args.Contains("regtest", comparer); var isTestNet = args.Contains("testnet", comparer); var isStratis = args.Contains("stratis", comparer); var isLight = args.Contains("light", comparer); var useRegistration = args.Contains("registration", comparer); var useTumblebit = args.Contains("tumblebit", comparer); var useTor = !args.Contains("noTor", comparer); string registrationStoreDirectoryPath = args.GetValueOf("-storedir"); TumblerProtocolType tumblerProtocol; try { string tumblerProtocolString = args.GetValueOf("-tumblerProtocol"); if (!isRegTest && (tumblerProtocolString != null || !useTor)) { Console.WriteLine("Options -TumblerProtocol and -NoTor can only be used in combination with -RegTest switch."); return; } if (tumblerProtocolString != null) { tumblerProtocol = Enum.Parse <TumblerProtocolType>(tumblerProtocolString, true); } else { tumblerProtocol = TumblerProtocolType.Tcp; } if (useTor && tumblerProtocol == TumblerProtocolType.Http) { Console.WriteLine("TumblerProtocol can only be changed to Http when Tor is disabled. Please use -NoTor switch to disable Tor."); return; } } catch { Console.WriteLine($"Incorrect tumbling prococol specified; the valid values are {TumblerProtocolType.Tcp} and {TumblerProtocolType.Http}"); return; } var agent = "Breeze"; NodeSettings nodeSettings; if (isStratis) { //if (NodeSettings.PrintHelp(args, Network.StratisMain)) // return; Network network; if (isRegTest) { network = Network.StratisRegTest; } else if (isTestNet) { args = args.Append("-addnode=51.141.28.47").ToArray(); // TODO: fix this temp hack network = Network.StratisTest; } else { network = Network.StratisMain; } nodeSettings = new NodeSettings(network, ProtocolVersion.ALT_PROTOCOL_VERSION, agent, args: args, loadConfiguration: false); } else { nodeSettings = new NodeSettings(agent: agent, args: args, loadConfiguration: false); } IFullNodeBuilder fullNodeBuilder = null; if (isLight) { fullNodeBuilder = new FullNodeBuilder() .UseNodeSettings(nodeSettings) .UseLightWallet() .UseWatchOnlyWallet() .UseBlockNotification() .UseTransactionNotification() .UseApi(); } else { fullNodeBuilder = new FullNodeBuilder() .UseNodeSettings(nodeSettings); if (isStratis) { fullNodeBuilder.UsePosConsensus(); } else { fullNodeBuilder.UsePowConsensus(); } fullNodeBuilder.UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .UseWallet() .UseWatchOnlyWallet(); if (isStratis) { fullNodeBuilder.AddPowPosMining(); } else { fullNodeBuilder.AddMining(); } fullNodeBuilder.AddRPC() .UseApi(); } if (useRegistration) { //fullNodeBuilder.UseInterNodeCommunication(); fullNodeBuilder.UseRegistration(); } // Need this to happen for both TB and non-TB daemon string dataDir = nodeSettings.DataDir; if (string.IsNullOrEmpty(dataDir)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StratisNode"); } else { dataDir = Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".stratisnode"); } } string logDir = Path.Combine(dataDir, nodeSettings.Network.RootFolderName, nodeSettings.Network.Name, "Logs"); Logs.Configure(new FuncLoggerFactory(i => new DualLogger(i, (a, b) => true, false)), logDir); // Start NTumbleBit logging to the console SetupTumbleBitConsoleLogs(nodeSettings); // Currently TumbleBit is bitcoin only if (useTumblebit) { if (string.IsNullOrEmpty(registrationStoreDirectoryPath)) { string networkName; if (isRegTest) { networkName = "StratisRegTest"; } else if (isTestNet) { networkName = "StratisTest"; } else { networkName = "StratisMain"; } registrationStoreDirectoryPath = Path.Combine(dataDir, "stratis", networkName, "registrationHistory.json"); } // Those settings are not in NodeSettings yet, so get it directly from the args ConfigurationOptionWrapper <object> registrationStoreDirectory = new ConfigurationOptionWrapper <object>("RegistrationStoreDirectory", registrationStoreDirectoryPath); ConfigurationOptionWrapper <object> torOption = new ConfigurationOptionWrapper <object>("Tor", useTor); ConfigurationOptionWrapper <object> tumblerProtocolOption = new ConfigurationOptionWrapper <object>("TumblerProtocol", tumblerProtocol); ConfigurationOptionWrapper <object> useDummyAddressOption = new ConfigurationOptionWrapper <object>("UseDummyAddress", true); ConfigurationOptionWrapper <object>[] tumblebitConfigurationOptions = { registrationStoreDirectory, torOption, tumblerProtocolOption, useDummyAddressOption }; // We no longer pass the URI in via the command line, the registration feature selects a random one fullNodeBuilder.UseTumbleBit(tumblebitConfigurationOptions); } IFullNode node = fullNodeBuilder.Build(); // Add logging to NLog SetupTumbleBitNLogs(nodeSettings); // Start Full Node - this will also start the API. await node.RunAsync(); } catch (Exception ex) { Console.WriteLine("There was a problem initializing the node. Details: '{0}'", ex.Message); } }
public TumbleBitManager( ILoggerFactory loggerFactory, NodeSettings nodeSettings, IWalletManager walletManager, IWatchOnlyWalletManager watchOnlyWalletManager, ConcurrentChain chain, Network network, Signals signals, IWalletTransactionHandler walletTransactionHandler, IWalletSyncManager walletSyncManager, IWalletFeePolicy walletFeePolicy, IBroadcasterManager broadcasterManager, FullNode fullNode, ConfigurationOptionWrapper <string> registrationStoreDirectory) { this.walletManager = walletManager as WalletManager; this.watchOnlyWalletManager = watchOnlyWalletManager; this.walletSyncManager = walletSyncManager as WalletSyncManager; this.walletTransactionHandler = walletTransactionHandler as WalletTransactionHandler; this.chain = chain; this.signals = signals; this.network = network; this.nodeSettings = nodeSettings; this.loggerFactory = loggerFactory; this.logger = loggerFactory.CreateLogger(this.GetType().FullName); this.walletFeePolicy = walletFeePolicy; this.broadcasterManager = broadcasterManager; this.fullNode = fullNode; if (registrationStoreDirectory.Value != null) { this.registrationStore = new RegistrationStore(registrationStoreDirectory.Value); } else { this.registrationStore = new RegistrationStore(this.nodeSettings.DataDir); } this.tumblingState = new TumblingState( this.loggerFactory, this.chain, this.walletManager, this.watchOnlyWalletManager, this.network, this.walletTransactionHandler, this.walletSyncManager, this.walletFeePolicy, this.nodeSettings, this.broadcasterManager); // Load saved state e.g. previously selected server if (File.Exists(this.tumblingState.GetStateFilePath())) { try { this.tumblingState.LoadStateFromMemory(); } catch (NullReferenceException) { // The file appears to get corrupted sometimes, not clear why // May be if the node is not shut down correctly } } this.tumblingState.Save(); // Remove the progress file from previous session as it is now stale ProgressInfo.RemoveProgressFile(); }