From 390a725b9ee275cb0e5bb9c0e58228f1d9a9bd88 Mon Sep 17 00:00:00 2001 From: sigHash Date: Sun, 7 Oct 2018 13:26:39 -0700 Subject: [PATCH 1/2] Add suppport for aws sqs broker --- NBXplorer.Tests/NBXplorer.Tests.csproj | 1 + NBXplorer.Tests/ServerTester.cs | 679 ++-- NBXplorer.Tests/TestConfig.cs | 140 +- NBXplorer.Tests/UnitTest1.cs | 2972 +++++++++-------- .../Configuration/DefaultConfiguration.cs | 329 +- .../Configuration/ExplorerConfiguration.cs | 381 ++- NBXplorer/MessageBrokers/AWSBroker.cs | 55 + .../MessageBrokers/BrokerHostedService.cs | 265 +- NBXplorer/NBXplorer.csproj | 1 + 9 files changed, 2509 insertions(+), 2314 deletions(-) create mode 100644 NBXplorer/MessageBrokers/AWSBroker.cs diff --git a/NBXplorer.Tests/NBXplorer.Tests.csproj b/NBXplorer.Tests/NBXplorer.Tests.csproj index 164ac9279..b43273372 100644 --- a/NBXplorer.Tests/NBXplorer.Tests.csproj +++ b/NBXplorer.Tests/NBXplorer.Tests.csproj @@ -3,6 +3,7 @@ netcoreapp2.1 + diff --git a/NBXplorer.Tests/ServerTester.cs b/NBXplorer.Tests/ServerTester.cs index 1b3725417..3f9e60786 100644 --- a/NBXplorer.Tests/ServerTester.cs +++ b/NBXplorer.Tests/ServerTester.cs @@ -25,341 +25,346 @@ namespace NBXplorer.Tests { - public partial class ServerTester : IDisposable - { - private readonly string _Directory; - - public static ServerTester Create([CallerMemberNameAttribute]string caller = null) - { - return new ServerTester(caller); - } - - public void Dispose() - { - if(Host != null) - { - Host.Dispose(); - Host = null; - } - if(NodeBuilder != null) - { - NodeBuilder.Dispose(); - NodeBuilder = null; - } - } - - NodeDownloadData nodeDownloadData; - - public string CryptoCode - { - get; set; - } - - public ServerTester(string directory) - { - SetEnvironment(); - try - { - var rootTestData = "TestData"; - directory = Path.Combine(rootTestData, directory); - _Directory = directory; - if(!Directory.Exists(rootTestData)) - Directory.CreateDirectory(rootTestData); - - var cryptoSettings = new NBXplorerNetworkProvider(NetworkType.Regtest).GetFromCryptoCode(CryptoCode); - NodeBuilder = NodeBuilder.Create(nodeDownloadData, Network, directory); - - - User1 = NodeBuilder.CreateNode(); - User2 = NodeBuilder.CreateNode(); - Explorer = NodeBuilder.CreateNode(); - foreach(var node in NodeBuilder.Nodes) - { - node.WhiteBind = true; - node.CookieAuth = cryptoSettings.SupportCookieAuthentication; - } - NodeBuilder.StartAll(); - - User1.CreateRPCClient().Generate(1); - User1.Sync(Explorer, true); - Explorer.CreateRPCClient().Generate(1); - Explorer.Sync(User2, true); - User2.CreateRPCClient().EnsureGenerate(Network.Consensus.CoinbaseMaturity + 1); - User1.Sync(User2, true); - - var port = CustomServer.FreeTcpPort(); - var datadir = Path.Combine(directory, "explorer"); - DeleteRecursivelyWithMagicDust(datadir); - List<(string key, string value)> keyValues = new List<(string key, string value)>(); - keyValues.Add(("conf", Path.Combine(directory, "explorer", "settings.config"))); - keyValues.Add(("datadir", datadir)); - keyValues.Add(("port", port.ToString())); - keyValues.Add(("network", "regtest")); - keyValues.Add(("chains", CryptoCode.ToLowerInvariant())); - keyValues.Add(("verbose", "1")); - keyValues.Add(($"{CryptoCode.ToLowerInvariant()}rpcauth", Explorer.GetRPCAuth())); - keyValues.Add(($"{CryptoCode.ToLowerInvariant()}rpcurl", Explorer.CreateRPCClient().Address.AbsoluteUri)); - keyValues.Add(("cachechain", "0")); - keyValues.Add(("rpcnotest", "1")); - keyValues.Add(("mingapsize", "2")); - keyValues.Add(("maxgapsize", "4")); - keyValues.Add(($"{CryptoCode.ToLowerInvariant()}startheight", Explorer.CreateRPCClient().GetBlockCount().ToString())); - keyValues.Add(($"{CryptoCode.ToLowerInvariant()}nodeendpoint", $"{Explorer.Endpoint.Address}:{Explorer.Endpoint.Port}")); - keyValues.Add(("asbcnstr", AzureServiceBusTestConfig.ConnectionString)); - keyValues.Add(("asbblockq", AzureServiceBusTestConfig.NewBlockQueue)); - keyValues.Add(("asbtranq", AzureServiceBusTestConfig.NewTransactionQueue)); - keyValues.Add(("asbblockt", AzureServiceBusTestConfig.NewBlockTopic)); - keyValues.Add(("asbtrant", AzureServiceBusTestConfig.NewTransactionTopic)); - - var args = keyValues.SelectMany(kv => new[] { $"--{kv.key}", kv.value }).ToArray(); - Host = new WebHostBuilder() - .UseConfiguration(new DefaultConfiguration().CreateConfiguration(args)) - .UseKestrel() - .ConfigureLogging(l => - { - l.SetMinimumLevel(LogLevel.Information) - .AddFilter("Microsoft", LogLevel.Error) - .AddFilter("Hangfire", LogLevel.Error) - .AddFilter("NBXplorer.Authentication.BasicAuthenticationHandler", LogLevel.Critical) - .AddProvider(Logs.LogProvider); - }) - .UseStartup() - .Build(); - - RPC = ((RPCClientProvider)Host.Services.GetService(typeof(RPCClientProvider))).GetRPCClient(CryptoCode); - var nbxnetwork = ((NBXplorerNetworkProvider)Host.Services.GetService(typeof(NBXplorerNetworkProvider))).GetFromCryptoCode(CryptoCode); - Network = nbxnetwork.NBitcoinNetwork; - var conf = (ExplorerConfiguration)Host.Services.GetService(typeof(ExplorerConfiguration)); - Host.Start(); - - _Client = new ExplorerClient(nbxnetwork, Address); - _Client.SetCookieAuth(Path.Combine(conf.DataDir, ".cookie")); - } - catch - { - Dispose(); - throw; - } - } - - private NetworkCredential ExtractCredentials(string config) - { - var user = Regex.Match(config, "rpcuser=([^\r\n]*)"); - var pass = Regex.Match(config, "rpcpassword=([^\r\n]*)"); - return new NetworkCredential(user.Groups[1].Value, pass.Groups[1].Value); - } - - public Uri Address - { - get - { - - var address = ((KestrelServer)(Host.Services.GetService(typeof(IServer)))).Features.Get().Addresses.FirstOrDefault(); - return new Uri(address); - } - } - - ExplorerClient _Client; - public ExplorerClient Client - { - get - { - return _Client; - } - } - - public CoreNode Explorer - { - get; set; - } - - public CoreNode User1 - { - get; set; - } - - public CoreNode User2 - { - get; set; - } - - public NodeBuilder NodeBuilder - { - get; set; - } - - - public IWebHost Host - { - get; set; - } - - public string BaseDirectory - { - get - { - return _Directory; - } - } - - public RPCClient RPC - { - get; set; - } - - public Network Network - { - get; - internal set; - } - - private static bool TryDelete(string directory, bool throws) - { - try - { - DeleteRecursivelyWithMagicDust(directory); - return true; - } - catch(DirectoryNotFoundException) - { - return true; - } - catch(Exception) - { - if(throws) - throw; - } - return false; - } - - // http://stackoverflow.com/a/14933880/2061103 - public static void DeleteRecursivelyWithMagicDust(string destinationDir) - { - const int magicDust = 10; - for(var gnomes = 1; gnomes <= magicDust; gnomes++) - { - try - { - Directory.Delete(destinationDir, true); - } - catch(DirectoryNotFoundException) - { - return; // good! - } - catch(IOException) - { - if(gnomes == magicDust) - throw; - // System.IO.IOException: The directory is not empty - System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes); - - // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic - Thread.Sleep(100 * gnomes); - continue; - } - catch(UnauthorizedAccessException) - { - if(gnomes == magicDust) - throw; - // Wait, maybe another software make us authorized a little later - System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes); - - // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic - Thread.Sleep(100); - continue; - } - return; - } - // depending on your use case, consider throwing an exception here - } - - static void Copy(string sourceDirectory, string targetDirectory) - { - DirectoryInfo diSource = new DirectoryInfo(sourceDirectory); - DirectoryInfo diTarget = new DirectoryInfo(targetDirectory); - - CopyAll(diSource, diTarget); - } - - static void CopyAll(DirectoryInfo source, DirectoryInfo target) - { - Directory.CreateDirectory(target.FullName); - - // Copy each file into the new directory. - foreach(FileInfo fi in source.GetFiles()) - { - fi.CopyTo(Path.Combine(target.FullName, fi.Name), true); - } - - // Copy each subdirectory using recursion. - foreach(DirectoryInfo diSourceSubDir in source.GetDirectories()) - { - DirectoryInfo nextTargetSubDir = - target.CreateSubdirectory(diSourceSubDir.Name); - CopyAll(diSourceSubDir, nextTargetSubDir); - } - } - - public BitcoinSecret PrivateKeyOf(BitcoinExtKey key, string path) - { - return new BitcoinSecret(key.ExtKey.Derive(new KeyPath(path)).PrivateKey, Network); - } - - public BitcoinAddress AddressOf(BitcoinExtKey key, string path) - { - if(SupportSegwit()) - return key.ExtKey.Derive(new KeyPath(path)).Neuter().PubKey.WitHash.GetAddress(Network); - else - return key.ExtKey.Derive(new KeyPath(path)).Neuter().PubKey.Hash.GetAddress(Network); - } - - public BitcoinAddress AddressOf(DerivationStrategyBase scheme, string path) - { - return scheme.Derive(KeyPath.Parse(path)).ScriptPubKey.GetDestinationAddress(Network); - } - - public DirectDerivationStrategy CreateDerivationStrategy(ExtPubKey pubKey = null) - { - return (DirectDerivationStrategy)CreateDerivationStrategy(pubKey, false); - } - public DerivationStrategyBase CreateDerivationStrategy(ExtPubKey pubKey, bool p2sh) - { - pubKey = pubKey ?? new ExtKey().Neuter(); - string suffix = SupportSegwit() ? "" : "-[legacy]"; - suffix += p2sh ? "-[p2sh]" : ""; - return new DerivationStrategyFactory(this.Network).Parse($"{pubKey.ToString(this.Network)}{suffix}"); - } - - public bool RPCSupportSegwit - { - get; set; - } = true; - - public bool RPCStringAmount - { - get; set; - } = true; - - public bool SupportSegwit() - { - return RPCSupportSegwit && Network.Consensus.SupportSegwit; - } - - public uint256 SendToAddress(BitcoinAddress address, Money amount) - { - return SendToAddressAsync(address, amount).GetAwaiter().GetResult(); - } - - public async Task SendToAddressAsync(BitcoinAddress address, Money amount) - { - List parameters = new List(); - parameters.Add(address.ToString()); - if(RPCStringAmount) - parameters.Add(amount.ToString()); - else - parameters.Add(amount.ToDecimal(MoneyUnit.BTC)); - var resp = await RPC.SendCommandAsync(RPCOperations.sendtoaddress, parameters.ToArray()); - return uint256.Parse(resp.Result.ToString()); - } - } + public partial class ServerTester : IDisposable + { + private readonly string _Directory; + + public static ServerTester Create([CallerMemberNameAttribute]string caller = null) + { + return new ServerTester(caller); + } + + public void Dispose() + { + if (Host != null) + { + Host.Dispose(); + Host = null; + } + if (NodeBuilder != null) + { + NodeBuilder.Dispose(); + NodeBuilder = null; + } + } + + NodeDownloadData nodeDownloadData; + + public string CryptoCode + { + get; set; + } + + public ServerTester(string directory) + { + SetEnvironment(); + try + { + var rootTestData = "TestData"; + directory = Path.Combine(rootTestData, directory); + _Directory = directory; + if (!Directory.Exists(rootTestData)) + Directory.CreateDirectory(rootTestData); + + var cryptoSettings = new NBXplorerNetworkProvider(NetworkType.Regtest).GetFromCryptoCode(CryptoCode); + NodeBuilder = NodeBuilder.Create(nodeDownloadData, Network, directory); + + + User1 = NodeBuilder.CreateNode(); + User2 = NodeBuilder.CreateNode(); + Explorer = NodeBuilder.CreateNode(); + foreach (var node in NodeBuilder.Nodes) + { + node.WhiteBind = true; + node.CookieAuth = cryptoSettings.SupportCookieAuthentication; + } + NodeBuilder.StartAll(); + + User1.CreateRPCClient().Generate(1); + User1.Sync(Explorer, true); + Explorer.CreateRPCClient().Generate(1); + Explorer.Sync(User2, true); + User2.CreateRPCClient().EnsureGenerate(Network.Consensus.CoinbaseMaturity + 1); + User1.Sync(User2, true); + + var port = CustomServer.FreeTcpPort(); + var datadir = Path.Combine(directory, "explorer"); + DeleteRecursivelyWithMagicDust(datadir); + List<(string key, string value)> keyValues = new List<(string key, string value)>(); + keyValues.Add(("conf", Path.Combine(directory, "explorer", "settings.config"))); + keyValues.Add(("datadir", datadir)); + keyValues.Add(("port", port.ToString())); + keyValues.Add(("network", "regtest")); + keyValues.Add(("chains", CryptoCode.ToLowerInvariant())); + keyValues.Add(("verbose", "1")); + keyValues.Add(($"{CryptoCode.ToLowerInvariant()}rpcauth", Explorer.GetRPCAuth())); + keyValues.Add(($"{CryptoCode.ToLowerInvariant()}rpcurl", Explorer.CreateRPCClient().Address.AbsoluteUri)); + keyValues.Add(("cachechain", "0")); + keyValues.Add(("rpcnotest", "1")); + keyValues.Add(("mingapsize", "2")); + keyValues.Add(("maxgapsize", "4")); + keyValues.Add(($"{CryptoCode.ToLowerInvariant()}startheight", Explorer.CreateRPCClient().GetBlockCount().ToString())); + keyValues.Add(($"{CryptoCode.ToLowerInvariant()}nodeendpoint", $"{Explorer.Endpoint.Address}:{Explorer.Endpoint.Port}")); + keyValues.Add(("asbcnstr", AzureServiceBusTestConfig.ConnectionString)); + keyValues.Add(("asbblockq", AzureServiceBusTestConfig.NewBlockQueue)); + keyValues.Add(("asbtranq", AzureServiceBusTestConfig.NewTransactionQueue)); + keyValues.Add(("asbblockt", AzureServiceBusTestConfig.NewBlockTopic)); + keyValues.Add(("asbtrant", AzureServiceBusTestConfig.NewTransactionTopic)); + keyValues.Add(("AWSSQSBlockQueueURL", AWSSQSTestConfig.AWSSQSBlockQueueURL)); + keyValues.Add(("AWSSQSTransactionQueueURL", AWSSQSTestConfig.AWSSQSTransactionQueueURL)); + + + + + var args = keyValues.SelectMany(kv => new[] { $"--{kv.key}", kv.value }).ToArray(); + Host = new WebHostBuilder() + .UseConfiguration(new DefaultConfiguration().CreateConfiguration(args)) + .UseKestrel() + .ConfigureLogging(l => + { + l.SetMinimumLevel(LogLevel.Information) + .AddFilter("Microsoft", LogLevel.Error) + .AddFilter("Hangfire", LogLevel.Error) + .AddFilter("NBXplorer.Authentication.BasicAuthenticationHandler", LogLevel.Critical) + .AddProvider(Logs.LogProvider); + }) + .UseStartup() + .Build(); + + RPC = ((RPCClientProvider)Host.Services.GetService(typeof(RPCClientProvider))).GetRPCClient(CryptoCode); + var nbxnetwork = ((NBXplorerNetworkProvider)Host.Services.GetService(typeof(NBXplorerNetworkProvider))).GetFromCryptoCode(CryptoCode); + Network = nbxnetwork.NBitcoinNetwork; + var conf = (ExplorerConfiguration)Host.Services.GetService(typeof(ExplorerConfiguration)); + Host.Start(); + + _Client = new ExplorerClient(nbxnetwork, Address); + _Client.SetCookieAuth(Path.Combine(conf.DataDir, ".cookie")); + } + catch + { + Dispose(); + throw; + } + } + + private NetworkCredential ExtractCredentials(string config) + { + var user = Regex.Match(config, "rpcuser=([^\r\n]*)"); + var pass = Regex.Match(config, "rpcpassword=([^\r\n]*)"); + return new NetworkCredential(user.Groups[1].Value, pass.Groups[1].Value); + } + + public Uri Address + { + get + { + + var address = ((KestrelServer)(Host.Services.GetService(typeof(IServer)))).Features.Get().Addresses.FirstOrDefault(); + return new Uri(address); + } + } + + ExplorerClient _Client; + public ExplorerClient Client + { + get + { + return _Client; + } + } + + public CoreNode Explorer + { + get; set; + } + + public CoreNode User1 + { + get; set; + } + + public CoreNode User2 + { + get; set; + } + + public NodeBuilder NodeBuilder + { + get; set; + } + + + public IWebHost Host + { + get; set; + } + + public string BaseDirectory + { + get + { + return _Directory; + } + } + + public RPCClient RPC + { + get; set; + } + + public Network Network + { + get; + internal set; + } + + private static bool TryDelete(string directory, bool throws) + { + try + { + DeleteRecursivelyWithMagicDust(directory); + return true; + } + catch (DirectoryNotFoundException) + { + return true; + } + catch (Exception) + { + if (throws) + throw; + } + return false; + } + + // http://stackoverflow.com/a/14933880/2061103 + public static void DeleteRecursivelyWithMagicDust(string destinationDir) + { + const int magicDust = 10; + for (var gnomes = 1; gnomes <= magicDust; gnomes++) + { + try + { + Directory.Delete(destinationDir, true); + } + catch (DirectoryNotFoundException) + { + return; // good! + } + catch (IOException) + { + if (gnomes == magicDust) + throw; + // System.IO.IOException: The directory is not empty + System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes); + + // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic + Thread.Sleep(100 * gnomes); + continue; + } + catch (UnauthorizedAccessException) + { + if (gnomes == magicDust) + throw; + // Wait, maybe another software make us authorized a little later + System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes); + + // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic + Thread.Sleep(100); + continue; + } + return; + } + // depending on your use case, consider throwing an exception here + } + + static void Copy(string sourceDirectory, string targetDirectory) + { + DirectoryInfo diSource = new DirectoryInfo(sourceDirectory); + DirectoryInfo diTarget = new DirectoryInfo(targetDirectory); + + CopyAll(diSource, diTarget); + } + + static void CopyAll(DirectoryInfo source, DirectoryInfo target) + { + Directory.CreateDirectory(target.FullName); + + // Copy each file into the new directory. + foreach (FileInfo fi in source.GetFiles()) + { + fi.CopyTo(Path.Combine(target.FullName, fi.Name), true); + } + + // Copy each subdirectory using recursion. + foreach (DirectoryInfo diSourceSubDir in source.GetDirectories()) + { + DirectoryInfo nextTargetSubDir = + target.CreateSubdirectory(diSourceSubDir.Name); + CopyAll(diSourceSubDir, nextTargetSubDir); + } + } + + public BitcoinSecret PrivateKeyOf(BitcoinExtKey key, string path) + { + return new BitcoinSecret(key.ExtKey.Derive(new KeyPath(path)).PrivateKey, Network); + } + + public BitcoinAddress AddressOf(BitcoinExtKey key, string path) + { + if (SupportSegwit()) + return key.ExtKey.Derive(new KeyPath(path)).Neuter().PubKey.WitHash.GetAddress(Network); + else + return key.ExtKey.Derive(new KeyPath(path)).Neuter().PubKey.Hash.GetAddress(Network); + } + + public BitcoinAddress AddressOf(DerivationStrategyBase scheme, string path) + { + return scheme.Derive(KeyPath.Parse(path)).ScriptPubKey.GetDestinationAddress(Network); + } + + public DirectDerivationStrategy CreateDerivationStrategy(ExtPubKey pubKey = null) + { + return (DirectDerivationStrategy)CreateDerivationStrategy(pubKey, false); + } + public DerivationStrategyBase CreateDerivationStrategy(ExtPubKey pubKey, bool p2sh) + { + pubKey = pubKey ?? new ExtKey().Neuter(); + string suffix = SupportSegwit() ? "" : "-[legacy]"; + suffix += p2sh ? "-[p2sh]" : ""; + return new DerivationStrategyFactory(this.Network).Parse($"{pubKey.ToString(this.Network)}{suffix}"); + } + + public bool RPCSupportSegwit + { + get; set; + } = true; + + public bool RPCStringAmount + { + get; set; + } = true; + + public bool SupportSegwit() + { + return RPCSupportSegwit && Network.Consensus.SupportSegwit; + } + + public uint256 SendToAddress(BitcoinAddress address, Money amount) + { + return SendToAddressAsync(address, amount).GetAwaiter().GetResult(); + } + + public async Task SendToAddressAsync(BitcoinAddress address, Money amount) + { + List parameters = new List(); + parameters.Add(address.ToString()); + if (RPCStringAmount) + parameters.Add(amount.ToString()); + else + parameters.Add(amount.ToDecimal(MoneyUnit.BTC)); + var resp = await RPC.SendCommandAsync(RPCOperations.sendtoaddress, parameters.ToArray()); + return uint256.Parse(resp.Result.ToString()); + } + } } diff --git a/NBXplorer.Tests/TestConfig.cs b/NBXplorer.Tests/TestConfig.cs index a27355f14..d87ef9b91 100644 --- a/NBXplorer.Tests/TestConfig.cs +++ b/NBXplorer.Tests/TestConfig.cs @@ -1,62 +1,84 @@ namespace NBXplorer.Tests { - public static class AzureServiceBusTestConfig - { - public static string ConnectionString - { - get - { - //Put your service bus connection string here - requires READ / WRITE permissions - return ""; - } - } - - public static string NewBlockQueue - { - get - { - return "newblock"; - } - } - public static string NewBlockTopic - { - get - { - return "newbitcoinblock"; - } - } - - public static string NewBlockSubscription - { - get - { - return "NewBlock"; - } - } - - public static string NewTransactionQueue - { - get - { - return "newtransaction"; - } - } - - public static string NewTransactionTopic - { - get - { - return "newbitcointransaction"; - } - } - - public static string NewTransactionSubscription - { - get - { - return "NewTransaction"; - } - } - - } + public static class AzureServiceBusTestConfig + { + public static string ConnectionString + { + get + { + //Put your service bus connection string here - requires READ / WRITE permissions + return ""; + } + } + + public static string NewBlockQueue + { + get + { + return "newblock"; + } + } + public static string NewBlockTopic + { + get + { + return "newbitcoinblock"; + } + } + + public static string NewBlockSubscription + { + get + { + return "NewBlock"; + } + } + + public static string NewTransactionQueue + { + get + { + return "newtransaction"; + } + } + + public static string NewTransactionTopic + { + get + { + return "newbitcointransaction"; + } + } + + public static string NewTransactionSubscription + { + get + { + return "NewTransaction"; + } + } + + } + + public static class AWSSQSTestConfig + { + + public static string AWSSQSBlockQueueURL + { + get + { + return ""; + } + + } + public static string AWSSQSTransactionQueueURL + { + get + { + return ""; + } + + } + + } } diff --git a/NBXplorer.Tests/UnitTest1.cs b/NBXplorer.Tests/UnitTest1.cs index c96b66db8..927b535ca 100644 --- a/NBXplorer.Tests/UnitTest1.cs +++ b/NBXplorer.Tests/UnitTest1.cs @@ -1,5 +1,6 @@ using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus.Core; +using Amazon.SQS; using NBitcoin; using NBitcoin.Protocol; using NBitcoin.RPC; @@ -20,1498 +21,1565 @@ namespace NBXplorer.Tests { - public class UnitTest1 - { - public UnitTest1(ITestOutputHelper helper) - { - Logs.Tester = new XUnitLog(helper) { Name = "Tests" }; - Logs.LogProvider = new XUnitLogProvider(helper); - } - - [Fact] - public void RepositoryCanTrackAddresses() - { - using(var tester = RepositoryTester.Create(true)) - { - var dummy = new DirectDerivationStrategy(new ExtKey().Neuter().GetWif(Network.RegTest)) { Segwit = false }; - RepositoryCanTrackAddressesCore(tester, dummy); - } - } - - [Fact] - public void CanSerializeKeyPathFast() - { - using(var tester = RepositoryTester.Create(true)) - { - var dummy = new DirectDerivationStrategy(new ExtKey().Neuter().GetWif(Network.RegTest)) { Segwit = false }; - var seria = new Serializer(Network.RegTest); - var keyInfo = new KeyPathInformation() { - TrackedSource = new DerivationSchemeTrackedSource(dummy), - DerivationStrategy = dummy, Feature = DerivationFeature.Change, KeyPath = new KeyPath("0/1"), Redeem = Script.Empty, ScriptPubKey = Script.Empty }; - var str = seria.ToString(keyInfo); - for(int i = 0; i < 1500; i++) - { - seria.ToObject(str); - } - } - } - - private static void RepositoryCanTrackAddressesCore(RepositoryTester tester, DerivationStrategyBase dummy) - { - Assert.Equal(2, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit, 2).Result); - var keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(0).ScriptPubKey); - Assert.NotNull(keyInfo); - Assert.Equal(new KeyPath("0/0"), keyInfo.KeyPath); - Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(1).ScriptPubKey); - Assert.NotNull(keyInfo); - Assert.Equal(new KeyPath("0/1"), keyInfo.KeyPath); - Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(2).ScriptPubKey); - Assert.Null(keyInfo); - Assert.Equal(28, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit).Result); - Assert.Equal(30, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Change).Result); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(29).ScriptPubKey); - Assert.NotNull(keyInfo); - Assert.Equal(new KeyPath("0/29"), keyInfo.KeyPath); - Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); - - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(29).ScriptPubKey); - Assert.NotNull(keyInfo); - Assert.Equal(new KeyPath("1/29"), keyInfo.KeyPath); - Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); - Assert.Null(keyInfo); - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(30).ScriptPubKey); - Assert.Null(keyInfo); - - MarkAsUsed(tester.Repository, dummy, new KeyPath("1/5")); - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(25).ScriptPubKey); - Assert.NotNull(keyInfo); - Assert.Equal(new KeyPath("1/25"), keyInfo.KeyPath); - Assert.Equal(keyInfo.DerivationStrategy, dummy); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(36).ScriptPubKey); - Assert.Null(keyInfo); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); - Assert.Null(keyInfo); - - for(int i = 0; i < 10; i++) - { - Assert.Equal(0, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit).Result); - MarkAsUsed(tester.Repository, dummy, new KeyPath("1/" + i)); - } - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); - Assert.Null(keyInfo); - MarkAsUsed(tester.Repository, dummy, new KeyPath("1/10")); - Assert.Equal(11, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit).Result); - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); - Assert.NotNull(keyInfo); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(39).ScriptPubKey); - Assert.NotNull(keyInfo); - - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(41).ScriptPubKey); - Assert.Null(keyInfo); - - //No op - MarkAsUsed(tester.Repository, dummy, new KeyPath("1/6")); - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(29).ScriptPubKey); - Assert.NotNull(keyInfo); - Assert.Equal(new KeyPath("1/29"), keyInfo.KeyPath); - Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); - keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(30).ScriptPubKey); - Assert.Null(keyInfo); - } - - private static void MarkAsUsed(Repository repository, DerivationStrategyBase strat, KeyPath keyPath) - { - repository.SaveMatches(DateTimeOffset.UtcNow, - new[] { - new MatchedTransaction() - { - Match = new TransactionMatch() - { - TrackedSource = new DerivationSchemeTrackedSource(strat), - DerivationStrategy = strat, - Transaction = repository.Network.NBitcoinNetwork.Consensus.ConsensusFactory.CreateTransaction(), - Outputs = new List - { - new KeyPathInformation() - { - TrackedSource = new DerivationSchemeTrackedSource(strat), - Feature = DerivationFeature.Deposit, - DerivationStrategy = strat, - KeyPath = keyPath - } - } - } - } - }); - } - - [Fact] - public void ShouldBlockIfNoChange() - { - using(var tester = ServerTester.Create()) - { - var bob = tester.CreateDerivationStrategy(); - var utxo = tester.Client.GetUTXOs(bob, null, false); - Stopwatch watch = new Stopwatch(); - watch.Start(); - var result = tester.Client.GetUTXOs(bob, utxo); - watch.Stop(); - Assert.True(watch.Elapsed > TimeSpan.FromSeconds(10)); - } - } - - [Fact] - public void CanEasilySpendUTXOs() - { - using(var tester = ServerTester.Create()) - { - var userExtKey = new ExtKey(); - var userDerivationScheme = tester.Client.Network.DerivationStrategyFactory.CreateDirectDerivationStrategy(userExtKey.Neuter(), new DerivationStrategyOptions() - { - // Use non-segwit - Legacy = true - }); - tester.Client.Track(userDerivationScheme); - var utxos = tester.Client.GetUTXOs(userDerivationScheme, null, false); - - // Send 1 BTC - var newAddress = tester.Client.GetUnused(userDerivationScheme, DerivationFeature.Direct); - tester.Explorer.CreateRPCClient().SendToAddress(newAddress.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); - utxos = tester.Client.GetUTXOs(userDerivationScheme, utxos, true); - - // Send 1 more BTC - newAddress = tester.Client.GetUnused(userDerivationScheme, DerivationFeature.Deposit); - tester.Explorer.CreateRPCClient().SendToAddress(newAddress.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); - utxos = tester.Client.GetUTXOs(userDerivationScheme, utxos, true); - - utxos = tester.Client.GetUTXOs(userDerivationScheme, null, false); - Assert.Equal(2, utxos.GetUnspentCoins().Length); - for(int i = 0; i < 3; i++) - { - var changeAddress = tester.Client.GetUnused(userDerivationScheme, DerivationFeature.Change); - var coins = utxos.GetUnspentCoins(); - var keys = utxos.GetKeys(userExtKey); - TransactionBuilder builder = new TransactionBuilder(); - builder.AddCoins(coins); - builder.AddKeys(keys); - builder.Send(new Key(), Money.Coins(0.5m)); - builder.SetChange(changeAddress.ScriptPubKey); - - var fallbackFeeRate = new FeeRate(Money.Satoshis(100), 1); - var feeRate = tester.Client.GetFeeRate(1, fallbackFeeRate).FeeRate; - - builder.SendEstimatedFees(feeRate); - builder.SetConsensusFactory(tester.Network); - var tx = builder.BuildTransaction(true); - Assert.True(builder.Verify(tx)); - Assert.True(tester.Client.Broadcast(tx).Success); - - utxos = tester.Client.GetUTXOs(userDerivationScheme, utxos, true); - utxos = tester.Client.GetUTXOs(userDerivationScheme, null, false); - - if(i == 0) - Assert.Equal(2, utxos.GetUnspentCoins().Length); - - Assert.Contains(utxos.GetUnspentCoins(), u => u.ScriptPubKey == changeAddress.ScriptPubKey); - Assert.Contains(utxos.Unconfirmed.UTXOs, u => u.ScriptPubKey == changeAddress.ScriptPubKey && u.Feature == DerivationFeature.Change); - } - } - } - - [Fact] - public void ShowRBFedTransaction() - { - using(var tester = ServerTester.Create()) - { - var bob = tester.CreateDerivationStrategy(); - tester.Client.Track(bob); - var utxo = tester.Client.GetUTXOs(bob, null, false); //Track things do not wait - var a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); - - var payment1 = Money.Coins(0.04m); - var payment2 = Money.Coins(0.08m); - - var tx1 = new uint256(tester.RPC.SendCommand("sendtoaddress", new object[] - { - a1.ScriptPubKey.GetDestinationAddress(tester.Network).ToString(), - payment1.ToString(), - null, //comment + public class UnitTest1 + { + public UnitTest1(ITestOutputHelper helper) + { + Logs.Tester = new XUnitLog(helper) { Name = "Tests" }; + Logs.LogProvider = new XUnitLogProvider(helper); + } + + [Fact] + public void RepositoryCanTrackAddresses() + { + using (var tester = RepositoryTester.Create(true)) + { + var dummy = new DirectDerivationStrategy(new ExtKey().Neuter().GetWif(Network.RegTest)) { Segwit = false }; + RepositoryCanTrackAddressesCore(tester, dummy); + } + } + + [Fact] + public void CanSerializeKeyPathFast() + { + using (var tester = RepositoryTester.Create(true)) + { + var dummy = new DirectDerivationStrategy(new ExtKey().Neuter().GetWif(Network.RegTest)) { Segwit = false }; + var seria = new Serializer(Network.RegTest); + var keyInfo = new KeyPathInformation() + { + TrackedSource = new DerivationSchemeTrackedSource(dummy), + DerivationStrategy = dummy, + Feature = DerivationFeature.Change, + KeyPath = new KeyPath("0/1"), + Redeem = Script.Empty, + ScriptPubKey = Script.Empty + }; + var str = seria.ToString(keyInfo); + for (int i = 0; i < 1500; i++) + { + seria.ToObject(str); + } + } + } + + private static void RepositoryCanTrackAddressesCore(RepositoryTester tester, DerivationStrategyBase dummy) + { + Assert.Equal(2, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit, 2).Result); + var keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(0).ScriptPubKey); + Assert.NotNull(keyInfo); + Assert.Equal(new KeyPath("0/0"), keyInfo.KeyPath); + Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(1).ScriptPubKey); + Assert.NotNull(keyInfo); + Assert.Equal(new KeyPath("0/1"), keyInfo.KeyPath); + Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(2).ScriptPubKey); + Assert.Null(keyInfo); + Assert.Equal(28, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit).Result); + Assert.Equal(30, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Change).Result); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(29).ScriptPubKey); + Assert.NotNull(keyInfo); + Assert.Equal(new KeyPath("0/29"), keyInfo.KeyPath); + Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); + + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(29).ScriptPubKey); + Assert.NotNull(keyInfo); + Assert.Equal(new KeyPath("1/29"), keyInfo.KeyPath); + Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); + Assert.Null(keyInfo); + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(30).ScriptPubKey); + Assert.Null(keyInfo); + + MarkAsUsed(tester.Repository, dummy, new KeyPath("1/5")); + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(25).ScriptPubKey); + Assert.NotNull(keyInfo); + Assert.Equal(new KeyPath("1/25"), keyInfo.KeyPath); + Assert.Equal(keyInfo.DerivationStrategy, dummy); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(36).ScriptPubKey); + Assert.Null(keyInfo); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); + Assert.Null(keyInfo); + + for (int i = 0; i < 10; i++) + { + Assert.Equal(0, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit).Result); + MarkAsUsed(tester.Repository, dummy, new KeyPath("1/" + i)); + } + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); + Assert.Null(keyInfo); + MarkAsUsed(tester.Repository, dummy, new KeyPath("1/10")); + Assert.Equal(11, tester.Repository.RefillAddressPoolIfNeeded(dummy, DerivationFeature.Deposit).Result); + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(30).ScriptPubKey); + Assert.NotNull(keyInfo); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(39).ScriptPubKey); + Assert.NotNull(keyInfo); + + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Deposit).Derive(41).ScriptPubKey); + Assert.Null(keyInfo); + + //No op + MarkAsUsed(tester.Repository, dummy, new KeyPath("1/6")); + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(29).ScriptPubKey); + Assert.NotNull(keyInfo); + Assert.Equal(new KeyPath("1/29"), keyInfo.KeyPath); + Assert.Equal(keyInfo.DerivationStrategy.ToString(), dummy.ToString()); + keyInfo = tester.Repository.GetKeyInformation(dummy.GetLineFor(DerivationFeature.Change).Derive(30).ScriptPubKey); + Assert.Null(keyInfo); + } + + private static void MarkAsUsed(Repository repository, DerivationStrategyBase strat, KeyPath keyPath) + { + repository.SaveMatches(DateTimeOffset.UtcNow, + new[] { + new MatchedTransaction() + { + Match = new TransactionMatch() + { + TrackedSource = new DerivationSchemeTrackedSource(strat), + DerivationStrategy = strat, + Transaction = repository.Network.NBitcoinNetwork.Consensus.ConsensusFactory.CreateTransaction(), + Outputs = new List + { + new KeyPathInformation() + { + TrackedSource = new DerivationSchemeTrackedSource(strat), + Feature = DerivationFeature.Deposit, + DerivationStrategy = strat, + KeyPath = keyPath + } + } + } + } + }); + } + + [Fact] + public void ShouldBlockIfNoChange() + { + using (var tester = ServerTester.Create()) + { + var bob = tester.CreateDerivationStrategy(); + var utxo = tester.Client.GetUTXOs(bob, null, false); + Stopwatch watch = new Stopwatch(); + watch.Start(); + var result = tester.Client.GetUTXOs(bob, utxo); + watch.Stop(); + Assert.True(watch.Elapsed > TimeSpan.FromSeconds(10)); + } + } + + [Fact] + public void CanEasilySpendUTXOs() + { + using (var tester = ServerTester.Create()) + { + var userExtKey = new ExtKey(); + var userDerivationScheme = tester.Client.Network.DerivationStrategyFactory.CreateDirectDerivationStrategy(userExtKey.Neuter(), new DerivationStrategyOptions() + { + // Use non-segwit + Legacy = true + }); + tester.Client.Track(userDerivationScheme); + var utxos = tester.Client.GetUTXOs(userDerivationScheme, null, false); + + // Send 1 BTC + var newAddress = tester.Client.GetUnused(userDerivationScheme, DerivationFeature.Direct); + tester.Explorer.CreateRPCClient().SendToAddress(newAddress.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); + utxos = tester.Client.GetUTXOs(userDerivationScheme, utxos, true); + + // Send 1 more BTC + newAddress = tester.Client.GetUnused(userDerivationScheme, DerivationFeature.Deposit); + tester.Explorer.CreateRPCClient().SendToAddress(newAddress.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); + utxos = tester.Client.GetUTXOs(userDerivationScheme, utxos, true); + + utxos = tester.Client.GetUTXOs(userDerivationScheme, null, false); + Assert.Equal(2, utxos.GetUnspentCoins().Length); + for (int i = 0; i < 3; i++) + { + var changeAddress = tester.Client.GetUnused(userDerivationScheme, DerivationFeature.Change); + var coins = utxos.GetUnspentCoins(); + var keys = utxos.GetKeys(userExtKey); + TransactionBuilder builder = new TransactionBuilder(); + builder.AddCoins(coins); + builder.AddKeys(keys); + builder.Send(new Key(), Money.Coins(0.5m)); + builder.SetChange(changeAddress.ScriptPubKey); + + var fallbackFeeRate = new FeeRate(Money.Satoshis(100), 1); + var feeRate = tester.Client.GetFeeRate(1, fallbackFeeRate).FeeRate; + + builder.SendEstimatedFees(feeRate); + builder.SetConsensusFactory(tester.Network); + var tx = builder.BuildTransaction(true); + Assert.True(builder.Verify(tx)); + Assert.True(tester.Client.Broadcast(tx).Success); + + utxos = tester.Client.GetUTXOs(userDerivationScheme, utxos, true); + utxos = tester.Client.GetUTXOs(userDerivationScheme, null, false); + + if (i == 0) + Assert.Equal(2, utxos.GetUnspentCoins().Length); + + Assert.Contains(utxos.GetUnspentCoins(), u => u.ScriptPubKey == changeAddress.ScriptPubKey); + Assert.Contains(utxos.Unconfirmed.UTXOs, u => u.ScriptPubKey == changeAddress.ScriptPubKey && u.Feature == DerivationFeature.Change); + } + } + } + + [Fact] + public void ShowRBFedTransaction() + { + using (var tester = ServerTester.Create()) + { + var bob = tester.CreateDerivationStrategy(); + tester.Client.Track(bob); + var utxo = tester.Client.GetUTXOs(bob, null, false); //Track things do not wait + var a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); + + var payment1 = Money.Coins(0.04m); + var payment2 = Money.Coins(0.08m); + + var tx1 = new uint256(tester.RPC.SendCommand("sendtoaddress", new object[] + { + a1.ScriptPubKey.GetDestinationAddress(tester.Network).ToString(), + payment1.ToString(), + null, //comment null, //comment_to false, //subtractfeefromamount true, //replaceable }).ResultString); - utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received - Assert.Equal(tx1, utxo.Unconfirmed.UTXOs[0].Outpoint.Hash); - - var tx = tester.RPC.GetRawTransaction(new uint256(tx1)); - foreach(var input in tx.Inputs) - { - input.ScriptSig = Script.Empty; //Strip signatures - } - var output = tx.Outputs.First(o => o.Value == payment1); - output.Value = payment2; - var change = tx.Outputs.First(o => o.Value != payment1); - change.Value -= (payment2 - payment1) * 2; //Add more fees - var replacement = tester.RPC.SignRawTransaction(tx); - - tester.RPC.SendRawTransaction(replacement); - - var prevUtxo = utxo; - utxo = tester.Client.GetUTXOs(bob, prevUtxo); //Wait tx received - Assert.Null(utxo.Unconfirmed.KnownBookmark); - Assert.Equal(replacement.GetHash(), utxo.Unconfirmed.UTXOs[0].Outpoint.Hash); - Assert.Single(utxo.Unconfirmed.UTXOs); - - var txs = tester.Client.GetTransactions(bob, null); - Assert.Single(txs.UnconfirmedTransactions.Transactions); - Assert.Equal(replacement.GetHash(), txs.UnconfirmedTransactions.Transactions[0].TransactionId); - Assert.Single(txs.ReplacedTransactions.Transactions); - Assert.Equal(tx1, txs.ReplacedTransactions.Transactions[0].TransactionId); - } - } - - [Fact] - public void CanGetUnusedAddresses() - { - using(var tester = ServerTester.Create()) - { - var bob = tester.CreateDerivationStrategy(); - var utxo = tester.Client.GetUTXOs(bob, null, false); //Track things do not wait - - var a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); - Assert.Null(a1); - tester.Client.Track(bob); - a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); - Assert.NotNull(a1); - Assert.Equal(a1.ScriptPubKey, tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0).ScriptPubKey); - Assert.Equal(a1.ScriptPubKey, bob.Derive(new KeyPath("0/0")).ScriptPubKey); - - var a2 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, skip: 1); - Assert.Equal(a2.ScriptPubKey, bob.Derive(new KeyPath("0/1")).ScriptPubKey); - - var a3 = tester.Client.GetUnused(bob, DerivationFeature.Change, skip: 0); - Assert.Equal(a3.ScriptPubKey, bob.Derive(new KeyPath("1/0")).ScriptPubKey); - - var a4 = tester.Client.GetUnused(bob, DerivationFeature.Direct, skip: 1); - Assert.Equal(a4.ScriptPubKey, bob.Derive(new KeyPath("1")).ScriptPubKey); - - Assert.Null(tester.Client.GetUnused(bob, DerivationFeature.Change, skip: 30)); - - a3 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, skip: 2); - Assert.Equal(new KeyPath("0/2"), a3.KeyPath); + utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received + Assert.Equal(tx1, utxo.Unconfirmed.UTXOs[0].Outpoint.Hash); + + var tx = tester.RPC.GetRawTransaction(new uint256(tx1)); + foreach (var input in tx.Inputs) + { + input.ScriptSig = Script.Empty; //Strip signatures + } + var output = tx.Outputs.First(o => o.Value == payment1); + output.Value = payment2; + var change = tx.Outputs.First(o => o.Value != payment1); + change.Value -= (payment2 - payment1) * 2; //Add more fees + var replacement = tester.RPC.SignRawTransaction(tx); + + tester.RPC.SendRawTransaction(replacement); + + var prevUtxo = utxo; + utxo = tester.Client.GetUTXOs(bob, prevUtxo); //Wait tx received + Assert.Null(utxo.Unconfirmed.KnownBookmark); + Assert.Equal(replacement.GetHash(), utxo.Unconfirmed.UTXOs[0].Outpoint.Hash); + Assert.Single(utxo.Unconfirmed.UTXOs); + + var txs = tester.Client.GetTransactions(bob, null); + Assert.Single(txs.UnconfirmedTransactions.Transactions); + Assert.Equal(replacement.GetHash(), txs.UnconfirmedTransactions.Transactions[0].TransactionId); + Assert.Single(txs.ReplacedTransactions.Transactions); + Assert.Equal(tx1, txs.ReplacedTransactions.Transactions[0].TransactionId); + } + } + + [Fact] + public void CanGetUnusedAddresses() + { + using (var tester = ServerTester.Create()) + { + var bob = tester.CreateDerivationStrategy(); + var utxo = tester.Client.GetUTXOs(bob, null, false); //Track things do not wait + + var a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); + Assert.Null(a1); + tester.Client.Track(bob); + a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); + Assert.NotNull(a1); + Assert.Equal(a1.ScriptPubKey, tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0).ScriptPubKey); + Assert.Equal(a1.ScriptPubKey, bob.Derive(new KeyPath("0/0")).ScriptPubKey); + + var a2 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, skip: 1); + Assert.Equal(a2.ScriptPubKey, bob.Derive(new KeyPath("0/1")).ScriptPubKey); + + var a3 = tester.Client.GetUnused(bob, DerivationFeature.Change, skip: 0); + Assert.Equal(a3.ScriptPubKey, bob.Derive(new KeyPath("1/0")).ScriptPubKey); + + var a4 = tester.Client.GetUnused(bob, DerivationFeature.Direct, skip: 1); + Assert.Equal(a4.ScriptPubKey, bob.Derive(new KeyPath("1")).ScriptPubKey); + + Assert.Null(tester.Client.GetUnused(bob, DerivationFeature.Change, skip: 30)); + + a3 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, skip: 2); + Assert.Equal(new KeyPath("0/2"), a3.KeyPath); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - // 0/0 and 0/2 used - tester.SendToAddressAsync(a1.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); - utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received + // 0/0 and 0/2 used + tester.SendToAddressAsync(a1.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); + utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received - tester.SendToAddressAsync(a3.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); - utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received + tester.SendToAddressAsync(a3.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); + utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received - tester.SendToAddressAsync(a4.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); - utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received + tester.SendToAddressAsync(a4.ScriptPubKey.GetDestinationAddress(tester.Network), Money.Coins(1.0m)); + utxo = tester.Client.GetUTXOs(bob, utxo); //Wait tx received #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); - Assert.Equal(a1.ScriptPubKey, bob.Derive(new KeyPath("0/1")).ScriptPubKey); - a2 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, skip: 1); - Assert.Equal(a2.ScriptPubKey, bob.Derive(new KeyPath("0/3")).ScriptPubKey); + a1 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, 0); + Assert.Equal(a1.ScriptPubKey, bob.Derive(new KeyPath("0/1")).ScriptPubKey); + a2 = tester.Client.GetUnused(bob, DerivationFeature.Deposit, skip: 1); + Assert.Equal(a2.ScriptPubKey, bob.Derive(new KeyPath("0/3")).ScriptPubKey); + + a4 = tester.Client.GetUnused(bob, DerivationFeature.Direct, skip: 1); + Assert.Equal(a4.ScriptPubKey, bob.Derive(new KeyPath("2")).ScriptPubKey); + + } + } + + [Fact] + public async Task CanSendAWSSQSNewBlockEventMessage() + { + Assert.False(string.IsNullOrWhiteSpace(AWSSQSTestConfig.AWSSQSBlockQueueURL), "Please set new block queue url in TestConfig.cs AWSSQSTestConfig class. "); + using (var tester = ServerTester.Create()) + { + tester.Client.WaitServerStarted(); + + var sqsClient = new AmazonSQSClient(); + + // Purge queue in order to delelet all messages from previous tests + await sqsClient.PurgeQueueAsync(AWSSQSTestConfig.AWSSQSBlockQueueURL); + + // The message deletation process takes about 60 seconds. + await Task.Delay(60000); + + var expectedBlockId = tester.Explorer.CreateRPCClient().Generate(1)[0]; + + var receiveMessageResponse = await sqsClient.ReceiveMessageAsync(AWSSQSTestConfig.AWSSQSBlockQueueURL); + + JsonSerializerSettings settings = new JsonSerializerSettings(); + new Serializer(Network.RegTest).ConfigureSerializer(settings); + + Assert.True(receiveMessageResponse.Messages.Count() > 0, $"No message received in AWS SQS new block queue : {AWSSQSTestConfig.AWSSQSBlockQueueURL}"); + var newBlockEvent = JsonConvert.DeserializeObject(receiveMessageResponse.Messages.FirstOrDefault().Body, settings); + Assert.Equal(expectedBlockId, newBlockEvent.Hash); + + } + } + + [Fact] + public async Task CanSendAWSSQSNewTransactionEventMessage() + { + Assert.False(string.IsNullOrWhiteSpace(AWSSQSTestConfig.AWSSQSTransactionQueueURL), "Please set new transaction queue url in TestConfig.cs AWSSQSTestConfig class. "); + using (var tester = ServerTester.Create()) + { + tester.Client.WaitServerStarted(); + var key = new BitcoinExtKey(new ExtKey(), tester.Network); + var pubkey = tester.CreateDerivationStrategy(key.Neuter(), true); + tester.Client.Track(pubkey); + + JsonSerializerSettings settings = new JsonSerializerSettings(); + new Serializer(Network.RegTest).ConfigureSerializer(settings); + + var sqsClient = new AmazonSQSClient(); + + // Purge queue in order to delelet all messages from previous tests + await sqsClient.PurgeQueueAsync(AWSSQSTestConfig.AWSSQSTransactionQueueURL); + // The message deletation process takes about 60 seconds. + await Task.Delay(60000); - a4 = tester.Client.GetUnused(bob, DerivationFeature.Direct, skip: 1); - Assert.Equal(a4.ScriptPubKey, bob.Derive(new KeyPath("2")).ScriptPubKey); + tester.Explorer.CreateRPCClient().SendToAddress(tester.AddressOf(pubkey, "0/1"), Money.Coins(1.0m)); - } - } + var receiveMessageResponse = await sqsClient.ReceiveMessageAsync(AWSSQSTestConfig.AWSSQSTransactionQueueURL); - CancellationToken Cancel => new CancellationTokenSource(5000).Token; + Assert.True(receiveMessageResponse.Messages.Count() > 0, $"No message received in AWS SQS new transaction queue : {AWSSQSTestConfig.AWSSQSTransactionQueueURL}"); + var newTransactionEvent = JsonConvert.DeserializeObject(receiveMessageResponse.Messages.FirstOrDefault().Body, settings); + Assert.Equal(newTransactionEvent.DerivationStrategy, pubkey); + } + } - [Fact] - public async Task CanSendAzureServiceBusNewBlockEventMessage() - { + CancellationToken Cancel => new CancellationTokenSource(5000).Token; - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.ConnectionString), "Please Set Azure Service Bus Connection string in TestConfig.cs AzureServiceBusTestConfig Class. "); + [Fact] + public async Task CanSendAzureServiceBusNewBlockEventMessage() + { - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewBlockQueue), "Please Set Azure Service Bus NewBlockQueue name in TestConfig.cs AzureServiceBusTestConfig Class. "); + Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.ConnectionString), "Please Set Azure Service Bus Connection string in TestConfig.cs AzureServiceBusTestConfig Class. "); - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewBlockTopic), "Please Set Azure Service Bus NewBlockTopic name in TestConfig.cs AzureServiceBusTestConfig Class. "); + Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewBlockQueue), "Please Set Azure Service Bus NewBlockQueue name in TestConfig.cs AzureServiceBusTestConfig Class. "); - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewBlockSubscription), "Please Set Azure Service Bus NewBlock Subscription name in TestConfig.cs AzureServiceBusTestConfig Class. "); + Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewBlockTopic), "Please Set Azure Service Bus NewBlockTopic name in TestConfig.cs AzureServiceBusTestConfig Class. "); + Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewBlockSubscription), "Please Set Azure Service Bus NewBlock Subscription name in TestConfig.cs AzureServiceBusTestConfig Class. "); - using(var tester = ServerTester.Create()) - { - tester.Client.WaitServerStarted(); - var key = new BitcoinExtKey(new ExtKey(), tester.Network); - var pubkey = tester.CreateDerivationStrategy(key.Neuter(), true); - tester.Client.Track(pubkey); - IQueueClient blockClient = new QueueClient(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockQueue); - ISubscriptionClient subscriptionClient = new SubscriptionClient(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockTopic, AzureServiceBusTestConfig.NewBlockSubscription); + using (var tester = ServerTester.Create()) + { + tester.Client.WaitServerStarted(); + var key = new BitcoinExtKey(new ExtKey(), tester.Network); + var pubkey = tester.CreateDerivationStrategy(key.Neuter(), true); + tester.Client.Track(pubkey); - //Configure Service Bus Subscription callback + IQueueClient blockClient = new QueueClient(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockQueue); + ISubscriptionClient subscriptionClient = new SubscriptionClient(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockTopic, AzureServiceBusTestConfig.NewBlockSubscription); - //We may have existing messages from other tests - push all message to a LIFO stack - var busMessages = new ConcurrentStack(); + //Configure Service Bus Subscription callback - var messageHandlerOptions = new MessageHandlerOptions((e) => - { - throw e.Exception; - }) - { - // Maximum number of Concurrent calls to the callback `ProcessMessagesAsync`, set to 1 for simplicity. - // Set it according to how many messages the application wants to process in parallel. - MaxConcurrentCalls = 1, + //We may have existing messages from other tests - push all message to a LIFO stack + var busMessages = new ConcurrentStack(); - // Indicates whether MessagePump should automatically complete the messages after returning from User Callback. - // False below indicates the Complete will be handled by the User Callback as in `ProcessMessagesAsync` below. - AutoComplete = false - }; + var messageHandlerOptions = new MessageHandlerOptions((e) => + { + throw e.Exception; + }) + { + // Maximum number of Concurrent calls to the callback `ProcessMessagesAsync`, set to 1 for simplicity. + // Set it according to how many messages the application wants to process in parallel. + MaxConcurrentCalls = 1, - //Service Bus Topic Message Handler - subscriptionClient.RegisterMessageHandler(async (m, t) => - { - busMessages.Push(m); - await subscriptionClient.CompleteAsync(m.SystemProperties.LockToken); - }, messageHandlerOptions); + // Indicates whether MessagePump should automatically complete the messages after returning from User Callback. + // False below indicates the Complete will be handled by the User Callback as in `ProcessMessagesAsync` below. + AutoComplete = false + }; + //Service Bus Topic Message Handler + subscriptionClient.RegisterMessageHandler(async (m, t) => + { + busMessages.Push(m); + await subscriptionClient.CompleteAsync(m.SystemProperties.LockToken); + }, messageHandlerOptions); - //Test Service Bus Queue - //Retry 10 times - var retryPolicy = new RetryExponential(new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), 10); - var messageReceiver = new MessageReceiver(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockQueue, ReceiveMode.ReceiveAndDelete, retryPolicy); - Microsoft.Azure.ServiceBus.Message msg = null; - - //Clear any existing messages from queue - while(await messageReceiver.PeekAsync() != null) - { - // Batch the receive operation - var brokeredMessages = await messageReceiver.ReceiveAsync(300); - } - await messageReceiver.CloseAsync(); //Close queue , otherwise receiver will consume our test message + //Test Service Bus Queue + //Retry 10 times + var retryPolicy = new RetryExponential(new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), 10); - messageReceiver = new MessageReceiver(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockQueue, ReceiveMode.ReceiveAndDelete, retryPolicy); + var messageReceiver = new MessageReceiver(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockQueue, ReceiveMode.ReceiveAndDelete, retryPolicy); + Microsoft.Azure.ServiceBus.Message msg = null; + + //Clear any existing messages from queue + while (await messageReceiver.PeekAsync() != null) + { + // Batch the receive operation + var brokeredMessages = await messageReceiver.ReceiveAsync(300); + } + await messageReceiver.CloseAsync(); //Close queue , otherwise receiver will consume our test message + + messageReceiver = new MessageReceiver(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewBlockQueue, ReceiveMode.ReceiveAndDelete, retryPolicy); - //Create a new Block - AzureServiceBus broker will receive a message from EventAggregator and publish to queue - var expectedBlockId = tester.Explorer.CreateRPCClient().Generate(1)[0]; + //Create a new Block - AzureServiceBus broker will receive a message from EventAggregator and publish to queue + var expectedBlockId = tester.Explorer.CreateRPCClient().Generate(1)[0]; - msg = await messageReceiver.ReceiveAsync(); + msg = await messageReceiver.ReceiveAsync(); - JsonSerializerSettings settings = new JsonSerializerSettings(); - new Serializer(Network.RegTest).ConfigureSerializer(settings); + JsonSerializerSettings settings = new JsonSerializerSettings(); + new Serializer(Network.RegTest).ConfigureSerializer(settings); - Assert.True(msg != null, $"No message received on Azure Service Bus Block Queue : {AzureServiceBusTestConfig.NewBlockQueue} after 10 read attempts."); + Assert.True(msg != null, $"No message received on Azure Service Bus Block Queue : {AzureServiceBusTestConfig.NewBlockQueue} after 10 read attempts."); - Assert.Equal(msg.ContentType, typeof(NewBlockEvent).ToString()); + Assert.Equal(msg.ContentType, typeof(NewBlockEvent).ToString()); - var blockEventQ = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(msg.Body), settings); - Assert.IsType(blockEventQ); - Assert.Equal(expectedBlockId.ToString().ToUpperInvariant(), msg.MessageId.ToUpperInvariant()); - Assert.Equal(expectedBlockId, blockEventQ.Hash); - Assert.NotEqual(0, blockEventQ.Height); + var blockEventQ = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(msg.Body), settings); + Assert.IsType(blockEventQ); + Assert.Equal(expectedBlockId.ToString().ToUpperInvariant(), msg.MessageId.ToUpperInvariant()); + Assert.Equal(expectedBlockId, blockEventQ.Hash); + Assert.NotEqual(0, blockEventQ.Height); - await Task.Delay(1000); - Assert.True(busMessages.Count > 0, $"No message received on Azure Service Bus Block Topic : {AzureServiceBusTestConfig.NewBlockTopic}."); - Microsoft.Azure.ServiceBus.Message busMsg = null; - busMessages.TryPop(out busMsg); - var blockEventS = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(busMsg.Body), settings); - Assert.IsType(blockEventS); - Assert.Equal(expectedBlockId.ToString().ToUpperInvariant(), busMsg.MessageId.ToUpperInvariant()); - Assert.Equal(expectedBlockId, blockEventS.Hash); - Assert.NotEqual(0, blockEventS.Height); - } - } - - [Fact] - public async Task CanSendAzureServiceBusNewTransactionEventMessage() - { - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.ConnectionString), "Please Set Azure Service Bus Connection string in TestConfig.cs AzureServiceBusTestConfig Class."); - - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewTransactionQueue), "Please Set Azure Service Bus NewTransactionQueue name in TestConfig.cs AzureServiceBusTestConfig Class."); - - Assert.False(string.IsNullOrWhiteSpace(AzureServiceBusTestConfig.NewTransactionSubscription), "Please Set Azure Service Bus NewTransactionSubscription name in TestConfig.cs AzureServiceBusTestConfig Class."); - - - using(var tester = ServerTester.Create()) - { - tester.Client.WaitServerStarted(); - var key = new BitcoinExtKey(new ExtKey(), tester.Network); - var pubkey = tester.CreateDerivationStrategy(key.Neuter(), true); - tester.Client.Track(pubkey); - - IQueueClient tranClient = new QueueClient(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewTransactionQueue); - ISubscriptionClient subscriptionClient = new SubscriptionClient(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewTransactionTopic, AzureServiceBusTestConfig.NewTransactionSubscription); - - //Configure Service Bus Subscription callback - //We may have existing messages from other tests - push all message to a LIFO stack - var busMessages = new ConcurrentStack(); - - var messageHandlerOptions = new MessageHandlerOptions((e) => - { - throw e.Exception; - }) - { - // Maximum number of Concurrent calls to the callback `ProcessMessagesAsync`, set to 1 for simplicity. - // Set it according to how many messages the application wants to process in parallel. - MaxConcurrentCalls = 1, - - // Indicates whether MessagePump should automatically complete the messages after returning from User Callback. - // False below indicates the Complete will be handled by the User Callback as in `ProcessMessagesAsync` below. - AutoComplete = false - }; - - //Service Bus Topic Message Handler - subscriptionClient.RegisterMessageHandler(async (m, t) => - { - busMessages.Push(m); - await subscriptionClient.CompleteAsync(m.SystemProperties.LockToken); - }, messageHandlerOptions); - - //Configure JSON custom serialization - JsonSerializerSettings settings = new JsonSerializerSettings(); - new Serializer(Network.RegTest).ConfigureSerializer(settings); - - //Test Service Bus Queue - //Retry 10 times - var retryPolicy = new RetryExponential(new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), 10); - - //Setup Message Receiver and clear queue - var messageReceiver = new MessageReceiver(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewTransactionQueue, ReceiveMode.ReceiveAndDelete, retryPolicy); - while(await messageReceiver.PeekAsync() != null) - { - // Batch the receive operation - var brokeredMessages = await messageReceiver.ReceiveAsync(300); - } - await messageReceiver.CloseAsync(); - - - //New message receiver to listen to our test event - messageReceiver = new MessageReceiver(AzureServiceBusTestConfig.ConnectionString, AzureServiceBusTestConfig.NewTransactionQueue, ReceiveMode.ReceiveAndDelete, retryPolicy); - - //Create a new UTXO for our tracked key - tester.Explorer.CreateRPCClient().SendToAddress(tester.AddressOf(pubkey, "0/1"), Money.Coins(1.0m)); - - //Check Queue Message - Microsoft.Azure.ServiceBus.Message msg = null; - msg = await messageReceiver.ReceiveAsync(); - - Assert.True(msg != null, $"No message received on Azure Service Bus Transaction Queue : {AzureServiceBusTestConfig.NewTransactionQueue} after 10 read attempts."); - - var txEventQ = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(msg.Body), settings); - Assert.Equal(txEventQ.DerivationStrategy, pubkey); - - await Task.Delay(1000); - Assert.True(busMessages.Count > 0, $"No message received on Azure Service Bus Transaction Topic : {AzureServiceBusTestConfig.NewTransactionTopic}."); - //Check Service Bus Topic Payload - - Microsoft.Azure.ServiceBus.Message busMsg = null; - busMessages.TryPop(out busMsg); - var blockEventS = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(busMsg.Body), settings); - Assert.IsType(blockEventS); - Assert.Equal(blockEventS.DerivationStrategy, pubkey); - - } - } - [Fact] - public void CanUseWebSockets() - { - using(var tester = ServerTester.Create()) - { - tester.Client.WaitServerStarted(); - var key = new BitcoinExtKey(new ExtKey(), tester.Network); - var pubkey = tester.CreateDerivationStrategy(key.Neuter(), true); - tester.Client.Track(pubkey); - using(var connected = tester.Client.CreateNotificationSession()) - { - connected.ListenNewBlock(); - var expectedBlockId = tester.Explorer.CreateRPCClient().Generate(1)[0]; - var blockEvent = (Models.NewBlockEvent)connected.NextEvent(Cancel); - Assert.Equal(expectedBlockId, blockEvent.Hash); - Assert.NotEqual(0, blockEvent.Height); - - connected.ListenDerivationSchemes(new[] { pubkey }); - tester.Explorer.CreateRPCClient().SendToAddress(tester.AddressOf(pubkey, "0/1"), Money.Coins(1.0m)); - - var txEvent = (Models.NewTransactionEvent)connected.NextEvent(Cancel); - Assert.Equal(txEvent.DerivationStrategy, pubkey); - } - - using(var connected = tester.Client.CreateNotificationSession()) - { - connected.ListenAllDerivationSchemes(); - tester.Explorer.CreateRPCClient().SendToAddress(tester.AddressOf(pubkey, "0/1"), Money.Coins(1.0m)); - - var txEvent = (Models.NewTransactionEvent)connected.NextEvent(Cancel); - Assert.Equal(txEvent.DerivationStrategy, pubkey); - } - - using (var connected = tester.Client.CreateNotificationSession()) - { - connected.ListenAllTrackedSource(); - tester.Explorer.CreateRPCClient().SendToAddress(tester.AddressOf(pubkey, "0/1"), Money.Coins(1.0m)); - - var txEvent = (Models.NewTransactionEvent)connected.NextEvent(Cancel); - Assert.Equal(txEvent.DerivationStrategy, pubkey); - } - } - } - - [Fact] - public void CanUseWebSockets2() - { - using(var tester = ServerTester.Create()) - { - tester.Client.WaitServerStarted(); - var key = new BitcoinExtKey(new ExtKey(), tester.Network); - var pubkey = tester.CreateDerivationStrategy(key.Neuter(), false); - - var pubkey2 = tester.CreateDerivationStrategy(key.Neuter(), true); - - tester.Client.Track(pubkey); - tester.Client.Track(pubkey2); - using(var connected = tester.Client.CreateNotificationSession()) - { - connected.ListenAllDerivationSchemes(); - tester.Explorer.CreateRPCClient().SendCommand(RPCOperations.sendmany, "", - JObject.Parse($"{{ \"{tester.AddressOf(pubkey, "0/1")}\": \"0.9\", \"{tester.AddressOf(pubkey, "1/1")}\": \"0.5\"," + - $"\"{tester.AddressOf(pubkey2, "0/2")}\": \"0.9\", \"{tester.AddressOf(pubkey2, "1/2")}\": \"0.5\" }}")); - - var schemes = new[] { pubkey.ToString(), pubkey2.ToString() }.ToList(); - - int expectedOutput = tester.SupportSegwit() ? 2 : 4; // if does not support segwit pubkey == pubkey2 - var txEvent = (Models.NewTransactionEvent)connected.NextEvent(Cancel); - Assert.Equal(expectedOutput, txEvent.Outputs.Count); - Assert.Contains(txEvent.DerivationStrategy.ToString(), schemes); - schemes.Remove(txEvent.DerivationStrategy.ToString()); - - if(!tester.SupportSegwit()) - return; - - txEvent = (Models.NewTransactionEvent)connected.NextEvent(Cancel); - Assert.Equal(2, txEvent.Outputs.Count); - Assert.Contains(txEvent.DerivationStrategy.ToString(), new[] { pubkey.ToString(), pubkey2.ToString() }); - } - } - } - - - [Fact] - public void CanTrack4() - { - using(var tester = ServerTester.Create()) - { - var bob = new BitcoinExtKey(new ExtKey(), tester.Network); - var alice = new BitcoinExtKey(new ExtKey(), tester.Network); - - var bobPubKey = tester.CreateDerivationStrategy(bob.Neuter()); - var alicePubKey = tester.CreateDerivationStrategy(alice.Neuter()); - - tester.Client.Track(alicePubKey); - var utxoAlice = tester.Client.GetUTXOs(alicePubKey, Bookmark.Start, Bookmark.Start, true); //Track things do not wait - tester.Client.Track(bobPubKey); - var utxoBob = tester.Client.GetUTXOs(bobPubKey, null, false); //Track things do not wait - Assert.NotNull(utxoAlice.Confirmed.KnownBookmark); - Assert.NotNull(utxoAlice.Unconfirmed.KnownBookmark); - - var id = tester.SendToAddress(tester.AddressOf(alice, "0/1"), Money.Coins(1.0m)); - id = tester.SendToAddress(tester.AddressOf(bob, "0/2"), Money.Coins(0.1m)); - utxoAlice = tester.Client.GetUTXOs(alicePubKey, utxoAlice); - utxoBob = tester.Client.GetUTXOs(bobPubKey, utxoBob); - Assert.NotNull(utxoAlice.Unconfirmed.KnownBookmark); - - tester.RPC.EnsureGenerate(1); - - utxoAlice = tester.Client.GetUTXOs(alicePubKey, utxoAlice); - utxoBob = tester.Client.GetUTXOs(bobPubKey, utxoBob); - Assert.NotNull(utxoAlice.Confirmed.KnownBookmark); - - LockTestCoins(tester.RPC); - tester.RPC.ImportPrivKey(tester.PrivateKeyOf(alice, "0/1")); - tester.SendToAddress(tester.AddressOf(bob, "0/3"), Money.Coins(0.6m)); - - utxoAlice = tester.Client.GetUTXOs(alicePubKey, utxoAlice); - utxoBob = tester.Client.GetUTXOs(bobPubKey, utxoBob); - Assert.NotNull(utxoAlice.Unconfirmed.KnownBookmark); - - utxoAlice = tester.Client.GetUTXOs(alicePubKey, utxoAlice, false); - Assert.NotNull(utxoAlice.Unconfirmed.KnownBookmark); - - tester.RPC.EnsureGenerate(1); - - utxoAlice = tester.Client.GetUTXOs(alicePubKey, utxoAlice); - utxoBob = tester.Client.GetUTXOs(bobPubKey, utxoBob); - - Assert.NotNull(utxoAlice.Confirmed.KnownBookmark); - Assert.Single(utxoAlice.Confirmed.SpentOutpoints); - Assert.Empty(utxoAlice.Confirmed.UTXOs); - - Assert.Empty(utxoBob.Confirmed.SpentOutpoints); - Assert.Single(utxoBob.Confirmed.UTXOs); - Assert.Equal("0/3", utxoBob.Confirmed.UTXOs[0].KeyPath.ToString()); - } - } - - [Fact] - public void CanTrack3() - { - using(var tester = ServerTester.Create()) - { - var key = new BitcoinExtKey(new ExtKey(), tester.Network); - var pubkey = tester.CreateDerivationStrategy(key.Neuter()); - tester.Client.Track(pubkey); - tester.Client.GetUTXOs(pubkey, null, false); //Track things do not wait - var events = tester.Client.CreateNotificationSession(); - events.ListenDerivationSchemes(new[] { pubkey }); - - var id = tester.SendToAddress(tester.AddressOf(key, "0/0"), Money.Coins(1.0m)); - id = tester.SendToAddress(tester.AddressOf(key, "0/1"), Money.Coins(1.1m)); - id = tester.SendToAddress(tester.AddressOf(key, "0/2"), Money.Coins(1.2m)); - id = tester.SendToAddress(tester.AddressOf(key, "0"), Money.Coins(1.2m)); - id = tester.SendToAddress(tester.AddressOf(key, "1"), Money.Coins(1.2m)); - - events.NextEvent(Timeout); - events.NextEvent(Timeout); - events.NextEvent(Timeout); - events.NextEvent(Timeout); - events.NextEvent(Timeout); - var utxo = tester.Client.GetUTXOs(pubkey, null); - - tester.RPC.EnsureGenerate(1); - - var prev = utxo; - utxo = tester.Client.GetUTXOs(pubkey, prev); - Assert.True(utxo.HasChanges); - Assert.Equal(5, utxo.Confirmed.UTXOs.Count); - utxo = tester.Client.GetUTXOs(pubkey, utxo, false); - Assert.False(utxo.HasChanges); - } - } - - [Fact] - public void CanTrackSeveralTransactions() - { - using(var tester = ServerTester.Create()) - { - var key = new BitcoinExtKey(new ExtKey(), tester.Network); - var pubkey = tester.CreateDerivationStrategy(key.Neuter()); - tester.Client.Track(pubkey); - var utxo = tester.Client.GetUTXOs(pubkey, null, false); //Track things do not wait - - var addresses = new HashSet