private static async Task CanChangeCircuitWithinSameHttpClientAsync()
        {
            var       requestUri = "https://api.ipify.org/";
            IPAddress torIp;
            IPAddress changedIp;

            // 1. Get TOR IP
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var httpClient = new HttpClient(handler))
            {
                var content =
                    await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync()
                    .ConfigureAwait(false);
                var gotIp = IPAddress.TryParse(content.Replace("\n", ""), out torIp);
                Assert.True(gotIp);

                // 2. Change TOR IP
                var controlPortClient = new ControlPort.Client(Shared.HostAddress, Shared.ControlPort, Shared.ControlPortPassword);
                await controlPortClient.ChangeCircuitAsync().ConfigureAwait(false);

                // 3. Get changed TOR IP
                content =
                    await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync()
                    .ConfigureAwait(false);
                gotIp = IPAddress.TryParse(content.Replace("\n", ""), out changedIp);
                Assert.True(gotIp);
            }

            Assert.NotEqual(changedIp, torIp);
        }
Example #2
0
        public async Task CanRequestDifferentWithSameHandlerAsync()
        {
            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    HttpResponseMessage message =
                        await httpClient.GetAsync("http://api.qbit.ninja/whatisit/what%20is%20my%20future").ConfigureAwait(false);

                    var content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                    Assert.Equal(content, "\"Good question Holmes !\"");

                    IPAddress ip;
                    message = await httpClient.GetAsync("http://icanhazip.com/").ConfigureAwait(false);

                    content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                    var gotIp = IPAddress.TryParse(content.Replace("\n", ""), out ip);
                    Assert.True(gotIp);


                    message = await httpClient.GetAsync("http://api.qbit.ninja/whatisit/what%20is%20my%20future").ConfigureAwait(false);

                    content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                    Assert.Equal(content, "\"Good question Holmes !\"");
                }
        }
Example #3
0
        public async Task CanReuseHandlerAsync()
        {
            // YOU HAVE TO SET THE HTTP CLIENT NOT TO DISPOSE THE HANDLER
            var requestUri = "http://api.qbit.ninja/whatisit/what%20is%20my%20future";
            var handler    = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var httpClient = new HttpClient(handler, disposeHandler: false))
            {
                HttpResponseMessage message = await httpClient.GetAsync(requestUri).ConfigureAwait(false);

                var content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                Assert.Equal(content, "\"Good question Holmes !\"");
            }
            using (var httpClient = new HttpClient(handler))
            {
                HttpResponseMessage message = await httpClient.GetAsync(requestUri).ConfigureAwait(false);

                var content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                Assert.Equal(content, "\"Good question Holmes !\"");
            }
            using (var httpClient = new HttpClient(handler))
            {
                HttpResponseMessage message = await httpClient.GetAsync(requestUri).ConfigureAwait(false);

                var content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                Assert.Equal(content, "\"Good question Holmes !\"");
            }
        }
Example #4
0
        private static async Task TorIpIsNotTheRealOneAsync()
        {
            var       requestUri = "http://icanhazip.com/";
            IPAddress realIp;
            IPAddress torIp;

            // 1. Get real IP
            using (var httpClient = new HttpClient())
            {
                var content = await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false);
                var gotIp   = IPAddress.TryParse(content.Replace("\n", ""), out realIp);
                Assert.True(gotIp);
            }

            // 2. Get TOR IP
            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    var content =
                        await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync()
                        .ConfigureAwait(false);
                    var gotIp = IPAddress.TryParse(content.Replace("\n", ""), out torIp);
                    Assert.True(gotIp);
                }

            Assert.NotEqual(realIp, torIp);
        }
Example #5
0
        public async Task CanDoRequestManyDifferentAsync()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var client = new HttpClient(handler))
            {
                await QBitTestAsync(client, 10, alterRequests : true).ConfigureAwait(false);
            }
        }
Example #6
0
        public async Task CanRequestInRowAsync()
        {
            var firstRequest = "http://api.qbit.ninja/transactions/38d4cfeb57d6685753b7a3b3534c3cb576c34ca7344cd4582f9613ebf0c2b02a?format=json&headeronly=true";

            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    await(await httpClient.GetAsync(firstRequest).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false);
                    await(await httpClient.GetAsync("http://api.qbit.ninja/balances/15sYbVpRh6dyWycZMwPdxJWD4xbfxReeHe?unspentonly=true").ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false);
                    await(await httpClient.GetAsync("http://api.qbit.ninja/balances/akEBcY5k1dn2yeEdFnTMwdhVbHxtgHb6GGi?from=tip&until=336000").ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false);
                }
        }
Example #7
0
        public async Task TestMicrosoftNCSI()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var client = new HttpClient(handler))
            {
                var response = await client.GetAsync("http://www.msftncsi.com/ncsi.txt").ConfigureAwait(false);

                var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                Assert.Equal(content, "Microsoft NCSI");
            }
        }
Example #8
0
        public async Task CanRequestChunkEncodedAsync()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var client = new HttpClient(handler))
            {
                var response = await client.GetAsync("https://jigsaw.w3.org/HTTP/ChunkedScript").ConfigureAwait(false);

                var content = await response.Content.ReadAsStringAsync();

                Assert.Equal(1000, Regex.Matches(content, "01234567890123456789012345678901234567890123456789012345678901234567890").Count);
            }
        }
Example #9
0
        public async Task CanDoBasicRequestAsync()
        {
            var requestUri = "http://api.qbit.ninja/whatisit/what%20is%20my%20future";

            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    HttpResponseMessage message = await httpClient.GetAsync(requestUri).ConfigureAwait(false);

                    var content = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                    Assert.Equal(content, "\"Good question Holmes !\"");
                }
        }
Example #10
0
        public async Task CanRequestGzipEncoded()
        {
            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
            {
                var client = new QBitNinjaClient(Network.Main);
                client.SetHttpMessageHandler(handler);

                var response = await client.GetBlock(new QBitNinja.Client.Models.BlockFeature(new uint256("0000000000000000004e24d06073aef7a5313d4ea83a5c105b3cadd0d38cc1f0")), true).ConfigureAwait(false);

                Assert.Equal(474010, response.AdditionalInformation.Height);
                Assert.Equal(null, response.Block);
                Assert.Equal(null, response.ExtendedInformation);
            }
        }
Example #11
0
        public async Task CanRequestOnionAsync()
        {
            var requestUri = "http://msydqstlz2kzerdg.onion/";

            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    var content =
                        await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync()
                        .ConfigureAwait(false);

                    Assert.True(content.Contains("Learn more about Ahmia and its team"));
                }
        }
Example #12
0
        public async Task CanDoHttpsAsync()
        {
            var requestUri = "https://slack.com/api/api.test";

            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    var content =
                        await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync()
                        .ConfigureAwait(false);

                    Assert.Equal(content, "{\"ok\":true}");
                }
        }
Example #13
0
        public async Task CanDoHttpsRequest1Async()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort, ignoreSslCertification: true);

            using (var client = new HttpClient(handler))
            {
                var request = "https://api.qbit.ninja/whatisit/what%20is%20my%20future";
                var res     = await client.GetAsync(request).ConfigureAwait(false);

                var content = await res.Content.ReadAsStringAsync().ConfigureAwait(false);

                Assert.Equal(content, "\"Good question Holmes !\"");
            }
        }
Example #14
0
        public async Task CanDoHttpsRequestManyAsync()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort, ignoreSslCertification: true);

            using (var client = new HttpClient(handler))
            {
                var contents = await QBitTestAsync(client, 15, https : true).ConfigureAwait(false);

                foreach (var content in contents)
                {
                    Assert.Equal(content, "\"Good question Holmes !\"");
                }
            }
        }
Example #15
0
        public async Task CanDoRequest2Async()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var client = new HttpClient(handler))
            {
                var contents = await QBitTestAsync(client, 2).ConfigureAwait(false);

                foreach (var content in contents)
                {
                    Assert.Equal(content, "\"Good question Holmes !\"");
                }
            }
        }
Example #16
0
        public async Task CanRequestOnionAsync()
        {
            var requestUri = "http://bitmixer2whesjgj.onion/order.php?addr1=16HGUokcXuJXn9yiV6uQ4N3umAWteE2cRR&pr1=33&time1=8&addr2=1F1Afwxr2xrs3ZQpf6ifqfNMxJWZt2JupK&pr2=67&time2=16&bitcode=AcOw&fee=0.6523";

            using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort))
                using (var httpClient = new HttpClient(handler))
                {
                    var content =
                        await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync()
                        .ConfigureAwait(false);

                    Assert.Equal(content, "error=Invalid Bitcode");
                }
        }
        private static async Task <IPAddress> GetTorIpAsync(string requestUri)
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            IPAddress torIp;

            using (var httpClient = new HttpClient(handler))
            {
                var content =
                    await(await httpClient.GetAsync(requestUri).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false);
                var gotIp = IPAddress.TryParse(content.Replace("\n", ""), out torIp);
                Assert.True(gotIp);
            }
            return(torIp);
        }
Example #18
0
 public async Task CanRequestInRowHttpsAsync()
 {
     using (var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort, ignoreSslCertification: true))
     {
         for (int i = 0; i < 2; i++)
         {
             using (var httpClient = new HttpClient(handler, disposeHandler: false))
             {
                 await(await httpClient.GetAsync(
                           "https://api.qbit.ninja/transactions/38d4cfeb57d6685753b7a3b3534c3cb576c34ca7344cd4582f9613ebf0c2b02a?format=json&headeronly=true")
                       .ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false);
             }
         }
     }
 }
Example #19
0
        public async Task CanRequestChunkEncodedAsync()
        {
            var handler = new SocksPortHandler(Shared.HostAddress, Shared.SocksPort);

            using (var client = new HttpClient(handler))
            {
                var response = await client.GetAsync("https://jigsaw.w3.org/HTTP/ChunkedScript").ConfigureAwait(false);

                var content = await response.Content.ReadAsStringAsync();

                Assert.Equal(1000, Regex.Matches(content, "01234567890123456789012345678901234567890123456789012345678901234567890").Count);

                var tumbleBitResponse = await client.GetAsync("http://testnet.ntumblebit.metaco.com/api/v1/tumblers/0/parameters").ConfigureAwait(false);

                var tumbleBitContent = await tumbleBitResponse.Content.ReadAsStringAsync();

                Assert.True(tumbleBitContent.Contains("TestNet"));
                Assert.True(tumbleBitContent.Contains("fakePuzzleCount"));
                Assert.True(tumbleBitContent.Contains("30820124020100300d06092a864886f70d01010105000482010e3082010a0282010100b520935292dd6ff1e6d69af2a6936bb0bb52681ec6e700b2b256cc88e80a1264c4d1390b5e37dc3540c0069680df10ffd4e16b3511264488b9f7e27eb74e4fdf97c3f18b331a2aa33541b6e2b6fbad8ebf9b2799e14af0d5b327260f162c84b16c08fdfb0730a4dac956116b6e200b33cbcdf19b270250e820c5aec8f9dcc224b5cb08f2a1a4adb583a4d70c76a252492f1b0da6e89f7d586c12f426dd1e5a9843b542eea760eb89c4a2e44cb3b1f4815866d9150b6c2c9bdf5d0e99ece9ac6df09cd13e43dc02dad19fc828f5f737b2ac9e3318ee2374bfd1b70da4884e807c6150a0ceeca20a1a62814dad7408a542f5865d7b5b3f0c1bfd8878372514dca10203010001"));
                Assert.True(tumbleBitContent.Contains("realTransactionCount"));
                Assert.True(tumbleBitContent.Contains("denomination"));
                Assert.True(tumbleBitContent.Contains("fakeFormat"));
            }
        }
Example #20
0
        public override HttpMessageHandler CreateHttpHandler()
        {
            SocksPortHandler handler = new SocksPortHandler(Proxy);

            return(handler);
        }
Example #21
0
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddResponseCompression();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
            services.AddMvc(option => option.EnableEndpointRouting = false);

            services.AddControllers()
            .AddNewtonsoftJson();


            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
                {
                    License = new Microsoft.OpenApi.Models.OpenApiLicense
                    {
                        Name = "Attribution-NonCommercial-NoDerivatives 4.0 International",
                        Url  = new Uri("https://raw.githubusercontent.com/tangramproject/Tangram.Vector/initial/LICENSE")
                    },
                    Title          = "Tangram Membership HTTP API",
                    Version        = "v1",
                    Description    = "Backend services.",
                    TermsOfService = new Uri("https://tangrams.io/legal/"),
                    Contact        = new Microsoft.OpenApi.Models.OpenApiContact
                    {
                        Email = "*****@*****.**",
                        Url   = new Uri("https://tangrams.io/about-tangram/team/")
                    }
                });
            });

            services.AddHttpContextAccessor();

            services.AddOptions();

            services.AddSingleton <IOnionServiceClientConfiguration, OnionServiceClientConfiguration>();

            services.AddHttpClient <IOnionServiceClient, OnionServiceClient>();

            services.AddSingleton(sp =>
            {
                var logger = sp.GetService <ILogger <Startup> >();

                //var onionStarted = sp.GetService<IOnionServiceClient>()
                //                     .IsTorStartedAsync()
                //                     .GetAwaiter()
                //                     .GetResult();

                //while(!onionStarted)
                //{
                //    logger.LogWarning("Unable to verify Tor is started... retrying in a few seconds");
                //    Thread.Sleep(5000);
                //    onionStarted = sp.GetService<IOnionServiceClient>()
                //                     .IsTorStartedAsync()
                //                     .GetAwaiter()
                //                     .GetResult();
                //}

                //logger.LogInformation("Tor is started... configuring Socks Port Handler");

                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

                var onionServiceClientConfiguration = sp.GetService <IOnionServiceClientConfiguration>();

                var handler = new SocksPortHandler(onionServiceClientConfiguration.SocksHost, onionServiceClientConfiguration.SocksPort);

                return(handler);
            });

            services.AddHttpClient <ITorClient, TorClient>()
            //.ConfigurePrimaryHttpMessageHandler(
            //    p => p.GetRequiredService<SocksPortHandler>()
            //)
            .SetHandlerLifetime(TimeSpan.FromMinutes(5));

            services.AddSingleton <ISwimNode, SwimNode>(sp =>
            {
                //var onionServiceClientDetails = sp.GetService<IOnionServiceClient>()
                //                                  .GetHiddenServiceDetailsAsync()
                //                                  .GetAwaiter()
                //                                  .GetResult();

                var publicIp   = GetPublicHostIp();
                var publicPort = GetPublicHostPort();

                return(new SwimNode($"http://{publicIp}:{publicPort}"));
            });

            services.AddSingleton <ISwimProtocolProvider, SwimProtocolProvider>();
            services.AddSingleton <ISwimProtocol, FailureDetection>();

            services.AddHostedService <FailureDetection>();
        }
Example #22
0
 public Client(IConfiguration apiRestSection, ILogger logger, SocksPortHandler socksPortHandler)
 {
     this.apiRestSection   = apiRestSection.GetSection(Constant.ApiGateway);
     this.logger           = logger;
     this.socksPortHandler = socksPortHandler;
 }
Example #23
0
 public Client(ILogger logger, SocksPortHandler socksPortHandler)
 {
     this.logger           = logger;
     this.socksPortHandler = socksPortHandler;
 }
Example #24
0
        private static async Task MainAsync(IReadOnlyList <string> args)
        {
            //args = new string[] { "help" };
            //args = new string[] { "generate-wallet" };
            //args = new string[] { "generate-wallet", "wallet-file=test2.json" };
            ////math super cool donate beach mobile sunny web board kingdom bacon crisp
            ////no password
            //args = new string[] { "recover-wallet", "wallet-file=test5.json" };
            //args = new string[] { "show-balances"};
            //args = new string[] { "receive" };
            //args = new string[] { "send","btc=1", "address=mqjVoPiXtLdBdxdqQzWvFSMSBv93swPUUH", "wallet-file=MoliWallet.json" };
            //args = new string[] { "send", "btc=0.1", "address=mkpC5HFC8QHbJbuwajYLDkwPoqcftMU1ga" };
            //args = new string[] { "send", "btc=all", "address=mzz63n3n89KVeHQXRqJEVsQX8MZj5zeqCw", "wallet-file=test4.json" };

            // Load config file
            // It also creates it with default settings if doesn't exist
            Config.Load();

            // Configure QBitNinjaClient
            _qBitClient = new QBitNinjaClient(Config.Network);
            _httpClient = new HttpClient();
            if (Config.UseTor)
            {
                var torHandler = new SocksPortHandler(Config.TorHost, Config.TorSocksPort, ignoreSslCertification: true);                 // ignoreSslCertification needed for linux, until QBit or DotNetTor fixes its issues
                _qBitClient.SetHttpMessageHandler(torHandler);
                _httpClient = new HttpClient(torHandler);
            }

            if (args.Count == 0)
            {
                DisplayHelp();
                Exit(color: ConsoleColor.Green);
            }
            var command = args[0];

            if (!Commands.Contains(command))
            {
                WriteLine("Wrong command is specified.");
                DisplayHelp();
            }
            foreach (var arg in args.Skip(1).Where(arg => !arg.Contains('=')))
            {
                Exit($"Wrong argument format specified: {arg}");
            }

            #region HelpCommand

            if (command == "help")
            {
                AssertArgumentsLenght(args.Count, 1, 1);
                DisplayHelp();
            }

            #endregion HelpCommand

            #region GenerateWalletCommand

            if (command == "generate-wallet")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var walletFilePath = GetWalletFilePath(args);
                AssertWalletNotExists(walletFilePath);

                string pw;
                string pwConf;
                do
                {
                    // 1. Get password from user
                    WriteLine("Choose a password:"******"Confirm password:"******"Passwords do not match. Try again!");
                    }
                } while (pw != pwConf);

                // 3. Create wallet
                string mnemonic;
                Safe.Create(out mnemonic, pw, walletFilePath, Config.Network);
                // If no exception thrown the wallet is successfully created.
                WriteLine();
                WriteLine("Wallet is successfully created.");
                WriteLine($"Wallet file: {walletFilePath}");

                // 4. Display mnemonic
                WriteLine();
                WriteLine("Write down the following mnemonic words.");
                WriteLine("With the mnemonic words AND your password you can recover this wallet by using the recover-wallet command.");
                WriteLine();
                WriteLine("-------");
                WriteLine(mnemonic);
                WriteLine("-------");
            }

            #endregion GenerateWalletCommand

            #region RecoverWalletCommand

            if (command == "recover-wallet")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var walletFilePath = GetWalletFilePath(args);
                AssertWalletNotExists(walletFilePath);

                WriteLine($"Your software is configured using the Bitcoin {Config.Network} network.");
                WriteLine("Provide your mnemonic words, separated by spaces:");
                var mnemonic = ReadLine();
                AssertCorrectMnemonicFormat(mnemonic);

                WriteLine("Provide your password. Please note the wallet cannot check if your password is correct or not. If you provide a wrong password a wallet will be recovered with your provided mnemonic AND password pair:");
                var password = PasswordConsole.ReadPassword();

                Safe.Recover(mnemonic, password, walletFilePath, Config.Network);
                // If no exception thrown the wallet is successfully recovered.
                WriteLine();
                WriteLine("Wallet is successfully recovered.");
                WriteLine($"Wallet file: {walletFilePath}");
            }

            #endregion RecoverWalletCommand

            #region ShowBalancesCommand

            if (command == "show-balances")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var  walletFilePath = GetWalletFilePath(args);
                Safe safe           = DecryptWalletByAskingForPassword(walletFilePath);

                if (Config.ConnectionType == ConnectionType.Http)
                {
                    await AssertCorrectQBitBlockHeightAsync().ConfigureAwait(false);

                    // 0. Query all operations, grouped by addresses
                    Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerAddresses = await QueryOperationsPerSafeAddressesAsync(_qBitClient, safe, MinUnusedKeyNum).ConfigureAwait(false);

                    // 1. Get all address history record with a wrapper class
                    var addressHistoryRecords = new List <AddressHistoryRecord>();
                    foreach (var elem in operationsPerAddresses)
                    {
                        foreach (BalanceOperation op in elem.Value)
                        {
                            addressHistoryRecords.Add(new AddressHistoryRecord(elem.Key, op));
                        }
                    }

                    // 2. Calculate wallet balances
                    Money confirmedWalletBalance;
                    Money unconfirmedWalletBalance;
                    GetBalances(addressHistoryRecords, out confirmedWalletBalance, out unconfirmedWalletBalance);

                    // 3. Group all address history records by addresses
                    var addressHistoryRecordsPerAddresses = new Dictionary <BitcoinAddress, HashSet <AddressHistoryRecord> >();
                    foreach (BitcoinAddress address in operationsPerAddresses.Keys)
                    {
                        var recs = new HashSet <AddressHistoryRecord>();
                        foreach (AddressHistoryRecord record in addressHistoryRecords)
                        {
                            if (record.Address == address)
                            {
                                recs.Add(record);
                            }
                        }

                        addressHistoryRecordsPerAddresses.Add(address, recs);
                    }

                    // 4. Calculate address balances
                    WriteLine();
                    WriteLine("---------------------------------------------------------------------------");
                    WriteLine(@"Address					Confirmed	Unconfirmed");
                    WriteLine("---------------------------------------------------------------------------");
                    foreach (var elem in addressHistoryRecordsPerAddresses)
                    {
                        Money confirmedBalance;
                        Money unconfirmedBalance;
                        GetBalances(elem.Value, out confirmedBalance, out unconfirmedBalance);
                        if (confirmedBalance != Money.Zero || unconfirmedBalance != Money.Zero)
                        {
                            WriteLine($@"{elem.Key.ToWif()}	{confirmedBalance.ToDecimal(MoneyUnit.BTC):0.#############################}		{unconfirmedBalance.ToDecimal(MoneyUnit.BTC):0.#############################}");
                        }
                    }

                    WriteLine("---------------------------------------------------------------------------");
                    WriteLine($"Confirmed Wallet Balance: {confirmedWalletBalance.ToDecimal(MoneyUnit.BTC):0.#############################}btc");
                    WriteLine($"Unconfirmed Wallet Balance: {unconfirmedWalletBalance.ToDecimal(MoneyUnit.BTC):0.#############################}btc");
                    WriteLine("---------------------------------------------------------------------------");
                }
                else if (Config.ConnectionType == ConnectionType.FullNode)
                {
                    throw new NotImplementedException();
                }
                else
                {
                    Exit("Invalid connection type.");
                }
            }

            #endregion ShowBalancesCommand

            #region ShowHistoryCommand

            if (command == "show-history")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var  walletFilePath = GetWalletFilePath(args);
                Safe safe           = DecryptWalletByAskingForPassword(walletFilePath);

                if (Config.ConnectionType == ConnectionType.Http)
                {
                    await AssertCorrectQBitBlockHeightAsync().ConfigureAwait(false);

                    // 0. Query all operations, grouped our used safe addresses
                    Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerAddresses = await QueryOperationsPerSafeAddressesAsync(_qBitClient, safe, MinUnusedKeyNum).ConfigureAwait(false);

                    WriteLine();
                    WriteLine("---------------------------------------------------------------------------");
                    WriteLine(@"Date			Amount		Confirmed	Transaction Id");
                    WriteLine("---------------------------------------------------------------------------");

                    Dictionary <uint256, List <BalanceOperation> > operationsPerTransactions = GetOperationsPerTransactions(operationsPerAddresses);

                    // 3. Create history records from the transactions
                    // History records is arbitrary data we want to show to the user
                    var txHistoryRecords = new List <Tuple <DateTimeOffset, Money, int, uint256> >();
                    foreach (var elem in operationsPerTransactions)
                    {
                        var amount = Money.Zero;
                        foreach (var op in elem.Value)
                        {
                            amount += op.Amount;
                        }

                        var firstOp = elem.Value.First();

                        txHistoryRecords
                        .Add(new Tuple <DateTimeOffset, Money, int, uint256>(
                                 firstOp.FirstSeen,
                                 amount,
                                 firstOp.Confirmations,
                                 elem.Key));
                    }

                    // 4. Order the records by confirmations and time (Simply time does not work, because of a QBitNinja issue)
                    var orderedTxHistoryRecords = txHistoryRecords
                                                  .OrderByDescending(x => x.Item3) // Confirmations
                                                  .ThenBy(x => x.Item1);           // FirstSeen
                    foreach (var record in orderedTxHistoryRecords)
                    {
                        // Item2 is the Amount
                        if (record.Item2 > 0)
                        {
                            ForegroundColor = ConsoleColor.Green;
                        }
                        else if (record.Item2 < 0)
                        {
                            ForegroundColor = ConsoleColor.DarkGreen;
                        }
                        WriteLine($@"{record.Item1.DateTime}	{record.Item2}	{record.Item3 > 0}		{record.Item4}");
                        ResetColor();
                    }
                }
                else if (Config.ConnectionType == ConnectionType.FullNode)
                {
                    throw new NotImplementedException();
                }
                else
                {
                    Exit("Invalid connection type.");
                }
            }

            #endregion ShowHistoryCommand

            #region ShowExtKeys

            if (command == "show-extkey")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var  walletFilePath = GetWalletFilePath(args);
                Safe safe           = DecryptWalletByAskingForPassword(walletFilePath);

                WriteLine($"ExtKey: {safe.BitcoinExtKey}");
                WriteLine($"Network: {safe.Network}");
            }
            if (command == "show-extpubkey")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var  walletFilePath = GetWalletFilePath(args);
                Safe safe           = DecryptWalletByAskingForPassword(walletFilePath);

                WriteLine($"ExtPubKey: {safe.BitcoinExtPubKey}");
                WriteLine($"Network: {safe.Network}");
            }

            #endregion ShowExtKeys

            #region ReceiveCommand

            if (command == "receive")
            {
                AssertArgumentsLenght(args.Count, 1, 2);
                var  walletFilePath = GetWalletFilePath(args);
                Safe safe           = DecryptWalletByAskingForPassword(walletFilePath);

                if (Config.ConnectionType == ConnectionType.Http)
                {
                    await AssertCorrectQBitBlockHeightAsync().ConfigureAwait(false);

                    Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerReceiveAddresses = await QueryOperationsPerSafeAddressesAsync(_qBitClient, safe, 7, Safe.HdPathType.Receive).ConfigureAwait(false);

                    WriteLine("---------------------------------------------------------------------------");
                    WriteLine("Unused Receive Addresses");
                    WriteLine("---------------------------------------------------------------------------");
                    foreach (var elem in operationsPerReceiveAddresses)
                    {
                        if (elem.Value.Count == 0)
                        {
                            WriteLine($"{elem.Key.ToWif()}");
                        }
                    }
                }
                else if (Config.ConnectionType == ConnectionType.FullNode)
                {
                    throw new NotImplementedException();
                }
                else
                {
                    Exit("Invalid connection type.");
                }
            }

            #endregion ReceiveCommand

            #region SendCommand

            if (command == "send")
            {
                await AssertCorrectQBitBlockHeightAsync().ConfigureAwait(false);

                AssertArgumentsLenght(args.Count, 3, 4);
                var            walletFilePath = GetWalletFilePath(args);
                BitcoinAddress addressToSend;
                try
                {
                    addressToSend = BitcoinAddress.Create(GetArgumentValue(args, argName: "address", required: true), Config.Network);
                }
                catch (Exception ex)
                {
                    Exit(ex.ToString());
                    throw;
                }

                Safe safe = DecryptWalletByAskingForPassword(walletFilePath);

                if (Config.ConnectionType == ConnectionType.Http)
                {
                    Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerAddresses = await QueryOperationsPerSafeAddressesAsync(_qBitClient, safe, MinUnusedKeyNum).ConfigureAwait(false);

                    // 1. Gather all the not empty private keys
                    WriteLine("Finding not empty private keys...");
                    var operationsPerNotEmptyPrivateKeys = new Dictionary <BitcoinExtKey, List <BalanceOperation> >();
                    foreach (var elem in operationsPerAddresses)
                    {
                        var balance = Money.Zero;
                        foreach (var op in elem.Value)
                        {
                            balance += op.Amount;
                        }

                        if (balance > Money.Zero)
                        {
                            var secret = safe.FindPrivateKey(elem.Key);
                            operationsPerNotEmptyPrivateKeys.Add(secret, elem.Value);
                        }
                    }

                    // 2. Get the script pubkey of the change.
                    WriteLine("Select change address...");
                    Script changeScriptPubKey = null;
                    Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerChangeAddresses = await QueryOperationsPerSafeAddressesAsync(_qBitClient, safe, minUnusedKeys : 1, hdPathType : Safe.HdPathType.Change).ConfigureAwait(false);

                    foreach (var elem in operationsPerChangeAddresses)
                    {
                        if (elem.Value.Count == 0)
                        {
                            changeScriptPubKey = safe.FindPrivateKey(elem.Key).ScriptPubKey;
                        }
                    }

                    if (changeScriptPubKey == null)
                    {
                        throw new ArgumentNullException();
                    }

                    // 3. Gather coins can be spend
                    WriteLine("Gathering unspent coins...");
                    Dictionary <Coin, bool> unspentCoins = await GetUnspentCoinsAsync(operationsPerNotEmptyPrivateKeys.Keys, _qBitClient).ConfigureAwait(false);

                    // 4. How much money we can spend?
                    var availableAmount            = Money.Zero;
                    var unconfirmedAvailableAmount = Money.Zero;
                    foreach (var elem in unspentCoins)
                    {
                        // If can spend unconfirmed add all
                        if (Config.CanSpendUnconfirmed)
                        {
                            availableAmount += elem.Key.Amount;
                            if (!elem.Value)
                            {
                                unconfirmedAvailableAmount += elem.Key.Amount;
                            }
                        }
                        // else only add confirmed ones
                        else
                        {
                            if (elem.Value)
                            {
                                availableAmount += elem.Key.Amount;
                            }
                        }
                    }

                    // 5. Get and calculate fee
                    WriteLine("Calculating dynamic transaction fee...");
                    Money feePerBytes = null;
                    try
                    {
                        feePerBytes = await QueryFeePerBytesAsync().ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        WriteLine(ex.Message);
                        Exit("Couldn't calculate transaction fee, try it again later.");
                    }

                    int inNum;

                    string amountString = GetArgumentValue(args, argName: "btc", required: true);
                    if (string.Equals(amountString, "all", StringComparison.OrdinalIgnoreCase))
                    {
                        inNum = unspentCoins.Count;
                    }
                    else
                    {
                        const int expectedMinTxSize = 1 * 148 + 2 * 34 + 10 - 1;
                        inNum = SelectCoinsToSpend(unspentCoins, ParseBtcString(amountString) + feePerBytes * expectedMinTxSize).Count;
                    }
                    const int outNum          = 2;                                      // 1 address to send + 1 for change
                    var       estimatedTxSize = inNum * 148 + outNum * 34 + 10 + inNum; // http://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending
                    WriteLine($"Estimated tx size: {estimatedTxSize} bytes");
                    Money fee = feePerBytes * estimatedTxSize;
                    WriteLine($"Fee: {fee.ToDecimal(MoneyUnit.BTC):0.#############################}btc");

                    // 6. How much to spend?
                    Money amountToSend = null;
                    if (string.Equals(amountString, "all", StringComparison.OrdinalIgnoreCase))
                    {
                        amountToSend  = availableAmount;
                        amountToSend -= fee;
                    }
                    else
                    {
                        amountToSend = ParseBtcString(amountString);
                    }

                    // 7. Do some checks
                    if (amountToSend < Money.Zero || availableAmount < amountToSend + fee)
                    {
                        Exit("Not enough coins.");
                    }

                    decimal feePc = Math.Round((100 * fee.ToDecimal(MoneyUnit.BTC)) / amountToSend.ToDecimal(MoneyUnit.BTC));
                    if (feePc > 1)
                    {
                        WriteLine();
                        WriteLine($"The transaction fee is {feePc:0.#}% of your transaction amount.");
                        WriteLine($"Sending:\t {amountToSend.ToDecimal(MoneyUnit.BTC):0.#############################}btc");
                        WriteLine($"Fee:\t\t {fee.ToDecimal(MoneyUnit.BTC):0.#############################}btc");
                        ConsoleKey response = GetYesNoAnswerFromUser();
                        if (response == ConsoleKey.N)
                        {
                            Exit("User interruption.");
                        }
                    }

                    var confirmedAvailableAmount = availableAmount - unconfirmedAvailableAmount;
                    var totalOutAmount           = amountToSend + fee;
                    if (confirmedAvailableAmount < totalOutAmount)
                    {
                        var unconfirmedToSend = totalOutAmount - confirmedAvailableAmount;
                        WriteLine();
                        WriteLine($"In order to complete this transaction you have to spend {unconfirmedToSend.ToDecimal(MoneyUnit.BTC):0.#############################} unconfirmed btc.");
                        ConsoleKey response = GetYesNoAnswerFromUser();
                        if (response == ConsoleKey.N)
                        {
                            Exit("User interruption.");
                        }
                    }

                    // 8. Select coins
                    WriteLine("Selecting coins...");
                    HashSet <Coin> coinsToSpend = SelectCoinsToSpend(unspentCoins, totalOutAmount);

                    // 9. Get signing keys
                    var signingKeys = new HashSet <ISecret>();
                    foreach (var coin in coinsToSpend)
                    {
                        foreach (var elem in operationsPerNotEmptyPrivateKeys)
                        {
                            if (elem.Key.ScriptPubKey == coin.ScriptPubKey)
                            {
                                signingKeys.Add(elem.Key);
                            }
                        }
                    }

                    // 10. Build the transaction
                    WriteLine("Signing transaction...");
                    var builder = new TransactionBuilder();
                    var tx      = builder
                                  .AddCoins(coinsToSpend)
                                  .AddKeys(signingKeys.ToArray())
                                  .Send(addressToSend, amountToSend)
                                  .SetChange(changeScriptPubKey)
                                  .SendFees(fee)
                                  .BuildTransaction(true);

                    if (!builder.Verify(tx))
                    {
                        Exit("Couldn't build the transaction.");
                    }

                    WriteLine($"Transaction Id: {tx.GetHash()}");

                    // QBit's success response is buggy so let's check manually, too
                    BroadcastResponse broadcastResponse;
                    var       success = false;
                    var       tried   = 0;
                    const int maxTry  = 7;
                    do
                    {
                        tried++;
                        WriteLine($"Try broadcasting transaction... ({tried})");
                        broadcastResponse = await _qBitClient.Broadcast(tx).ConfigureAwait(false);

                        var getTxResp = await _qBitClient.GetTransaction(tx.GetHash()).ConfigureAwait(false);

                        if (getTxResp != null)
                        {
                            success = true;
                            break;
                        }
                        else
                        {
                            await Task.Delay(3000).ConfigureAwait(false);
                        }
                    } while (tried < maxTry);

                    if (!success)
                    {
                        if (broadcastResponse.Error != null)
                        {
                            // Try broadcasting with smartbit if QBit fails (QBit issue)
                            if (broadcastResponse.Error.ErrorCode == NBitcoin.Protocol.RejectCode.INVALID && broadcastResponse.Error.Reason == "Unknown")
                            {
                                WriteLine("Try broadcasting transaction with smartbit...");

                                var post = "https://testnet-api.smartbit.com.au/v1/blockchain/pushtx";
                                if (Config.Network == Network.Main)
                                {
                                    post = "https://api.smartbit.com.au/v1/blockchain/pushtx";
                                }

                                var content = new StringContent(new JObject(new JProperty("hex", tx.ToHex())).ToString(), Encoding.UTF8, "application/json");
                                var resp    = await _httpClient.PostAsync(post, content).ConfigureAwait(false);

                                var json = JObject.Parse(await resp.Content.ReadAsStringAsync().ConfigureAwait(false));
                                if (json.Value <bool>("success"))
                                {
                                    Exit("Transaction is successfully propagated on the network.", ConsoleColor.Green);
                                }
                                else
                                {
                                    WriteLine($"Error code: {json["error"].Value<string>("code")} Reason: {json["error"].Value<string>("message")}");
                                }
                            }
                            else
                            {
                                WriteLine($"Error code: {broadcastResponse.Error.ErrorCode} Reason: {broadcastResponse.Error.Reason}");
                            }
                        }
                        Exit("The transaction might not have been successfully broadcasted. Please check the Transaction ID in a block explorer.", ConsoleColor.Blue);
                    }
                    Exit("Transaction is successfully propagated on the network.", ConsoleColor.Green);
                }
                else if (Config.ConnectionType == ConnectionType.FullNode)
                {
                    throw new NotImplementedException();
                }
                else
                {
                    Exit("Invalid connection type.");
                }
            }

            #endregion SendCommand

            Exit(color: ConsoleColor.Green);
        }